diff options
69 files changed, 34376 insertions, 32423 deletions
@@ -1,4 +1,8 @@ 2006-01-08 Karl Runge <[email protected]> + * x11vnc: the big split. (and -afteraccept and -passwdfile read:..) + * examples/pnmshow24.c: fix typo. + +2006-01-08 Karl Runge <[email protected]> * libvncclient/vncviewer.c: fix non-jpeg/libz builds. * examples/pnmshow24.c: fix non-ALLOW24BPP builds. * libvncserver/main.c: fix 'static int' defn. diff --git a/examples/pnmshow24.c b/examples/pnmshow24.c index b44dc3e..2dec1c1 100644 --- a/examples/pnmshow24.c +++ b/examples/pnmshow24.c @@ -4,7 +4,8 @@ #ifndef LIBVNCSERVER_ALLOW24BPP int main() { - printf("I need the ALLOW24BPP LibVNCSever flag to work\n"); + printf("I need the ALLOW24BPP LibVNCServer flag to work\n"); + exit(1); } #else diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog index 143987e..af81d27 100644 --- a/x11vnc/ChangeLog +++ b/x11vnc/ChangeLog @@ -1,3 +1,6 @@ +2006-01-08 Karl Runge <[email protected]> + * x11vnc: the big split. opts: -afteraccept and -passwdfile read: + 2005-12-24 Karl Runge <[email protected]> * x11vnc: enhance -passwdfile features, filetransfer on by default, call rfbRegisterTightVNCFileTransferExtension() earlier. diff --git a/x11vnc/Makefile.am b/x11vnc/Makefile.am index 5deccea..6c8c977 100644 --- a/x11vnc/Makefile.am +++ b/x11vnc/Makefile.am @@ -13,7 +13,7 @@ endif if HAVE_X bin_PROGRAMS=x11vnc -x11vnc_SOURCES=x11vnc.c tkx11vnc.h +x11vnc_SOURCES = cleanup.c connections.c cursor.c gui.c help.c inet.c keyboard.c options.c pointer.c rates.c remote.c scan.c screen.c selection.c solid.c user.c userinput.c util.c win_utils.c x11vnc.c x11vnc_defs.c xdamage.c xevents.c xinerama.c xkb_bell.c xrandr.c xrecord.c xwrappers.c allowed_input_t.h blackout_t.h cleanup.h connections.h cursor.h enums.h gui.h help.h inet.h keyboard.h options.h params.h pointer.h rates.h remote.h scan.h screen.h scrollevent_t.h selection.h solid.h tkx11vnc.h user.h userinput.h util.h win_utils.h winattr_t.h x11vnc.h xdamage.h xevents.h xinerama.h xkb_bell.h xrandr.h xrecord.h xwrappers.h INCLUDES=@X_CFLAGS@ x11vnc_LDADD=@X_LIBS@ $(LD_CYGIPC) $(LDADD) endif diff --git a/x11vnc/README b/x11vnc/README index c63ef6a..960994e 100644 --- a/x11vnc/README +++ b/x11vnc/README @@ -1,5 +1,5 @@ -x11vnc README file Date: Sat Dec 24 10:14:27 EST 2005 +x11vnc README file Date: Sun Jan 8 17:44:25 EST 2006 The following information is taken from these URLs: @@ -2347,6 +2347,31 @@ exit 1 # reject it x11vnc -display :0 -forever -gone 'kdesktop_lock' x11vnc -display :0 -forever -gone 'xlock &' + Here is a scheme using the [269]-afteraccept option (in version 0.7.3) + to unlock the screen after the first valid VNC login and to lock the + screen after the last valid VNC login disconnects: + x11vnc -display :0 -forever -shared -afteraccept ./myxlocker -gone ./myxlocke +r + + Where the script ./myxlocker is: +#!/bin/sh + +#/usr/bin/env | grep RFB_ | sort # for viewing RFB_* settings. + +if [ "X$RFB_MODE" = "Xafteraccept" ]; then + if [ "X$RFB_STATE" = "XNORMAL" ]; then # require valid login + if [ "X$RFB_CLIENT_COUNT" = "X1" ]; then + killall xlock # Linux only. + fi + fi +elif [ "X$RFB_MODE" = "Xgone" ]; then + if [ "X$RFB_STATE" = "XNORMAL" ]; then # require valid login + if [ "X$RFB_CLIENT_COUNT" = "X0" ]; then + xlock & + fi + fi +fi + [Display Managers and Services] @@ -2361,11 +2386,11 @@ exit 1 # reject it need to have sufficient permissions to connect to the X display. Here are some ideas: - * Use the description under "Continuously" in the [269]FAQ on x11vnc + * Use the description under "Continuously" in the [270]FAQ on x11vnc and Display Managers - * Use the description in the [270]FAQ on x11vnc and inetd(1) + * Use the description in the [271]FAQ on x11vnc and inetd(1) * Start x11vnc from your $HOME/.xsession (or $HOME/.xinitrc) - * Although less reliable, see the [271]x11vnc_loop rc.local hack + * Although less reliable, see the [272]x11vnc_loop rc.local hack below. The display manager scheme will not be specific to which user has the @@ -2396,7 +2421,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 [272]-auth option sets the XAUTHORITY variable for you). + (the [273]-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 @@ -2421,7 +2446,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 - [273]the example at the end of this FAQ). Then restart dtlogin, e.g.: + [274]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 @@ -2484,7 +2509,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 [274]full details + killed immediately after the user logs in. Here are [275]full details on how to configure gdm _________________________________________________________________ @@ -2526,13 +2551,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: [275]x11vnc_loop It will need some local + file like rc.local: [276]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 - [276]this FAQ. + [277]this FAQ. Q-42: Can I run x11vnc out of inetd(1)? How about xinetd(1)? @@ -2542,7 +2567,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 [277]-inetd + where the shell script /usr/local/bin/x11vnc_sh uses the [278]-inetd option and looks something like (you'll need to customize to your settings). #!/bin/sh @@ -2555,7 +2580,7 @@ rever -bg and that confuses it greatly, causing it to abort). 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 [278]-quiet) option: "/usr/local/bin/x11vnc -q + specify the -q (aka [279]-quiet) option: "/usr/local/bin/x11vnc -q -inetd ...". When you supply both -q and -inet and no "-o logfile" then stderr will automatically be closed (to prevent, e.g. library stderr messages leaking out to the viewer). The recommended practice @@ -2563,7 +2588,7 @@ rever -bg script with "2>logfile" redirection because the errors and warnings printed out are very useful in troubleshooting problems. - Note also the need to set XAUTHORITY via [279]-auth to point to the + Note also the need to set XAUTHORITY via [280]-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 @@ -2631,7 +2656,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 [280]option: + that connect to it, run x11vnc with this [281]option: -httpdir /path/to/the/java/classes/dir (this directory will contain the files index.vnc and, for example, @@ -2650,7 +2675,7 @@ service x11vncservice then you can connect to that URL with any Java enabled browser. Feel free to customize the default index.vnc file in the classes directory. - As of May/2005 the [281]-http option will try to guess where the Java + As of May/2005 the [282]-http option will try to guess where the Java classes jar file is by looking a expected locations. Also note that if you wanted to, you could also start the Java viewer @@ -2664,7 +2689,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 [282]-connect option. To connect immediately + starts up x11vnc with the [283]-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 @@ -2672,7 +2697,7 @@ service x11vncservice hosts to connect to. To use the vncconnect(1) program (from the core VNC package at - www.realvnc.com) specify the [283]-vncconnect option to x11vnc (Note: + www.realvnc.com) specify the [284]-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 @@ -2716,7 +2741,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 - [284]-add_keysyms option to have keysyms added automatically. Also, to + [285]-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" @@ -2738,11 +2763,11 @@ xmodmap -e "add Control = Control_L Control_R" The main drawback to this method (besides requiring extra configuration and possibly root permission) is that it also does the - Linux Virtual Console/Terminal (VC/VT) [285]switching even though it + Linux Virtual Console/Terminal (VC/VT) [286]switching even though it does not need to (since it doesn't use a real framebuffer). There are some "dual headed" (actually multi-headed/multi-user) patches to the X server that turn off the VT usage in the X server. Update: As of - Jul/2005 we have an LD_PRELOAD script [286]Xdummy that allows you to + Jul/2005 we have an LD_PRELOAD script [287]Xdummy that allows you to use a stock (i.e. unpatched) Xorg or XFree86 server with the "dummy" driver and not have any VT switching problems! Currently Xdummy needs to be run as root, but with some luck that may be relaxed in the @@ -2774,7 +2799,7 @@ startx -- /path/to/Xdummy :1 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 [287]this FAQ) and access it from + display via x11vnc (e.g. see [288]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 @@ -2817,7 +2842,7 @@ startx -- /path/to/Xdummy :1 19/03/2004 10:10:58 error creating tile-row shm for len=4 19/03/2004 10:10:58 reverting to single_copytile mode - Here is a shell script [288]shm_clear to list and prompt for removal + Here is a shell script [289]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, @@ -2851,36 +2876,36 @@ ied) in /etc/system. See the next paragraph for more workarounds. To minimize the number of shm segments used by x11vnc try using the - [289]-onetile option (corresponds to only 3 shm segments used, and + [290]-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 - [290]-noshm option. Performance will be somewhat degraded but when + [291]-noshm option. Performance will be somewhat degraded but when done over local machine sockets it should be acceptable (see an - [291]earlier question discussing -noshm). + [292]earlier question discussing -noshm). Q-48: How can I make x11vnc use less system resources? - The [292]-nap and "[293]-wait n" (where n is the sleep between polls + The [293]-nap and "[294]-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. The ShadowFB will make x11vnc's screen polling less severe. Using the - [294]-onetile option will use less memory and use fewer shared memory - slots (add [295]-fs 1.0 for one less slot). + [295]-onetile option will use less memory and use fewer shared memory + slots (add [296]-fs 1.0 for one less slot). Q-49: How can I make x11vnc use MORE system resources? - You can try [296]-threads and dial down the wait time (e.g. -wait 1) - and possibly dial down [297]-defer as well. Note that if you try to + You can try [297]-threads and dial down the wait time (e.g. -wait 1) + and possibly dial down [298]-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 [298]-id option) can be streamed over a LAN or wireless at + the x11vnc [299]-id option) can be streamed over a LAN or wireless at a reasonable frame rate. @@ -2896,16 +2921,16 @@ 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 - [299]-solid [color] option to try to do this automatically. + [300]-solid [color] option to try to do this automatically. * Configure your window manager or desktop "theme" to not use fancy images, shading, and gradients for the window decorations, etc. Disable window animations, etc. Maybe your desktop has a "low bandwidth" theme you can easily switch into and out of. * Avoid small scrolls of large windows using the Arrow keys or scrollbar. Try to use PageUp/PageDown instead. (not so much of a - problem in x11vnc 0.7.2 if [300]-scrollcopyrect is active and + problem in x11vnc 0.7.2 if [301]-scrollcopyrect is active and detecting scrolls for the application). - * If the [301]-wireframe option is not available (earlier than + * If the [302]-wireframe option is not available (earlier than x11vnc 0.7.2 or you have disabled it via -nowireframe) then Disable Opaque Moves and Resizes in the window manager/desktop. * However if -wireframe is active (on by default in x11vnc 0.7.2) @@ -2925,7 +2950,7 @@ ied) noticed. VNC viewer parameters: - * Use a [302]TightVNC enabled viewer! (Actually, RealVNC 4.x viewer + * Use a [303]TightVNC enabled viewer! (Actually, RealVNC 4.x viewer with ZRLE encoding is not too bad either; some claim it is faster). * Make sure the tight (or zrle) encoding is being used (look at @@ -2948,28 +2973,28 @@ ied) file. x11vnc parameters: - * Try using [303]-nodragging (no screen updates when dragging mouse, + * Try using [304]-nodragging (no screen updates when dragging mouse, but sometimes you miss visual feedback) - * Make sure the [304]-wireframe option is active (it should be on by + * Make sure the [305]-wireframe option is active (it should be on by default) and you have Opaque Moves/Resizes Enabled in the window manager. - * Make sure the [305]-scrollcopyrect option is active (it should be + * Make sure the [306]-scrollcopyrect option is active (it should be on by default). This detects scrolls in many (but not all) applications an applies the CopyRect encoding for a big speedup. - * Set [306]-fs 1.0 (disables fullscreen updates) - * Try increasing [307]-wait or [308]-defer (reduces the maximum + * Set [307]-fs 1.0 (disables fullscreen updates) + * Try increasing [308]-wait or [309]-defer (reduces the maximum "frame rate", but won't help much for large screen changes) - * Try the [309]-progressive pixelheight mode with the block + * Try the [310]-progressive pixelheight mode with the block pixelheight 100 or so (delays sending vertical blocks since they may change while viewer is receiving earlier ones) - * If you just want to watch one (simple) window use [310]-id (cuts + * If you just want to watch one (simple) window use [311]-id (cuts down extraneous polling and updates, but can be buggy or insufficient) - * Set [311]-nosel (disables all clipboard selection exchange) - * Use [312]-nocursor and [313]-nocursorpos (repainting the remote + * Set [312]-nosel (disables all clipboard selection exchange) + * Use [313]-nocursor and [314]-nocursorpos (repainting the remote cursor position and shape takes resources and round trips) * On very slow links (e.g. <= 28.8) you may need to increase the - [314]-readtimeout n setting if it sometimes takes more than 20sec + [315]-readtimeout n setting if it sometimes takes more than 20sec to paint the full screen, etc. @@ -2991,7 +3016,7 @@ ied) Note that the DAMAGE extension does not speed up the actual reading of pixels from the video card framebuffer memory, by, say, mirroring them - in main memory. So reading the fb is still painfully [315]slow (e.g. + in main memory. So reading the fb is still painfully [316]slow (e.g. 5MB/sec), and so even using X DAMAGE when large changes occur on the screen the bulk of the time is still spent retrieving them. Not ideal, but use of the ShadowFB XFree86/Xorg option speeds up the reading @@ -3009,27 +3034,27 @@ 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 "[316]-xd_area A" option to adjust the size + skipped). You can use the "[317]-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 "[317]-xd_mem f" may also be of use in tuning the - algorithm. To disable using DAMAGE entirely use "[318]-noxdamage". + The option "[318]-xd_mem f" may also be of use in tuning the + algorithm. To disable using DAMAGE entirely use "[319]-noxdamage". Q-52: When I drag windows around with the mouse or scroll up and down things really bog down (unless I do the drag in a single, quick motion). Is there anything to do to improve things? - This problem is primarily due to [319]slow hardware read rates from + This problem is primarily due to [320]slow hardware read rates from video cards: as you scroll or move a large window around the screen changes are much too rapid for x11vnc to keep up them (it can usually only read the video card at about 5-10 MB/sec, so it can take a good fraction of a 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 - [320]-pointer_mode option for more info. The next bottleneck is + [321]-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 @@ -3039,26 +3064,26 @@ 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 "[321]-pointer_mode 1" option. + way via the "[322]-pointer_mode 1" option. - Also added was the [322]-nodragging option that disables all screen + Also added was the [323]-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 [323]-pointer_mode n option + As of Dec/2004 in the libvncserver CVS the [324]-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 [324]-threads option can improve + Also, in some circumstances the [325]-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). As of Apr/2005 in the libvncserver CVS two new options (see the - [325]wireframe FAQ and [326]scrollcopyrect FAQ below) provide schemes + [326]wireframe FAQ and [327]scrollcopyrect FAQ below) provide schemes to sweep this problem under the rug for window moves or resizes and for some (but not all) window scrolls. @@ -3074,8 +3099,8 @@ 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 [327]slow video - card read rates (see [328]here too). A displacement, even a small one, + of the intermediate steps. BTW the lurching is due to [328]slow video + card read rates (see [329]here too). 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. @@ -3083,7 +3108,7 @@ ied) for -wireframe to do any good. The mode is currently on by default because most people are inflicted - with the problem. It can be disabled with the [329]-nowireframe option + with the problem. It can be disabled with the [330]-nowireframe option (aka -nowf). 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 @@ -3128,13 +3153,13 @@ ied) * Maximum time to show a wireframe animation. * Minimum time between sending wireframe outlines. - See the [330]"-wireframe tweaks" option for more details. On a slow + See the [331]"-wireframe tweaks" option for more details. On a slow link, e.g. dialup modem, the parameters may be automatically adjusted for better response. CopyRect encoding: In addition to the above there is the - [331]"-wirecopyrect mode" option. It is also on by default. This + [332]"-wirecopyrect mode" option. It is also on by default. This instructs x11vnc to not only 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 the @@ -3182,7 +3207,7 @@ ied) requiring the image data to be transmitted over the network. For fast links the speedup is primarily due to x11vnc not having to read the scrolled framebuffer data from the X server (recall that reading from - the hardware framebuffer is [332]slow). + the hardware framebuffer is [333]slow). To do this x11vnc uses the RECORD X extension to snoop the X11 protocol between the X client with the focus window and the X server. @@ -3204,10 +3229,10 @@ ied) the X server display: if one falls too far behind it could become a mess... - The initial implementation of [333]-scrollcopyrect option is useful in + The initial implementation of [334]-scrollcopyrect option is useful in that it detects many scrolls and thus gives a much nicer working - environment (especially when combined with the [334]-wireframe - [335]-wirecopyrect [336]options, which are also on by default; and if + environment (especially when combined with the [335]-wireframe + [336]-wirecopyrect [337]options, which are also on by default; and if you are willing to enable the ShadowFB things are very fast). The fact that there aren't long delays or lurches during scrolling is the primary improvement. @@ -3240,10 +3265,10 @@ ied) One can tap the Alt_L key (Left "Alt" key) 3 times in a row to signal x11vnc to refresh the screen to all viewers. Your VNC-viewer may have its own screen refresh hot-key or button. See - also: [337]-fixscreen + also: [338]-fixscreen * Some applications, notably OpenOffice, do XCopyArea scrolls in weird ways that assume ancestor window clipping is taking place. - See the [338]-scr_skip option for ways to tweak this on a + See the [339]-scr_skip option for ways to tweak this on a per-application basis. * Selecting text while dragging the mouse may be slower, especially if the Button-down event happens near the window's edge. This is @@ -3260,7 +3285,7 @@ ied) because it fails to detect scrolls in it. Sometimes clicking inside the application window or selecting some text in it to force the focus helps. - * When using the [339]-scale option there will be a quick CopyRect + * When using the [340]-scale option there will be a quick CopyRect scroll, but it needs to be followed by a slower "cleanup" update. This is because for a fixed finite screen resolution (e.g. 75 dpi) scaling and copyrect-ing are not exactly independent. Scaling @@ -3273,7 +3298,7 @@ ied) If you find the -scrollcopyrect behavior too approximate or distracting you can go back to the standard polling-only update method - with the [340]-noscrollcopyrect (or -noscr for short). If you find + with the [341]-noscrollcopyrect (or -noscr for short). If you find some extremely bad and repeatable behavior for -scrollcopyrect please report a bug. @@ -3312,16 +3337,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 "[341]-cursor X" option that + A simple kludge is provided by the "[342]-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 "[342]-cursor some" option for + work for those cases. Also see the "[343]-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 [343]-overlay option is supplied. See [344]this FAQ + cursor when the [344]-overlay option is supplied. See [345]this FAQ for more info. Also as of Dec/2004 in the libvncserver CVS XFIXES X extension support @@ -3329,7 +3354,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 [345]Solaris 10. + distros and [346]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 @@ -3337,7 +3362,7 @@ ied) situations where the 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. [346]Details can be found here. + hidden alpha channel data under 32bpp. [347]Details can be found here. Q-56: When using XFIXES cursorshape mode, some of the cursors look @@ -3370,17 +3395,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 "[347]-alphacut n" option lets + course!) some tunable parameters. The "[348]-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 "[348]-alphafrac f" option tries to correct individual + 240. The "[349]-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 [349]-alpharemove that is useful for + Finally, there is an option [350]-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 @@ -3406,11 +3431,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 [350]-nocursorshape x11vnc option. + disabled for all clients with the [351]-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 - [351]-noalphablend option to disable this behavior (always approximate + [352]-noalphablend option to disable this behavior (always approximate transparent cursors with opaque RGB values). The CursorShapeUpdates VNC extension complicates matters because the @@ -3438,9 +3463,9 @@ ied) Q-58: 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 [352]tightvnc extension + This default takes advantage of a [353]tightvnc extension (CursorShapeUpdates) that allows specifying a cursor image shape for - the local VNC viewer. You may disable it with the [353]-nocursor + the local VNC viewer. You may disable it with the [354]-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 @@ -3454,18 +3479,18 @@ ied) clients (i.e. passive viewers can see the mouse cursor being moved around by another viewer)? - Use the [354]-cursorpos option when starting x11vnc. A VNC viewer must + Use the [355]-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 [355]-nocursorpos - and [356]-nocursorshape. + libvncserver CVS -cursorpos is the default. See also [356]-nocursorpos + and [357]-nocursorshape. Q-60: 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: [357]-buttonmap + You can remap the mouse buttons via something like: [358]-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. @@ -3473,7 +3498,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 [358]-debug_pointer option prints out much info for + Note that the [359]-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 @@ -3495,7 +3520,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 [359]-buttonmap but rather configuring the X server + consider not using [360]-buttonmap but rather configuring the X server to think it has a mouse with 5 buttons even though the physical mouse does not. (e.g. 'Option "ZAxisMapping" "4 5"'). @@ -3525,7 +3550,7 @@ ied) Q-61: How can I get my AltGr and Shift modifiers to work between keyboards for different languages? - The option [360]-modtweak should help here. It is a mode that monitors + The option [361]-modtweak should help here. 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. @@ -3534,17 +3559,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 [361]this FAQ + and ">" it. This key does not exist on the keyboard (see [362]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 [362]FAQ about the -xkb option for a more powerful method + Also see the [363]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 - [363]-debug_keyboard option prints out much info for every keystroke + [364]-debug_keyboard option prints out much info for every keystroke and so can be useful debugging things. @@ -3556,9 +3581,9 @@ ied) (e.g. pc105 in the XF86Config file when it should be something else, say pc104). - Short Cut: Try the [364]-xkb or [365]-sloppy_keys options and see if + Short Cut: Try the [365]-xkb or [366]-sloppy_keys options and see if that helps the situation. The discussion below is a bit outdated (e.g. - [366]-modtweak is now the default) but is useful reference for various + [367]-modtweak is now the default) but is useful reference for various tricks and so is kept. @@ -3601,17 +3626,17 @@ ied) -remap less-comma These are convenient in that they do not modify the actual X server - settings. The former ([367]-modtweak) is a mode that monitors the + settings. The former ([368]-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 ([368]-remap less-comma) is an immediate remapping of the + The latter ([369]-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 [369]FAQ about the -xkb option as a possible workaround + See also the [370]FAQ about the -xkb option as a possible workaround using the XKEYBOARD extension. - Note that the [370]-debug_keyboard option prints out much info for + Note that the [371]-debug_keyboard option prints out much info for every keystroke to aid debugging keyboard problems. @@ -3619,13 +3644,13 @@ ied) (i.e. an extra comma). This is likely because you press "Shift" then "<" but then released - the Shift key before releasing the "<". Because of a [371]keymapping + the Shift key before releasing the "<". Because of a [372]keymapping ambiguity the last event "< up" is interpreted as "," because that key unshifted is the comma. - This should not happen in [372]-xkb mode, because it works hard to + This should not happen in [373]-xkb mode, because it works hard to resolve the ambiguities. If you do not want to use -xkb, try the - option [373]-sloppy_keys to attempt a similar type of algorithm. + option [374]-sloppy_keys to attempt a similar type of algorithm. Q-64: I'm using an "international" keyboard (e.g. German "de", or @@ -3649,7 +3674,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 [374]-modtweak + This all worked fine with x11vnc running with the [375]-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 @@ -3667,7 +3692,7 @@ ied) * there is a new option -xkb to use the XKEYBOARD extension API to do the Modifier key tweaking. - The [375]-xkb option seems to fix all of the missing keys: "@", "<", + The [376]-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 @@ -3675,7 +3700,7 @@ ied) debugging output (send it along with any problems you report). Update: as of Jun/2005 x11vnc will try to automatically enable - [376]-xkb if it appears that would be beneficial (e.g. if it sees any + [377]-xkb if it appears that would be beneficial (e.g. if it sees any of "@", "<", ">", "[" and similar keys are mapped in a way that needs the -xkb to access them). To disable this automatic check use -noxkb. @@ -3690,7 +3715,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: - [377]-skip_keycodes 93 + [378]-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 @@ -3707,16 +3732,16 @@ 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 [378]-remap x11vnc option: + created using the [379]-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 - [379]-add_keysyms option in the next paragraph. - Update: for convenience "[380]-remap DEAD" does many of these + [380]-add_keysyms option in the next paragraph. + Update: for convenience "[381]-remap DEAD" does many of these mappings at once. - * To complement the above workaround using the [381]-remap, an - option [382]-add_keysyms was added. This option instructs x11vnc + * To complement the above workaround using the [382]-remap, an + option [383]-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 @@ -3735,7 +3760,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) [383]-norepeat x11vnc option. You will still + use the new (Jul/2004) [384]-norepeat x11vnc option. You will still have autorepeating because that is taken care of on your VNC viewer side. @@ -3759,7 +3784,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 [384]-norepeat (which has since Dec/2004 been + needed, or to use the [385]-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 @@ -3770,7 +3795,7 @@ ied) keystrokes!! Are you using x11vnc to log in to an X session? (as described in - [385]this FAQ) If so, x11vnc is starting before your session and it + [386]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 @@ -3794,7 +3819,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 "[386]-remap Super_R-Mode_switch" x11vnc option may + Something like "[387]-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)). @@ -3817,7 +3842,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 [387]-remap Alt_L-Meta_L to x11vnc may be sufficient for ones + the [388]-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 in Unix. @@ -3828,7 +3853,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 [388]-remap + have x11vnc do the remapping. This can be done via the [389]-remap option using the fake "keysyms" Button1, Button2, etc. as the "to" keys (i.e. the ones after the "-") @@ -3837,7 +3862,7 @@ ied) button "paste" because (using XFree86/Xorg Emulate3Buttons) you have to click both buttons on the touch pad at the same time. This remapping: - [389]-remap Super_R-Button2 + [390]-remap Super_R-Button2 maps the Super_R "flag" key press to the Button2 click, thereby making X pasting a bit easier. @@ -3867,7 +3892,7 @@ ied) There may also be scaling viewers out there (e.g. TightVNC or UltraVNC on Windows) that automatically shrink or expand the remote framebuffer to fit the local display. Especially for hand-held devices. See also - [390]this FAQ on x11vnc scaling. + [391]this FAQ on x11vnc scaling. Q-71: Does x11vnc support server-side framebuffer scaling? (E.g. to @@ -3875,7 +3900,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 "[391]-scale fraction" + per-client setting. To enable it use the "[392]-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. @@ -3896,7 +3921,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 - [392]applications. Since with integer scale factors the framebuffers + [393]applications. Since with integer scale factors the framebuffers become huge and scaling operations time consuming, be sure to use ":nb" for the fastest response. @@ -3922,12 +3947,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 ([393]-rfbport option, + different scalings listening on separate ports ([394]-rfbport option, etc.). BTW, whenever you run two or more x11vnc's on the same X display and - use the [394]GUI, then to avoid all of the x11vnc's simultaneously - answering the gui you will need to use something like [395]"-connect + use the [395]GUI, then to avoid all of the x11vnc's simultaneously + answering the gui you will need to use something like [396]"-connect file1 -gui ..." with different connect files for each x11vnc you want to control via the gui (or remote-control). The "-connect file1" usage gives separate communication channels between a x11vnc proces and the @@ -3936,7 +3961,7 @@ ied) Update: 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 [396]"-scale_cursor frac" option to set the cursor + want that, use the [397]"-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). @@ -3958,16 +3983,16 @@ ied) screen is not 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 [397]-blackout x11vnc option + may be distracting to the viewer. The [398]-blackout x11vnc option allows you to blacken-out rectangles by manually specifying their WxH+X+Y geometries. If your system has the libXinerama library, the - [398]-xinerama x11vnc option can be used to have it automatically + [399]-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 [399]-xwarppointer + of the large display. If this happens try using the [400]-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 @@ -3992,23 +4017,23 @@ 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 [400]FAQ to increase the limit. It - is probably also a good idea to run with the [401]-onetile option in + /etc/system as mentioned in another [401]FAQ to increase the limit. It + is probably also a good idea to run with the [402]-onetile option in this case (to limit each x11vnc to 3 shm segments), or even - [402]-noshm to use no shm segments. + [403]-noshm to use no shm segments. Q-74: 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 "[403]-clip + As of Mar/2005 in the libvncserver CVS x11vnc has the "[404]-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. One user used -clip to split up a - large [404]Xinerama screen into two more managable smaller screens. + large [405]Xinerama screen into two more managable smaller screens. This also works to view a sub-region of a single application window if - the [405]-id or [406]-sid options are used. The offset is measured + the [406]-id or [407]-sid options are used. The offset is measured from the upper left corner of the selected window. @@ -4017,7 +4042,7 @@ ied) crash. As of Dec/2004 in the libvncserver CVS x11vnc supports XRANDR. You - enable it with the [407]-xrandr option to make x11vnc monitor XRANDR + enable it with the [408]-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. @@ -4027,7 +4052,7 @@ ied) then the 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 [408]-padgeom option to make the region big + viewers you can try the [409]-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 @@ -4081,9 +4106,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 [409]this FAQ + desktop in a separate Virtual Console (e.g. VC 8) (see [410]this FAQ on VC's for background). Unfortunately, this Fullscreen VC is not an X - server. So x11vnc cannot access it (however, [410]see this for a + server. So x11vnc cannot access it (however, [411]see this for a possible partial workaround). x11vnc works fine with "Normal X application window" and "Quick-Switch mode" because these use X. @@ -4100,13 +4125,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 [411]is in the active VC. + as long as the VMWare X session [412]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 [412]-id windowid option. The + the VMWare menu buttons) by use of the [413]-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 @@ -4187,7 +4212,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 [413]X API, but you get + of Xvfb x11vnc can poll it much better via the [414]X API, but you get the idea. By default in -rawfb mode x11vnc will actually close any X display it @@ -4240,7 +4265,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 [414]active VC) one + view and interact with VC #2 (assuming it is the [415]active VC) one can run something like: x11vnc -rawfb map:/dev/fb0@1024x768x16 -pipeinput './vcinject.pl 2' @@ -4253,7 +4278,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 - [415]VMWare Fullscreen mode is actually viewable under -rawfb. But + [416]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... @@ -4285,10 +4310,10 @@ 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 [416]-nosel option. If you + the Clipboard/Selection exchanged use the [417]-nosel option. If you don't want the PRIMARY selection to be polled for changes use the - [417]-noprimary option. You can also fine-tune it a bit with the - [418]-seldir dir option. + [418]-noprimary option. You can also fine-tune it a bit with the + [419]-seldir dir option. You may need to watch out for desktop utilities such as KDE's "Klipper" that do odd things with the selection, clipboard, and @@ -4303,7 +4328,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 [419]-nobell option. If + If you don't want to hear the beeps use the [420]-nobell option. If you want to hear the audio from the remote applications, consider trying a redirector such as esd. @@ -4319,7 +4344,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 - [420]email! + [421]email! [PayPal] @@ -4593,158 +4618,159 @@ References 266. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-accept 267. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gone 268. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gone - 269. http://www.karlrunge.com/x11vnc/index.html#display-manager-continuously - 270. http://www.karlrunge.com/x11vnc/index.html#faq-inetd - 271. http://www.karlrunge.com/x11vnc/index.html#x11vnc_loop - 272. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth - 273. http://www.karlrunge.com/x11vnc/index.html#dtlogin_solaris - 274. http://www.jirka.org/gdm-documentation/x241.html - 275. http://www.karlrunge.com/x11vnc/x11vnc_loop - 276. http://www.karlrunge.com/x11vnc/index.html#faq-xterminal-xauth - 277. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-inetd - 278. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-q - 279. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth - 280. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-httpdir - 281. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-http - 282. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-connect - 283. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-vncconnect - 284. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms - 285. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc - 286. http://www.karlrunge.com/x11vnc/Xdummy - 287. http://www.karlrunge.com/x11vnc/index.html#display-manager-continuously - 288. http://www.karlrunge.com/x11vnc/shm_clear - 289. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile - 290. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm - 291. http://www.karlrunge.com/x11vnc/index.html#faq-noshm - 292. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nap - 293. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wait - 294. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile - 295. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fs - 296. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-threads - 297. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-defer - 298. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 299. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-solid - 300. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scrollcopyrect - 301. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe - 302. http://www.tightvnc.com/ - 303. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nodragging - 304. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe - 305. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scrollcopyrect - 306. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fs - 307. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wait - 308. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-defer - 309. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-progressive - 310. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 311. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nosel - 312. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursor - 313. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorpos - 314. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-readtimeout - 315. http://www.karlrunge.com/x11vnc/index.html#fb_read_slow - 316. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xd_area - 317. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xd_mem - 318. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noxdamage - 319. http://www.karlrunge.com/x11vnc/index.html#fb_read_slow - 320. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode + 269. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-afteraccept + 270. http://www.karlrunge.com/x11vnc/index.html#display-manager-continuously + 271. http://www.karlrunge.com/x11vnc/index.html#faq-inetd + 272. http://www.karlrunge.com/x11vnc/index.html#x11vnc_loop + 273. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth + 274. http://www.karlrunge.com/x11vnc/index.html#dtlogin_solaris + 275. http://www.jirka.org/gdm-documentation/x241.html + 276. http://www.karlrunge.com/x11vnc/x11vnc_loop + 277. http://www.karlrunge.com/x11vnc/index.html#faq-xterminal-xauth + 278. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-inetd + 279. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-q + 280. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-auth + 281. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-httpdir + 282. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-http + 283. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-connect + 284. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-vncconnect + 285. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms + 286. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc + 287. http://www.karlrunge.com/x11vnc/Xdummy + 288. http://www.karlrunge.com/x11vnc/index.html#display-manager-continuously + 289. http://www.karlrunge.com/x11vnc/shm_clear + 290. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile + 291. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm + 292. http://www.karlrunge.com/x11vnc/index.html#faq-noshm + 293. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nap + 294. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wait + 295. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile + 296. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fs + 297. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-threads + 298. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-defer + 299. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 300. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-solid + 301. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scrollcopyrect + 302. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe + 303. http://www.tightvnc.com/ + 304. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nodragging + 305. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe + 306. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scrollcopyrect + 307. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fs + 308. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wait + 309. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-defer + 310. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-progressive + 311. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 312. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nosel + 313. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursor + 314. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorpos + 315. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-readtimeout + 316. http://www.karlrunge.com/x11vnc/index.html#fb_read_slow + 317. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xd_area + 318. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xd_mem + 319. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noxdamage + 320. http://www.karlrunge.com/x11vnc/index.html#fb_read_slow 321. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode - 322. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nodragging - 323. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode - 324. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-threads - 325. http://www.karlrunge.com/x11vnc/index.html#faq-wireframe - 326. http://www.karlrunge.com/x11vnc/index.html#faq-scrollcopyrect - 327. http://www.karlrunge.com/x11vnc/index.html#faq-pointer-mode - 328. http://www.karlrunge.com/x11vnc/index.html#fb_read_slow - 329. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe + 322. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode + 323. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nodragging + 324. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-pointer_mode + 325. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-threads + 326. http://www.karlrunge.com/x11vnc/index.html#faq-wireframe + 327. http://www.karlrunge.com/x11vnc/index.html#faq-scrollcopyrect + 328. http://www.karlrunge.com/x11vnc/index.html#faq-pointer-mode + 329. http://www.karlrunge.com/x11vnc/index.html#fb_read_slow 330. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe 331. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe - 332. http://www.karlrunge.com/x11vnc/index.html#fb_read_slow - 333. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scrollcopyrect - 334. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe - 335. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wirecopyrect - 336. http://www.karlrunge.com/x11vnc/index.html#faq-wireframe - 337. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fixscreen - 338. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scr_skip - 339. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale - 340. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scrollcopyrect - 341. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursor + 332. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe + 333. http://www.karlrunge.com/x11vnc/index.html#fb_read_slow + 334. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scrollcopyrect + 335. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wireframe + 336. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-wirecopyrect + 337. http://www.karlrunge.com/x11vnc/index.html#faq-wireframe + 338. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-fixscreen + 339. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scr_skip + 340. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale + 341. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scrollcopyrect 342. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursor - 343. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay - 344. http://www.karlrunge.com/x11vnc/index.html#the-overlay-mode - 345. http://www.karlrunge.com/x11vnc/index.html#solaris10-build - 346. http://www.karlrunge.com/x11vnc/index.html#faq-xfixes-alpha-hacks - 347. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alphacut - 348. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alphafrac - 349. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alpharemove - 350. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorshape - 351. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noalphablend - 352. http://www.tightvnc.com/ - 353. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursor - 354. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursorpos - 355. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorpos - 356. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorshape - 357. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-buttonmap - 358. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_pointer - 359. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-buttonmap - 360. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak - 361. http://www.karlrunge.com/x11vnc/index.html#faq-greaterless - 362. http://www.karlrunge.com/x11vnc/index.html#faq-xkbmodtweak - 363. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_keyboard - 364. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb - 365. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-sloppy_keys - 366. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak + 343. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursor + 344. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-overlay + 345. http://www.karlrunge.com/x11vnc/index.html#the-overlay-mode + 346. http://www.karlrunge.com/x11vnc/index.html#solaris10-build + 347. http://www.karlrunge.com/x11vnc/index.html#faq-xfixes-alpha-hacks + 348. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alphacut + 349. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alphafrac + 350. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-alpharemove + 351. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorshape + 352. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noalphablend + 353. http://www.tightvnc.com/ + 354. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursor + 355. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-cursorpos + 356. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorpos + 357. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nocursorshape + 358. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-buttonmap + 359. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_pointer + 360. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-buttonmap + 361. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak + 362. http://www.karlrunge.com/x11vnc/index.html#faq-greaterless + 363. http://www.karlrunge.com/x11vnc/index.html#faq-xkbmodtweak + 364. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_keyboard + 365. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb + 366. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-sloppy_keys 367. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak - 368. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap - 369. http://www.karlrunge.com/x11vnc/index.html#faq-xkbmodtweak - 370. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_keyboard - 371. http://www.karlrunge.com/x11vnc/index.html#faq-greaterless - 372. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb - 373. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-sloppy_keys - 374. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak - 375. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb + 368. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak + 369. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 370. http://www.karlrunge.com/x11vnc/index.html#faq-xkbmodtweak + 371. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-debug_keyboard + 372. http://www.karlrunge.com/x11vnc/index.html#faq-greaterless + 373. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb + 374. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-sloppy_keys + 375. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-modtweak 376. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb - 377. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-skip_keycodes - 378. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap - 379. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms - 380. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 377. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xkb + 378. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-skip_keycodes + 379. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 380. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms 381. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap - 382. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms - 383. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-norepeat + 382. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 383. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-add_keysyms 384. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-norepeat - 385. http://www.karlrunge.com/x11vnc/index.html#faq-display-manager - 386. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 385. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-norepeat + 386. http://www.karlrunge.com/x11vnc/index.html#faq-display-manager 387. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap 388. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap 389. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap - 390. http://www.karlrunge.com/x11vnc/index.html#faq-scaling - 391. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale - 392. http://www.cus.cam.ac.uk/~ssb22/source/vnc-magnification.html - 393. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbport - 394. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gui - 395. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-connect - 396. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale_cursor - 397. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-blackout - 398. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xinerama - 399. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xwarppointer - 400. http://www.karlrunge.com/x11vnc/index.html#faq-solshm - 401. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile - 402. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm - 403. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-clip - 404. http://www.karlrunge.com/x11vnc/index.html#faq-xinerama - 405. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 390. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-remap + 391. http://www.karlrunge.com/x11vnc/index.html#faq-scaling + 392. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale + 393. http://www.cus.cam.ac.uk/~ssb22/source/vnc-magnification.html + 394. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-rfbport + 395. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-gui + 396. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-connect + 397. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-scale_cursor + 398. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-blackout + 399. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xinerama + 400. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xwarppointer + 401. http://www.karlrunge.com/x11vnc/index.html#faq-solshm + 402. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-onetile + 403. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noshm + 404. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-clip + 405. http://www.karlrunge.com/x11vnc/index.html#faq-xinerama 406. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 407. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xrandr - 408. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-padgeom - 409. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc - 410. http://www.karlrunge.com/x11vnc/index.html#faq-rawfb - 411. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc - 412. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id - 413. http://www.karlrunge.com/x11vnc/index.html#faq-xvfb - 414. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc - 415. http://www.karlrunge.com/x11vnc/index.html#faq-vmware - 416. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nosel - 417. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noprimary - 418. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-seldir - 419. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nobell - 420. mailto:[email protected] + 407. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 408. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-xrandr + 409. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-padgeom + 410. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc + 411. http://www.karlrunge.com/x11vnc/index.html#faq-rawfb + 412. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc + 413. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-id + 414. http://www.karlrunge.com/x11vnc/index.html#faq-xvfb + 415. http://www.karlrunge.com/x11vnc/index.html#faq-linuxvc + 416. http://www.karlrunge.com/x11vnc/index.html#faq-vmware + 417. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nosel + 418. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-noprimary + 419. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-seldir + 420. http://www.karlrunge.com/x11vnc/x11vnc_opts.html#opt-nobell + 421. mailto:[email protected] ======================================================================= @@ -4757,7 +4783,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.3 lastmod: 2005-12-24 +x11vnc: allow VNC connections to real X11 displays. 0.7.3 lastmod: 2006-01-08 x11vnc options: -display disp -auth file @@ -4777,65 +4803,65 @@ x11vnc options: -input string -viewpasswd string -passwdfile filename -nopw -storepasswd pass file -accept string - -gone string -users list - -noshm -flipbyteorder - -onetile -solid [color] - -blackout string -xinerama - -xtrap -xrandr [mode] - -padgeom WxH -o logfile - -flag file -rc filename - -norc -h, -help - -?, -opts -V, -version - -dbg -q - -bg -modtweak - -nomodtweak -xkb - -noxkb -skip_keycodes string - -sloppy_keys -skip_dups - -noskip_dups -add_keysyms - -noadd_keysyms -clear_mods - -clear_keys -remap string - -norepeat -repeat - -nofb -nobell - -nosel -noprimary - -seldir string -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 -scr_skip list - -scr_inc list -scr_keys list - -scr_term list -scr_keyrepeat lo-hi - -scr_parms string -fixscreen string - -debug_scroll -noxrecord - -grab_buster -nograb_buster - -debug_grabs -pointer_mode n - -input_skip n -speeds rd,bw,lat - -wmdt string -debug_pointer - -debug_keyboard -defer time - -wait time -wait_ui factor - -nowait_bog -slow_fb 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 - -debug_tiles -snapfb - -rawfb string -pipeinput cmd - -gui [gui-opts] -remote command - -query variable -QD variable - -sync -noremote - -yesremote -unsafe - -safer -privremote - -nocmds -deny_all - + -afteraccept string -gone string + -users list -noshm + -flipbyteorder -onetile + -solid [color] -blackout string + -xinerama -xtrap + -xrandr [mode] -padgeom WxH + -o logfile -flag file + -rc filename -norc + -h, -help -?, -opts + -V, -version -dbg + -q -bg + -modtweak -nomodtweak + -xkb -noxkb + -skip_keycodes string -sloppy_keys + -skip_dups -noskip_dups + -add_keysyms -noadd_keysyms + -clear_mods -clear_keys + -remap string -norepeat + -repeat -nofb + -nobell -nosel + -noprimary -seldir string + -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 + -scr_skip list -scr_inc list + -scr_keys list -scr_term list + -scr_keyrepeat lo-hi -scr_parms string + -fixscreen string -debug_scroll + -noxrecord -grab_buster + -nograb_buster -debug_grabs + -pointer_mode n -input_skip n + -speeds rd,bw,lat -wmdt string + -debug_pointer -debug_keyboard + -defer time -wait time + -wait_ui factor -nowait_bog + -slow_fb 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 -debug_tiles + -snapfb -rawfb string + -pipeinput cmd -gui [gui-opts] + -remote command -query variable + -QD variable -sync + -noremote -yesremote + -unsafe -safer + -privremote -nocmds + -deny_all libvncserver options: -rfbport port TCP port for RFB protocol @@ -4867,7 +4893,7 @@ libvncserver-tight-extension options: % x11vnc -help -x11vnc: allow VNC connections to real X11 displays. 0.7.3 lastmod: 2005-12-24 +x11vnc: allow VNC connections to real X11 displays. 0.7.3 lastmod: 2006-01-08 Typical usage is: @@ -5133,11 +5159,17 @@ Options: -passwdfile filename Specify the libvncserver password via the first line of the file "filename" (instead of via -passwd on the command line where others might see it via ps(1)). + See below for how to supply multiple passwords. If the filename is prefixed with "rm:" it will be - removed after being read. In general, the password file - should not be readable by untrusted users (BTW: neither - should the VNC -rfbauth file: it is NOT encrypted). + removed after being read. Perhaps this is useful in + limiting the readability of the file. In general, + the password file should not be readable by untrusted + users (BTW: neither should the VNC -rfbauth file: + it is NOT encrypted). + + If the filename is prefixed with "read:" it will + periodically be checked for changes and reread. Note that only the first 8 characters of a password are used. @@ -5157,9 +5189,10 @@ Options: viewonly access. For compatibility, as a special case if the file contains only two password lines the 2nd one is automatically taken as the viewonly password. - Otherwise the "__BEGIN_VIEWONLY__" token must be used - to have viewonly passwords. (tip: make it the 3rd and - last line to have 2 full-access passwords) + Otherwise the "__BEGIN_VIEWONLY__" token must be + used to have viewonly passwords. (tip: make the 3rd + and last line be "__BEGIN_VIEWONLY__" to have 2 + full-access passwords) -nopw Disable the big warning message when you use x11vnc without some sort of password. -storepasswd pass file Store password "pass" as the VNC password in the @@ -5189,7 +5222,13 @@ Options: of the tcp virtual circuit. The x11vnc process id will be in RFB_X11VNC_PID, a client id number in RFB_CLIENT_ID, and the number of other connected clients - in RFB_CLIENT_COUNT. RFB_MODE will be "accept" + in RFB_CLIENT_COUNT. RFB_MODE will be "accept". + RFB_STATE will be PROTOCOL_VERSION, SECURITY_TYPE, + AUTHENTICATION, INITIALISATION, NORMAL, or UNKNOWN + indicating up to which state the client has acheived. + RFB_LOGIN_VIEWONLY will be 0, 1, or -1 (unknown). + RFB_USERNAME, RFB_LOGIN_TIME, and RFB_CURRENT_TIME may + also be set. If "string" is "popup" then a builtin popup window is used. The popup will time out after 120 seconds, @@ -5228,6 +5267,12 @@ Options: clicking. All 3 of the popup keywords can be followed by +N+M to supply a position for the popup window. The default is to center the popup window. +-afteraccept string As -accept, except to run a user supplied command after + a client has been accepted and authenticated. RFB_MODE + will be set to "afteraccept" and the other RFB_* + variables are as in -accept. Unlike -accept, the + command return code is not interpreted by x11vnc. + Example: -afteraccept 'killall xlock &' -gone string As -accept, except to run a user supplied command when a client goes away (disconnects). RFB_MODE will be set to "gone" and the other RFB_* variables are as @@ -6455,6 +6500,7 @@ n disconnect, e.g. client_input:host:MB or client_input:0x2:K accept:cmd set -accept "cmd" (empty to disable). + afteraccept:cmd set -afteraccept (empty to disable). gone:cmd set -gone "cmd" (empty to disable). noshm enable -noshm mode. shm disable -noshm mode (i.e. use shm). @@ -6666,20 +6712,21 @@ n overlay_yescursor nooverlay_nocursor nooverlay_cursor nooverlay_yescursor overlay_nocursor visual scale scale_cursor viewonly noviewonly shared noshared - forever noforever once timeout deny lock nodeny unlock - connect allowonce allow localhost nolocalhost listen - lookup nolookup accept gone shm noshm flipbyteorder - 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 sloppy_keys - nosloppy_keys 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 seldir cursorshape - nocursorshape cursorpos nocursorpos cursor show_cursor - noshow_cursor nocursor arrow xfixes noxfixes xdamage - noxdamage xd_area xd_mem alphacut alphafrac alpharemove + forever noforever once timeout filexfer deny lock + nodeny unlock connect allowonce allow localhost + nolocalhost listen lookup nolookup accept afteraccept + gone shm noshm flipbyteorder 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 sloppy_keys nosloppy_keys + 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 seldir 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 diff --git a/x11vnc/allowed_input_t.h b/x11vnc/allowed_input_t.h new file mode 100644 index 0000000..f58b8cf --- /dev/null +++ b/x11vnc/allowed_input_t.h @@ -0,0 +1,12 @@ +#ifndef _X11VNC_ALLOWED_INPUT_T_H +#define _X11VNC_ALLOWED_INPUT_T_H + +/* -- allowed_input_t.h -- */ + +typedef struct allowed_input { + int keystroke; + int motion; + int button; +} allowed_input_t; + +#endif /* _X11VNC_ALLOWED_INPUT_T_H */ diff --git a/x11vnc/blackout_t.h b/x11vnc/blackout_t.h new file mode 100644 index 0000000..cac8bac --- /dev/null +++ b/x11vnc/blackout_t.h @@ -0,0 +1,17 @@ +#ifndef _X11VNC_BLACKOUT_T_H +#define _X11VNC_BLACKOUT_T_H + +/* -- blackout_t.h -- */ + +typedef struct bout { + int x1, y1, x2, y2; +} blackout_t; + +#define BO_MAX 32 +typedef struct tbout { + blackout_t bo[BO_MAX]; /* hardwired max rectangles. */ + int cover; + int count; +} tile_blackout_t; + +#endif /* _X11VNC_BLACKOUT_T_H */ diff --git a/x11vnc/cleanup.c b/x11vnc/cleanup.c new file mode 100644 index 0000000..f8c4ae4 --- /dev/null +++ b/x11vnc/cleanup.c @@ -0,0 +1,427 @@ +/* -- cleanup.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "xdamage.h" +#include "remote.h" +#include "keyboard.h" +#include "scan.h" +#include "gui.h" +#include "solid.h" + +/* + * Exiting and error handling routines + */ + +int trapped_xerror = 0; +int trapped_xioerror = 0; +int trapped_getimage_xerror = 0; +int trapped_record_xerror = 0; +XErrorEvent *trapped_xerror_event; + +/* XXX CHECK BEFORE RELEASE */ +int crash_debug = 0; + +void clean_shm(int quick); +void clean_up_exit (int ret); +int trap_xerror(Display *d, XErrorEvent *error); +int trap_xioerror(Display *d); +int trap_getimage_xerror(Display *d, XErrorEvent *error); +char *xerror_string(XErrorEvent *error); +void initialize_crash_handler(void); +void initialize_signals(void); +int known_sigpipe_mode(char *s); + + +static int exit_flag = 0; +static int exit_sig = 0; + +static void clean_icon_mode(void); +static int Xerror(Display *d, XErrorEvent *error); +static int XIOerr(Display *d); +static void crash_shell_help(void); +static void crash_shell(void); +static void interrupted (int sig); + + +void clean_shm(int quick) { + int i, cnt = 0; + + if (raw_fb) quick = 1; /* raw_fb hack */ + + /* + * to avoid deadlock, etc, under quick=1 we just delete the shm + * areas and leave the X stuff hanging. + */ + if (quick) { + shm_delete(&scanline_shm); + shm_delete(&fullscreen_shm); + shm_delete(&snaprect_shm); + } else { + shm_clean(&scanline_shm, scanline); + shm_clean(&fullscreen_shm, fullscreen); + shm_clean(&snaprect_shm, snaprect); + } + + /* + * Here we have to clean up quite a few shm areas for all + * the possible tile row runs (40 for 1280), not as robust + * as one might like... sometimes need to run ipcrm(1). + */ + for(i=1; i<=ntiles_x; i++) { + if (i > tile_shm_count) { + break; + } + if (quick) { + shm_delete(&tile_row_shm[i]); + } else { + shm_clean(&tile_row_shm[i], tile_row[i]); + } + cnt++; + if (single_copytile_count && i >= single_copytile_count) { + break; + } + } + if (!quiet) { + rfbLog("deleted %d tile_row polling images.\n", cnt); + } +} + +static void clean_icon_mode(void) { + if (icon_mode && icon_mode_fh) { + fprintf(icon_mode_fh, "quit\n"); + fflush(icon_mode_fh); + fclose(icon_mode_fh); + icon_mode_fh = NULL; + if (icon_mode_file) { + unlink(icon_mode_file); + icon_mode_file = NULL; + } + } +} + +/* + * Normal exiting + */ +void clean_up_exit (int ret) { + exit_flag = 1; + + if (icon_mode) { + clean_icon_mode(); + } + + /* remove the shm areas: */ + clean_shm(0); + + if (! dpy) exit(ret); /* raw_rb hack */ + + /* X keyboard cleanups */ + delete_added_keycodes(0); + + if (clear_mods == 1) { + clear_modifiers(0); + } else if (clear_mods == 2) { + clear_keys(); + } + + if (no_autorepeat) { + autorepeat(1, 0); + } + if (use_solid_bg) { + solid_bg(1); + } + X_LOCK; + XTestDiscard_wr(dpy); +#if LIBVNCSERVER_HAVE_LIBXDAMAGE + if (xdamage) { + XDamageDestroy(dpy, xdamage); + } +#endif +#if LIBVNCSERVER_HAVE_LIBXTRAP + if (trap_ctx) { + XEFreeTC(trap_ctx); + } +#endif + /* XXX rdpy_ctrl, etc. cannot close w/o blocking */ + XCloseDisplay(dpy); + X_UNLOCK; + + fflush(stderr); + exit(ret); +} + +/* X11 error handlers */ + +static XErrorHandler Xerror_def; +static XIOErrorHandler XIOerr_def; + +int trap_xerror(Display *d, XErrorEvent *error) { + trapped_xerror = 1; + trapped_xerror_event = error; + + if (d) {} /* unused vars warning: */ + + return 0; +} + +int trap_xioerror(Display *d) { + trapped_xioerror = 1; + + if (d) {} /* unused vars warning: */ + + return 0; +} + +int trap_getimage_xerror(Display *d, XErrorEvent *error) { + trapped_getimage_xerror = 1; + trapped_xerror_event = error; + + if (d) {} /* unused vars warning: */ + + return 0; +} + +static int Xerror(Display *d, XErrorEvent *error) { + X_UNLOCK; + interrupted(0); + + if (d) {} /* unused vars warning: */ + + return (*Xerror_def)(d, error); +} + +static int XIOerr(Display *d) { + X_UNLOCK; + interrupted(-1); + + if (d) {} /* unused vars warning: */ + + return (*XIOerr_def)(d); +} + +static char *xerrors[] = { + "Success", + "BadRequest", + "BadValue", + "BadWindow", + "BadPixmap", + "BadAtom", + "BadCursor", + "BadFont", + "BadMatch", + "BadDrawable", + "BadAccess", + "BadAlloc", + "BadColor", + "BadGC", + "BadIDChoice", + "BadName", + "BadLength", + "BadImplementation", + "unknown" +}; +static int xerrors_max = BadImplementation; + +char *xerror_string(XErrorEvent *error) { + int index = -1; + if (error) { + index = (int) error->error_code; + } + if (0 <= index && index <= xerrors_max) { + return xerrors[index]; + } else { + return xerrors[xerrors_max+1]; + } +} + +static char *crash_stack_command1 = NULL; +static char *crash_stack_command2 = NULL; +static char *crash_debug_command = NULL; + +void initialize_crash_handler(void) { + int pid = program_pid; + crash_stack_command1 = (char *) malloc(1000); + crash_stack_command2 = (char *) malloc(1000); + crash_debug_command = (char *) 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); +} + +static 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"); +} + +static 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 + */ +static void interrupted (int sig) { + exit_sig = sig; + if (exit_flag) { + exit_flag++; + if (use_threads) { + usleep2(250 * 1000); + } else if (exit_flag <= 2) { + return; + } + exit(4); + } + exit_flag++; + if (sig == 0) { + fprintf(stderr, "caught X11 error:\n"); + } else if (sig == -1) { + fprintf(stderr, "caught XIO error:\n"); + } else { + fprintf(stderr, "caught signal: %d\n", sig); + } + if (sig == SIGINT) { + shut_down = 1; + return; + } + + X_UNLOCK; + + if (icon_mode) { + clean_icon_mode(); + } + /* remove the shm areas with quick=1: */ + clean_shm(1); + + if (sig == -1) { + /* not worth trying any more cleanup, X server probably gone */ + exit(3); + } + + /* X keyboard cleanups */ + delete_added_keycodes(0); + + if (clear_mods == 1) { + clear_modifiers(0); + } else if (clear_mods == 2) { + clear_keys(); + } + if (no_autorepeat) { + autorepeat(1, 0); + } + if (use_solid_bg) { + solid_bg(1); + } + + if (crash_debug) { + crash_shell(); + } + + if (sig) { + exit(2); + } +} + +/* signal handlers */ +void initialize_signals(void) { + signal(SIGHUP, interrupted); + signal(SIGINT, interrupted); + signal(SIGQUIT, interrupted); + signal(SIGABRT, interrupted); + signal(SIGTERM, interrupted); + signal(SIGBUS, interrupted); + signal(SIGSEGV, interrupted); + signal(SIGFPE, interrupted); + + if (!sigpipe || *sigpipe == '\0' || !strcmp(sigpipe, "skip")) { + ; + } else if (!strcmp(sigpipe, "ignore")) { +#ifdef SIG_IGN + signal(SIGPIPE, SIG_IGN); +#endif + } else if (!strcmp(sigpipe, "exit")) { + rfbLog("initialize_signals: will exit on SIGPIPE\n"); + signal(SIGPIPE, interrupted); + } + + X_LOCK; + Xerror_def = XSetErrorHandler(Xerror); + XIOerr_def = XSetIOErrorHandler(XIOerr); + X_UNLOCK; +} + +int known_sigpipe_mode(char *s) { +/* + * skip, ignore, exit + */ + if (strcmp(s, "skip") && strcmp(s, "ignore") && + strcmp(s, "exit")) { + return 0; + } else { + return 1; + } +} + + diff --git a/x11vnc/cleanup.h b/x11vnc/cleanup.h new file mode 100644 index 0000000..c34b7e5 --- /dev/null +++ b/x11vnc/cleanup.h @@ -0,0 +1,25 @@ +#ifndef _X11VNC_CLEANUP_H +#define _X11VNC_CLEANUP_H + +/* -- cleanup.h -- */ + +extern int trapped_xerror; +extern int trapped_xioerror; +extern int trapped_getimage_xerror; +extern int trapped_record_xerror; +extern XErrorEvent *trapped_xerror_event; + +extern int crash_debug; + +extern void clean_shm(int quick); +extern void clean_up_exit (int ret); + +extern int trap_xerror(Display *d, XErrorEvent *error); +extern int trap_xioerror(Display *d); +extern int trap_getimage_xerror(Display *d, XErrorEvent *error); +extern char *xerror_string(XErrorEvent *error); +extern void initialize_crash_handler(void); +extern void initialize_signals(void); +extern int known_sigpipe_mode(char *s); + +#endif /* _X11VNC_CLEANUP_H */ diff --git a/x11vnc/connections.c b/x11vnc/connections.c new file mode 100644 index 0000000..addd215 --- /dev/null +++ b/x11vnc/connections.c @@ -0,0 +1,1859 @@ +/* -- connections.c -- */ + +#include "x11vnc.h" +#include "inet.h" +#include "remote.h" +#include "keyboard.h" +#include "cleanup.h" +#include "gui.h" +#include "solid.h" +#include "rates.h" +#include "screen.h" + +/* + * routines for handling incoming, outgoing, etc connections + */ + +/* string for the VNC_CONNECT property */ +char vnc_connect_str[VNC_CONNECT_MAX+1]; +Atom vnc_connect_prop = None; + +int all_clients_initialized(void); +char *list_clients(void); +int new_fb_size_clients(rfbScreenInfoPtr s); +void close_all_clients(void); +void close_clients(char *str); +void set_client_input(char *str); +void set_child_info(void); +void reverse_connect(char *str); +void set_vnc_connect_prop(char *str); +void read_vnc_connect_prop(void); +void check_connect_inputs(void); +void check_gui_inputs(void); +enum rfbNewClientAction new_client(rfbClientPtr client); +void start_client_info_sock(char *host_port_cookie); +void send_client_info(char *str); +void check_new_clients(void); + + +static rfbClientPtr *client_match(char *str); +static int run_user_command(char *cmd, rfbClientPtr client, char *mode); +static void client_gone(rfbClientPtr client); +static int check_access(char *addr); +static int ugly_accept_window(char *addr, char *userhost, int X, int Y, + int timeout, char *mode); +static int action_match(char *action, int rc); +static int accept_client(rfbClientPtr client); +static void check_connect_file(char *file); +static void send_client_connect(void); + + +/* + * check that all clients are in RFB_NORMAL state + */ +int all_clients_initialized(void) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int ok = 1; + + if (! screen) { + return ok; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->state != RFB_NORMAL) { + ok = 0; + break; + } + } + rfbReleaseClientIterator(iter); + + return ok; +} + +char *list_clients(void) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + char *list, tmp[32]; + int count = 0; + + if (!screen) { + return strdup(""); + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + count++; + } + rfbReleaseClientIterator(iter); + + /* + * each client: + * <id>:<ip>:<port>:<user>:<hostname>:<input>:<loginview>, + * 8+1+16+1+5+1+24+1+256+1+5+1+1+1 + * 123.123.123.123:60000/0x11111111-rw, + * so count+1 * 400 must cover it. + */ + list = (char *) malloc((count+1)*400); + + list[0] = '\0'; + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + if (*list != '\0') { + strcat(list, ","); + } + sprintf(tmp, "0x%x:", cd->uid); + strcat(list, tmp); + strcat(list, cl->host); + strcat(list, ":"); + sprintf(tmp, "%d:", cd->client_port); + strcat(list, tmp); + if (*(cd->username) == '\0') { + char *s = ident_username(cl); + if (s) free(s); + } + strcat(list, cd->username); + strcat(list, ":"); + strcat(list, cd->hostname); + strcat(list, ":"); + strcat(list, cd->input); + strcat(list, ":"); + sprintf(tmp, "%d", cd->login_viewonly); + strcat(list, tmp); + } + rfbReleaseClientIterator(iter); + return list; +} + +/* count number of clients supporting NewFBSize */ +int new_fb_size_clients(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int count = 0; + + if (! s) { + return 0; + } + + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->useNewFBSize) { + count++; + } + } + rfbReleaseClientIterator(iter); + return count; +} + +void close_all_clients(void) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + if (! screen) { + return; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + } + rfbReleaseClientIterator(iter); +} + +static rfbClientPtr *client_match(char *str) { + rfbClientIteratorPtr iter; + rfbClientPtr cl, *cl_list; + int i, n, host_warn = 0, hex_warn = 0; + + n = client_count + 10; + cl_list = (rfbClientPtr *) malloc(n * sizeof(rfbClientPtr)); + + i = 0; + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (strstr(str, "0x") == str) { + unsigned int in; + int id; + ClientData *cd = (ClientData *) cl->clientData; + if (sscanf(str, "0x%x", &in) != 1) { + if (hex_warn++) { + continue; + } + rfbLog("skipping invalid client hex id: %s\n", + str); + continue; + } + id = (unsigned int) in; + if (cd->uid == id) { + cl_list[i++] = cl; + } + } else { + char *rstr = str; + if (! dotted_ip(str)) { + rstr = host2ip(str); + if (rstr == NULL || *rstr == '\0') { + if (host_warn++) { + continue; + } + rfbLog("skipping bad lookup: \"%s\"\n", + str); + continue; + } + rfbLog("lookup: %s -> %s\n", str, rstr); + } + if (!strcmp(rstr, cl->host)) { + cl_list[i++] = cl; + } + if (rstr != str) { + free(rstr); + } + } + if (i >= n - 1) { + break; + } + } + rfbReleaseClientIterator(iter); + + cl_list[i] = NULL; + + return cl_list; +} + +void close_clients(char *str) { + rfbClientPtr *cl_list, *cp; + + if (!strcmp(str, "all") || !strcmp(str, "*")) { + close_all_clients(); + return; + } + + if (! screen) { + return; + } + + cl_list = client_match(str); + + cp = cl_list; + while (*cp) { + rfbCloseClient(*cp); + rfbClientConnectionGone(*cp); + cp++; + } + free(cl_list); +} + +void set_client_input(char *str) { + rfbClientPtr *cl_list, *cp; + char *p, *val; + + /* str is "match:value" */ + + if (! screen) { + return; + } + + p = strchr(str, ':'); + if (! p) { + return; + } + *p = '\0'; + p++; + val = short_kmb(p); + + cl_list = client_match(str); + + cp = cl_list; + while (*cp) { + ClientData *cd = (ClientData *) (*cp)->clientData; + cd->input[0] = '\0'; + strcat(cd->input, "_"); + strcat(cd->input, val); + cp++; + } + + free(val); + free(cl_list); +} + +void set_child_info(void) { + char pid[16]; + /* set up useful environment for child process */ + sprintf(pid, "%d", (int) getpid()); + set_env("X11VNC_PID", pid); + if (program_name) { + /* e.g. for remote control -R */ + set_env("X11VNC_PROG", program_name); + } + if (program_cmdline) { + set_env("X11VNC_CMDLINE", program_cmdline); + } + if (raw_fb_str) { + set_env("X11VNC_RAWFB_STR", raw_fb_str); + } else { + set_env("X11VNC_RAWFB_STR", ""); + } +} + +/* + * utility to run a user supplied command setting some RFB_ env vars. + * used by, e.g., accept_client() and client_gone() + */ +static int run_user_command(char *cmd, rfbClientPtr client, char *mode) { + char *old_display = NULL; + char *addr = client->host; + char str[100]; + int rc; + ClientData *cd = (ClientData *) client->clientData; + + if (addr == NULL || addr[0] == '\0') { + addr = "unknown-host"; + } + + /* set RFB_CLIENT_ID to semi unique id for command to use */ + if (cd && cd->uid) { + sprintf(str, "0x%x", cd->uid); + } else { + /* not accepted yet: */ + sprintf(str, "0x%x", clients_served); + } + set_env("RFB_CLIENT_ID", str); + + /* set RFB_CLIENT_IP to IP addr for command to use */ + set_env("RFB_CLIENT_IP", addr); + + /* set RFB_X11VNC_PID to our pid for command to use */ + sprintf(str, "%d", (int) getpid()); + set_env("RFB_X11VNC_PID", str); + + if (client->state == RFB_PROTOCOL_VERSION) { + set_env("RFB_STATE", "PROTOCOL_VERSION"); + } else if (client->state == RFB_SECURITY_TYPE) { + set_env("RFB_STATE", "SECURITY_TYPE"); + } else if (client->state == RFB_AUTHENTICATION) { + set_env("RFB_STATE", "AUTHENTICATION"); + } else if (client->state == RFB_INITIALISATION) { + set_env("RFB_STATE", "INITIALISATION"); + } else if (client->state == RFB_NORMAL) { + set_env("RFB_STATE", "NORMAL"); + } else { + set_env("RFB_STATE", "UNKNOWN"); + } + + /* set RFB_CLIENT_PORT to peer port for command to use */ + if (cd && cd->client_port > 0) { + sprintf(str, "%d", cd->client_port); + } else { + sprintf(str, "%d", get_remote_port(client->sock)); + } + set_env("RFB_CLIENT_PORT", str); + + set_env("RFB_MODE", mode); + + /* + * now do RFB_SERVER_IP and RFB_SERVER_PORT (i.e. us!) + * This will establish a 5-tuple (including tcp) the external + * program can potentially use to work out the virtual circuit + * for this connection. + */ + if (cd && cd->server_ip) { + set_env("RFB_SERVER_IP", cd->server_ip); + } else { + char *sip = get_local_host(client->sock); + set_env("RFB_SERVER_IP", sip); + if (sip) free(sip); + } + + if (cd && cd->server_port > 0) { + sprintf(str, "%d", cd->server_port); + } else { + sprintf(str, "%d", get_local_port(client->sock)); + } + set_env("RFB_SERVER_PORT", str); + + if (cd) { + sprintf(str, "%d", cd->login_viewonly); + } else { + sprintf(str, "%d", -1); + } + set_env("RFB_LOGIN_VIEWONLY", str); + + if (cd) { + sprintf(str, "%d", (int) cd->login_time); + } else { + sprintf(str, ">%d", (int) time(0)); + } + set_env("RFB_LOGIN_TIME", str); + + sprintf(str, "%d", (int) time(0)); + set_env("RFB_CURRENT_TIME", str); + + if (!cd || !cd->username || cd->username[0] == '\0') { + set_env("RFB_USERNAME", "unknown-user"); + } else { + set_env("RFB_USERNAME", cd->username); + } + /* + * Better set DISPLAY to the one we are polling, if they + * want something trickier, they can handle on their own + * via environment, etc. + */ + if (getenv("DISPLAY")) { + old_display = strdup(getenv("DISPLAY")); + } + + if (raw_fb && ! dpy) { /* raw_fb hack */ + set_env("DISPLAY", "rawfb"); + } else { + set_env("DISPLAY", DisplayString(dpy)); + } + + /* + * work out the number of clients (have to use client_count + * since there is deadlock in rfbGetClientIterator) + */ + sprintf(str, "%d", client_count); + set_env("RFB_CLIENT_COUNT", str); + + if (no_external_cmds) { + rfbLogEnable(1); + rfbLog("cannot run external commands in -nocmds mode:\n"); + rfbLog(" \"%s\"\n", cmd); + rfbLog(" exiting.\n"); + clean_up_exit(1); + } + rfbLog("running command:\n"); + rfbLog(" %s\n", cmd); + + /* XXX need to close port 5900, etc.. */ + rc = system(cmd); + + if (rc >= 256) { + rc = rc/256; + } + rfbLog("command returned: %d\n", rc); + + if (old_display) { + set_env("DISPLAY", old_display); + free(old_display); + } + + return rc; +} + +static int accepted_client = 0; + +/* + * callback for when a client disconnects + */ +static void client_gone(rfbClientPtr client) { + + client_count--; + if (client_count < 0) client_count = 0; + + speeds_net_rate_measured = 0; + speeds_net_latency_measured = 0; + + rfbLog("client_count: %d\n", client_count); + + if (no_autorepeat && client_count == 0) { + autorepeat(1, 0); + } + if (use_solid_bg && client_count == 0) { + solid_bg(1); + } + if (gone_cmd && *gone_cmd != '\0') { + rfbLog("client_gone: using cmd for: %s\n", client->host); + run_user_command(gone_cmd, client, "gone"); + } + + if (client->clientData) { + ClientData *cd = (ClientData *) client->clientData; + if (cd) { + if (cd->server_ip) { + free(cd->server_ip); + cd->server_ip = NULL; + } + if (cd->hostname) { + free(cd->hostname); + cd->hostname = NULL; + } + if (cd->username) { + free(cd->username); + cd->username = NULL; + } + } + free(client->clientData); + client->clientData = NULL; + } + + if (inetd) { + rfbLog("viewer exited.\n"); + clean_up_exit(0); + } + if (connect_once) { + /* + * This non-exit is done for a bad passwd to be consistent + * with our RFB_CLIENT_REFUSE behavior in new_client() (i.e. + * we disconnect after 1 successful connection). + */ + if ((client->state == RFB_PROTOCOL_VERSION || + client->state == RFB_AUTHENTICATION) && accepted_client) { + rfbLog("connect_once: invalid password or early " + "disconnect.\n"); + rfbLog("connect_once: waiting for next connection.\n"); + accepted_client = 0; + return; + } + if (shared && client_count > 0) { + rfbLog("connect_once: other shared clients still " + "connected, not exiting.\n"); + return; + } + + rfbLog("viewer exited.\n"); + clean_up_exit(0); + } +} + +/* + * Simple routine to limit access via string compare. A power user will + * want to compile libvncserver with libwrap support and use /etc/hosts.allow. + */ +static int check_access(char *addr) { + int allowed = 0; + char *p, *list; + + if (deny_all) { + rfbLog("check_access: new connections are currently " + "blocked.\n"); + return 0; + } + if (addr == NULL || *addr == '\0') { + rfbLog("check_access: denying empty host IP address string.\n"); + return 0; + } + + if (allow_list == NULL) { + /* set to "" to possibly append allow_once */ + allow_list = strdup(""); + } + if (*allow_list == '\0' && allow_once == NULL) { + /* no constraints, accept it */ + return 1; + } + + if (strchr(allow_list, '/')) { + /* a file of IP addresess or prefixes */ + int len, len2 = 0; + struct stat sbuf; + FILE *in; + char line[1024], *q; + + if (stat(allow_list, &sbuf) != 0) { + rfbLogEnable(1); + rfbLog("check_access: failure stating file: %s\n", + allow_list); + rfbLogPerror("stat"); + clean_up_exit(1); + } + len = sbuf.st_size + 1; /* 1 more for '\0' at end */ + if (allow_once) { + len2 = strlen(allow_once) + 2; + len += len2; + } + list = (char *) malloc(len); + list[0] = '\0'; + + in = fopen(allow_list, "r"); + if (in == NULL) { + rfbLogEnable(1); + rfbLog("check_access: cannot open: %s\n", allow_list); + rfbLogPerror("fopen"); + clean_up_exit(1); + } + while (fgets(line, 1024, in) != NULL) { + if ( (q = strchr(line, '#')) != NULL) { + *q = '\0'; + } + if (strlen(list) + strlen(line) >= + (size_t) (len - len2)) { + /* file grew since our stat() */ + break; + } + strcat(list, line); + } + fclose(in); + if (allow_once) { + strcat(list, "\n"); + strcat(list, allow_once); + strcat(list, "\n"); + } + } else { + int len = strlen(allow_list) + 1; + if (allow_once) { + len += strlen(allow_once) + 1; + } + list = (char *) malloc(len); + list[0] = '\0'; + strcat(list, allow_list); + if (allow_once) { + strcat(list, ","); + strcat(list, allow_once); + } + } + + if (allow_once) { + free(allow_once); + allow_once = NULL; + } + + p = strtok(list, ", \t\n\r"); + while (p) { + char *chk, *q, *r = NULL; + if (*p == '\0') { + p = strtok(NULL, ", \t\n\r"); + continue; + } + if (! dotted_ip(p)) { + r = host2ip(p); + if (r == NULL || *r == '\0') { + rfbLog("check_access: bad lookup \"%s\"\n", p); + p = strtok(NULL, ", \t\n\r"); + continue; + } + rfbLog("check_access: lookup %s -> %s\n", p, r); + chk = r; + } else { + chk = p; + } + + q = strstr(addr, chk); + if (chk[strlen(chk)-1] != '.') { + if (!strcmp(addr, chk)) { + if (chk != p) { + rfbLog("check_access: client %s " + "matches host %s=%s\n", addr, + chk, p); + } else { + rfbLog("check_access: client %s " + "matches host %s\n", addr, chk); + } + allowed = 1; + } else if(!strcmp(chk, "localhost") && + !strcmp(addr, "127.0.0.1")) { + allowed = 1; + } + } else if (q == addr) { + rfbLog("check_access: client %s matches pattern %s\n", + addr, chk); + allowed = 1; + } + p = strtok(NULL, ", \t\n\r"); + if (r) { + free(r); + } + if (allowed) { + break; + } + } + free(list); + return allowed; +} + +/* + * x11vnc's first (and only) visible widget: accept/reject dialog window. + * We go through this pain to avoid dependency on libXt... + */ +static int ugly_accept_window(char *addr, char *userhost, int X, int Y, + int timeout, char *mode) { + +#define t2x2_width 16 +#define t2x2_height 16 +static unsigned char t2x2_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, + 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, + 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33}; + + Window awin; + GC gc; + XSizeHints hints; + XGCValues values; + static XFontStruct *font_info = NULL; + static Pixmap ico = 0; + unsigned long valuemask = 0; + static char dash_list[] = {20, 40}; + int list_length = sizeof(dash_list); + + Atom wm_protocols; + Atom wm_delete_window; + + XEvent ev; + long evmask = ExposureMask | KeyPressMask | ButtonPressMask + | StructureNotifyMask; + double waited = 0.0; + + /* strings and geometries y/n */ + KeyCode key_y, key_n, key_v; + char strh[100]; + char stri[100]; + char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button"; + char str2_b[] = "To reject: press \"n\" or click the \"No\" button"; + char str3_b[] = "View only: press \"v\" or click the \"View\" button"; + char str1_m[] = "To accept: click the \"Yes\" button"; + char str2_m[] = "To reject: click the \"No\" button"; + char str3_m[] = "View only: click the \"View\" button"; + char str1_k[] = "To accept: press \"y\""; + char str2_k[] = "To reject: press \"n\""; + char str3_k[] = "View only: press \"v\""; + char *str1, *str2, *str3; + char str_y[] = "Yes"; + char str_n[] = "No"; + char str_v[] = "View"; + int x, y, w = 345, h = 175, ret = 0; + int X_sh = 20, Y_sh = 30, dY = 20; + int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20; + int No_x = 75, No_y = 0, No_w = 45, No_h = 20; + int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20; + + if (raw_fb && ! dpy) return 0; /* raw_fb hack */ + + if (!strcmp(mode, "mouse_only")) { + str1 = str1_m; + str2 = str2_m; + str3 = str3_m; + } else if (!strcmp(mode, "key_only")) { + str1 = str1_k; + str2 = str2_k; + str3 = str3_k; + h -= dY; + } else { + str1 = str1_b; + str2 = str2_b; + str3 = str3_b; + } + if (view_only) { + h -= dY; + } + + /* XXX handle coff_x/coff_y? */ + if (X < -dpy_x) { + x = (dpy_x - w)/2; /* large negative: center */ + if (x < 0) x = 0; + } else if (X < 0) { + x = dpy_x + X - w; /* from lower right */ + } else { + x = X; /* from upper left */ + } + + if (Y < -dpy_y) { + y = (dpy_y - h)/2; + if (y < 0) y = 0; + } else if (Y < 0) { + y = dpy_y + Y - h; + } else { + y = Y; + } + + X_LOCK; + + awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4, + BlackPixel(dpy, scr), WhitePixel(dpy, scr)); + + wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); + wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dpy, awin, &wm_delete_window, 1); + + if (! ico) { + ico = XCreateBitmapFromData(dpy, awin, (char *) t2x2_bits, + t2x2_width, t2x2_height); + } + + hints.flags = PPosition | PSize | PMinSize; + hints.x = x; + hints.y = y; + hints.width = w; + hints.height = h; + hints.min_width = w; + hints.min_height = h; + + XSetStandardProperties(dpy, awin, "new x11vnc client", "x11vnc query", + ico, NULL, 0, &hints); + + XSelectInput(dpy, awin, evmask); + + if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) { + rfbLogEnable(1); + rfbLog("ugly_accept_window: cannot locate font fixed.\n"); + X_UNLOCK; + clean_up_exit(1); + } + + gc = XCreateGC(dpy, awin, valuemask, &values); + XSetFont(dpy, gc, font_info->fid); + XSetForeground(dpy, gc, BlackPixel(dpy, scr)); + XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); + XSetDashes(dpy, gc, 0, dash_list, list_length); + + XMapWindow(dpy, awin); + XFlush(dpy); + + snprintf(strh, 100, "x11vnc: accept connection from %s?", addr); + snprintf(stri, 100, " (%s)", userhost); + key_y = XKeysymToKeycode(dpy, XStringToKeysym("y")); + key_n = XKeysymToKeycode(dpy, XStringToKeysym("n")); + key_v = XKeysymToKeycode(dpy, XStringToKeysym("v")); + + while (1) { + int out = -1, x, y, tw, k; + + if (XCheckWindowEvent(dpy, awin, evmask, &ev)) { + ; /* proceed to handling */ + } else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) { + ; /* proceed to handling */ + } else { + int ms = 100; /* sleep a bit */ + usleep(ms * 1000); + waited += ((double) ms)/1000.; + if (timeout && (int) waited >= timeout) { + rfbLog("accept_client: popup timed out after " + "%d seconds.\n", timeout); + out = 0; + ev.type = 0; + } else { + continue; + } + } + + switch(ev.type) { + case Expose: + while (XCheckTypedEvent(dpy, Expose, &ev)) { + ; + } + k=0; + + /* instructions */ + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + strh, strlen(strh)); + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + stri, strlen(stri)); + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str1, strlen(str1)); + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str2, strlen(str2)); + if (! view_only) { + XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, + str3, strlen(str3)); + } + + if (!strcmp(mode, "key_only")) { + break; + } + + /* buttons */ + Ye_y = Y_sh+k*dY; + No_y = Y_sh+k*dY; + Vi_y = Y_sh+k*dY; + XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h); + XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h); + if (! view_only) { + XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y, + Vi_w, Vi_h); + } + + tw = XTextWidth(font_info, str_y, strlen(str_y)); + tw = (Ye_w - tw)/2; + if (tw < 0) tw = 1; + XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5, + str_y, strlen(str_y)); + + tw = XTextWidth(font_info, str_n, strlen(str_n)); + tw = (No_w - tw)/2; + if (tw < 0) tw = 1; + XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5, + str_n, strlen(str_n)); + + if (! view_only) { + tw = XTextWidth(font_info, str_v, + strlen(str_v)); + tw = (Vi_w - tw)/2; + if (tw < 0) tw = 1; + XDrawString(dpy, awin, gc, Vi_x+tw, + Vi_y+Vi_h-5, str_v, strlen(str_v)); + } + + break; + + case ClientMessage: + if (ev.xclient.message_type == wm_protocols && + (Atom) ev.xclient.data.l[0] == wm_delete_window) { + out = 0; + } + break; + + case ButtonPress: + x = ev.xbutton.x; + y = ev.xbutton.y; + if (!strcmp(mode, "key_only")) { + ; + } else if (x > No_x && x < No_x+No_w && y > No_y + && y < No_y+No_h) { + out = 0; + } else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y + && y < Ye_y+Ye_h) { + out = 1; + } else if (! view_only && x > Vi_x && x < Vi_x+Vi_w + && y > Vi_y && y < Vi_y+Ye_h) { + out = 2; + } + break; + + case KeyPress: + if (!strcmp(mode, "mouse_only")) { + ; + } else if (ev.xkey.keycode == key_y) { + out = 1; + } else if (ev.xkey.keycode == key_n) { + out = 0; + } else if (! view_only && ev.xkey.keycode == key_v) { + out = 2; + } + break; + default: + break; + } + if (out != -1) { + ret = out; + XSelectInput(dpy, awin, 0); + XUnmapWindow(dpy, awin); + XFreeGC(dpy, gc); + XDestroyWindow(dpy, awin); + XFlush(dpy); + break; + } + } + X_UNLOCK; + + return ret; +} + +/* + * process a "yes:0,no:*,view:3" type action list comparing to command + * return code rc. * means the default action with no other match. + */ +static int action_match(char *action, int rc) { + char *p, *q, *s = strdup(action); + int cases[4], i, result; + char *labels[4]; + + labels[1] = "yes"; + labels[2] = "no"; + labels[3] = "view"; + + rfbLog("accept_client: process action line: %s\n", + action); + + for (i=1; i <= 3; i++) { + cases[i] = -2; + } + + p = strtok(s, ","); + while (p) { + if ((q = strchr(p, ':')) != NULL) { + int in, k; + *q = '\0'; + q++; + if (strstr(p, "yes") == p) { + k = 1; + } else if (strstr(p, "no") == p) { + k = 2; + } else if (strstr(p, "view") == p) { + k = 3; + } else { + rfbLogEnable(1); + rfbLog("invalid action line: %s\n", action); + clean_up_exit(1); + } + if (*q == '*') { + cases[k] = -1; + } else if (sscanf(q, "%d", &in) == 1) { + if (in < 0) { + rfbLogEnable(1); + rfbLog("invalid action line: %s\n", + action); + clean_up_exit(1); + } + cases[k] = in; + } else { + rfbLogEnable(1); + rfbLog("invalid action line: %s\n", action); + clean_up_exit(1); + } + } else { + rfbLogEnable(1); + rfbLog("invalid action line: %s\n", action); + clean_up_exit(1); + } + p = strtok(NULL, ","); + } + free(s); + + result = -1; + for (i=1; i <= 3; i++) { + if (cases[i] == -1) { + rfbLog("accept_client: default action is case=%d %s\n", + i, labels[i]); + result = i; + break; + } + } + if (result == -1) { + rfbLog("accept_client: no default action\n"); + } + for (i=1; i <= 3; i++) { + if (cases[i] >= 0 && cases[i] == rc) { + rfbLog("accept_client: matched action is case=%d %s\n", + i, labels[i]); + result = i; + break; + } + } + if (result < 0) { + rfbLog("no action match: %s rc=%d set to no\n", action, rc); + result = 2; + } + return result; +} + +/* + * Simple routine to prompt the user on the X display whether an incoming + * client should be allowed to connect or not. If a gui is involved it + * will be running in the environment/context of the X11 DISPLAY. + * + * The command supplied via -accept is run as is (i.e. no string + * substitution) with the RFB_CLIENT_IP environment variable set to the + * incoming client's numerical IP address. + * + * If the external command exits with 0 the client is accepted, otherwise + * the client is rejected. + * + * Some builtins are provided: + * + * xmessage: use homebrew xmessage(1) for the external command. + * popup: use internal X widgets for prompting. + * + */ +static int accept_client(rfbClientPtr client) { + + char xmessage[200], *cmd = NULL; + char *addr = client->host; + char *action = NULL; + + if (accept_cmd == NULL || *accept_cmd == '\0') { + return 1; /* no command specified, so we accept */ + } + + if (addr == NULL || addr[0] == '\0') { + addr = "unknown-host"; + } + + if (strstr(accept_cmd, "popup") == accept_cmd) { + /* use our builtin popup button */ + + /* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */ + + int ret, timeout = 120; + int x = -64000, y = -64000; + char *p, *mode; + char *userhost = ident_username(client); + + /* extract timeout */ + if ((p = strchr(accept_cmd, ':')) != NULL) { + int in; + if (sscanf(p+1, "%d", &in) == 1) { + timeout = in; + } + } + /* extract geometry */ + if ((p = strpbrk(accept_cmd, "+-")) != NULL) { + int x1, y1; + if (sscanf(p, "+%d+%d", &x1, &y1) == 2) { + x = x1; + y = y1; + } else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) { + x = x1; + y = -y1; + } else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) { + x = -x1; + y = y1; + } else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) { + x = -x1; + y = -y1; + } + } + + /* find mode: mouse, key, or both */ + if (strstr(accept_cmd, "popupmouse") == accept_cmd) { + mode = "mouse_only"; + } else if (strstr(accept_cmd, "popupkey") == accept_cmd) { + mode = "key_only"; + } else { + mode = "both"; + } + + rfbLog("accept_client: using builtin popup for: %s\n", addr); + if ((ret = ugly_accept_window(addr, userhost, x, y, timeout, + mode))) { + free(userhost); + if (ret == 2) { + rfbLog("accept_client: viewonly: %s\n", addr); + client->viewOnly = TRUE; + } + rfbLog("accept_client: popup accepted: %s\n", addr); + return 1; + } else { + free(userhost); + rfbLog("accept_client: popup rejected: %s\n", addr); + return 0; + } + + } else if (!strcmp(accept_cmd, "xmessage")) { + /* make our own command using xmessage(1) */ + + if (view_only) { + sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center" + " 'x11vnc: accept connection from %s?'", addr); + } else { + sprintf(xmessage, "xmessage -buttons yes:0,no:2," + "view-only:3 -center" " 'x11vnc: accept connection" + " from %s?'", addr); + action = "yes:0,no:*,view:3"; + } + cmd = xmessage; + + } else { + /* use the user supplied command: */ + + cmd = accept_cmd; + + /* extract any action prefix: yes:N,no:M,view:K */ + if (strstr(accept_cmd, "yes:") == accept_cmd) { + char *p; + if ((p = strpbrk(accept_cmd, " \t")) != NULL) { + int i; + cmd = p; + p = accept_cmd; + for (i=0; i<200; i++) { + if (*p == ' ' || *p == '\t') { + xmessage[i] = '\0'; + break; + } + xmessage[i] = *p; + p++; + } + xmessage[200-1] = '\0'; + action = xmessage; + } + } + } + + if (cmd) { + int rc; + + rfbLog("accept_client: using cmd for: %s\n", addr); + rc = run_user_command(cmd, client, "accept"); + + if (action) { + int result; + + if (rc < 0) { + rfbLog("accept_client: cannot use negative " + "rc: %d, action %s\n", rc, action); + result = 2; + } else { + result = action_match(action, rc); + } + + if (result == 1) { + rc = 0; + } else if (result == 2) { + rc = 1; + } else if (result == 3) { + rc = 0; + rfbLog("accept_client: viewonly: %s\n", addr); + client->viewOnly = TRUE; + } else { + rc = 1; /* NOTREACHED */ + } + } + + if (rc == 0) { + rfbLog("accept_client: accepted: %s\n", addr); + return 1; + } else { + rfbLog("accept_client: rejected: %s\n", addr); + return 0; + } + } else { + rfbLog("accept_client: no command, rejecting %s\n", addr); + return 0; + } + + /* return 0; NOTREACHED */ +} + +/* + * For the -connect <file> option: periodically read the file looking for + * a connect string. If one is found set client_connect to it. + */ +static void check_connect_file(char *file) { + FILE *in; + char line[VNC_CONNECT_MAX], host[VNC_CONNECT_MAX]; + static int first_warn = 1, truncate_ok = 1; + static time_t last_time = 0; + time_t now = time(0); + + if (last_time == 0) { + last_time = now; + } + if (now - last_time < 1) { + /* check only once a second */ + return; + } + last_time = now; + + if (! truncate_ok) { + /* check if permissions changed */ + if (access(file, W_OK) == 0) { + truncate_ok = 1; + } else { + return; + } + } + + in = fopen(file, "r"); + if (in == NULL) { + if (first_warn) { + rfbLog("check_connect_file: fopen failure: %s\n", file); + rfbLogPerror("fopen"); + first_warn = 0; + } + return; + } + + if (fgets(line, VNC_CONNECT_MAX, in) != NULL) { + if (sscanf(line, "%s", host) == 1) { + if (strlen(host) > 0) { + char *str = strdup(host); + if (strlen(str) > 38) { + char trim[100]; + trim[0] = '\0'; + strncat(trim, str, 38); + rfbLog("read connect file: %s ...\n", + trim); + } else { + rfbLog("read connect file: %s\n", str); + } + client_connect = str; + } + } + } + fclose(in); + + /* truncate file */ + in = fopen(file, "w"); + if (in != NULL) { + fclose(in); + } else { + /* disable if we cannot truncate */ + rfbLog("check_connect_file: could not truncate %s, " + "disabling checking.\n", file); + truncate_ok = 0; + } +} + +/* + * Do a reverse connect for a single "host" or "host:port" + */ +static int do_reverse_connect(char *str) { + rfbClientPtr cl; + char *host, *p; + int rport = 5500, len = strlen(str); + + if (len < 1) { + return 0; + } + if (len > 1024) { + rfbLog("reverse_connect: string too long: %d bytes\n", len); + return 0; + } + if (!screen) { + rfbLog("reverse_connect: screen not setup yet.\n"); + return 0; + } + + /* copy in to host */ + host = (char *) malloc(len+1); + if (! host) { + rfbLog("reverse_connect: could not malloc string %d\n", len); + return 0; + } + strncpy(host, str, len); + host[len] = '\0'; + + /* extract port, if any */ + if ((p = strchr(host, ':')) != NULL) { + rport = atoi(p+1); + *p = '\0'; + } + + cl = rfbReverseConnection(screen, host, rport); + free(host); + + if (cl == NULL) { + rfbLog("reverse_connect: %s failed\n", str); + return 0; + } else { + rfbLog("reverse_connect: %s/%s OK\n", str, cl->host); + return 1; + } +} + +/* + * Break up comma separated list of hosts and call do_reverse_connect() + */ +void reverse_connect(char *str) { + char *p, *tmp = strdup(str); + int sleep_between_host = 300; + int sleep_min = 1500, sleep_max = 4500, n_max = 5; + int n, tot, t, dt = 100, cnt = 0; + + p = strtok(tmp, ", \t\r\n"); + while (p) { + if ((n = do_reverse_connect(p)) != 0) { + rfbPE(-1); + } + cnt += n; + + p = strtok(NULL, ", \t\r\n"); + if (p) { + t = 0; + while (t < sleep_between_host) { + usleep(dt * 1000); + rfbPE(-1); + t += dt; + } + } + } + free(tmp); + + if (cnt == 0) { + return; + } + + /* + * XXX: we need to process some of the initial handshaking + * events, otherwise the client can get messed up (why??) + * so we send rfbProcessEvents() all over the place. + */ + + n = cnt; + if (n >= n_max) { + n = n_max; + } + t = sleep_max - sleep_min; + tot = sleep_min + ((n-1) * t) / (n_max-1); + + t = 0; + while (t < tot) { + rfbPE(-1); + usleep(dt * 1000); + t += dt; + } +} + +/* + * Routines for monitoring the VNC_CONNECT property for changes. + * The vncconnect(1) will set it on our X display. + */ +void set_vnc_connect_prop(char *str) { + XChangeProperty(dpy, rootwin, vnc_connect_prop, XA_STRING, 8, + PropModeReplace, (unsigned char *)str, strlen(str)); +} + +void read_vnc_connect_prop(void) { + Atom type; + int format, slen, dlen; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + int db = 1; + + vnc_connect_str[0] = '\0'; + slen = 0; + + if (! vnc_connect || vnc_connect_prop == None) { + /* not active or problem with VNC_CONNECT atom */ + return; + } + + /* read the property value into vnc_connect_str: */ + do { + if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), + vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False, + AnyPropertyType, &type, &format, &nitems, &bytes_after, + &data) == Success) { + + dlen = nitems * (format/8); + if (slen + dlen > VNC_CONNECT_MAX) { + /* too big */ + rfbLog("warning: truncating large VNC_CONNECT" + " string > %d bytes.\n", VNC_CONNECT_MAX); + XFree(data); + break; + } + memcpy(vnc_connect_str+slen, data, dlen); + slen += dlen; + vnc_connect_str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); + + vnc_connect_str[VNC_CONNECT_MAX] = '\0'; + if (! db) { + ; + } else if (strstr(vnc_connect_str, "ans=stop:N/A,ans=quit:N/A,ans=")) { + ; + } else if (strstr(vnc_connect_str, "qry=stop,quit,exit")) { + ; + } else if (strstr(vnc_connect_str, "ack=") == vnc_connect_str) { + ; + } else if (quiet && strstr(vnc_connect_str, "qry=ping") == + vnc_connect_str) { + ; + } else if (strstr(vnc_connect_str, "cmd=") && + strstr(vnc_connect_str, "passwd")) { + rfbLog("read VNC_CONNECT: *\n"); + } else if (strlen(vnc_connect_str) > 38) { + char trim[100]; + trim[0] = '\0'; + strncat(trim, vnc_connect_str, 38); + rfbLog("read VNC_CONNECT: %s ...\n", trim); + + } else { + rfbLog("read VNC_CONNECT: %s\n", vnc_connect_str); + } +} + +/* + * check if client_connect has been set, if so make the reverse connections. + */ +static void send_client_connect(void) { + if (client_connect != NULL) { + char *str = client_connect; + if (strstr(str, "cmd=") == str || strstr(str, "qry=") == str) { + process_remote_cmd(client_connect, 0); + } else if (strstr(str, "ans=") == str + || strstr(str, "aro=") == str) { + ; + } else if (strstr(str, "ack=") == str) { + ; + } else { + reverse_connect(client_connect); + } + free(client_connect); + client_connect = NULL; + } +} + +/* + * monitor the various input methods + */ +void check_connect_inputs(void) { + + /* flush any already set: */ + send_client_connect(); + + /* connect file: */ + if (client_connect_file != NULL) { + check_connect_file(client_connect_file); + } + send_client_connect(); + + /* VNC_CONNECT property (vncconnect program) */ + if (vnc_connect && *vnc_connect_str != '\0') { + client_connect = strdup(vnc_connect_str); + vnc_connect_str[0] = '\0'; + } + send_client_connect(); +} + +void check_gui_inputs(void) { + int i, nmax = 0, n = 0, nfds; + int socks[ICON_MODE_SOCKS]; + fd_set fds; + struct timeval tv; + char buf[VNC_CONNECT_MAX+1]; + ssize_t nbytes; + + for (i=0; i<ICON_MODE_SOCKS; i++) { + if (icon_mode_socks[i] >= 0) { + socks[n++] = i; + if (icon_mode_socks[i] > nmax) { + nmax = icon_mode_socks[i]; + } + } + } + + if (! n) { + return; + } + + FD_ZERO(&fds); + for (i=0; i<n; i++) { + FD_SET(icon_mode_socks[socks[i]], &fds); + } + tv.tv_sec = 0; + tv.tv_usec = 0; + + nfds = select(nmax+1, &fds, NULL, NULL, &tv); + if (nfds <= 0) { + return; + } + + for (i=0; i<n; i++) { + int k, fd = icon_mode_socks[socks[i]]; + char *p; + if (! FD_ISSET(fd, &fds)) { + continue; + } + for (k=0; k<=VNC_CONNECT_MAX; k++) { + buf[k] = '\0'; + } + nbytes = read(fd, buf, VNC_CONNECT_MAX); + if (nbytes <= 0) { + close(fd); + icon_mode_socks[socks[i]] = -1; + continue; + } + + p = strtok(buf, "\r\n"); + while (p) { + if (strstr(p, "cmd=") == p || + strstr(p, "qry=") == p) { + char *str = process_remote_cmd(p, 1); + if (! str) { + str = strdup(""); + } + nbytes = write(fd, str, strlen(str)); + write(fd, "\n", 1); + free(str); + if (nbytes < 0) { + close(fd); + icon_mode_socks[socks[i]] = -1; + break; + } + } + p = strtok(NULL, "\r\n"); + } + } +} + +/* + * libvncserver callback for when a new client connects + */ +enum rfbNewClientAction new_client(rfbClientPtr client) { + ClientData *cd; + + last_event = last_input = time(0); + + if (inetd) { + /* + * Set this so we exit as soon as connection closes, + * otherwise client_gone is only called after RFB_CLIENT_ACCEPT + */ + client->clientGoneHook = client_gone; + } + + clients_served++; + + if (connect_once) { + if (screen->dontDisconnect && screen->neverShared) { + if (! shared && accepted_client) { + rfbLog("denying additional client: %s\n", + client->host); + return(RFB_CLIENT_REFUSE); + } + } + } + if (! check_access(client->host)) { + rfbLog("denying client: %s does not match %s\n", client->host, + allow_list ? allow_list : "(null)" ); + return(RFB_CLIENT_REFUSE); + } + if (! accept_client(client)) { + rfbLog("denying client: %s local user rejected connection.\n", + client->host); + rfbLog("denying client: accept_cmd=\"%s\"\n", + accept_cmd ? accept_cmd : "(null)" ); + return(RFB_CLIENT_REFUSE); + } + + client->clientData = (void *) calloc(sizeof(ClientData), 1); + cd = (ClientData *) client->clientData; + + cd->uid = clients_served; + + cd->client_port = get_remote_port(client->sock); + cd->server_port = get_local_port(client->sock); + cd->server_ip = get_local_host(client->sock); + cd->hostname = ip2host(client->host); + cd->username = strdup(""); + + cd->input[0] = '-'; + cd->login_viewonly = -1; + cd->login_time = time(0); + + client->clientGoneHook = client_gone; + + if (client_count) { + speeds_net_rate_measured = 0; + speeds_net_latency_measured = 0; + } + 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, 0); + } + if (use_solid_bg && client_count == 1) { + solid_bg(0); + } + + if (pad_geometry) { + install_padded_fb(pad_geometry); + } + + cd->timer = dnow(); + cd->send_cmp_rate = 0.0; + cd->send_raw_rate = 0.0; + cd->latency = 0.0; + cd->cmp_bytes_sent = 0; + cd->raw_bytes_sent = 0; + + accepted_client = 1; + last_client = time(0); + + return(RFB_CLIENT_ACCEPT); +} + +void start_client_info_sock(char *host_port_cookie) { + char *host = NULL, *cookie = NULL, *p; + char *str = strdup(host_port_cookie); + int i, port, sock, next = -1; + static time_t start_time[ICON_MODE_SOCKS]; + time_t oldest = 0; + int db = 0; + + for (i = 0; i < ICON_MODE_SOCKS; i++) { + if (icon_mode_socks[i] < 0) { + next = i; + break; + } + if (oldest == 0 || start_time[i] < oldest) { + next = i; + oldest = start_time[i]; + } + } + + p = strtok(str, ":"); + i = 0; + while (p) { + if (i == 0) { + host = strdup(p); + } else if (i == 1) { + port = atoi(p); + } else if (i == 2) { + cookie = strdup(p); + } + i++; + p = strtok(NULL, ":"); + } + free(str); + + if (db) fprintf(stderr, "%s/%d/%s next=%d\n", host, port, cookie, next); + + if (host && port && cookie) { + if (*host == '\0') { + free(host); + host = strdup("localhost"); + } + sock = rfbConnectToTcpAddr(host, port); + if (sock < 0) { + usleep(200 * 1000); + sock = rfbConnectToTcpAddr(host, port); + } + if (sock >= 0) { + char *lst = list_clients(); + icon_mode_socks[next] = sock; + start_time[next] = time(0); + write(sock, "COOKIE:", strlen("COOKIE:")); + write(sock, cookie, strlen(cookie)); + write(sock, "\n", strlen("\n")); + write(sock, "none\n", strlen("none\n")); + write(sock, "none\n", strlen("none\n")); + write(sock, lst, strlen(lst)); + write(sock, "\n", strlen("\n")); + if (db) { + fprintf(stderr, "list: %s\n", lst); + } + free(lst); + rfbLog("client_info_sock to: %s:%d\n", host, port); + } else { + rfbLog("failed client_info_sock: %s:%d\n", host, port); + } + } else { + rfbLog("malformed client_info_sock: %s\n", host_port_cookie); + } + + if (host) free(host); + if (cookie) free(cookie); +} + +void send_client_info(char *str) { + int i; + static char *pstr = NULL; + static int len = 128; + + if (!str || strlen(str) == 0) { + return; + } + + if (!pstr) { + pstr = (char *)malloc(len); + } + if (strlen(str) + 2 > (size_t) len) { + free(pstr); + len *= 2; + pstr = (char *)malloc(len); + } + strcpy(pstr, str); + strcat(pstr, "\n"); + + if (icon_mode_fh) { + fprintf(icon_mode_fh, "%s", pstr); + fflush(icon_mode_fh); + } + + for (i=0; i<ICON_MODE_SOCKS; i++) { + int len, n, sock = icon_mode_socks[i]; + char *buf = pstr; + + if (sock < 0) { + continue; + } + + len = strlen(pstr); + while (len > 0) { + n = write(sock, buf, len); + if (n > 0) { + buf += n; + len -= n; + continue; + } + + if (n < 0 && errno == EINTR) { + continue; + } + close(sock); + icon_mode_socks[i] = -1; + break; + } + } +} + +void check_new_clients(void) { + static int last_count = 0; + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int i, send_info = 0; + int run_after_accept = 0; + + if (client_count == last_count) { + return; + } + + if (! all_clients_initialized()) { + return; + } + + if (client_count > last_count) { + if (afteraccept_cmd != NULL && afteraccept_cmd[0] != '\0') { + run_after_accept = 1; + } + } + + last_count = client_count; + + if (! screen) { + return; + } + if (! client_count) { + send_client_info("none"); + return; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + + if (cd->login_viewonly < 0) { + /* this is a general trigger to initialize things */ + if (cl->viewOnly) { + cd->login_viewonly = 1; + if (allowed_input_view_only) { + cl->viewOnly = FALSE; + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_view_only, CILEN); + } + } else { + cd->login_viewonly = 0; + if (allowed_input_normal) { + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_normal, CILEN); + } + } + if (run_after_accept) { + run_user_command(afteraccept_cmd, cl, + "afteraccept"); + } + } + } + rfbReleaseClientIterator(iter); + + if (icon_mode_fh) { + send_info++; + } + for (i = 0; i < ICON_MODE_SOCKS; i++) { + if (send_info || icon_mode_socks[i] >= 0) { + send_info++; + break; + } + } + if (send_info) { + char *str = list_clients(); + send_client_info(str); + free(str); + } +} + + diff --git a/x11vnc/connections.h b/x11vnc/connections.h new file mode 100644 index 0000000..7d51ab5 --- /dev/null +++ b/x11vnc/connections.h @@ -0,0 +1,26 @@ +#ifndef _X11VNC_CONNECTIONS_H +#define _X11VNC_CONNECTIONS_H + +/* -- connections.h -- */ + +extern char vnc_connect_str[]; +extern Atom vnc_connect_prop; + +extern int all_clients_initialized(void); +extern char *list_clients(void); +extern int new_fb_size_clients(rfbScreenInfoPtr s); +extern void close_all_clients(void); +extern void close_clients(char *str); +extern void set_client_input(char *str); +extern void set_child_info(void); +extern void reverse_connect(char *str); +extern void set_vnc_connect_prop(char *str); +extern void read_vnc_connect_prop(void); +extern void check_connect_inputs(void); +extern void check_gui_inputs(void); +extern enum rfbNewClientAction new_client(rfbClientPtr client); +extern void start_client_info_sock(char *host_port_cookie); +extern void send_client_info(char *str); +extern void check_new_clients(void); + +#endif /* _X11VNC_CONNECTIONS_H */ diff --git a/x11vnc/cursor.c b/x11vnc/cursor.c new file mode 100644 index 0000000..4c8cca7 --- /dev/null +++ b/x11vnc/cursor.c @@ -0,0 +1,1786 @@ +/* -- cursor.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "cleanup.h" +#include "screen.h" +#include "scan.h" + +int xfixes_present = 0; +int use_xfixes = 1; +int got_xfixes_cursor_notify = 0; +int cursor_changes = 0; +int alpha_threshold = 240; +double alpha_frac = 0.33; +int alpha_remove = 0; +int alpha_blend = 1; +int alt_arrow = 1; + + +void first_cursor(void); +void setup_cursors_and_push(void); +void initialize_xfixes(void); +int known_cursors_mode(char *s); +void initialize_cursors_mode(void); +int get_which_cursor(void); +void restore_cursor_shape_updates(rfbScreenInfoPtr s); +void disable_cursor_shape_updates(rfbScreenInfoPtr s); +int cursor_shape_updates_clients(rfbScreenInfoPtr s); +int cursor_pos_updates_clients(rfbScreenInfoPtr s); +void cursor_position(int x, int y); +void set_no_cursor(void); +int set_cursor(int x, int y, int which); +int check_x11_pointer(void); + + +typedef struct win_str_info { + char *wm_name; + char *res_name; + char *res_class; +} win_str_info_t; + +typedef struct cursor_info { + char *data; /* data and mask pointers */ + char *mask; + int wx, wy; /* size of cursor */ + int sx, sy; /* shift to its centering point */ + int reverse; /* swap black and white */ + rfbCursorPtr rfb; +} cursor_info_t; + + +static void curs_copy(cursor_info_t *dest, cursor_info_t *src); +static void setup_cursors(void); +static void set_rfb_cursor(int which); +static void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo); +static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, + int xhot, int yhot, int Bpp); +static int get_xfixes_cursor(int init); +static void set_cursor_was_changed(rfbScreenInfoPtr s); + + +/* + * Here begins a bit of a mess to experiment with multiple cursors + * drawn on the remote background ... + */ +static void curs_copy(cursor_info_t *dest, cursor_info_t *src) { + if (src->data != NULL) { + dest->data = strdup(src->data); + } else { + dest->data = NULL; + } + if (src->mask != NULL) { + dest->mask = strdup(src->mask); + } else { + dest->mask = NULL; + } + dest->wx = src->wx; + dest->wy = src->wy; + dest->sx = src->sx; + dest->sy = src->sy; + dest->reverse = src->reverse; + dest->rfb = src->rfb; +} + +/* empty cursor */ +static char* curs_empty_data = +" " +" "; + +static char* curs_empty_mask = +" " +" "; +static cursor_info_t cur_empty = {NULL, NULL, 2, 2, 0, 0, 0, NULL}; + +/* dot cursor */ +static char* curs_dot_data = +" " +" x"; + +static char* curs_dot_mask = +" " +" x"; +static cursor_info_t cur_dot = {NULL, NULL, 2, 2, 0, 0, 0, NULL}; + + +/* main cursor */ +static char* curs_arrow_data = +" " +" x " +" xx " +" xxx " +" xxxx " +" xxxxx " +" xxxxxx " +" xxxxxxx " +" xxxxxxxx " +" xxxxx " +" xx xx " +" x xx " +" xx " +" xx " +" xx " +" " +" " +" "; + +static char* curs_arrow_mask = +"xx " +"xxx " +"xxxx " +"xxxxx " +"xxxxxx " +"xxxxxxx " +"xxxxxxxx " +"xxxxxxxxx " +"xxxxxxxxxx " +"xxxxxxxxxx " +"xxxxxxx " +"xxx xxxx " +"xx xxxx " +" xxxx " +" xxxx " +" xx " +" " +" "; +static cursor_info_t cur_arrow = {NULL, NULL, 18, 18, 0, 0, 1, NULL}; + +static char* curs_arrow2_data = +" " +" x " +" xx " +" xxx " +" xxxx " +" xxxxx " +" xxxxxx " +" xxxxxxx " +" xxxxxxxx " +" xxxxx " +" xx xx " +" x xx " +" xx " +" xx " +" xx " +" " +" " +" "; + +static char* curs_arrow2_mask = +"xx " +"xxx " +"xxxx " +"xxxxx " +"xxxxxx " +"xxxxxxx " +"xxxxxxxx " +"xxxxxxxxx " +"xxxxxxxxxx " +"xxxxxxxxxx " +"xxxxxxx " +"xxx xxxx " +"xx xxxx " +" xxxx " +" xxxx " +" xx " +" " +" "; +static cursor_info_t cur_arrow2 = {NULL, NULL, 18, 18, 0, 0, 0, NULL}; + +static char* curs_arrow3_data = +" " +" xx " +" xxxx " +" xxxxx " +" xxxxxxx " +" xxxxxxxx " +" xxxxxxxxxx " +" xxxxx " +" xxxxx " +" xx x " +" xx x " +" x x " +" x x " +" x " +" x " +" "; + +static char* curs_arrow3_mask = +"xxx " +"xxxxx " +"xxxxxxx " +" xxxxxxxx " +" xxxxxxxxxx " +" xxxxxxxxxxxx " +" xxxxxxxxxxxx " +" xxxxxxxxxxx " +" xxxxxxx " +" xxxxxxx " +" xxxx xxx " +" xxx xxx " +" xxx xxx " +" xxx xxx " +" xxx" +" xx"; + +static cursor_info_t cur_arrow3 = {NULL, NULL, 16, 16, 0, 0, 1, NULL}; + +static char* curs_arrow4_data = +" " +" xx " +" xxxx " +" xxxxx " +" xxxxxxx " +" xxxxxxxx " +" xxxxxxxxxx " +" xxxxx " +" xxxxx " +" xx x " +" xx x " +" x x " +" x x " +" x " +" x " +" "; + +static char* curs_arrow4_mask = +"xxx " +"xxxxx " +"xxxxxxx " +" xxxxxxxx " +" xxxxxxxxxx " +" xxxxxxxxxxxx " +" xxxxxxxxxxxx " +" xxxxxxxxxxx " +" xxxxxxx " +" xxxxxxx " +" xxxx xxx " +" xxx xxx " +" xxx xxx " +" xxx xxx " +" xxx" +" xx"; + +static cursor_info_t cur_arrow4 = {NULL, NULL, 16, 16, 0, 0, 0, NULL}; + +static char* curs_arrow5_data = +"x " +" xx " +" xxxx " +" xxxxx " +" xxxxxxx " +" xxx " +" xx x " +" x x " +" x x " +" x " +" x " +" x " +" x " +" x " +" x"; + +static char* curs_arrow5_mask = +"xx " +"xxxx " +" xxxxx " +" xxxxxxx " +" xxxxxxxx " +" xxxxxxxx " +" xxxxx " +" xxxxxx " +" xx xxx " +" x xxx " +" xxx " +" xxx " +" xxx " +" xxx" +" xx"; + +static cursor_info_t cur_arrow5 = {NULL, NULL, 15, 15, 0, 0, 1, NULL}; + +static char* curs_arrow6_data = +"x " +" xx " +" xxxx " +" xxxxx " +" xxxxxxx " +" xxx " +" xx x " +" x x " +" x x " +" x " +" x " +" x " +" x " +" x " +" x"; + +static char* curs_arrow6_mask = +"xx " +"xxxx " +" xxxxx " +" xxxxxxx " +" xxxxxxxx " +" xxxxxxxx " +" xxxxx " +" xxxxxx " +" xx xxx " +" x xxx " +" xxx " +" xxx " +" xxx " +" xxx" +" xx"; + +static cursor_info_t cur_arrow6 = {NULL, NULL, 15, 15, 0, 0, 0, NULL}; + +int alt_arrow_max = 6; +/* + * It turns out we can at least detect mouse is on the root window so + * show it (under -cursor X) with this familiar cursor... + */ +static char* curs_root_data = +" " +" " +" xxx xxx " +" xxxx xxxx " +" xxxxx xxxxx " +" xxxxx xxxxx " +" xxxxxxxxxx " +" xxxxxxxx " +" xxxxxx " +" xxxxxx " +" xxxxxxxx " +" xxxxxxxxxx " +" xxxxx xxxxx " +" xxxxx xxxxx " +" xxxx xxxx " +" xxx xxx " +" " +" "; + +static char* curs_root_mask = +" " +" xxxx xxxx " +" xxxxx xxxxx " +" xxxxxx xxxxxx " +" xxxxxxx xxxxxxx " +" xxxxxxxxxxxxxx " +" xxxxxxxxxxxx " +" xxxxxxxxxx " +" xxxxxxxx " +" xxxxxxxx " +" xxxxxxxxxx " +" xxxxxxxxxxxx " +" xxxxxxxxxxxxxx " +" xxxxxxx xxxxxxx " +" xxxxxx xxxxxx " +" xxxxx xxxxx " +" xxxx xxxx " +" "; +static cursor_info_t cur_root = {NULL, NULL, 18, 18, 8, 8, 1, NULL}; + +static char* curs_fleur_data = +" " +" xx " +" xxxx " +" xxxxxx " +" xx " +" x xx x " +" xx xx xx " +" xxxxxxxxxxxxxx " +" xxxxxxxxxxxxxx " +" xx xx xx " +" x xx x " +" xx " +" xxxxxx " +" xxxx " +" xx " +" "; + +static char* curs_fleur_mask = +" xxxx " +" xxxxx " +" xxxxxx " +" xxxxxxxx " +" x xxxxxx x " +" xxx xxxx xxx " +"xxxxxxxxxxxxxxxx" +"xxxxxxxxxxxxxxxx" +"xxxxxxxxxxxxxxxx" +"xxxxxxxxxxxxxxxx" +" xxx xxxx xxx " +" x xxxxxx x " +" xxxxxxxx " +" xxxxxx " +" xxxx " +" xxxx "; + +static cursor_info_t cur_fleur = {NULL, NULL, 16, 16, 8, 8, 1, NULL}; + +static char* curs_plus_data = +" " +" xx " +" xx " +" xx " +" xx " +" xxxxxxxxxx " +" xxxxxxxxxx " +" xx " +" xx " +" xx " +" xx " +" "; + +static char* curs_plus_mask = +" xxxx " +" xxxx " +" xxxx " +" xxxx " +"xxxxxxxxxxxx" +"xxxxxxxxxxxx" +"xxxxxxxxxxxx" +"xxxxxxxxxxxx" +" xxxx " +" xxxx " +" xxxx " +" xxxx "; +static cursor_info_t cur_plus = {NULL, NULL, 12, 12, 5, 6, 1, NULL}; + +static char* curs_xterm_data = +" " +" xxx xxx " +" xxx " +" x " +" x " +" x " +" x " +" x " +" x " +" x " +" x " +" x " +" x " +" xxx " +" xxx xxx " +" "; + +static char* curs_xterm_mask = +" xxxx xxxx " +" xxxxxxxxx " +" xxxxxxxxx " +" xxxxx " +" xxx " +" xxx " +" xxx " +" xxx " +" xxx " +" xxx " +" xxx " +" xxx " +" xxxxx " +" xxxxxxxxx " +" xxxxxxxxx " +" xxxx xxxx "; +static cursor_info_t cur_xterm = {NULL, NULL, 16, 16, 8, 8, 1, NULL}; + +enum cursor_names { + CURS_EMPTY = 0, + CURS_DOT, + + CURS_ARROW, + CURS_ROOT, + CURS_WM, + CURS_TERM, + CURS_PLUS, + + CURS_DYN1, + CURS_DYN2, + CURS_DYN3, + CURS_DYN4, + CURS_DYN5, + CURS_DYN6, + CURS_DYN7, + CURS_DYN8, + CURS_DYN9, + CURS_DYN10, + CURS_DYN11, + CURS_DYN12, + CURS_DYN13, + CURS_DYN14, + CURS_DYN15, + CURS_DYN16 +}; + +#define CURS_DYN_MIN CURS_DYN1 +#define CURS_DYN_MAX CURS_DYN16 +#define CURS_DYN_NUM (CURS_DYN_MAX - CURS_DYN_MIN + 1) + +#define CURS_MAX 32 +static cursor_info_t *cursors[CURS_MAX]; + +void first_cursor(void) { + if (! screen) { + return; + } + if (! show_cursor) { + screen->cursor = NULL; + } else { + got_xfixes_cursor_notify++; + set_rfb_cursor(get_which_cursor()); + set_cursor_was_changed(screen); + } +} + +static void setup_cursors(void) { + rfbCursorPtr rfb_curs; + char *scale = NULL; + int i, j, n = 0; + static int first = 1; + + rfbLog("setting up %d cursors...\n", CURS_MAX); + + if (first) { + for (i=0; i<CURS_MAX; i++) { + cursors[i] = NULL; + } + } + first = 0; + + if (screen) { + screen->cursor = NULL; + LOCK(screen->cursorMutex); + } + + for (i=0; i<CURS_MAX; i++) { + cursor_info_t *ci; + if (cursors[i]) { + /* clear out any existing ones: */ + ci = cursors[i]; + if (ci->rfb) { + /* this is the rfbCursor part: */ + if (ci->rfb->richSource) { + free(ci->rfb->richSource); + ci->rfb->richSource = NULL; + } + if (ci->rfb->source) { + free(ci->rfb->source); + ci->rfb->source = NULL; + } + if (ci->rfb->mask) { + free(ci->rfb->mask); + ci->rfb->mask = NULL; + } + free(ci->rfb); + ci->rfb = NULL; + } + if (ci->data) { + free(ci->data); + ci->data = NULL; + } + if (ci->mask) { + free(ci->mask); + ci->mask = NULL; + } + free(ci); + ci = NULL; + } + + /* create new struct: */ + ci = (cursor_info_t *) malloc(sizeof(cursor_info_t)); + ci->data = NULL; + ci->mask = NULL; + ci->wx = 0; + ci->wy = 0; + ci->sx = 0; + ci->sy = 0; + ci->reverse = 0; + ci->rfb = NULL; + cursors[i] = ci; + } + + /* clear any xfixes cursor cache (no freeing is done) */ + get_xfixes_cursor(1); + + /* manually fill in the data+masks: */ + cur_empty.data = curs_empty_data; + cur_empty.mask = curs_empty_mask; + + cur_dot.data = curs_dot_data; + cur_dot.mask = curs_dot_mask; + + cur_arrow.data = curs_arrow_data; + cur_arrow.mask = curs_arrow_mask; + cur_arrow2.data = curs_arrow2_data; + cur_arrow2.mask = curs_arrow2_mask; + cur_arrow3.data = curs_arrow3_data; + cur_arrow3.mask = curs_arrow3_mask; + cur_arrow4.data = curs_arrow4_data; + cur_arrow4.mask = curs_arrow4_mask; + cur_arrow5.data = curs_arrow5_data; + cur_arrow5.mask = curs_arrow5_mask; + cur_arrow6.data = curs_arrow6_data; + cur_arrow6.mask = curs_arrow6_mask; + + cur_root.data = curs_root_data; + cur_root.mask = curs_root_mask; + + cur_plus.data = curs_plus_data; + cur_plus.mask = curs_plus_mask; + + cur_fleur.data = curs_fleur_data; + cur_fleur.mask = curs_fleur_mask; + + cur_xterm.data = curs_xterm_data; + cur_xterm.mask = curs_xterm_mask; + + curs_copy(cursors[CURS_EMPTY], &cur_empty); n++; + curs_copy(cursors[CURS_DOT], &cur_dot); n++; + + if (alt_arrow < 1 || alt_arrow > alt_arrow_max) { + alt_arrow = 1; + } + if (alt_arrow == 1) { + curs_copy(cursors[CURS_ARROW], &cur_arrow); n++; + } else if (alt_arrow == 2) { + curs_copy(cursors[CURS_ARROW], &cur_arrow2); n++; + } else if (alt_arrow == 3) { + curs_copy(cursors[CURS_ARROW], &cur_arrow3); n++; + } else if (alt_arrow == 4) { + curs_copy(cursors[CURS_ARROW], &cur_arrow4); n++; + } else if (alt_arrow == 5) { + curs_copy(cursors[CURS_ARROW], &cur_arrow5); n++; + } else if (alt_arrow == 6) { + curs_copy(cursors[CURS_ARROW], &cur_arrow6); n++; + } else { + alt_arrow = 1; + curs_copy(cursors[CURS_ARROW], &cur_arrow); n++; + } + + curs_copy(cursors[CURS_ROOT], &cur_root); n++; + curs_copy(cursors[CURS_WM], &cur_fleur); n++; + curs_copy(cursors[CURS_TERM], &cur_xterm); n++; + curs_copy(cursors[CURS_PLUS], &cur_plus); n++; + + if (scale_cursor_str) { + scale = scale_cursor_str; + } else if (scaling && scale_str) { + scale = scale_str; + } + /* scale = NULL zeroes everything */ + parse_scale_string(scale, &scale_cursor_fac, &scaling_cursor, + &scaling_cursor_blend, &j, &j, &scaling_cursor_interpolate, + &scale_cursor_numer, &scale_cursor_denom); + + for (i=0; i<n; i++) { + /* create rfbCursors for the special cursors: */ + + cursor_info_t *ci = cursors[i]; + + if (scaling_cursor && scale_cursor_fac != 1.0) { + int w, h, x, y, k; + unsigned long *pixels; + + w = ci->wx; + h = ci->wy; + + pixels = (unsigned long *) malloc(w * h + * sizeof(unsigned long)); + + k = 0; + for (y=0; y<h; y++) { + for (x=0; x<w; x++) { + char d = ci->data[k]; + char m = ci->mask[k]; + unsigned long *p; + + p = pixels + k; + + /* set alpha on */ + *p = 0xff000000; + + if (d == ' ' && m == ' ') { + /* alpha off */ + *p = 0x00000000; + } else if (d != ' ') { + /* body */ + if (ci->reverse) { + *p |= 0x00000000; + } else { + *p |= 0x00ffffff; + } + } else if (m != ' ') { + /* edge */ + if (ci->reverse) { + *p |= 0x00ffffff; + } else { + *p |= 0x00000000; + } + } + k++; + } + } + + rfb_curs = pixels2curs(pixels, w, h, ci->sx, ci->sy, + bpp/8); + + free(pixels); + + } else { + + /* standard X cursor */ + rfb_curs = rfbMakeXCursor(ci->wx, ci->wy, + ci->data, ci->mask); + + if (ci->reverse) { + rfb_curs->foreRed = 0x0000; + rfb_curs->foreGreen = 0x0000; + rfb_curs->foreBlue = 0x0000; + rfb_curs->backRed = 0xffff; + rfb_curs->backGreen = 0xffff; + rfb_curs->backBlue = 0xffff; + } + rfb_curs->alphaSource = NULL; + + rfb_curs->xhot = ci->sx; + rfb_curs->yhot = ci->sy; + rfb_curs->cleanup = FALSE; + rfb_curs->cleanupSource = FALSE; + rfb_curs->cleanupMask = FALSE; + rfb_curs->cleanupRichSource = FALSE; + + if (bpp == 8 && indexed_color) { + /* + * use richsource in PseudoColor for better + * looking cursors (i.e. two-color). + */ + int x, y, k = 0, bw; + int black = 0, white = 1; + char d, m; + + if (dpy) { /* raw_fb hack */ + black = BlackPixel(dpy, scr); + white = WhitePixel(dpy, scr); + } + + rfb_curs->richSource = (unsigned char *) + calloc(ci->wx * ci->wy, 1); + + for (y = 0; y < ci->wy; y++) { + for (x = 0; x < ci->wx; x++) { + d = *(ci->data + k); + m = *(ci->mask + k); + if (d == ' ' && m == ' ') { + k++; + continue; + } else if (m != ' ' && d == ' ') { + bw = black; + } else { + bw = white; + } + if (ci->reverse) { + if (bw == black) { + bw = white; + } else { + bw = black; + } + } + *(rfb_curs->richSource+k) = + (unsigned char) bw; + k++; + } + } + } + } + ci->rfb = rfb_curs; + } + if (screen) { + UNLOCK(screen->cursorMutex); + } + rfbLog(" done.\n"); +} + +void setup_cursors_and_push(void) { + setup_cursors(); + first_cursor(); +} + +/* + * Descends window tree at pointer until the window cursor matches the current + * cursor. So far only used to detect if mouse is on root background or not. + * (returns 0 in that case, 1 otherwise). + * + */ +static void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) { + Window r, c; + int i, rx, ry, wx, wy; + unsigned int mask; + Window wins[10]; + int descend, maxtries = 10; + char *name, *s = multiple_cursors_mode; + static XClassHint *classhint = NULL; + int nm_info = 1; + XErrorHandler old_handler; + + X_LOCK; + + if (!strcmp(s, "default") || !strcmp(s, "X") || !strcmp(s, "arrow")) { + nm_info = 0; + } + + *(winfo->wm_name) = '\0'; + *(winfo->res_name) = '\0'; + *(winfo->res_class) = '\0'; + + + /* some times a window can go away before we get to it */ + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + + c = window; + descend = -1; + + while (c) { + wins[++descend] = c; + if (descend >= maxtries - 1) { + break; + } + if ( XTestCompareCurrentCursorWithWindow_wr(dpy, c) ) { + break; + } + /* TBD: query_pointer() */ + XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask); + } + + if (nm_info) { + int got_wm_name = 0, got_res_name = 0, got_res_class = 0; + + if (! classhint) { + classhint = XAllocClassHint(); + } + + for (i = descend; i >=0; i--) { + c = wins[i]; + if (! c) { + continue; + } + + if (! got_wm_name && XFetchName(dpy, c, &name)) { + if (name) { + if (*name != '\0') { + strcpy(winfo->wm_name, name); + got_wm_name = 1; + } + XFree(name); + } + } + if (classhint && (! got_res_name || ! got_res_class)) { + if (XGetClassHint(dpy, c, classhint)) { + char *p; + p = classhint->res_name; + if (p) { + if (*p != '\0' && ! got_res_name) { + strcpy(winfo->res_name, p); + got_res_name = 1; + } + XFree(p); + classhint->res_name = NULL; + } + p = classhint->res_class; + if (p) { + if (*p != '\0' && ! got_res_class) { + strcpy(winfo->res_class, p); + got_res_class = 1; + } + XFree(p); + classhint->res_class = NULL; + } + } + } + } + } + + XSetErrorHandler(old_handler); + trapped_xerror = 0; + + X_UNLOCK; + + *depth = descend; + *w = wins[descend]; +} + +void initialize_xfixes(void) { +#if LIBVNCSERVER_HAVE_LIBXFIXES + if (xfixes_present) { + X_LOCK; + if (use_xfixes) { + XFixesSelectCursorInput(dpy, rootwin, + XFixesDisplayCursorNotifyMask); + } else { + XFixesSelectCursorInput(dpy, rootwin, 0); + } + X_UNLOCK; + } +#endif +} + +static rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, + int xhot, int yhot, int Bpp) { + rfbCursorPtr c; + static unsigned long black = 0, white = 1; + static int first = 1; + char *bitmap, *rich, *alpha; + char *pixels_new = NULL; + int n_opaque, n_trans, n_alpha, len, histo[256]; + int send_alpha = 0, alpha_shift, thresh; + int i, x, y; + + if (first && dpy) { /* raw_fb hack */ + X_LOCK; + black = BlackPixel(dpy, scr); + white = WhitePixel(dpy, scr); + X_UNLOCK; + first = 0; + } + + if (scaling_cursor && scale_cursor_fac != 1.0) { + int W, H; + char *pixels_use = (char *) pixels; + unsigned int *pixels32 = NULL; + + W = w; + H = h; + + w = scale_round(W, scale_cursor_fac); + h = scale_round(H, scale_cursor_fac); + + pixels_new = (char *) malloc(4*w*h); + + if (sizeof(unsigned long) == 8) { + int i, j, k = 0; + /* + * to avoid 64bpp code in scale_rect() we knock + * down to unsigned int on 64bit machines: + */ + pixels32 = (unsigned int*) malloc(4*W*H); + for (j=0; j<H; j++) { + for (i=0; i<W; i++) { + *(pixels32+k) = 0xffffffff & (*(pixels+k)); + k++; + } + } + pixels_use = (char *) pixels32; + } + + scale_rect(scale_cursor_fac, scaling_cursor_blend, + scaling_cursor_interpolate, + 4, pixels_use, 4*W, pixels_new, 4*w, + W, H, w, h, 0, 0, W, H, 0); + + if (sizeof(unsigned long) == 8) { + int i, j, k = 0; + unsigned long *pixels64; + unsigned int* source = (unsigned int*) pixels_new; + /* + * now knock it back up to unsigned long: + */ + pixels64 = (unsigned long*) malloc(8*w*h); + for (j=0; j<h; j++) { + for (i=0; i<w; i++) { + *(pixels64+k) = (unsigned long) (*(source+k)); + k++; + } + } + free(pixels_new); + pixels_new = (char *) pixels64; + if (pixels32) { + free(pixels32); + pixels32 = NULL; + } + } + + pixels = (unsigned long *) pixels_new; + + xhot = scale_round(xhot, scale_cursor_fac); + yhot = scale_round(yhot, scale_cursor_fac); + } + + len = w * h; + /* for bitmap data */ + bitmap = (char *) malloc(len+1); + bitmap[len] = '\0'; + + /* for rich cursor pixel data */ + rich = (char *)calloc(Bpp*len, 1); + alpha = (char *)calloc(1*len, 1); + + n_opaque = 0; + n_trans = 0; + n_alpha = 0; + for (i=0; i<256; i++) { + histo[i] = 0; + } + + i = 0; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + unsigned long a; + + a = 0xff000000 & (*(pixels+i)); + a = a >> 24; /* alpha channel */ + if (a > 0) { + n_alpha++; + } + histo[a]++; + if (a < (unsigned int) alpha_threshold) { + n_trans++; + } else { + n_opaque++; + } + i++; + } + } + if (alpha_blend) { + send_alpha = 0; + if (Bpp == 4) { + send_alpha = 1; + } + alpha_shift = 24; + if (main_red_shift == 24 || main_green_shift == 24 || + main_blue_shift == 24) { + alpha_shift = 0; /* XXX correct? */ + } + } + if (n_opaque >= alpha_frac * n_alpha) { + thresh = alpha_threshold; + } else { + n_opaque = 0; + for (i=255; i>=0; i--) { + n_opaque += histo[i]; + thresh = i; + if (n_opaque >= alpha_frac * n_alpha) { + break; + } + } + } + + i = 0; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + unsigned long r, g, b, a; + unsigned int ui; + char *p; + + a = 0xff000000 & (*(pixels+i)); + a = a >> 24; /* alpha channel */ + + + if (a < (unsigned int) thresh) { + bitmap[i] = ' '; + } else { + bitmap[i] = 'x'; + } + + r = 0x00ff0000 & (*(pixels+i)); + g = 0x0000ff00 & (*(pixels+i)); + b = 0x000000ff & (*(pixels+i)); + r = r >> 16; /* red */ + g = g >> 8; /* green */ + b = b >> 0; /* blue */ + + if (alpha_remove && a != 0) { + r = (255 * r) / a; + g = (255 * g) / a; + b = (255 * b) / a; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + } + + if (indexed_color) { + /* + * Choose black or white for + * PseudoColor case. + */ + int value = (r+g+b)/3; + if (value > 127) { + ui = white; + } else { + ui = black; + } + } else { + /* + * Otherwise map the RGB data onto + * the framebuffer format: + */ + r = (main_red_max * r)/255; + g = (main_green_max * g)/255; + b = (main_blue_max * b)/255; + ui = 0; + ui |= (r << main_red_shift); + ui |= (g << main_green_shift); + ui |= (b << main_blue_shift); + if (send_alpha) { + ui |= (a << alpha_shift); + } + } + + /* insert value into rich source: */ + p = rich + Bpp*i; + + if (Bpp == 1) { + *((unsigned char *)p) + = (unsigned char) ui; + } else if (Bpp == 2) { + *((unsigned short *)p) + = (unsigned short) ui; + } else if (Bpp == 3) { + *((unsigned char *)p) + = (unsigned char) ((ui & 0x0000ff) >> 0); + *((unsigned char *)(p+1)) + = (unsigned char) ((ui & 0x00ff00) >> 8); + *((unsigned char *)(p+2)) + = (unsigned char) ((ui & 0xff0000) >> 16); + } else if (Bpp == 4) { + *((unsigned int *)p) + = (unsigned int) ui; + } + + /* insert alpha value into alpha source: */ + p = alpha + i; + *((unsigned char *)p) = (unsigned char) a; + + i++; + } + } + + /* create the cursor with the bitmap: */ + c = rfbMakeXCursor(w, h, bitmap, bitmap); + free(bitmap); + + if (pixels_new) { + free(pixels_new); + } + + /* set up the cursor parameters: */ + c->xhot = xhot; + c->yhot = yhot; + c->cleanup = FALSE; + c->cleanupSource = FALSE; + c->cleanupMask = FALSE; + c->cleanupRichSource = FALSE; + c->richSource = (unsigned char *) rich; + + if (alpha_blend && !indexed_color) { + c->alphaSource = (unsigned char *) alpha; + c->alphaPreMultiplied = TRUE; + } else { + free(alpha); + c->alphaSource = NULL; + } + return c; +} + +static int get_xfixes_cursor(int init) { + static unsigned long last_cursor = 0; + static int last_index = 0; + static time_t curs_times[CURS_MAX]; + static unsigned long curs_index[CURS_MAX]; + int which = CURS_ARROW; + + if (init) { + /* zero out our cache (cursors are not freed) */ + int i; + for (i=0; i<CURS_MAX; i++) { + curs_times[i] = 0; + curs_index[i] = 0; + } + last_cursor = 0; + last_index = 0; + return -1; + } + + if (xfixes_present) { +#if LIBVNCSERVER_HAVE_LIBXFIXES + int use, oldest, i; + time_t oldtime, now; + XFixesCursorImage *xfc; + + if (! got_xfixes_cursor_notify && xfixes_base_event_type) { + /* try again for XFixesCursorNotify event */ + XEvent xev; + X_LOCK; + if (XCheckTypedEvent(dpy, xfixes_base_event_type + + XFixesCursorNotify, &xev)) { + got_xfixes_cursor_notify++; + } + X_UNLOCK; + } + if (! got_xfixes_cursor_notify) { + /* evidently no cursor change, just return last one */ + if (last_index) { + return last_index; + } else { + return CURS_ARROW; + } + } + got_xfixes_cursor_notify = 0; + + /* retrieve the cursor info + pixels from server: */ + X_LOCK; + xfc = XFixesGetCursorImage(dpy); + X_UNLOCK; + if (! xfc) { + /* failure. */ + return(which); + } + + if (xfc->cursor_serial == last_cursor) { + /* same serial index: no change */ + X_LOCK; + XFree(xfc); + X_UNLOCK; + if (last_index) { + return last_index; + } else { + return CURS_ARROW; + } + } + + oldest = CURS_DYN_MIN; + if (screen && screen->cursor == cursors[oldest]->rfb) { + oldest++; + } + oldtime = curs_times[oldest]; + now = time(0); + for (i = CURS_DYN_MIN; i <= CURS_DYN_MAX; i++) { + if (screen && screen->cursor == cursors[i]->rfb) { + ; + } else if (curs_times[i] < oldtime) { + /* watch for oldest one to overwrite */ + oldest = i; + oldtime = curs_times[i]; + } + if (xfc->cursor_serial == curs_index[i]) { + /* + * got a hit with an existing cursor, + * use that one. + */ + last_cursor = curs_index[i]; + curs_times[i] = now; + last_index = i; + X_LOCK; + XFree(xfc); + X_UNLOCK; + return last_index; + } + } + + /* we need to create the cursor and overwrite oldest */ + use = oldest; + if (cursors[use]->rfb) { + /* clean up oldest if it exists */ + if (cursors[use]->rfb->richSource) { + free(cursors[use]->rfb->richSource); + cursors[use]->rfb->richSource = NULL; + } + if (cursors[use]->rfb->alphaSource) { + free(cursors[use]->rfb->alphaSource); + cursors[use]->rfb->alphaSource = NULL; + } + if (cursors[use]->rfb->source) { + free(cursors[use]->rfb->source); + cursors[use]->rfb->source = NULL; + } + if (cursors[use]->rfb->mask) { + free(cursors[use]->rfb->mask); + cursors[use]->rfb->mask = NULL; + } + free(cursors[use]->rfb); + cursors[use]->rfb = NULL; + } + + /* place cursor into our collection */ + cursors[use]->rfb = pixels2curs(xfc->pixels, xfc->width, + xfc->height, xfc->xhot, xfc->yhot, bpp/8); + + /* update time and serial index: */ + curs_times[use] = now; + curs_index[use] = xfc->cursor_serial; + last_index = use; + last_cursor = xfc->cursor_serial; + + which = last_index; + + X_LOCK; + XFree(xfc); + X_UNLOCK; +#endif + } + return(which); +} + +int known_cursors_mode(char *s) { +/* + * default: see initialize_cursors_mode() for default behavior. + * arrow: unchanging white arrow. + * Xn*: show X on root background. Optional n sets treedepth. + * some: do the heuristics for root, wm, term detection. + * most: if display have overlay or xfixes, show all cursors, + * otherwise do the same as "some" + * none: show no cursor. + */ + if (strcmp(s, "default") && strcmp(s, "arrow") && *s != 'X' && + strcmp(s, "some") && strcmp(s, "most") && strcmp(s, "none")) { + return 0; + } else { + return 1; + } +} + +void initialize_cursors_mode(void) { + char *s = multiple_cursors_mode; + if (!s || !known_cursors_mode(s)) { + rfbLog("unknown cursors mode: %s\n", s); + rfbLog("resetting cursors mode to \"default\"\n"); + if (multiple_cursors_mode) free(multiple_cursors_mode); + multiple_cursors_mode = strdup("default"); + s = multiple_cursors_mode; + } + if (!strcmp(s, "none")) { + show_cursor = 0; + } else { + /* we do NOT set show_cursor = 1, let the caller do that */ + } + + show_multiple_cursors = 0; + if (show_cursor) { + if (!strcmp(s, "default")) { + if(multiple_cursors_mode) free(multiple_cursors_mode); + multiple_cursors_mode = strdup("X"); + s = multiple_cursors_mode; + } + if (*s == 'X' || !strcmp(s, "some") || !strcmp(s, "most")) { + show_multiple_cursors = 1; + } else { + show_multiple_cursors = 0; + /* hmmm, some bug going back to arrow mode.. */ + set_rfb_cursor(CURS_ARROW); + } + if (screen) { + set_cursor_was_changed(screen); + } + } else { + if (screen) { + screen->cursor = NULL; /* dangerous? */ + set_cursor_was_changed(screen); + } + } +} + +int get_which_cursor(void) { + int which = CURS_ARROW; + + if (show_multiple_cursors) { + int depth; + static win_str_info_t winfo; + static int first = 1, depth_cutoff = -1; + Window win; + XErrorHandler old_handler; + int mode = 0; + + if (drag_in_progress || button_mask) { + /* XXX not exactly what we want for menus */ + return -1; + } + + if (!strcmp(multiple_cursors_mode, "arrow")) { + /* should not happen... */ + return CURS_ARROW; + } else if (!strcmp(multiple_cursors_mode, "default")) { + mode = 0; + } else if (!strcmp(multiple_cursors_mode, "X")) { + mode = 1; + } else if (!strcmp(multiple_cursors_mode, "some")) { + mode = 2; + } else if (!strcmp(multiple_cursors_mode, "most")) { + mode = 3; + } + + if (mode == 3 && xfixes_present && use_xfixes) { + return get_xfixes_cursor(0); + } + + if (depth_cutoff < 0) { + int din; + if (sscanf(multiple_cursors_mode, "X%d", &din) == 1) { + depth_cutoff = din; + } else { + depth_cutoff = 0; + } + } + + if (first) { + winfo.wm_name = (char *) malloc(1024); + winfo.res_name = (char *) malloc(1024); + winfo.res_class = (char *) malloc(1024); + } + first = 0; + + tree_descend_cursor(&depth, &win, &winfo); + + if (depth <= depth_cutoff && !subwin) { + which = CURS_ROOT; + + } else if (mode == 2 || mode == 3) { + int which0 = which; + + /* apply crude heuristics to choose a cursor... */ + if (win) { + int ratio = 10, x, y; + unsigned int w, h, bw, d; + Window r; + + trapped_xerror = 0; + X_LOCK; + old_handler = XSetErrorHandler(trap_xerror); + + /* "narrow" windows are WM */ + if (XGetGeometry(dpy, win, &r, &x, &y, &w, &h, + &bw, &d)) { + if (w > ratio * h || h > ratio * w) { + which = CURS_WM; + } + } + XSetErrorHandler(old_handler); + X_UNLOCK; + trapped_xerror = 0; + } + if (which == which0) { + /* the string "term" mean I-beam. */ + char *name, *class; + lowercase(winfo.res_name); + lowercase(winfo.res_class); + name = winfo.res_name; + class = winfo.res_class; + if (strstr(name, "term")) { + which = CURS_TERM; + } else if (strstr(class, "term")) { + which = CURS_TERM; + } else if (strstr(name, "text")) { + which = CURS_TERM; + } else if (strstr(class, "text")) { + which = CURS_TERM; + } else if (strstr(name, "onsole")) { + which = CURS_TERM; + } else if (strstr(class, "onsole")) { + which = CURS_TERM; + } else if (strstr(name, "cmdtool")) { + which = CURS_TERM; + } else if (strstr(class, "cmdtool")) { + which = CURS_TERM; + } else if (strstr(name, "shelltool")) { + which = CURS_TERM; + } else if (strstr(class, "shelltool")) { + which = CURS_TERM; + } + } + } + } + return which; +} + +static void set_cursor_was_changed(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + if (! s) { + return; + } + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + cl->cursorWasChanged = TRUE; + } + rfbReleaseClientIterator(iter); +} + +#if 0 +/* not yet used */ +static void set_cursor_was_moved(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + if (! s) { + return; + } + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + cl->cursorWasMoved = TRUE; + } + rfbReleaseClientIterator(iter); +} +#endif + +void restore_cursor_shape_updates(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int count = 0; + + if (! s || ! s->clientHead) { + return; + } + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + int changed = 0; + ClientData *cd = (ClientData *) cl->clientData; + + if (cd->had_cursor_shape_updates) { + rfbLog("restoring enableCursorShapeUpdates for client" + " 0x%x\n", cl); + cl->enableCursorShapeUpdates = TRUE; + changed = 1; + } + if (cd->had_cursor_pos_updates) { + rfbLog("restoring enableCursorPosUpdates for client" + " 0x%x\n", cl); + cl->enableCursorPosUpdates = TRUE; + changed = 1; + } + if (changed) { + cl->cursorWasChanged = TRUE; + count++; + } + } + rfbReleaseClientIterator(iter); +} + +void disable_cursor_shape_updates(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + static int changed = 0; + int count = 0; + + if (! s || ! s->clientHead) { + return; + } + + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd; + cd = (ClientData *) cl->clientData; + + if (cl->enableCursorShapeUpdates) { + cd->had_cursor_shape_updates = 1; + count++; + if (debug_pointer) { + rfbLog("%s disable HCSU\n", cl->host); + } + } + if (cl->enableCursorPosUpdates) { + cd->had_cursor_pos_updates = 1; + count++; + if (debug_pointer) { + rfbLog("%s disable HCPU\n", cl->host); + } + } + + cl->enableCursorShapeUpdates = FALSE; + cl->enableCursorPosUpdates = FALSE; + cl->cursorWasChanged = FALSE; + } + rfbReleaseClientIterator(iter); + + if (count) { + changed = 1; + } +} + +int cursor_shape_updates_clients(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int count = 0; + + if (! s) { + return 0; + } + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->enableCursorShapeUpdates) { + count++; + } + } + rfbReleaseClientIterator(iter); + return count; +} + +int cursor_pos_updates_clients(rfbScreenInfoPtr s) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int count = 0; + + if (! s) { + return 0; + } + iter = rfbGetClientIterator(s); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->enableCursorPosUpdates) { + count++; + } + } + rfbReleaseClientIterator(iter); + return count; +} + +/* + * Record rfb cursor position screen->cursorX, etc (a la defaultPtrAddEvent()) + * Then set up for sending rfbCursorPosUpdates back + * to clients that understand them. This seems to be TightVNC specific. + */ +void cursor_position(int x, int y) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int cnt = 0, nonCursorPosUpdates_clients = 0; + int x_in = x, y_in = y; + + /* x and y are current positions of X11 pointer on the X11 display */ + if (!screen) { + return; + } + + if (scaling) { + x = ((double) x / dpy_x) * scaled_x; + x = nfix(x, scaled_x); + y = ((double) y / dpy_y) * scaled_y; + y = nfix(y, scaled_y); + } + + if (x == screen->cursorX && y == screen->cursorY) { + return; + } + + LOCK(screen->cursorMutex); + screen->cursorX = x; + screen->cursorY = y; + UNLOCK(screen->cursorMutex); + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (! cl->enableCursorPosUpdates) { + nonCursorPosUpdates_clients++; + continue; + } + if (! cursor_pos_updates) { + continue; + } + if (cl == last_pointer_client) { + /* + * special case if this client was the last one to + * send a pointer position. + */ + if (x_in == cursor_x && y_in == cursor_y) { + cl->cursorWasMoved = FALSE; + } else { + /* an X11 app evidently warped the pointer */ + if (debug_pointer) { + rfbLog("cursor_position: warp " + "detected dx=%3d dy=%3d\n", + cursor_x - x, cursor_y - y); + } + cl->cursorWasMoved = TRUE; + cnt++; + } + } else { + cl->cursorWasMoved = TRUE; + cnt++; + } + } + rfbReleaseClientIterator(iter); + + if (debug_pointer && cnt) { + rfbLog("cursor_position: sent position x=%3d y=%3d to %d" + " clients\n", x, y, cnt); + } +} + +static void set_rfb_cursor(int which) { + + if (! show_cursor) { + return; + } + if (! screen) { + return; + } + + if (!cursors[which] || !cursors[which]->rfb) { + rfbLog("non-existent cursor: which=%d\n", which); + return; + } else { + rfbSetCursor(screen, cursors[which]->rfb); + } +} + +void set_no_cursor(void) { + set_rfb_cursor(CURS_EMPTY); +} + +int set_cursor(int x, int y, int which) { + static int last = -1; + int changed_cursor = 0; + + if (x || y) {} /* unused vars warning: */ + + if (which < 0) { + which = last; + } + if (last < 0 || which != last) { + set_rfb_cursor(which); + changed_cursor = 1; + } + last = which; + + return changed_cursor; +} + +/* + * routine called periodically to update cursor aspects, this catches + * warps and cursor shape changes. + */ +int check_x11_pointer(void) { + Window root_w, child_w; + rfbBool ret; + int root_x, root_y, win_x, win_y; + int x, y; + unsigned int mask; + + if (raw_fb && ! dpy) return 0; /* raw_fb hack */ + + X_LOCK; + ret = XQueryPointer(dpy, rootwin, &root_w, &child_w, &root_x, &root_y, + &win_x, &win_y, &mask); + X_UNLOCK; + + if (! ret) { + return 0; + } + if (debug_pointer) { + static int last_x = -1, last_y = -1; + if (root_x != last_x || root_y != last_y) { + rfbLog("XQueryPointer: x:%4d, y:%4d)\n", + root_x, root_y); + } + last_x = root_x; + last_y = root_y; + } + + /* offset subtracted since XQueryPointer relative to rootwin */ + x = root_x - off_x - coff_x; + y = root_y - off_y - coff_y; + + /* record the cursor position in the rfb screen */ + cursor_position(x, y); + + /* change the cursor shape if necessary */ + return set_cursor(x, y, get_which_cursor()); +} + + diff --git a/x11vnc/cursor.h b/x11vnc/cursor.h new file mode 100644 index 0000000..6b5f646 --- /dev/null +++ b/x11vnc/cursor.h @@ -0,0 +1,33 @@ +#ifndef _X11VNC_CURSOR_H +#define _X11VNC_CURSOR_H + +/* -- cursor.h -- */ + +extern int xfixes_present; +extern int use_xfixes; +extern int got_xfixes_cursor_notify; +extern int cursor_changes; +extern int alpha_threshold; +extern double alpha_frac; +extern int alpha_remove; +extern int alpha_blend; +extern int alt_arrow; +extern int alt_arrow_max; + + +extern void first_cursor(void); +extern void setup_cursors_and_push(void); +extern void initialize_xfixes(void); +extern int known_cursors_mode(char *s); +extern void initialize_cursors_mode(void); +extern int get_which_cursor(void); +extern void restore_cursor_shape_updates(rfbScreenInfoPtr s); +extern void disable_cursor_shape_updates(rfbScreenInfoPtr s); +extern int cursor_shape_updates_clients(rfbScreenInfoPtr s); +extern int cursor_pos_updates_clients(rfbScreenInfoPtr s); +extern void cursor_position(int x, int y); +extern void set_no_cursor(void); +extern int set_cursor(int x, int y, int which); +extern int check_x11_pointer(void); + +#endif /* _X11VNC_CURSOR_H */ diff --git a/x11vnc/enums.h b/x11vnc/enums.h new file mode 100644 index 0000000..bff4397 --- /dev/null +++ b/x11vnc/enums.h @@ -0,0 +1,22 @@ +#ifndef _X11VNC_ENUMS_H +#define _X11VNC_ENUMS_H + +/* -- enums.h -- */ + +enum { + LR_UNSET = 0, + LR_UNKNOWN, + LR_DIALUP, + LR_BROADBAND, + LR_LAN +}; + +enum scroll_types { + SCR_NONE = 0, + SCR_MOUSE, + SCR_KEY, + SCR_FAIL, + SCR_SUCCESS +}; + +#endif /* _X11VNC_ENUMS_H */ diff --git a/x11vnc/gui.c b/x11vnc/gui.c new file mode 100644 index 0000000..9e6ac54 --- /dev/null +++ b/x11vnc/gui.c @@ -0,0 +1,638 @@ +/* -- gui.c -- */ + +#include "x11vnc.h" +#include "xevents.h" +#include "win_utils.h" +#include "remote.h" +#include "cleanup.h" + +#include "tkx11vnc.h" + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 +#define XEMBED_VERSION 0 +#define XEMBED_MAPPED (1 << 0) + +int icon_mode = 0; /* hack for -gui tray */ +char *icon_mode_file = NULL; +FILE *icon_mode_fh = NULL; +int icon_mode_socks[ICON_MODE_SOCKS]; +int tray_manager_ok = 0; +Window tray_request = None; +Window tray_window = None; +int tray_unembed = 0; + + +char *get_gui_code(void); +int tray_embed(Window iconwin, int remove); +void do_gui(char *opts, int sleep); + + +static Window tweak_tk_window_id(Window win); +static int tray_manager_running(Display *d, Window *manager); +static void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int start_x11vnc, + int simple_gui, pid_t parent, char *gui_opts); + + +char *get_gui_code(void) { + return gui_code; +} + +static Window tweak_tk_window_id(Window win) { + char *name = NULL; + Window parent, new; + + /* hack for tk, does not report outermost window */ + new = win; + parent = parent_window(win, &name); + if (parent && name != NULL) { + lowercase(name); + if (strstr(name, "wish") || strstr(name, "x11vnc")) { + new = parent; + rfbLog("tray_embed: using parent: %s\n", name); + } + } + if (name != NULL) { + XFree(name); + } + return new; +} + +int tray_embed(Window iconwin, int remove) { + XEvent ev; + XErrorHandler old_handler; + Window manager; + Atom xembed_info; + Atom tatom; + XWindowAttributes attr; + long info[2] = {XEMBED_VERSION, XEMBED_MAPPED}; + long data = 0; + + if (remove) { + if (!valid_window(iconwin, &attr, 1)) { + return 0; + } + iconwin = tweak_tk_window_id(iconwin); + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + + /* + * unfortunately no desktops seem to obey this + * part of the XEMBED spec yet... + */ + XReparentWindow(dpy, iconwin, rootwin, 0, 0); + + XSetErrorHandler(old_handler); + if (trapped_xerror) { + trapped_xerror = 0; + return 0; + } + trapped_xerror = 0; + return 1; + } + + xembed_info = XInternAtom(dpy, "_XEMBED_INFO", False); + if (xembed_info == None) { + return 0; + } + + if (!tray_manager_running(dpy, &manager)) { + return 0; + } + + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = manager; + ev.xclient.message_type = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", + False); + ev.xclient.format = 32; + ev.xclient.data.l[0] = CurrentTime; + ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK; + ev.xclient.data.l[2] = iconwin; + ev.xclient.data.l[3] = 0; + ev.xclient.data.l[4] = 0; + + if (!valid_window(iconwin, &attr, 1)) { + return 0; + } + + iconwin = tweak_tk_window_id(iconwin); + ev.xclient.data.l[2] = iconwin; + + XUnmapWindow(dpy, iconwin); + + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + + XSendEvent(dpy, manager, False, NoEventMask, &ev); + XSync(dpy, False); + + if (trapped_xerror) { + XSetErrorHandler(old_handler); + trapped_xerror = 0; + return 0; + } + + XChangeProperty(dpy, iconwin, xembed_info, xembed_info, 32, + PropModeReplace, (unsigned char *)&info, 2); + + /* kludge for KDE evidently needed... */ + tatom = XInternAtom(dpy, "KWM_DOCKWINDOW", False); + XChangeProperty(dpy, iconwin, tatom, tatom, 32, PropModeReplace, + (unsigned char *)&data, 1); + tatom = XInternAtom(dpy, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); + XChangeProperty(dpy, iconwin, tatom, XA_WINDOW, 32, PropModeReplace, + (unsigned char *)&data, 1); + + XSetErrorHandler(old_handler); + trapped_xerror = 0; + return 1; +} + +static int tray_manager_running(Display *d, Window *manager) { + char tray_string[100]; + Atom tray_manager; + Window tray_win; + + if (manager) { + *manager = None; + } + sprintf(tray_string, "_NET_SYSTEM_TRAY_S%d", scr); + + tray_manager = XInternAtom(d, tray_string, True); + if (tray_manager == None) { + return 0; + } + + tray_win = XGetSelectionOwner(d, tray_manager); + if (manager) { + *manager = tray_win; + } + + if (tray_win == None) { + return 0; + } else { + return 1; + } +} + +static char *gui_geometry = NULL; +static int icon_in_tray = 0; +static char *icon_mode_embed_id = NULL; +static char *icon_mode_font = NULL; +static char *icon_mode_params = NULL; + +static void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int start_x11vnc, + int simple_gui, pid_t parent, char *gui_opts) { + char *x11vnc_xdisplay = NULL; + char extra_path[] = ":/usr/local/bin:/usr/bin/X11:/usr/sfw/bin" + ":/usr/X11R6/bin:/usr/openwin/bin:/usr/dt/bin"; + char cmd[100]; + char *wish = NULL, *orig_path, *full_path, *tpath, *p; + char *old_xauth = NULL; + int try_max = 4, sleep = 300; + pid_t mypid = getpid(); + FILE *pipe, *tmpf; + + if (*gui_code == '\0') { + rfbLog("gui: gui not compiled into this program.\n"); + exit(0); + } + if (getenv("DISPLAY") != NULL) { + /* worst case */ + x11vnc_xdisplay = strdup(getenv("DISPLAY")); + } + if (use_dpy) { + /* better */ + x11vnc_xdisplay = strdup(use_dpy); + } + if (connect_to_x11vnc) { + int rc, i; + rfbLogEnable(1); + if (! client_connect_file) { + if (getenv("XAUTHORITY") != NULL) { + old_xauth = strdup(getenv("XAUTHORITY")); + } else { + old_xauth = strdup(""); + } + dpy = XOpenDisplay(x11vnc_xdisplay); + if (! dpy && auth_file) { + set_env("XAUTHORITY", auth_file); + dpy = XOpenDisplay(x11vnc_xdisplay); + } + if (! dpy && ! x11vnc_xdisplay) { + /* worstest case */ + x11vnc_xdisplay = strdup(":0"); + dpy = XOpenDisplay(x11vnc_xdisplay); + } + if (! dpy) { + rfbLog("gui: could not open x11vnc " + "display: %s\n", NONUL(x11vnc_xdisplay)); + exit(1); + } + scr = DefaultScreen(dpy); + rootwin = RootWindow(dpy, scr); + initialize_vnc_connect_prop(); + } + usleep(2200*1000); + fprintf(stderr, "\n"); + if (!quiet) { + rfbLog("gui: trying to contact a x11vnc server at X" + " display %s ...\n", NONUL(x11vnc_xdisplay)); + } + for (i=0; i<try_max; i++) { + usleep(sleep*1000); + if (!quiet) { + rfbLog("gui: pinging %s try=%d ...\n", + NONUL(x11vnc_xdisplay), i+1); + } + rc = send_remote_cmd("qry=ping", 1, 1); + if (rc == 0) { + break; + } + if (parent && mypid != parent && kill(parent, 0) != 0) { + rfbLog("gui: parent process %d has gone" + " away: bailing out.\n", parent); + rc = 1; + break; + } + } + set_env("X11VNC_XDISPLAY", x11vnc_xdisplay); + if (getenv("XAUTHORITY") != NULL) { + set_env("X11VNC_AUTH_FILE", getenv("XAUTHORITY")); + } + if (rc == 0) { + rfbLog("gui: ping succeeded.\n"); + set_env("X11VNC_CONNECT", "1"); + } else { + rfbLog("gui: could not connect to: '%s', try" + " again manually.\n", x11vnc_xdisplay); + } + if (client_connect_file) { + set_env("X11VNC_CONNECT_FILE", client_connect_file); + } + if (dpy) { + XCloseDisplay(dpy); + dpy = NULL; + } + if (old_xauth) { + if (*old_xauth == '\0') { + /* wasn't set, hack it out if it is now */ + char *xauth = getenv("XAUTHORITY"); + if (xauth) { + *(xauth-2) = '_'; /* yow */ + } + } else { + set_env("XAUTHORITY", old_xauth); + } + free(old_xauth); + } + rfbLogEnable(0); + } + + orig_path = getenv("PATH"); + if (! orig_path) { + orig_path = strdup("/bin:/usr/bin:/usr/bin/X11"); + } + full_path = (char *) malloc(strlen(orig_path)+strlen(extra_path)+1); + strcpy(full_path, orig_path); + strcat(full_path, extra_path); + + tpath = strdup(full_path); + p = strtok(tpath, ":"); + + while (p) { + char *try; + struct stat sbuf; + char *wishes[] = {"wish", "wish8.3", "wish8.4", "wish8.5", + "wish8.0"}; + int nwishes = 3, i; + + try = (char *) malloc(strlen(p) + 1 + strlen("wish8.4") + 1); + for (i=0; i<nwishes; i++) { + sprintf(try, "%s/%s", p, wishes[i]); + if (stat(try, &sbuf) == 0) { + /* assume executable, should check mode */ + wish = wishes[i]; + } + } + free(try); + if (wish) { + break; + } + p = strtok(NULL, ":"); + } + free(tpath); + if (!wish) { + wish = strdup("wish"); + } + set_env("PATH", full_path); + set_env("DISPLAY", gui_xdisplay); + set_env("X11VNC_PROG", program_name); + set_env("X11VNC_CMDLINE", program_cmdline); + set_env("X11VNC_WISHCMD", wish); + if (simple_gui) { + set_env("X11VNC_SIMPLE_GUI", "1"); + } + if (gui_opts) { + set_env("X11VNC_GUI_PARAMS", gui_opts); + } + if (gui_geometry) { + set_env("X11VNC_GUI_GEOM", gui_geometry); + } + if (start_x11vnc) { + set_env("X11VNC_STARTED", "1"); + } + if (icon_mode) { + set_env("X11VNC_ICON_MODE", "1"); + if (icon_mode_file) { + set_env("X11VNC_CLIENT_FILE", icon_mode_file); + } + if (icon_in_tray) { + if (tray_manager_ok) { + set_env("X11VNC_ICON_MODE", "TRAY:RUNNING"); + } else { + set_env("X11VNC_ICON_MODE", "TRAY"); + } + } else { + set_env("X11VNC_ICON_MODE", "ICON"); + } + if (icon_mode_params) { + char *p, *str = strdup(icon_mode_params); + p = strtok(str, ":-/,.+"); + while (p) { + if(strstr(p, "setp") == p) { + set_env("X11VNC_ICON_SETPASS", "1"); + } else if(strstr(p, "noadvanced") == p) { + set_env("X11VNC_ICON_NOADVANCED", "1"); + } else if(strstr(p, "minimal") == p) { + set_env("X11VNC_ICON_MINIMAL", "1"); + } else if (strstr(p, "0x") == p) { + set_env("X11VNC_ICON_EMBED_ID", p); + icon_mode_embed_id = strdup(p); + } + p = strtok(NULL, ":-/,.+"); + } + free(str); + } + } + if (icon_mode_font) { + set_env("X11VNC_ICON_FONT", icon_mode_font); + } + + if (no_external_cmds) { + fprintf(stderr, "cannot run external commands in -nocmds " + "mode:\n"); + fprintf(stderr, " \"%s\"\n", "gui + wish"); + fprintf(stderr, " exiting.\n"); + fflush(stderr); + exit(1); + } + + tmpf = tmpfile(); + if (tmpf == NULL) { + /* if no tmpfile, use a pipe */ + if (icon_mode_embed_id) { + if (strlen(icon_mode_embed_id) < 20) { + strcat(cmd, " -use "); + strcat(cmd, icon_mode_embed_id); + } + } + pipe = popen(cmd, "w"); + if (! pipe) { + fprintf(stderr, "could not run: %s\n", cmd); + perror("popen"); + } + fprintf(pipe, "%s", gui_code); + pclose(pipe); + } else { + /* + * we prefer a tmpfile since then this x11vnc process + * will then be gone, otherwise the x11vnc program text + * will still be in use. + */ + int n = fileno(tmpf); + fprintf(tmpf, "%s", gui_code); + fflush(tmpf); + rewind(tmpf); + dup2(n, 0); + close(n); + if (icon_mode_embed_id) { + execlp(wish, wish, "-", "-use", icon_mode_embed_id, + (char *) NULL); + } else { + execlp(wish, wish, "-", (char *) NULL); + } + fprintf(stderr, "could not exec wish: %s -\n", wish); + perror("execlp"); + } + exit(0); +} + +void do_gui(char *opts, int sleep) { + char *s, *p; + char *old_xauth = NULL; + char *gui_xdisplay = NULL; + int got_gui_xdisplay = 0; + int start_x11vnc = 1; + int connect_to_x11vnc = 0; + int simple_gui = 0, none_gui = 0; + Display *test_dpy; + + if (opts) { + s = strdup(opts); + } else { + s = strdup(""); + } + + if (use_dpy) { + /* worst case */ + gui_xdisplay = strdup(use_dpy); + + } + if (getenv("DISPLAY") != NULL) { + /* better */ + gui_xdisplay = strdup(getenv("DISPLAY")); + } + + p = strtok(s, ","); + + while(p) { + if (*p == '\0') { + ; + } else if (strchr(p, ':') != NULL) { + /* best */ + if (gui_xdisplay) { + free(gui_xdisplay); + } + gui_xdisplay = strdup(p); + got_gui_xdisplay = 1; + } else if (!strcmp(p, "wait")) { + start_x11vnc = 0; + connect_to_x11vnc = 0; + } else if (!strcmp(p, "none")) { + none_gui = 1; + } else if (!strcmp(p, "conn") || !strcmp(p, "connect")) { + start_x11vnc = 0; + connect_to_x11vnc = 1; + } else if (!strcmp(p, "ez") || !strcmp(p, "simple")) { + simple_gui = 1; + } else if (strstr(p, "iconfont") == p) { + char *q; + if ((q = strchr(p, '=')) != NULL) { + icon_mode_font = strdup(q+1); + } + } else if (!strcmp(p, "full")) { + ; + } else if (strstr(p, "tray") == p || strstr(p, "icon") == p) { + char *q; + icon_mode = 1; + if ((q = strchr(p, '=')) != NULL) { + icon_mode_params = strdup(q+1); + if (strstr(icon_mode_params, "setp")) { + deny_all = 1; + } + } + if (strstr(p, "tray") == p) { + icon_in_tray = 1; + } + } else if (strstr(p, "geom") == p) { + char *q; + if ((q = strchr(p, '=')) != NULL) { + gui_geometry = strdup(q+1); + } + } else { + fprintf(stderr, "unrecognized gui opt: %s\n", p); + } + + p = strtok(NULL, ","); + } + free(s); + + if (none_gui) { + if (!start_x11vnc) { + exit(0); + } + return; + } + if (start_x11vnc) { + connect_to_x11vnc = 1; + } + + if (icon_mode && !got_gui_xdisplay) { + /* for tray mode, prefer the polled DISPLAY */ + if (use_dpy) { + if (gui_xdisplay) { + free(gui_xdisplay); + } + gui_xdisplay = strdup(use_dpy); + } + } + + if (! gui_xdisplay) { + fprintf(stderr, "error: cannot determine X DISPLAY for gui" + " to display on.\n"); + exit(1); + } + if (!quiet) { + fprintf(stderr, "starting gui, trying display: %s\n", + gui_xdisplay); + } + test_dpy = XOpenDisplay(gui_xdisplay); + if (! test_dpy && auth_file) { + if (getenv("XAUTHORITY") != NULL) { + old_xauth = strdup(getenv("XAUTHORITY")); + } + set_env("XAUTHORITY", auth_file); + test_dpy = XOpenDisplay(gui_xdisplay); + } + if (! test_dpy) { + if (! old_xauth && getenv("XAUTHORITY") != NULL) { + old_xauth = strdup(getenv("XAUTHORITY")); + } + set_env("XAUTHORITY", ""); + test_dpy = XOpenDisplay(gui_xdisplay); + } + if (! test_dpy) { + fprintf(stderr, "error: cannot connect to gui X DISPLAY: %s\n", + gui_xdisplay); + exit(1); + } + if (icon_mode && icon_in_tray) { + if (tray_manager_running(test_dpy, NULL)) { + tray_manager_ok = 1; + } else { + tray_manager_ok = 0; + } + } + XCloseDisplay(test_dpy); + + if (start_x11vnc) { + +#if LIBVNCSERVER_HAVE_FORK + /* fork into the background now */ + int p; + pid_t parent = getpid(); + + if (icon_mode) { + char tf[100]; + double dn = dnow(); + struct stat sbuf; + /* FIXME */ + dn = dn - ((int) dn); + sprintf(tf, "/tmp/x11vnc.tray%d%d", (int) (1000000*dn), + (int) getpid()); + unlink(tf); + /* race begins.. */ + if (stat(tf, &sbuf) == 0) { + icon_mode = 0; + } else { + icon_mode_fh = fopen(tf, "w"); + if (! icon_mode_fh) { + icon_mode = 0; + } else { + chmod(tf, 0400); + icon_mode_file = strdup(tf); + fprintf(icon_mode_fh, "none\n"); + fprintf(icon_mode_fh, "none\n"); + fflush(icon_mode_fh); + if (! got_connect_once) { + /* want -forever for tray */ + connect_once = 0; + } + } + } + } + + if ((p = fork()) > 0) { + ; /* parent */ + } else if (p == -1) { + fprintf(stderr, "could not fork\n"); + perror("fork"); + clean_up_exit(1); + } else { + if (sleep > 0) { + usleep(sleep * 1000 * 1000); + } + run_gui(gui_xdisplay, connect_to_x11vnc, start_x11vnc, + simple_gui, parent, opts); + exit(1); + } +#else + fprintf(stderr, "system does not support fork: start " + "x11vnc in the gui.\n"); + start_x11vnc = 0; +#endif + } + if (!start_x11vnc) { + run_gui(gui_xdisplay, connect_to_x11vnc, start_x11vnc, + simple_gui, 0, opts); + exit(1); + } + if (old_xauth) { + set_env("XAUTHORITY", old_xauth); + } +} + + diff --git a/x11vnc/gui.h b/x11vnc/gui.h new file mode 100644 index 0000000..7784445 --- /dev/null +++ b/x11vnc/gui.h @@ -0,0 +1,19 @@ +#ifndef _X11VNC_GUI_H +#define _X11VNC_GUI_H + +/* -- gui.h -- */ + +extern int icon_mode; +extern char *icon_mode_file; +extern FILE *icon_mode_fh; +extern int icon_mode_socks[]; +extern int tray_manager_ok; +extern Window tray_request; +extern Window tray_window; +extern int tray_unembed; + +extern char *get_gui_code(void); +extern int tray_embed(Window iconwin, int remove); +extern void do_gui(char *opts, int sleep); + +#endif /* _X11VNC_GUI_H */ diff --git a/x11vnc/help.c b/x11vnc/help.c new file mode 100644 index 0000000..0d6ddd5 --- /dev/null +++ b/x11vnc/help.c @@ -0,0 +1,2212 @@ +/* -- help.c -- */ + +#include "x11vnc.h" +#include "xdamage.h" +#include "cursor.h" + +/* + * text printed out under -help option + */ + +void print_help(int mode); +void xopen_display_fail_message(char *disp); +void nopassword_warning_msg(int gotloc); + + +void print_help(int mode) { +#if !SMALL_FOOTPRINT + char help[] = +"\n" +"x11vnc: allow VNC connections to real X11 displays. %s\n" +"\n" +"Typical usage is:\n" +"\n" +" Run this command in a shell on the remote machine \"far-host\"\n" +" with X session you wish to view:\n" +"\n" +" x11vnc -display :0\n" +"\n" +" Then run this in another window on the machine you are sitting at:\n" +"\n" +" vncviewer far-host:0\n" +"\n" +"Once x11vnc establishes connections with the X11 server and starts listening\n" +"as a VNC server it will print out a string: PORT=XXXX where XXXX is typically\n" +"5900 (the default VNC server port). One would next run something like\n" +"this on the local machine: \"vncviewer hostname:N\" where \"hostname\" is\n" +"the name of the machine running x11vnc and N is XXXX - 5900, i.e. usually\n" +"\"vncviewer hostname:0\".\n" +"\n" +"By default x11vnc will not allow the screen to be shared and it will exit\n" +"as soon as the client disconnects. See -shared and -forever below to override\n" +"these protections. See the FAQ for details how to tunnel the VNC connection\n" +"through an encrypted channel such as ssh(1). In brief:\n" +"\n" +" ssh -L 5900:localhost:5900 far-host 'x11vnc -localhost -display :0'\n" +"\n" +" vncviewer -encodings 'copyrect tight zrle hextile' localhost:0\n" +"\n" +"Also, use of a VNC password (-rfbauth or -passwdfile) is strongly recommend.\n" +"\n" +"For additional info see: http://www.karlrunge.com/x11vnc/\n" +" and http://www.karlrunge.com/x11vnc/#faq\n" +"\n" +"\n" +"Rudimentary config file support: if the file $HOME/.x11vncrc exists then each\n" +"line in it is treated as a single command line option. Disable with -norc.\n" +"For each option name, the leading character \"-\" is not required. E.g. a\n" +"line that is either \"forever\" or \"-forever\" may be used and are equivalent.\n" +"Likewise \"wait 100\" or \"-wait 100\" are acceptable and equivalent lines.\n" +"The \"#\" character comments out to the end of the line in the usual way\n" +"(backslash it for a literal). Leading and trailing whitespace is trimmed off.\n" +"Lines may be continued with a \"\\\" as the last character of a line (it\n" +"becomes a space character).\n" +"\n" +"Options:\n" +"\n" +"-display disp X11 server display to connect to, usually :0. The X\n" +" server process must be running on same machine and\n" +" support MIT-SHM. Equivalent to setting the DISPLAY\n" +" environment variable to \"disp\".\n" +"-auth file Set the X authority file to be \"file\", equivalent to\n" +" setting the XAUTHORITY environment variable to \"file\"\n" +" before startup. Same as -xauth file. See Xsecurity(7),\n" +" xauth(1) man pages for more info.\n" +"\n" +"-id windowid Show the window corresponding to \"windowid\" not\n" +" the entire display. New windows like popup menus,\n" +" transient toplevels, etc, may not be seen or may be\n" +" clipped. Disabling SaveUnders or BackingStore in the\n" +" X server may help show them. x11vnc may crash if the\n" +" window is initially partially obscured, changes size,\n" +" is iconified, etc. Some steps are taken to avoid this\n" +" and the -xrandr mechanism is used to track resizes. Use\n" +" xwininfo(1) to get the window id, or use \"-id pick\"\n" +" to have x11vnc run xwininfo(1) for you and extract\n" +" the id. The -id option is useful for exporting very\n" +" simple applications (e.g. the current view on a webcam).\n" +"-sid windowid As -id, but instead of using the window directly it\n" +" shifts a root view to it: this shows SaveUnders menus,\n" +" etc, although they will be clipped if they extend beyond\n" +" the window.\n" +"-clip WxH+X+Y Only show the sub-region of the full display that\n" +" corresponds to the rectangle with size WxH and offset\n" +" +X+Y. The VNC display has size WxH (i.e. smaller than\n" +" the full display). This also works for -id/-sid mode\n" +" where the offset is relative to the upper left corner\n" +" of the selected window.\n" +"\n" +"-flashcmap In 8bpp indexed color, let the installed colormap flash\n" +" as the pointer moves from window to window (slow).\n" +"-shiftcmap n Rare problem, but some 8bpp displays use less than 256\n" +" colorcells (e.g. 16-color grayscale, perhaps the other\n" +" bits are used for double buffering) *and* also need to\n" +" shift the pixels values away from 0, .., ncells. \"n\"\n" +" indicates the shift to be applied to the pixel values.\n" +" To see the pixel values set DEBUG_CMAP=1 to print out\n" +" a colormap histogram. Example: -shiftcmap 240\n" +"-notruecolor For 8bpp displays, force indexed color (i.e. a colormap)\n" +" even if it looks like 8bpp TrueColor (rare problem).\n" +"-visual n Experimental option: probably does not do what you\n" +" think. It simply *forces* the visual used for the\n" +" framebuffer; this may be a bad thing... (e.g. messes\n" +" up colors or cause a crash). It is useful for testing\n" +" and for some workarounds. n may be a decimal number,\n" +" or 0x hex. Run xdpyinfo(1) for the values. One may\n" +" also use \"TrueColor\", etc. see <X11/X.h> for a list.\n" +" If the string ends in \":m\" then for better or for\n" +" worse the visual depth is forced to be m.\n" +"\n" +"-overlay Handle multiple depth visuals on one screen, e.g. 8+24\n" +" and 24+8 overlay visuals (the 32 bits per pixel are\n" +" packed with 8 for PseudoColor and 24 for TrueColor).\n" +"\n" +" Currently -overlay only works on Solaris via\n" +" XReadScreen(3X11) and IRIX using XReadDisplay(3).\n" +" On Solaris there is a problem with image \"bleeding\"\n" +" around transient popup menus (but not for the menu\n" +" itself): a workaround is to disable SaveUnders\n" +" by passing the \"-su\" argument to Xsun (in\n" +" /etc/dt/config/Xservers).\n" +"\n" +" Use -overlay as a workaround for situations like these:\n" +" Some legacy applications require the default visual to\n" +" be 8bpp (8+24), or they will use 8bpp PseudoColor even\n" +" when the default visual is depth 24 TrueColor (24+8).\n" +" In these cases colors in some windows will be incorrect\n" +" in x11vnc unless -overlay is used. Another use of\n" +" -overlay is to enable showing the exact mouse cursor\n" +" shape (details below).\n" +"\n" +" Under -overlay, performance will be somewhat slower\n" +" due to the extra image transformations required.\n" +" For optimal performance do not use -overlay, but rather\n" +" configure the X server so that the default visual is\n" +" depth 24 TrueColor and try to have all apps use that\n" +" visual (e.g. some apps have -use24 or -visual options).\n" +"-overlay_nocursor Sets -overlay, but does not try to draw the exact mouse\n" +" cursor shape using the overlay mechanism.\n" +"\n" +"-scale fraction Scale the framebuffer by factor \"fraction\". Values\n" +" less than 1 shrink the fb, larger ones expand it. Note:\n" +" image may not be sharp and response may be slower.\n" +" If \"fraction\" contains a decimal point \".\" it\n" +" is taken as a floating point number, alternatively\n" +" the notation \"m/n\" may be used to denote fractions\n" +" exactly, e.g. -scale 2/3\n" +"\n" +" Scaling Options: can be added after \"fraction\" via\n" +" \":\", to supply multiple \":\" options use commas.\n" +" If you just want a quick, rough scaling without\n" +" blending, append \":nb\" to \"fraction\" (e.g. -scale\n" +" 1/3:nb). No blending is the default for 8bpp indexed\n" +" color, to force blending for this case use \":fb\".\n" +"\n" +" To disable -scrollcopyrect and -wirecopyrect under\n" +" -scale use \":nocr\". If you need to to enable them use\n" +" \":cr\" or specify them explicitly on the command line.\n" +" If a slow link is detected, \":nocr\" may be applied\n" +" automatically. Default: %s\n" +"\n" +" More esoteric options: for compatibility with vncviewers\n" +" the scaled width is adjusted to be a multiple of 4:\n" +" to disable this use \":n4\". \":in\" use interpolation\n" +" scheme even when shrinking, \":pad\" pad scaled width\n" +" and height to be multiples of scaling denominator\n" +" (e.g. 3 for 2/3).\n" +"\n" +"-scale_cursor frac By default if -scale is supplied the cursor shape is\n" +" scaled by the same factor. Depending on your usage,\n" +" you may want to scale the cursor independently of the\n" +" screen or not at all. If you specify -scale_cursor\n" +" the cursor will be scaled by that factor. When using\n" +" -scale mode to keep the cursor at its \"natural\" size\n" +" use \"-scale_cursor 1\". Most of the \":\" scaling\n" +" options apply here as well.\n" +"\n" +"-viewonly All VNC clients can only watch (default %s).\n" +"-shared VNC display is shared, i.e. more than one viewer can\n" +" connect at the same time (default %s).\n" +"-once Exit after the first successfully connected viewer\n" +" disconnects, opposite of -forever. This is the Default.\n" +"-forever Keep listening for more connections rather than exiting\n" +" as soon as the first client(s) disconnect. Same as -many\n" +"-loop Create an outer loop restarting the x11vnc process\n" +" whenever it terminates. -bg and -inetd are ignored in\n" +" this mode. Useful for continuing even if the X server\n" +" terminates and restarts (you will need permission to\n" +" reconnect of course). Use, e.g., -loop100 to sleep\n" +" 100 millisecs between restarts, etc. Default is 2000ms\n" +" (i.e. 2 secs) Use, e.g. -loop300,5 to sleep 300 ms\n" +" and only loop 5 times.\n" +"-timeout n Exit unless a client connects within the first n seconds\n" +" after startup.\n" +"-inetd Launched by inetd(1): stdio instead of listening socket.\n" +" Note: if you are not redirecting stderr to a log file\n" +" (via shell 2> or -o option) you MUST also specify the -q\n" +" option, otherwise the stderr goes to the viewer which\n" +" will cause it to abort. Specifying both -inetd and -q\n" +" and no -o will automatically close the stderr.\n" +"-nofilexfer Disable the TightVNC file transfer extension. (same as\n" +" -disablefiletransfer). Note that when the -viewonly\n" +" option is supplied all file transfers are disabled.\n" +" Also clients that log in viewonly cannot transfer files.\n" +" However, if the remote control mechanism is used to\n" +" change the global or per-client viewonly state the\n" +" filetransfer permissions will NOT change.\n" +"-http Instead of using -httpdir (see below) to specify\n" +" where the Java vncviewer applet is, have x11vnc try\n" +" to *guess* where the directory is by looking relative\n" +" to the program location and in standard locations\n" +" (/usr/local/share/x11vnc/classes, etc).\n" +"-connect string For use with \"vncviewer -listen\" reverse connections.\n" +" If \"string\" has the form \"host\" or \"host:port\"\n" +" the connection is made once at startup. Use commas\n" +" for a list of host's and host:port's.\n" +"\n" +" If \"string\" contains \"/\" it is instead interpreted\n" +" as a file to periodically check for new hosts.\n" +" The first line is read and then the file is truncated.\n" +" Be careful for this usage mode if x11vnc is running as\n" +" root (e.g. via gdm(1), etc).\n" +"-vncconnect Monitor the VNC_CONNECT X property set by the standard\n" +"-novncconnect VNC program vncconnect(1). When the property is\n" +" set to \"host\" or \"host:port\" establish a reverse\n" +" connection. Using xprop(1) instead of vncconnect may\n" +" work (see the FAQ). The -remote control mechanism also\n" +" uses this VNC_CONNECT channel. Default: %s\n" +"\n" +"-allow host1[,host2..] Only allow client connections from hosts matching\n" +" the comma separated list of hostnames or IP addresses.\n" +" Can also be a numerical IP prefix, e.g. \"192.168.100.\"\n" +" to match a simple subnet, for more control build\n" +" libvncserver with libwrap support (See the FAQ). If the\n" +" list contains a \"/\" it instead is a interpreted as a\n" +" file containing addresses or prefixes that is re-read\n" +" each time a new client connects. Lines can be commented\n" +" out with the \"#\" character in the usual way.\n" +"-localhost Same as \"-allow 127.0.0.1\".\n" +"\n" +" Note: if you want to restrict which network interface\n" +" x11vnc listens on, see the -listen option below.\n" +" E.g. \"-listen localhost\" or \"-listen 192.168.3.21\".\n" +" As a special case, the option \"-localhost\" implies\n" +" \"-listen localhost\".\n" +"\n" +" For non-localhost -listen usage, if you use the remote\n" +" control mechanism (-R) to change the -listen interface\n" +" you may need to manually adjust the -allow list (and\n" +" vice versa) to avoid situations where no connections\n" +" (or too many) are allowed.\n" +"\n" +"-nolookup Do not use gethostbyname() or gethostbyaddr() to look up\n" +" host names or IP numbers. Use this if name resolution\n" +" is incorrectly set up and leads to long pauses as name\n" +" lookups time out, etc.\n" +"\n" +"-input string Fine tuning of allowed user input. If \"string\" does\n" +" not contain a comma \",\" the tuning applies only to\n" +" normal clients. Otherwise the part before \",\" is\n" +" for normal clients and the part after for view-only\n" +" clients. \"K\" is for Keystroke input, \"M\" for\n" +" Mouse-motion input, and \"B\" for Button-click input.\n" +" Their presence in the string enables that type of input.\n" +" E.g. \"-input M\" means normal users can only move\n" +" the mouse and \"-input KMB,M\" lets normal users do\n" +" anything and enables view-only users to move the mouse.\n" +" This option is ignored when a global -viewonly is in\n" +" effect (all input is discarded in that case).\n" +"\n" +"-viewpasswd string Supply a 2nd password for view-only logins. The -passwd\n" +" (full-access) password must also be supplied.\n" +"\n" +"-passwdfile filename Specify the libvncserver password via the first line\n" +" of the file \"filename\" (instead of via -passwd on\n" +" the command line where others might see it via ps(1)).\n" +" See below for how to supply multiple passwords.\n" +"\n" +" If the filename is prefixed with \"rm:\" it will be\n" +" removed after being read. Perhaps this is useful in\n" +" limiting the readability of the file. In general,\n" +" the password file should not be readable by untrusted\n" +" users (BTW: neither should the VNC -rfbauth file:\n" +" it is NOT encrypted).\n" +"\n" +" If the filename is prefixed with \"read:\" it will\n" +" periodically be checked for changes and reread.\n" +"\n" +" Note that only the first 8 characters of a password\n" +" are used.\n" +"\n" +" If multiple non-blank lines exist in the file they are\n" +" all taken as valid passwords. Blank lines are ignored.\n" +" Password lines may be \"commented out\" (ignored) if\n" +" they begin with the charactor \"#\" or the line contains\n" +" the string \"__SKIP__\". Lines may be annotated by use\n" +" of the \"__COMM__\" string: from it to the end of the\n" +" line is ignored. An empty password may be specified\n" +" via the \"__EMPTY__\" string on a line by itself (note\n" +" your viewer might not accept empty passwords).\n" +"\n" +" If the string \"__BEGIN_VIEWONLY__\" appears on a\n" +" line by itself, the remaining passwords are used for\n" +" viewonly access. For compatibility, as a special case\n" +" if the file contains only two password lines the 2nd\n" +" one is automatically taken as the viewonly password.\n" +" Otherwise the \"__BEGIN_VIEWONLY__\" token must be\n" +" used to have viewonly passwords. (tip: make the 3rd\n" +" and last line be \"__BEGIN_VIEWONLY__\" to have 2\n" +" full-access passwords)\n" + +"-nopw Disable the big warning message when you use x11vnc\n" +" without some sort of password.\n" +"-storepasswd pass file Store password \"pass\" as the VNC password in the\n" +" file \"file\". Once the password is stored the\n" +" program exits. Use the password via \"-rfbauth file\"\n" +"\n" +"-accept string Run a command (possibly to prompt the user at the\n" +" X11 display) to decide whether an incoming client\n" +" should be allowed to connect or not. \"string\" is\n" +" an external command run via system(3) or some special\n" +" cases described below. Be sure to quote \"string\"\n" +" if it contains spaces, shell characters, etc. If the\n" +" external command returns 0 the client is accepted,\n" +" otherwise the client is rejected. See below for an\n" +" extension to accept a client view-only.\n" +"\n" +" If x11vnc is running as root (say from inetd(1) or from\n" +" display managers xdm(1), gdm(1), etc), think about the\n" +" security implications carefully before supplying this\n" +" option (likewise for the -gone option).\n" +"\n" +" Environment: The RFB_CLIENT_IP environment variable will\n" +" be set to the incoming client IP number and the port\n" +" in RFB_CLIENT_PORT (or -1 if unavailable). Similarly,\n" +" RFB_SERVER_IP and RFB_SERVER_PORT (the x11vnc side\n" +" of the connection), are set to allow identification\n" +" of the tcp virtual circuit. The x11vnc process\n" +" id will be in RFB_X11VNC_PID, a client id number in\n" +" RFB_CLIENT_ID, and the number of other connected clients\n" +" in RFB_CLIENT_COUNT. RFB_MODE will be \"accept\".\n" +" RFB_STATE will be PROTOCOL_VERSION, SECURITY_TYPE,\n" +" AUTHENTICATION, INITIALISATION, NORMAL, or UNKNOWN\n" +" indicating up to which state the client has acheived.\n" +" RFB_LOGIN_VIEWONLY will be 0, 1, or -1 (unknown).\n" +" RFB_USERNAME, RFB_LOGIN_TIME, and RFB_CURRENT_TIME may\n" +" also be set.\n" +"\n" +" If \"string\" is \"popup\" then a builtin popup window\n" +" is used. The popup will time out after 120 seconds,\n" +" use \"popup:N\" to modify the timeout to N seconds\n" +" (use 0 for no timeout).\n" +"\n" +" If \"string\" is \"xmessage\" then an xmessage(1)\n" +" invocation is used for the command. xmessage must be\n" +" installed on the machine for this to work.\n" +"\n" +" Both \"popup\" and \"xmessage\" will present an option\n" +" for accepting the client \"View-Only\" (the client\n" +" can only watch). This option will not be presented if\n" +" -viewonly has been specified, in which case the entire\n" +" display is view only.\n" +"\n" +" If the user supplied command is prefixed with something\n" +" like \"yes:0,no:*,view:3 mycommand ...\" then this\n" +" associates the numerical command return code with\n" +" the actions: accept, reject, and accept-view-only,\n" +" respectively. Use \"*\" instead of a number to indicate\n" +" the default action (in case the command returns an\n" +" unexpected value). E.g. \"no:*\" is a good choice.\n" +"\n" +" Note that x11vnc blocks while the external command\n" +" or popup is running (other clients may see no updates\n" +" during this period). So a person sitting a the physical\n" +" display is needed to respond to an popup prompt. (use\n" +" a 2nd x11vnc if you lock yourself out).\n" +"\n" +" More -accept tricks: use \"popupmouse\" to only allow\n" +" mouse clicks in the builtin popup to be recognized.\n" +" Similarly use \"popupkey\" to only recognize\n" +" keystroke responses. These are to help avoid the\n" +" user accidentally accepting a client by typing or\n" +" clicking. All 3 of the popup keywords can be followed\n" +" by +N+M to supply a position for the popup window.\n" +" The default is to center the popup window.\n" +"-afteraccept string As -accept, except to run a user supplied command after\n" +" a client has been accepted and authenticated. RFB_MODE\n" +" will be set to \"afteraccept\" and the other RFB_*\n" +" variables are as in -accept. Unlike -accept, the\n" +" command return code is not interpreted by x11vnc.\n" +" Example: -afteraccept 'killall xlock &'\n" +"-gone string As -accept, except to run a user supplied command when\n" +" a client goes away (disconnects). RFB_MODE will be\n" +" set to \"gone\" and the other RFB_* variables are as\n" +" in -accept. Unlike -accept, the command return code\n" +" is not interpreted by x11vnc. Example: -gone 'xlock &'\n" +"\n" +"-users list If x11vnc is started as root (say from inetd(1) or from\n" +" display managers xdm(1), gdm(1), etc), then as soon\n" +" as possible after connections to the X display are\n" +" established try to switch to one of the users in the\n" +" comma separated \"list\". If x11vnc is not running as\n" +" root this option is ignored.\n" +" \n" +" Why use this option? In general it is not needed since\n" +" x11vnc is already connected to the X display and can\n" +" perform its primary functions. The option was added\n" +" to make some of the *external* utility commands x11vnc\n" +" occasionally runs work properly. In particular under\n" +" GNOME and KDE to implement the \"-solid color\" feature\n" +" external commands (gconftool-2 and dcop) must be run\n" +" as the user owning the desktop session. Since this\n" +" option switches userid it also affects the userid used\n" +" to run the processes for the -accept and -gone options.\n" +" It also affects the ability to read files for options\n" +" such as -connect, -allow, and -remap. Note that the\n" +" -connect file is also sometimes written to.\n" +" \n" +" So be careful with this option since in many situations\n" +" its use can decrease security.\n" +" \n" +" The switch to a user will only take place if the\n" +" display can still be successfully opened as that user\n" +" (this is primarily to try to guess the actual owner\n" +" of the session). Example: \"-users fred,wilma,betty\".\n" +" Note that a malicious user \"barney\" by quickly using\n" +" \"xhost +\" when logging in may get x11vnc to switch\n" +" to user \"fred\". What happens next?\n" +" \n" +" Under display managers it may be a long time before\n" +" the switch succeeds (i.e. a user logs in). To make\n" +" it switch immediately regardless if the display\n" +" can be reopened prefix the username with the \"+\"\n" +" character. E.g. \"-users +bob\" or \"-users +nobody\".\n" +" The latter (i.e. switching immediately to user\n" +" \"nobody\") is probably the only use of this option\n" +" that increases security.\n" +" \n" +" To immediately switch to a user *before* connections\n" +" to the X display are made or any files opened use the\n" +" \"=\" character: \"-users =bob\". That user needs to\n" +" be able to open the X display of course.\n" +" \n" +" The special user \"guess=\" means to examine the utmpx\n" +" database (see who(1)) looking for a user attached to\n" +" the display number (from DISPLAY or -display option)\n" +" and try him/her. To limit the list of guesses, use:\n" +" \"-users guess=bob,betty\".\n" +" \n" +" Even more sinister is the special user \"lurk=\" that\n" +" means to try to guess the DISPLAY from the utmpx login\n" +" database as well. So it \"lurks\" waiting for anyone\n" +" to log into an X session and then connects to it.\n" +" Specify a list of users after the = to limit which\n" +" users will be tried. To enable a different searching\n" +" mode, if the first user in the list is something like\n" +" \":0\" or \":0-2\" that indicates a range of DISPLAY\n" +" numbers that will be tried (regardless of whether\n" +" they are in the utmpx database) for all users that\n" +" are logged in. Examples: \"-users lurk=\" and also\n" +" \"-users lurk=:0-1,bob,mary\"\n" +" \n" +" Be especially careful using the \"guess=\" and \"lurk=\"\n" +" modes. They are not recommended for use on machines\n" +" with untrustworthy local users.\n" +" \n" +"-noshm Do not use the MIT-SHM extension for the polling.\n" +" Remote displays can be polled this way: be careful this\n" +" can use large amounts of network bandwidth. This is\n" +" also of use if the local machine has a limited number\n" +" of shm segments and -onetile is not sufficient.\n" +"-flipbyteorder Sometimes needed if remotely polled host has different\n" +" endianness. Ignored unless -noshm is set.\n" +"-onetile Do not use the new copy_tiles() framebuffer mechanism,\n" +" just use 1 shm tile for polling. Limits shm segments\n" +" used to 3.\n" +"\n" +"-solid [color] To improve performance, when VNC clients are connected\n" +" try to change the desktop background to a solid color.\n" +" The [color] is optional: the default color is \"cyan4\".\n" +" For a different one specify the X color (rgb.txt name,\n" +" e.g. \"darkblue\" or numerical \"#RRGGBB\").\n" +"\n" +" Currently this option only works on GNOME, KDE, CDE,\n" +" and classic X (i.e. with the background image on the\n" +" root window). The \"gconftool-2\" and \"dcop\" external\n" +" commands are run for GNOME and KDE respectively.\n" +" Other desktops won't work, e.g. Xfce (send us the\n" +" corresponding commands if you find them). If x11vnc is\n" +" running as root (inetd(1) or gdm(1)), the -users option\n" +" may be needed for GNOME and KDE. If x11vnc guesses\n" +" your desktop incorrectly, you can force it by prefixing\n" +" color with \"gnome:\", \"kde:\", \"cde:\" or \"root:\".\n" +"-blackout string Black out rectangles on the screen. \"string\" is a\n" +" comma separated list of WxH+X+Y type geometries for\n" +" each rectangle. If one of the items on the list is the\n" +" string \"noptr\" the mouse pointer will not be allowed\n" +" to go into a blacked out region.\n" +"-xinerama If your screen is composed of multiple monitors\n" +" glued together via XINERAMA, and that screen is\n" +" not a rectangle this option will try to guess the\n" +" areas to black out (if your system has libXinerama).\n" +"\n" +" In general, we have noticed on XINERAMA displays you\n" +" may need to use the \"-xwarppointer\" option if the mouse\n" +" pointer misbehaves.\n" +"\n" +"-xtrap Use the DEC-XTRAP extension for keystroke and mouse\n" +" input insertion. For use on legacy systems, e.g. X11R5,\n" +" running an incomplete or missing XTEST extension.\n" +" By default DEC-XTRAP will be used if XTEST server grab\n" +" control is missing, use -xtrap to do the keystroke and\n" +" mouse insertion via DEC-XTRAP as well.\n" +"\n" +"-xrandr [mode] If the display supports the XRANDR (X Resize, Rotate\n" +" and Reflection) extension, and you expect XRANDR events\n" +" to occur to the display while x11vnc is running, this\n" +" options indicates x11vnc should try to respond to\n" +" them (as opposed to simply crashing by assuming the\n" +" old screen size). See the xrandr(1) manpage and run\n" +" 'xrandr -q' for more info. [mode] is optional and\n" +" described below.\n" +"\n" +" Since watching for XRANDR events and trapping errors\n" +" increases polling overhead, only use this option if\n" +" XRANDR changes are expected. For example on a rotatable\n" +" screen PDA or laptop, or using a XRANDR-aware Desktop\n" +" where you resize often. It is best to be viewing with a\n" +" vncviewer that supports the NewFBSize encoding, since it\n" +" knows how to react to screen size changes. Otherwise,\n" +" libvncserver tries to do so something reasonable for\n" +" viewers that cannot do this (portions of the screen\n" +" may be clipped, unused, etc).\n" +"\n" +" \"mode\" defaults to \"resize\", which means create a\n" +" new, resized, framebuffer and hope all viewers can cope\n" +" with the change. \"newfbsize\" means first disconnect\n" +" all viewers that do not support the NewFBSize VNC\n" +" encoding, and then resize the framebuffer. \"exit\"\n" +" means disconnect all viewer clients, and then terminate\n" +" x11vnc.\n" +"-padgeom WxH Whenever a new vncviewer connects, the framebuffer is\n" +" replaced with a fake, solid black one of geometry WxH.\n" +" Shortly afterwards the framebuffer is replaced with the\n" +" real one. This is intended for use with vncviewers\n" +" that do not support NewFBSize and one wants to make\n" +" sure the initial viewer geometry will be big enough\n" +" to handle all subsequent resizes (e.g. under -xrandr,\n" +" -remote id:windowid, rescaling, etc.)\n" +"\n" +"-o logfile Write stderr messages to file \"logfile\" instead of\n" +" to the terminal. Same as \"-logfile file\". To append\n" +" to the file use \"-oa file\" or \"-logappend file\".\n" +"-flag file Write the \"PORT=NNNN\" (e.g. PORT=5900) string to\n" +" \"file\" in addition to stdout. This option could be\n" +" useful by wrapper script to detect when x11vnc is ready.\n" +"\n" +"-rc filename Use \"filename\" instead of $HOME/.x11vncrc for rc file.\n" +"-norc Do not process any .x11vncrc file for options.\n" +"\n" +"-h, -help Print this help text.\n" +"-?, -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" +" stderr are lost unless -o logfile is used. Something\n" +" like this could be useful in a script:\n" +" port=`ssh $host \"x11vnc -display :0 -bg\" | grep PORT`\n" +" port=`echo \"$port\" | sed -e 's/PORT=//'`\n" +" port=`expr $port - 5900`\n" +" vncviewer $host:$port\n" +"\n" +"-modtweak Option -modtweak automatically tries to adjust the AltGr\n" +"-nomodtweak and Shift modifiers for differing language keyboards\n" +" between client and host. Otherwise, only a single key\n" +" press/release of a Keycode is simulated (i.e. ignoring\n" +" the state of the modifiers: this usually works for\n" +" identical keyboards). Also useful in resolving cases\n" +" where a Keysym is bound to multiple keys (e.g. \"<\" + \">\"\n" +" and \",\" + \"<\" keys). Default: %s\n" +"-xkb When in modtweak mode, use the XKEYBOARD extension (if\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" +" to -xkb mode. Use this option to help x11vnc in the\n" +" reverse problem it tries to solve: Keysym -> Keycode(s)\n" +" when ambiguities exist (more than one Keycode per\n" +" Keysym). Run 'xmodmap -pk' to see your keymapping.\n" +" Example: \"-skip_keycodes 94,114\"\n" +"-sloppy_keys Experimental option that tries to correct some\n" +" \"sloppy\" key behavior. E.g. if at the viewer you\n" +" press Shift+Key but then release the Shift before\n" +" Key that could give rise to extra unwanted characters\n" +" (usually only between keyboards of different languages).\n" +" Only use this option if you observe problems with\n" +" some keystrokes.\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 same\n" +" key, or 20 downs in a row for the same modifier key!\n" +" Setting -skip_dups means to skip these duplicates and\n" +" just process the first event. Note: some VNC viewers\n" +" assume they can send down's without the corresponding\n" +" up's and so you should not set this option for\n" +" these viewers (symptom: some keys do not autorepeat)\n" +" Default: %s\n" +"-add_keysyms If a Keysym is received from a VNC viewer and that\n" +"-noadd_keysyms Keysym does not exist in the X server, then add the\n" +" Keysym to the X server's keyboard mapping on an unused\n" +" key. Added Keysyms will be removed periodically and\n" +" also 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" +" an international keyboard (AltGr or ISO_Level3_Shift),\n" +" and the OS or keyboard where the VNC viewer is run\n" +" is not identical to that of the X server, and you are\n" +" having problems typing some keys. Implies -nobell.\n" +#endif +"-clear_mods At startup and exit clear the modifier keys by sending\n" +" KeyRelease for each one. The Lock modifiers are skipped.\n" +" Used to clear the state if the display was accidentally\n" +" left with any pressed down.\n" +"-clear_keys As -clear_mods, except try to release any pressed key.\n" +" Note that this option and -clear_mods can interfere\n" +" with a person typing at the physical keyboard.\n" +"-remap string Read Keysym remappings from file named \"string\".\n" +" Format is one pair of Keysyms per line (can be name\n" +" or hex value) separated by a space. If no file named\n" +" \"string\" exists, it is instead interpreted as this\n" +" form: key1-key2,key3-key4,... See <X11/keysymdef.h>\n" +" header file for a list of Keysym names, or use xev(1).\n" +" 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" +"\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: 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.\n" +"\n" +" Use \"-norepeat N\" to set how many times norepeat will\n" +" be reset if something else (e.g. X session manager)\n" +" undoes it. The default is 2. Use a negative value\n" +" for unlimited resets.\n" +"\n" +"-nofb Ignore video framebuffer: only process keyboard and\n" +" pointer. Intended for use with Win2VNC and x2vnc\n" +" dual-monitor setups.\n" +"-nobell Do not watch for XBell events. (no beeps will be heard)\n" +" Note: XBell monitoring requires the XKEYBOARD extension.\n" +"-nosel Do not manage exchange of X selection/cutbuffer between\n" +" VNC viewers and the X server.\n" +"-noprimary Do not poll the PRIMARY selection for changes to send\n" +" back to clients. (PRIMARY is still set on received\n" +" changes, however).\n" +"-seldir string If direction string is \"send\", only send the selection\n" +" to viewers, and if it is \"recv\" only receive it from\n" +" viewers. To work around apps setting the selection\n" +" too frequently and messing up the other end. You can\n" +" actually supply a comma separated list of directions,\n" +" including \"debug\" to turn on debugging output.\n" +"\n" +"-cursor [mode] Sets how the pointer cursor shape (little icon at the\n" +"-nocursor mouse pointer) should be handled. The \"mode\" string\n" +" is optional and is described below. The default\n" +" is to show some sort of cursor shape(s). How this\n" +" is done depends on the VNC viewer and the X server.\n" +" Use -nocursor to disable cursor shapes completely.\n" +"\n" +" Some VNC viewers support the TightVNC CursorPosUpdates\n" +" and CursorShapeUpdates extensions (cuts down on\n" +" network traffic by not having to send the cursor image\n" +" every time the pointer is moved), in which case these\n" +" extensions are used (see -nocursorshape and -nocursorpos\n" +" below to disable). For other viewers the cursor shape\n" +" is written directly to the framebuffer every time the\n" +" pointer is moved or changed and gets sent along with\n" +" the other framebuffer updates. In this case, there\n" +" will be some lag between the vnc viewer pointer and\n" +" the remote cursor position.\n" +"\n" +" If the X display supports retrieving the cursor shape\n" +" information from the X server, then the default is\n" +" to use that mode. On Solaris this can be done with\n" +" the SUN_OVL extension using -overlay (see also the\n" +" -overlay_nocursor option). A similar overlay scheme\n" +" is used on IRIX. Xorg (e.g. Linux) and recent Solaris\n" +" Xsun servers support the XFIXES extension to retrieve\n" +" the exact cursor shape from the X server. If XFIXES\n" +" is present it is preferred over Overlay and is used by\n" +" default (see -noxfixes below). This can be disabled\n" +" with -nocursor, and also some values of the \"mode\"\n" +" option below.\n" +" \n" +" Note that under XFIXES cursors with transparency (alpha\n" +" channel) will usually not be exactly represented and one\n" +" may find Overlay preferable. See also the -alphacut\n" +" and -alphafrac options below as fudge factors to try\n" +" to improve the situation for cursors with transparency\n" +" for a given theme.\n" +"\n" +" The \"mode\" string can be used to fine-tune the\n" +" displaying of cursor shapes. It can be used the\n" +" following ways:\n" +"\n" +" \"-cursor arrow\" - just show the standard arrow\n" +" nothing more or nothing less.\n" +"\n" +" \"-cursor none\" - same as \"-nocursor\"\n" +"\n" +" \"-cursor X\" - when the cursor appears to be on the\n" +" root window, draw the familiar X shape. Some desktops\n" +" such as GNOME cover up the root window completely,\n" +" and so this will not work, try \"X1\", etc, to try to\n" +" shift the tree depth. On high latency links or slow\n" +" machines there will be a time lag between expected and\n" +" the actual cursor shape.\n" +"\n" +" \"-cursor some\" - like \"X\" but use additional\n" +" heuristics to try to guess if the window should have\n" +" a windowmanager-like resizer cursor or a text input\n" +" I-beam cursor. This is a complete hack, but may be\n" +" useful in some situations because it provides a little\n" +" more feedback about the cursor shape.\n" +"\n" +" \"-cursor most\" - try to show as many cursors as\n" +" possible. Often this will only be the same as \"some\"\n" +" unless the display has overlay visuals or XFIXES\n" +" extensions available. On Solaris and IRIX if XFIXES\n" +" is not available, -overlay mode will be attempted.\n" +"\n" +"-arrow n Choose an alternate \"arrow\" cursor from a set of\n" +" some common ones. n can be 1 to %d. Default is: %d\n" +" Ignored when in XFIXES cursor-grabbing mode.\n" +"\n" +"-noxfixes Do not use the XFIXES extension to draw the exact cursor\n" +" shape even if it is available.\n" +"-alphacut n When using the XFIXES extension for the cursor shape,\n" +" cursors with transparency will not usually be displayed\n" +" exactly (but opaque ones will). This option sets n as\n" +" a cutoff for cursors that have transparency (\"alpha\n" +" channel\" with values ranging from 0 to 255) Any cursor\n" +" pixel with alpha value less than n becomes completely\n" +" transparent. Otherwise the pixel is completely opaque.\n" +" Default %d\n" +" \n" +"-alphafrac fraction With the threshold in -alphacut some cursors will become\n" +" almost completely transparent because their alpha values\n" +" are not high enough. For those cursors adjust the\n" +" alpha threshold until fraction of the non-zero alpha\n" +" channel pixels become opaque. Default %.2f\n" +"-alpharemove By default, XFIXES cursors pixels with transparency have\n" +" the alpha factor multiplied into the RGB color values\n" +" (i.e. that corresponding to blending the cursor with a\n" +" black background). Specify this option to remove the\n" +" alpha factor. (useful for light colored semi-transparent\n" +" cursors).\n" +"-noalphablend In XFIXES mode do not send cursor alpha channel data\n" +" to libvncserver. The default is to send it. The\n" +" alphablend effect will only be visible in -nocursorshape\n" +" mode or for clients with cursorshapeupdates turned\n" +" off. (However there is a hack for 32bpp with depth 24,\n" +" it uses the extra 8 bits to store cursor transparency\n" +" for use with a hacked vncviewer that applies the\n" +" transparency locally. See the FAQ for more info).\n" +"\n" +"-nocursorshape Do not use the TightVNC CursorShapeUpdates extension\n" +" even if clients support it. See -cursor above.\n" +"-cursorpos Option -cursorpos enables sending the X cursor position\n" +"-nocursorpos back to all vnc clients that support the TightVNC\n" +" CursorPosUpdates extension. Other clients will be able\n" +" to see the pointer motions. Default: %s\n" +"-xwarppointer Move the pointer with XWarpPointer(3X) instead of\n" +" the XTEST extension. Use this as a workaround\n" +" if the pointer motion behaves incorrectly, e.g.\n" +" on touchscreens or other non-standard setups.\n" +" Also sometimes needed on XINERAMA displays.\n" +"\n" +"-buttonmap string String to remap mouse buttons. Format: IJK-LMN, this\n" +" maps buttons I -> L, etc., e.g. -buttonmap 13-31\n" +"\n" +" Button presses can also be mapped to keystrokes: replace\n" +" a button digit on the right of the dash with :<sym>:\n" +" or :<sym1>+<sym2>: etc. for multiple keys. For example,\n" +" if the viewing machine has a mouse-wheel (buttons 4 5)\n" +" but the x11vnc side does not, these will do scrolls:\n" +" -buttonmap 12345-123:Prior::Next:\n" +" -buttonmap 12345-123:Up+Up+Up::Down+Down+Down:\n" +"\n" +" See <X11/keysymdef.h> header file for a list of Keysyms,\n" +" or use the xev(1) program. Note: mapping of button\n" +" clicks to Keysyms may not work if -modtweak or -xkb is\n" +" needed for the Keysym.\n" +"\n" +" If you include a modifier like \"Shift_L\" the\n" +" modifier's up/down state is toggled, e.g. to send\n" +" \"The\" use :Shift_L+t+Shift_L+h+e: (the 1st one is\n" +" shift down and the 2nd one is shift up). (note: the\n" +" initial state of the modifier is ignored and not reset)\n" +" To include button events use \"Button1\", ... etc.\n" +"\n" +"-nodragging Do not update the display during mouse dragging events\n" +" (mouse button held down). Greatly improves response on\n" +" slow setups, but you lose all visual feedback for drags,\n" +" text selection, and some menu traversals. It overrides\n" +" any -pointer_mode setting.\n" +"\n" +"-wireframe [str] Try to detect window moves or resizes when a mouse\n" +"-nowireframe button is held down and show a wireframe instead of\n" +" the full opaque window. This is based completely on\n" +" heuristics and may not always work: it depends on your\n" +" window manager and even how you move things around.\n" +" See -pointer_mode below for discussion of the \"bogging\n" +" down\" problem this tries to avoid.\n" +" Default: %s\n" +"\n" +" Shorter aliases: -wf [str] and -nowf\n" +"\n" +" The value \"str\" is optional and, of course, is\n" +" packed with many tunable parameters for this scheme:\n" +"\n" +" Format: shade,linewidth,percent,T+B+L+R,mod,t1+t2+t3+t4\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" +" \"shade\" indicate the \"color\" for the wireframe,\n" +" usually a greyscale: 0-255, however for 16 and 32bpp you\n" +" can specify an rgb.txt X color (e.g. \"dodgerblue\") or\n" +" a value > 255 is treated as RGB (e.g. red is 0xff0000).\n" +" \"linewidth\" sets the width of the wireframe in pixels.\n" +" \"percent\" indicates to not apply the wireframe scheme\n" +" to windows with area less than this percent of the\n" +" full screen.\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 wireframing.\n" +" This is a speedup to quickly exclude a window from being\n" +" wireframed: set them all to zero to not try the speedup\n" +" (scrolling and selecting text will likely be slower).\n" +"\n" +" \"mod\" specifies if a button down event in the\n" +" interior of the window with a modifier key (Alt, Shift,\n" +" etc.) down should indicate a wireframe opportunity.\n" +" It can be \"0\" or \"none\" to skip it, \"1\" or \"all\"\n" +" to apply it to any modifier, or \"Shift\", \"Alt\",\n" +" \"Control\", \"Meta\", \"Super\", or \"Hyper\" to only\n" +" apply for that type of modifier key.\n" +"\n" +" \"t1+t2+t3+t4\" specify four floating point times in\n" +" seconds: t1 is how long to wait for the pointer to move,\n" +" t2 is how long to wait for the window to start moving\n" +" or being resized (for some window managers this can be\n" +" rather long), t3 is how long to keep a wireframe moving\n" +" before repainting the window. t4 is the minimum time\n" +" between sending wireframe \"animations\". If a slow\n" +" link is detected, these values may be automatically\n" +" changed to something better for a slow link.\n" +"\n" +"-wirecopyrect mode Since the -wireframe mechanism evidently tracks moving\n" +"-nowirecopyrect windows accurately, a speedup can be obtained by\n" +" telling the VNC viewers to locally copy the translated\n" +" window region. This is the VNC CopyRect encoding:\n" +" the framebuffer update doesn't need to send the actual\n" +" new image data.\n" +"\n" +" Shorter aliases: -wcr [mode] and -nowcr\n" +"\n" +" \"mode\" can be \"never\" (same as -nowirecopyrect)\n" +" to never try the copyrect, \"top\" means only do it if\n" +" the window was not covered by any other windows, and\n" +" \"always\" means to translate the orginally unobscured\n" +" region (this may look odd as the remaining pieces come\n" +" in, but helps on a slow link). Default: \"%s\"\n" +"\n" +" Note: there can be painting errors or slow response\n" +" when using -scale so you may want to disable CopyRect\n" +" in this case \"-wirecopyrect never\" on the command\n" +" line or by remote-control. Or you can also use the\n" +" \"-scale xxx:nocr\" scale option.\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" +" to \"snoop\" on X applications (currently for certain\n" +" XCopyArea and XConfigureWindow X protocol requests).\n" +" Examples: Hitting <Return> in a terminal window when the\n" +" cursor was at the bottom, the text scrolls up one line.\n" +" Hitting <Down> arrow in a web browser window, the web\n" +" page scrolls up a small amount. Or scrolling with a\n" +" scrollbar or mouse wheel.\n" +"\n" +" Shorter aliases: -scr [mode] and -noscr\n" +"\n" +" This scheme will not always detect scrolls, but when\n" +" 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, 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" +" Screen clearing kludges: for testing at least, there\n" +" are some \"magic key sequences\" (must be done in less\n" +" than 1 second) to aid repairing painting errors that\n" +" may be seen when using this mode:\n" +"\n" +" 3 Alt_L's in a row: resend whole screen,\n" +" 4 Alt_L's in a row: reread and resend whole screen,\n" +" 3 Super_L's in a row: mark whole screen for polling,\n" +" 4 Super_L's in a row: reset RECORD context,\n" +" 5 Super_L's in a row: try to push a black screen\n" +"\n" +" note: Alt_L is the Left \"Alt\" key (a single key)\n" +" Super_L is the Left \"Super\" key (Windows flag).\n" +" Both of these are modifier keys, and so should not\n" +" generate characters when pressed by themselves. Also,\n" +" your VNC viewer may have its own refresh hot-key\n" +" or button.\n" +"\n" +" \"mode\" can be \"never\" (same as -noscrollcopyrect)\n" +" to never try the copyrect, \"keys\" means to try it\n" +" in response to keystrokes only, \"mouse\" means to\n" +" try it in response to mouse events only, \"always\"\n" +" means to do both. Default: \"%s\"\n" +"\n" +" Note: there can be painting errors or slow response\n" +" when using -scale so you may want to disable CopyRect\n" +" in this case \"-scrollcopyrect never\" on the command\n" +" line or by remote-control. Or you can also use the\n" +" \"-scale xxx:nocr\" scale option.\n" +"\n" +"-scr_area n Set the minimum area in pixels for a rectangle\n" +" to be considered for the -scrollcopyrect detection\n" +" scheme. This is to avoid wasting the effort on small\n" +" rectangles that would be quickly updated the normal way.\n" +" E.g. suppose an app updated the position of its skinny\n" +" scrollbar first and then shifted the large panel\n" +" 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_term list Yet another cosmetic kludge. Apply shell/terminal\n" +" heuristics to applications matching comma separated\n" +" list (same as for -scr_skip/-scr_inc). For example an\n" +" annoying transient under scroll detection is if you\n" +" hit Enter in a terminal shell with full text window,\n" +" the solid text cursor block will be scrolled up.\n" +" So for a short time there are two (or more) block\n" +" cursors on the screen. There are similar scenarios,\n" +" (e.g. an output line is duplicated).\n" +" \n" +" These transients are induced by the approximation of\n" +" scroll detection (e.g. it detects the scroll, but not\n" +" the fact that the block cursor was cleared just before\n" +" the scroll). In nearly all cases these transient errors\n" +" are repaired when the true X framebuffer is consulted\n" +" by the normal polling. But they are distracting, so\n" +" what this option provides is extra \"padding\" near the\n" +" bottom of the terminal window: a few extra lines near\n" +" the bottom will not be scrolled, but rather updated\n" +" from the actual X framebuffer. This usually reduces\n" +" the annoying artifacts. Use \"none\" to disable.\n" +" Default: \"%s\"\n" +"\n" +"-scr_keyrepeat lo-hi If a key is held down (or otherwise repeats rapidly) and\n" +" this induces a rapid sequence of scrolls (e.g. holding\n" +" down an Arrow key) the \"scrollcopyrect\" detection\n" +" and overhead may not be able to keep up. A time per\n" +" single scroll estimate is performed and if that estimate\n" +" predicts a sustainable scrollrate of keys per second\n" +" between \"lo\" and \"hi\" then repeated keys will be\n" +" DISCARDED to maintain the scrollrate. For example your\n" +" key autorepeat may be 25 keys/sec, but for a large\n" +" window or slow link only 8 scrolls per second can be\n" +" sustained, then roughly 2 out of every 3 repeated keys\n" +" will be discarded during this period. Default: \"%s\"\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" +"-fixscreen string Periodically \"repair\" the screen based on settings\n" +" in \"string\". Hopefully you won't need this option,\n" +" it is intended for cases when the -scrollcopyrect or\n" +" -wirecopyrect features leave too many painting errors,\n" +" but it can be used for any scenario. This option\n" +" periodically performs costly operations and so\n" +" interactive response may be reduced when it is on.\n" +" You can use 3 Alt_L's (the Left \"Alt\" key) taps in a\n" +" row described under -scrollcopyrect instead to manually\n" +" request a screen repaint when it is needed.\n" +"\n" +" \"string\" is a comma separated list of one or more of\n" +" the following: \"V=t\", \"C=t\", and \"X=t\". In these\n" +" \"t\" stands for a time in seconds (it is a floating\n" +" point even though one should usually use values > 2 to\n" +" avoid wasting resources). V sets how frequently the\n" +" entire screen should be sent to viewers (it is like the\n" +" 3 Alt_L's). C sets how long to wait after a CopyRect\n" +" to repaint the full screen. X sets how frequently\n" +" to reread the full X11 framebuffer from the X server\n" +" and push it out to connected viewers. Use of X should\n" +" be rare, please report a bug if you find you need it.\n" +" Examples: -fixscreen V=10 -fixscreen C=10\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" +"-noxrecord Disable any use of the RECORD extension. This is\n" +" currently used by the -scrollcopyrect scheme and to\n" +" monitor X server grabs.\n" +"\n" +"-grab_buster Some of the use of the RECORD extension can leave a\n" +"-nograb_buster tiny window for XGrabServer deadlock. This is only if\n" +" the whole-server grabbing application expects mouse or\n" +" keyboard input before releasing the grab. It is usually\n" +" a window manager that does this. x11vnc takes care to\n" +" avoid the the problem, but if caught x11vnc will freeze.\n" +" Without -grab_buster, the only solution is to go the\n" +" physical display and give it some input to satisfy the\n" +" grabbing app. Or manually kill and restart the window\n" +" manager if that is feasible. With -grab_buster, x11vnc\n" +" will fork a helper thread and if x11vnc appears to be\n" +" stuck in a grab after a period of time (20-30 sec) then\n" +" it will inject some user input: button clicks, Escape,\n" +" mouse motion, etc to try to break the grab. If you\n" +" experience a lot of grab deadlock, please report a bug.\n" +"\n" +"-debug_grabs Turn on debugging info printout with respect to\n" +" XGrabServer() deadlock for -scrollcopyrect mode.\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\n" +" changes when you drag a large window around opaquely.\n" +" Neither x11vnc's screen polling and vnc compression\n" +" routines nor the bandwidth to the vncviewers can keep\n" +" up these rapid screen changes: everything will bog down\n" +" when dragging or scrolling. So a scheme has to be used\n" +" to \"eat\" much of that pointer input before re-polling\n" +" the screen and sending out framebuffer updates. The\n" +" mode number \"n\" can be 0 to %d and selects one of\n" +" the schemes desribed below.\n" +"\n" +" Note that the -wireframe and -scrollcopyrect modes\n" +" complement -pointer_mode by detecting (and improving)\n" +" certain periods of \"rapid screen change\".\n" +"\n" +" n=0: does the same as -nodragging. (all screen polling\n" +" is suspended if a mouse button is pressed.)\n" +"\n" +" n=1: was the original scheme used to about Jan 2004:\n" +" it basically just skips -input_skip keyboard or pointer\n" +" events before repolling the screen.\n" +"\n" +" n=2 is an improved scheme: by watching the current rate\n" +" 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 a dynamic -nodragging mode: it detects\n" +" when the mouse motion has paused and then refreshes\n" +" the display.\n" +"\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 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" +"\n" +" To try out the different pointer modes to see which\n" +" one gives the best response for your usage, it is\n" +" convenient to use the remote control function, for\n" +" example \"x11vnc -R pm:4\" or the tcl/tk gui (Tuning ->\n" +" pointer_mode -> n).\n" +"\n" +"-input_skip n For the pointer handling when non-threaded: try to\n" +" read n user input events before scanning display. n < 0\n" +" means to act as though there is always user input.\n" +" Default: %d\n" +"\n" +"-speeds rd,bw,lat x11vnc tries to estimate some speed parameters that\n" +" are used to optimize scheduling (e.g. -pointer_mode\n" +" 4, -wireframe, -scrollcopyrect) and other things.\n" +" Use the -speeds option to set these manually.\n" +" The triple \"rd,bw,lat\" corresponds to video h/w\n" +" read rate in MB/sec, network bandwidth to clients in\n" +" KB/sec, and network latency to clients in milliseconds,\n" +" respectively. If a value is left blank, e.g. \"-speeds\n" +" ,100,15\", then the internal scheme is used to estimate\n" +" the empty value(s).\n" +"\n" +" Typical PC video cards have read rates of 5-10 MB/sec.\n" +" If the framebuffer is in main memory instead of video\n" +" h/w (e.g. SunRay, shadowfb, dummy driver, Xvfb), the\n" +" read rate may be much faster. \"x11perf -getimage500\"\n" +" can be used to get a lower bound (remember to factor\n" +" in the bytes per pixel). It is up to you to estimate\n" +" the network bandwith and latency to clients. For the\n" +" latency the ping(1) command can be used.\n" +"\n" +" For convenience there are some aliases provided,\n" +" e.g. \"-speeds modem\". The aliases are: \"modem\" for\n" +" 6,4,200; \"dsl\" for 6,100,50; and \"lan\" for 6,5000,1\n" +"\n" +"-wmdt string For some features, e.g. -wireframe and -scrollcopyrect,\n" +" x11vnc has to work around issues for certain window\n" +" managers or desktops (currently kde and xfce).\n" +" By default it tries to guess which one, but it can\n" +" guess incorrectly. Use this option to indicate which\n" +" wm/dt. \"string\" can be \"gnome\", \"kde\", \"cde\",\n" +" \"xfce\", or \"root\" (classic X wm). Anything else\n" +" is interpreted as \"root\".\n" +"\n" +"-debug_pointer Print debugging output for every pointer event.\n" +"-debug_keyboard Print debugging output for every keyboard event.\n" +" Same as -dp and -dk, respectively. Use multiple\n" +" times for more output.\n" +"\n" +"-defer time Time in ms to wait for updates before sending to client\n" +" (deferUpdateTime) Default: %d\n" +"-wait time Time in ms to pause between screen polls. Used to cut\n" +" down on load. Default: %d\n" +"-wait_ui factor Factor by which to cut the -wait time if there\n" +" has been recent user input (pointer or keyboard).\n" +" Improves response, but increases the load whenever you\n" +" are moving the mouse or typing. Default: %.2f\n" +"-nowait_bog Do not detect if the screen polling is \"bogging down\"\n" +" and sleep more. Some activities with no user input can\n" +" slow things down a lot: consider a large terminal window\n" +" with a long build running in it continously streaming\n" +" text output. By default x11vnc will try to detect this\n" +" (3 screen polls in a row each longer than 0.25 sec with\n" +" no user input), and sleep up to 1.5 secs to let things\n" +" \"catch up\". Use this option to disable that detection.\n" +"-slow_fb time Floating point time in seconds delay all screen polling.\n" +" For special purpose usage where a low frame rate is\n" +" acceptable and desirable, but you want the user input\n" +" processed at the normal rate so you cannot use -wait.\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" +"-sb time Time in seconds after NO activity (e.g. screen blank)\n" +" to really throttle down the screen polls (i.e. sleep\n" +" for about 1.5 secs). Use 0 to disable. Default: %d\n" +"\n" +"-noxdamage Do not use the X DAMAGE extension to detect framebuffer\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" +" and 2) detects changed areas (small ones by default)\n" +" more quickly.\n" +"\n" +" Currently the DAMAGE extension is overly conservative\n" +" and often reports large areas (e.g. a whole terminal\n" +" or browser window) as damaged even though the actual\n" +" changed region is much smaller (sometimes just a few\n" +" pixels). So heuristics were introduced to skip large\n" +" areas and use the damage rectangles only as \"hints\"\n" +" for the traditional scanline polling. The following\n" +" tuning parameters are introduced to adjust this\n" +" behavior:\n" +"\n" +"-xd_area A Set the largest DAMAGE rectangle area \"A\" (in\n" +" pixels: width * height) to trust as truly damaged:\n" +" the rectangle will be copied from the framebuffer\n" +" (slow) no matter what. Set to zero to trust *all*\n" +" rectangles. Default: %d\n" +"-xd_mem f Set how long DAMAGE rectangles should be \"remembered\",\n" +" \"f\" is a floating point number and is in units of the\n" +" scanline repeat cycle time (%d iterations). The default\n" +" (%.1f) should give no painting problems. Increase it if\n" +" there are problems or decrease it to live on the edge\n" +" (perhaps useful on a slow machine).\n" +"\n" +"-sigpipe string Broken pipe (SIGPIPE) handling. \"string\" can be\n" +" \"ignore\" or \"exit\". For \"ignore\" libvncserver\n" +" will handle the abrupt loss of a client and continue,\n" +" for \"exit\" x11vnc will cleanup and exit at the 1st\n" +" broken connection. Default: \"ignore\". This option\n" +" is obsolete.\n" +"-threads Whether or not to use the threaded libvncserver\n" +"-nothreads algorithm [rfbRunEventLoop] if libpthread is available\n" +" Default: %s\n" +"\n" +"-fs f If the fraction of changed tiles in a poll is greater\n" +" than f, the whole screen is updated. Default: %.2f\n" +"-gaps n Heuristic to fill in gaps in rows or cols of n or\n" +" less tiles. Used to improve text paging. Default: %d\n" +"-grow n Heuristic to grow islands of changed tiles n or wider\n" +" 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" +" memory and examine that copy for changes. Under some\n" +" circumstances this will improve interactive response,\n" +" or at least make things look smoother, but in others\n" +" (most!) it will make the response worse. If the video\n" +" h/w fb is such that reading small tiles is very slow\n" +" this mode could help. To keep the \"framerate\" up\n" +" the screen size x bpp cannot be too large. Note that\n" +" this mode is very wasteful of memory I/O resources\n" +" (it makes full screen copies even if nothing changes).\n" +" It may be of use in video capture-like applications,\n" +" or where window tearing is a problem.\n" +"\n" +"-rawfb string Experimental option, instead of polling X, poll the\n" +" memory object specified in \"string\". For shared\n" +" memory segments it is of the form: \"shm:N@WxHxB\"\n" +" which specifies a shmid N and framebuffer Width, Height,\n" +" and Bits per pixel. To memory map mmap(2) a file use:\n" +" \"map:/path/to/a/file@WxHxB\". If there is trouble\n" +" with mmap, use \"file:/...\" for slower lseek(2)\n" +" based reading. If you do not supply a type \"map\"\n" +" is assumed if the file exists.\n" +"\n" +" If string is \"setup:cmd\", then the command \"cmd\"\n" +" is run and the first line from it is read and used\n" +" as \"string\". This allows initializing the device,\n" +" determining WxHxB, etc. These are often done as root\n" +" so take care.\n" +"\n" +" Optional suffixes are \":R/G/B\" and \"+O\" to specify\n" +" red, green, and blue masks and an offset into the\n" +" memory object. If the masks are not provided x11vnc\n" +" guesses them based on the bpp.\n" +"\n" +" Examples:\n" +" -rawfb shm:210337933@800x600x32:ff/ff00/ff0000\n" +" -rawfb map:/dev/fb0@1024x768x32\n" +" -rawfb map:/tmp/Xvfb_screen0@640x480x8+3232\n" +" -rawfb file:/tmp/my.pnm@250x200x24+37\n" +"\n" +" (see ipcs(1) and fbset(1) for the first two examples)\n" +"\n" +" All user input is discarded by default (but see the\n" +" -pipeinput option). Most of the X11 (screen, keyboard,\n" +" mouse) options do not make sense and many will cause\n" +" this mode to crash, so please think twice before\n" +" setting/changing them.\n" +"\n" +" If you don't want x11vnc to close the X DISPLAY in\n" +" rawfb mode, then capitalize the prefix, SHM:, MAP:,\n" +" FILE: Keeping the display open enables the default\n" +" remote-control channel, which could be useful. Also,\n" +" if you also specify -noviewonly, then the mouse and\n" +" keyboard input are STILL sent to the X display, this\n" +" usage should be very rare, i.e. doing something strange\n" +" with /dev/fb0.\n" +"\n" +"-pipeinput cmd Another experimental option: it lets you supply an\n" +" external command in \"cmd\" that x11vnc will pipe\n" +" all of the user input events to in a simple format.\n" +" In -pipeinput mode by default x11vnc will not process\n" +" any of the user input events. If you prefix \"cmd\"\n" +" with \"tee:\" it will both send them to the pipe\n" +" command and process them. For a description of the\n" +" format run \"-pipeinput tee:/bin/cat\". Another prefix\n" +" is \"reopen\" which means to reopen pipe if it exits.\n" +" Separate multiple prefixes with commas.\n" +"\n" +" In combination with -rawfb one might be able to\n" +" do amusing things (e.g. control non-X devices).\n" +" To facilitate this, if -rawfb is in effect then the\n" +" value is stored in X11VNC_RAWFB_STR for the pipe command\n" +" to use if it wants. Do 'env | grep X11VNC' for more.\n" +"\n" +"-gui [gui-opts] Start up a simple tcl/tk gui based on the the remote\n" +" control options -remote/-query described below.\n" +" Requires the \"wish\" program to be installed on the\n" +" machine. \"gui-opts\" is not required: the default\n" +" is to start up both the full gui and x11vnc with the\n" +" gui showing up on the X display in the environment\n" +" variable DISPLAY.\n" +"\n" +" \"gui-opts\" can be a comma separated list of items.\n" +" Currently there are these types of items: 1) a gui\n" +" mode, a 2) gui \"simplicity\", 3) the X display the\n" +" gui should display on, 4) a \"tray\" or \"icon\" mode,\n" +" and 5) a gui geometry.\n" +"\n" +" 1) The gui mode can be \"start\", \"conn\", or \"wait\"\n" +" \"start\" is the default mode above and is not required.\n" +" \"conn\" means do not automatically start up x11vnc,\n" +" but instead just try to connect to an existing x11vnc\n" +" process. \"wait\" means just start the gui and nothing\n" +" else (you will later instruct the gui to start x11vnc\n" +" or connect to an existing one.)\n" +"\n" +" 2) The gui simplicity is off by default (a power-user\n" +" gui with all options is presented) To start with\n" +" something less daunting supply the string \"simple\"\n" +" (\"ez\" is an alias for this). Once the gui is\n" +" started you can toggle between the two with \"Misc ->\n" +" simple_gui\".\n" +"\n" +" 3) Note the possible confusion regarding the potentially\n" +" two different X displays: x11vnc polls one, but you\n" +" may want the gui to appear on another. For example, if\n" +" you ssh in and x11vnc is not running yet you may want\n" +" the gui to come back to you via your ssh redirected X\n" +" display (e.g. localhost:10).\n" +"\n" +" If you do not specify a gui X display in \"gui-opts\"\n" +" then the DISPLAY environment variable and -display\n" +" option are tried (in that order). Regarding the x11vnc\n" +" X display the gui will try to communication with, it\n" +" first tries -display and then DISPLAY. For example,\n" +" \"x11vnc -display :0 -gui otherhost:0\", will remote\n" +" control an x11vnc polling :0 and display the gui on\n" +" otherhost:0 The \"tray/icon\" mode below reverses this\n" +" preference, preferring to display on the x11vnc display.\n" +"\n" +" 4) When \"tray\" or \"icon\" is specified, the gui\n" +" presents itself as a small icon with behavior typical\n" +" of a \"system tray\" or \"dock applet\". The color\n" +" of the icon indicates status (connected clients) and\n" +" there is also a balloon status. Clicking on the icon\n" +" gives a menu from which properties, etc, can be set and\n" +" the full gui is available under \"Advanced\". To be\n" +" fully functional, the gui mode should be \"start\"\n" +" (the default).\n" +"\n" +" For \"icon\" the gui just a small standalone window.\n" +" For \"tray\" it will attempt to embed itself in the\n" +" \"system tray\" if possible. If \"=setpass\" is appended then\n" +" at startup the X11 user will be prompted to set the\n" +" VNC session password. If =<hexnumber> is appended\n" +" that icon will attempt to embed itself in the window\n" +" given by hexnumber. Use =noadvanced to disable the\n" +" full gui. (To supply more than one, use \"+\" sign).\n" +" E.g. -gui tray=setpass and -gui icon=0x3600028\n" +"\n" +" Other modes: \"full\", the default and need not be\n" +" specified. \"-gui none\", do not show a gui, useful\n" +" to override a ~/.x11vncrc setting, etc.\n" +"\n" +" 5) When \"geom=+X+Y\" is specified, that geometry\n" +" is passed to the gui toplevel. This is the icon in\n" +" icon/tray mode, or the full gui otherwise. You can\n" +" also specify width and height, i.e. WxH+X+Y, but it\n" +" is not recommended. In \"tray\" mode the geometry is\n" +" ignored unless the system tray manager does not seem\n" +" to be running. One could imagine using something like\n" +" \"-gui tray,geom=+4000+4000\" with a display manager\n" +" to keep the gui invisible until someone logs in...\n" +"\n" +" More icon tricks, \"icon=minimal\" gives an icon just\n" +" with the VNC display number. You can also set the font\n" +" with \"iconfont=...\". The following could be useful:\n" +" \"-gui icon=minimal,iconfont=5x8,geom=24x10+0-0\"\n" +"\n" +" General examples of the -gui option: \"x11vnc -gui\",\n" +" \"x11vnc -gui ez\" \"x11vnc -gui localhost:10\",\n" +" \"x11vnc -gui conn,host:0\", \"x11vnc -gui tray,ez\"\n" +" \"x11vnc -gui tray=setpass\"\n" +"\n" +" If you do not intend to start x11vnc from the gui\n" +" (i.e. just remote control an existing one), then the\n" +" gui process can run on a different machine from the\n" +" x11vnc server as long as X permissions, etc. permit\n" +" communication between the two.\n" +"\n" +"-remote command Remotely control some aspects of an already running\n" +" x11vnc server. \"-R\" and \"-r\" are aliases for\n" +" \"-remote\". After the remote control command is\n" +" sent to the running server the 'x11vnc -remote ...'\n" +" command exits. You can often use the -query command\n" +" (see below) to see if the x11vnc server processed your\n" +" -remote command.\n" +"\n" +" The default communication channel is that of X\n" +" properties (specifically VNC_CONNECT), and so this\n" +" command must be run with correct settings for DISPLAY\n" +" and possibly XAUTHORITY to connect to the X server\n" +" and set the property. Alternatively, use the -display\n" +" and -auth options to set them to the correct values.\n" +" The running server cannot use the -novncconnect option\n" +" because that disables the communication channel.\n" +" See below for alternate channels.\n" +"\n" +" For example: 'x11vnc -remote stop' (which is the same as\n" +" 'x11vnc -R stop') will close down the x11vnc server.\n" +" 'x11vnc -R shared' will enable shared connections, and\n" +" 'x11vnc -R scale:3/4' will rescale the desktop.\n" +"\n" +" The following -remote/-R commands are supported:\n" +"\n" +" stop terminate the server, same as \"quit\"\n" +" \"exit\" or \"shutdown\".\n" +" ping see if the x11vnc server responds.\n" +" Return is: ans=ping:<xdisplay>\n" +" blacken try to push a black fb update to all\n" +" clients (due to timings a client\n" +" could miss it). Same as \"zero\", also\n" +" \"zero:x1,y1,x2,y2\" for a rectangle.\n" +" refresh send the entire fb to all clients.\n" +" reset recreate the fb, polling memory, etc.\n" +/* ext. cmd. */ +" id:windowid set -id window to \"windowid\". empty\n" +" or \"root\" to go back to root window\n" +" sid:windowid set -sid window to \"windowid\"\n" +" waitmapped wait until subwin is mapped.\n" +" nowaitmapped do not wait until subwin is mapped.\n" +" clip:WxH+X+Y set -clip mode to \"WxH+X+Y\"\n" +" flashcmap enable -flashcmap mode.\n" +" noflashcmap disable -flashcmap mode.\n" +" shiftcmap:n set -shiftcmap to n.\n" +" notruecolor enable -notruecolor mode.\n" +" truecolor disable -notruecolor mode.\n" +" overlay enable -overlay mode (if applicable).\n" +" nooverlay disable -overlay mode.\n" +" overlay_cursor in -overlay mode, enable cursor drawing.\n" +" overlay_nocursor disable cursor drawing. same as\n" +" nooverlay_cursor.\n" +" visual:vis set -visual to \"vis\"\n" +" scale:frac set -scale to \"frac\"\n" +" scale_cursor:f set -scale_cursor to \"f\"\n" +" viewonly enable -viewonly mode.\n" +/* access view,share,forever */ +" noviewonly disable -viewonly mode.\n" +" shared enable -shared mode.\n" +" noshared disable -shared mode.\n" +" forever enable -forever mode.\n" +" noforever disable -forever mode.\n" +" timeout:n reset -timeout to n, if there are\n" +" currently no clients, exit unless one\n" +" connects in the next n secs.\n" +/* access */ +" http enable http client connections.\n" +" nohttp disable http client connections.\n" +" deny deny any new connections, same as \"lock\"\n" +" nodeny allow new connections, same as \"unlock\"\n" +/* access, filename */ +" connect:host do reverse connection to host, \"host\"\n" +" may be a comma separated list of hosts\n" +" or host:ports. See -connect.\n" +" disconnect:host disconnect any clients from \"host\"\n" +" same as \"close:host\". Use host\n" +" \"all\" to close all current clients.\n" +" If you know the client internal hex ID,\n" +" e.g. 0x3 (returned by \"-query clients\"\n" +" and RFB_CLIENT_ID) you can use that too.\n" +/* access */ +" allowonce:host For the next connection only, allow\n" +" connection from \"host\".\n" +/* access */ +" allow:hostlist set -allow list to (comma separated)\n" +" \"hostlist\". See -allow and -localhost.\n" +" Do not use with -allow /path/to/file\n" +" Use \"+host\" to add a single host, and\n" +" use \"-host\" to delete a single host\n" +" localhost enable -localhost mode\n" +" nolocalhost disable -localhost mode\n" +" listen:str set -listen to str, empty to disable.\n" +" nolookup enable -nolookup mode.\n" +" lookup disable -nolookup mode.\n" +" input:str set -input to \"str\", empty to disable.\n" +" client_input:str set the K, M, B -input on a per-client\n" +" basis. select which client as for\n" +" disconnect, e.g. client_input:host:MB\n" +" or client_input:0x2:K\n" +/* ext. cmd. */ +" accept:cmd set -accept \"cmd\" (empty to disable).\n" +" afteraccept:cmd set -afteraccept (empty to disable).\n" +" gone:cmd set -gone \"cmd\" (empty to disable).\n" +" noshm enable -noshm mode.\n" +" shm disable -noshm mode (i.e. use shm).\n" +" flipbyteorder enable -flipbyteorder mode, you may need\n" +" to set noshm for this to do something.\n" +" noflipbyteorder disable -flipbyteorder mode.\n" +" onetile enable -onetile mode. (you may need to\n" +" set shm for this to do something)\n" +" noonetile disable -onetile mode.\n" +/* ext. cmd. */ +" solid enable -solid mode\n" +" nosolid disable -solid mode.\n" +" solid_color:color set -solid color (and apply it).\n" +" blackout:str set -blackout \"str\" (empty to disable).\n" +" See -blackout for the form of \"str\"\n" +" (basically: WxH+X+Y,...)\n" +" Use \"+WxH+X+Y\" to append a single\n" +" rectangle use \"-WxH+X+Y\" to delete one\n" +" xinerama enable -xinerama mode. (if applicable)\n" +" noxinerama disable -xinerama mode.\n" +" xtrap enable -xtrap input mode(if applicable)\n" +" noxtrap disable -xtrap input mode.\n" +" xrandr enable -xrandr mode. (if applicable)\n" +" noxrandr disable -xrandr mode.\n" +" xrandr_mode:mode set the -xrandr mode to \"mode\".\n" +" padgeom:WxH set -padgeom to WxH (empty to disable)\n" +" If WxH is \"force\" or \"do\" the padded\n" +" geometry fb is immediately applied.\n" +" quiet enable -quiet mode.\n" +" noquiet disable -quiet mode.\n" +" modtweak enable -modtweak mode.\n" +" nomodtweak enable -nomodtweak mode.\n" +" xkb enable -xkb modtweak mode.\n" +" noxkb disable -xkb modtweak mode.\n" +" skip_keycodes:str enable -xkb -skip_keycodes \"str\".\n" +" sloppy_keys enable -sloppy_keys mode.\n" +" nosloppy_keys disable -sloppy_keys mode.\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" +" clear_mods enable -clear_mods mode and clear them.\n" +" noclear_mods disable -clear_mods mode.\n" +" clear_keys enable -clear_keys mode and clear them.\n" +" noclear_keys disable -clear_keys mode.\n" +/* filename */ +" remap:str set -remap \"str\" (empty to disable).\n" +" See -remap for the form of \"str\"\n" +" (basically: key1-key2,key3-key4,...)\n" +" Use \"+key1-key2\" to append a single\n" +" keymapping, use \"-key1-key2\" to delete.\n" +" norepeat enable -norepeat mode.\n" +" repeat disable -norepeat mode.\n" +" nofb enable -nofb mode.\n" +" fb disable -nofb mode.\n" +" bell enable bell (if supported).\n" +" nobell disable bell.\n" +" nosel enable -nosel mode.\n" +" sel disable -nosel mode.\n" +" noprimary enable -noprimary mode.\n" +" primary disable -noprimary mode.\n" +" seldir:str set -seldir to \"str\"\n" +" cursor:mode enable -cursor \"mode\".\n" +" show_cursor enable showing a cursor.\n" +" noshow_cursor disable showing a cursor. (same as\n" +" \"nocursor\")\n" +" arrow:n set -arrow to alternate n.\n" +" xfixes enable xfixes cursor shape mode.\n" +" noxfixes disable xfixes cursor shape mode.\n" +" alphacut:n set -alphacut to n.\n" +" alphafrac:f set -alphafrac to f.\n" +" alpharemove enable -alpharemove mode.\n" +" noalpharemove disable -alpharemove mode.\n" +" alphablend disable -noalphablend mode.\n" +" noalphablend enable -noalphablend mode.\n" +" cursorshape disable -nocursorshape mode.\n" +" nocursorshape enable -nocursorshape mode.\n" +" cursorpos disable -nocursorpos mode.\n" +" nocursorpos enable -nocursorpos mode.\n" +" xwarp enable -xwarppointer mode.\n" +" noxwarp disable -xwarppointer mode.\n" +" buttonmap:str set -buttonmap \"str\", empty to disable\n" +" dragging disable -nodragging mode.\n" +" nodragging enable -nodragging mode.\n" +" wireframe enable -wireframe mode. same as \"wf\"\n" +" nowireframe disable -wireframe mode. same as \"nowf\"\n" +" wireframe:str enable -wireframe mode string.\n" +" wireframe_mode:str enable -wireframe mode string.\n" +" wirecopyrect:str set -wirecopyrect string. same as \"wcr:\"\n" +" 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_term:list set -scr_term to \"list\"\n" +" scr_keyrepeat:str set -scr_keyrepeat to \"str\"\n" +" scr_parms:str set -scr_parms parameters.\n" +" fixscreen:str set -fixscreen to \"str\".\n" +" noxrecord disable all use of RECORD extension.\n" +" xrecord enable use of RECORD extension.\n" +" reset_record reset RECORD extension (if avail.)\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" +" wmdt:str set -wmdt to str.\n" +" debug_pointer enable -debug_pointer, same as \"dp\"\n" +" nodebug_pointer disable -debug_pointer, same as \"nodp\"\n" +" debug_keyboard enable -debug_keyboard, same as \"dk\"\n" +" nodebug_keyboard disable -debug_keyboard, same as \"nodk\"\n" +" defer:n set -defer to n ms,same as deferupdate:n\n" +" wait:n set -wait to n ms.\n" +" wait_ui:f set -wait_ui factor to f.\n" +" wait_bog disable -nowait_bog mode.\n" +" nowait_bog enable -nowait_bog mode.\n" +" slow_fb:f set -slow_fb to f seconds.\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" +" xdamage enable xdamage polling hints.\n" +" noxdamage disable xdamage polling hints.\n" +" xd_area:A set -xd_area max pixel area to \"A\"\n" +" xd_mem:f set -xd_mem remembrance to \"f\"\n" +" fs:frac set -fs fraction to \"frac\", e.g. 0.5\n" +" gaps:n set -gaps to n.\n" +" grow:n set -grow to n.\n" +" fuzz:n set -fuzz to n.\n" +" snapfb enable -snapfb mode.\n" +" nosnapfb disable -snapfb mode.\n" +" rawfb:str set -rawfb mode to \"str\".\n" +" progressive:n set libvncserver -progressive slice\n" +" height parameter to n.\n" +" desktop:str set -desktop name to str for new clients.\n" +" rfbport:n set -rfbport to n.\n" +/* access */ +" httpport:n set -httpport to n.\n" +" httpdir:dir set -httpdir to dir (and enable http).\n" +" enablehttpproxy enable -enablehttpproxy mode.\n" +" noenablehttpproxy disable -enablehttpproxy mode.\n" +" alwaysshared enable -alwaysshared mode.\n" +" noalwaysshared disable -alwaysshared mode.\n" +" (may interfere with other options)\n" +" nevershared enable -nevershared mode.\n" +" nonevershared disable -nevershared mode.\n" +" (may interfere with other options)\n" +" dontdisconnect enable -dontdisconnect mode.\n" +" nodontdisconnect disable -dontdisconnect mode.\n" +" (may interfere with other options)\n" +" debug_xevents enable debugging X events.\n" +" nodebug_xevents disable debugging X events.\n" +" debug_xdamage enable debugging X DAMAGE mechanism.\n" +" nodebug_xdamage disable debugging X DAMAGE mechanism.\n" +" debug_wireframe enable debugging wireframe mechanism.\n" +" 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" +" debug_grabs enable -debug_grabs\n" +" nodebug_grabs disable -debug_grabs\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" +"\n" +" The vncconnect(1) command from standard VNC\n" +" distributions may also be used if string is prefixed\n" +" with \"cmd=\" E.g. 'vncconnect cmd=stop'. Under some\n" +" circumstances xprop(1) can used if it supports -set\n" +" (see the FAQ).\n" +"\n" +" If \"-connect /path/to/file\" has been supplied to the\n" +" running x11vnc server then that file can be used as a\n" +" communication channel (this is the only way to remote\n" +" control one of many x11vnc's polling the same X display)\n" +" Simply run: 'x11vnc -connect /path/to/file -remote ...'\n" +" or you can directly write to the file via something\n" +" like: \"echo cmd=stop > /path/to/file\", etc.\n" +"\n" +"-query variable Like -remote, except just query the value of\n" +" \"variable\". \"-Q\" is an alias for \"-query\".\n" +" Multiple queries can be done by separating variables\n" +" by commas, e.g. -query var1,var2. The results come\n" +" back in the form ans=var1:value1,ans=var2:value2,...\n" +" to the standard output. If a variable is read-only,\n" +" it comes back with prefix \"aro=\" instead of \"ans=\".\n" +"\n" +" Some -remote commands are pure actions that do not make\n" +" sense as variables, e.g. \"stop\" or \"disconnect\",\n" +" in these cases the value returned is \"N/A\". To direct\n" +" a query straight to the VNC_CONNECT property or connect\n" +" file use \"qry=...\" instead of \"cmd=...\"\n" +"\n" +" Here is the current list of \"variables\" that can\n" +" be supplied to the -query command. This includes the\n" +" \"N/A\" ones that return no useful info. For variables\n" +" names that do not correspond to an x11vnc option or\n" +" remote command, we hope the name makes it obvious what\n" +" the returned value corresponds to (hint: the ext_*\n" +" variables correspond to the presence of X extensions):\n" +"\n" +" ans= stop quit exit shutdown ping blacken zero\n" +" refresh reset close disconnect id sid waitmapped\n" +" nowaitmapped clip flashcmap noflashcmap shiftcmap\n" +" truecolor notruecolor overlay nooverlay overlay_cursor\n" +" overlay_yescursor nooverlay_nocursor nooverlay_cursor\n" +" nooverlay_yescursor overlay_nocursor visual scale\n" +" scale_cursor viewonly noviewonly shared noshared\n" +" forever noforever once timeout filexfer deny lock\n" +" nodeny unlock connect allowonce allow localhost\n" +" nolocalhost listen lookup nolookup accept afteraccept\n" +" gone shm noshm flipbyteorder noflipbyteorder onetile\n" +" noonetile solid_color solid nosolid blackout xinerama\n" +" noxinerama xtrap noxtrap xrandr noxrandr xrandr_mode\n" +" padgeom quiet q noquiet modtweak nomodtweak xkb\n" +" noxkb skip_keycodes sloppy_keys nosloppy_keys\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 seldir cursorshape nocursorshape\n" +" cursorpos nocursorpos cursor show_cursor noshow_cursor\n" +" nocursor arrow xfixes noxfixes xdamage noxdamage\n" +" xd_area xd_mem alphacut alphafrac alpharemove\n" +" noalpharemove alphablend noalphablend xwarppointer\n" +" xwarp noxwarppointer noxwarp buttonmap dragging\n" +" nodragging wireframe_mode wireframe wf nowireframe\n" +" nowf wirecopyrect wcr nowirecopyrect nowcr scr_area\n" +" scr_skip scr_inc scr_keys scr_term scr_keyrepeat\n" +" scr_parms scrollcopyrect scr noscrollcopyrect noscr\n" +" fixscreen noxrecord xrecord reset_record pointer_mode\n" +" pm input_skip input client_input speeds wmdt\n" +" debug_pointer dp nodebug_pointer nodp debug_keyboard\n" +" dk nodebug_keyboard nodk deferupdate defer wait_ui\n" +" wait_bog nowait_bog slow_fb 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 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\n" +" debug_grabs nodebug_grabs dbg nodbg noremote\n" +"\n" +" aro= noop display vncdisplay desktopname guess_desktop\n" +" http_url auth xauth users rootshift clipshift\n" +" scale_str scaled_x scaled_y scale_numer scale_denom\n" +" scale_fac scaling_blend scaling_nomult4 scaling_pad\n" +" scaling_interpolate inetd privremote unsafe safer nocmds\n" +" passwdfile using_shm logfile o flag rc norc h help V\n" +" version lastmod bg sigpipe threads readrate netrate\n" +" netlatency pipeinput clients client_count pid ext_xtest\n" +" ext_xtrap ext_xrecord ext_xkb ext_xshm ext_xinerama\n" +" ext_overlay ext_xfixes ext_xdamage ext_xrandr rootwin\n" +" num_buttons button_mask mouse_x mouse_y bpp depth\n" +" indexed_color dpy_x dpy_y wdpy_x wdpy_y off_x off_y\n" +" cdpy_x cdpy_y coff_x coff_y rfbauth passwd viewpasswd\n" +"\n" +"-QD variable Just like -query variable, but returns the default\n" +" value for that parameter (no running x11vnc server\n" +" is consulted)\n" +"\n" +"-sync By default -remote commands are run asynchronously, that\n" +" is, the request is posted and the program immediately\n" +" exits. Use -sync to have the program wait for an\n" +" acknowledgement from the x11vnc server that command was\n" +" processed (somehow). On the other hand -query requests\n" +" are always processed synchronously because they have\n" +" to wait for the answer.\n" +"\n" +" Also note that if both -remote and -query requests are\n" +" supplied on the command line, the -remote is processed\n" +" first (synchronously: no need for -sync), and then\n" +" the -query request is processed in the normal way.\n" +" This allows for a reliable way to see if the -remote\n" +" command was processed by querying for any new settings.\n" +" Note however that there is timeout of a few seconds so\n" +" if the x11vnc takes longer than that to process the\n" +" requests the requestor will think that a failure has\n" +" taken place.\n" +"\n" +"-noremote Do not process any remote control commands or queries.\n" +"-yesremote Do process remote control commands or queries.\n" +" Default: %s\n" +"\n" +" A note about security wrt remote control commands.\n" +" If someone can connect to the X display and change\n" +" the property VNC_CONNECT, then they can remotely\n" +" control x11vnc. Normally access to the X display is\n" +" protected. Note that if they can modify VNC_CONNECT\n" +" on the X server, they have enough permissions to also\n" +" run their own x11vnc and thus have complete control\n" +" of the desktop. If the \"-connect /path/to/file\"\n" +" channel is being used, obviously anyone who can write\n" +" to /path/to/file can remotely control x11vnc. So be\n" +" sure to protect the X display and that file's write\n" +" permissions. See -privremote below.\n" +"\n" +" If you are paranoid and do not think -noremote is\n" +" enough, to disable the VNC_CONNECT property channel\n" +" completely use -novncconnect, or use the -safer\n" +" option that shuts many things off.\n" +"\n" +"-unsafe A few remote commands are disabled by default\n" +" (currently: id:pick, accept:<cmd>, gone:<cmd>, and\n" +" rawfb:setup:<cmd>) because they are associated with\n" +" running external programs. If you specify -unsafe, then\n" +" these remote-control commands are allowed. Note that\n" +" you can still specify these parameters on the command\n" +" line, they just cannot be invoked via remote-control.\n" +"-safer Equivalent to: -novncconnect -noremote and prohibiting\n" +" -gui and the -connect file. Shuts off communcation\n" +" channels.\n" +"-privremote Perform some sanity checks and disable remote-control\n" +" commands if it appears that the X DISPLAY and/or\n" +" connectfile can be accessed by other users. Once\n" +" remote-control is disabled it cannot be turned back on.\n" +"-nocmds No external commands (e.g. system(3), popen(3), exec(3))\n" +" will be run.\n" +"\n" +"-deny_all For use with -remote nodeny: start out denying all\n" +" incoming clients until \"-remote nodeny\" is used to\n" +" let them in.\n" +"%s\n" +"\n" +"These options are passed to libvncserver:\n" +"\n" +; + /* have both our help and rfbUsage to stdout for more(1), etc. */ + dup2(1, 2); + + /* register extention(s) to get their help output */ +#ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER + rfbRegisterTightVNCFileTransferExtension(); +#endif + + if (mode == 1) { + char *p; + int l = 0; + fprintf(stderr, "x11vnc: allow VNC connections to real " + "X11 displays. %s\n\nx11vnc options:\n", lastmod); + p = strtok(help, "\n"); + while (p) { + int w = 23; + char tmp[100]; + if (p[0] == '-') { + strncpy(tmp, p, w); + fprintf(stderr, " %s", tmp); + l++; + if (l % 2 == 0) { + fprintf(stderr, "\n"); + } + } + p = strtok(NULL, "\n"); + } + fprintf(stderr, "\n\nlibvncserver options:\n"); + rfbUsage(); + fprintf(stderr, "\n"); + exit(1); + } + fprintf(stderr, help, lastmod, + scaling_copyrect ? ":cr":":nocr", + view_only ? "on":"off", + 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, + alpha_frac, + cursor_pos_updates ? "-cursorpos":"-nocursorpos", + wireframe ? "-wireframe":"-nowireframe", + WIREFRAME_PARMS, + wireframe_copyrect_default, + scroll_copyrect_default, + scrollcopyrect_min_area, + scroll_skip_str0 ? scroll_skip_str0 : "(empty)", + scroll_term_str0, + max_keyrepeat_str0, + SCROLL_COPYRECT_PARMS, + pointer_mode_max, pointer_mode, + ui_skip, + defer_update, + waitms, + wait_ui, + rfbMaxClientWait/1000, + take_naps ? "take naps":"no naps", + screen_blank, + xdamage_max_area, NSCAN, xdamage_memory, + use_threads ? "-threads":"-nothreads", + fs_frac, + gaps_fill, + grow_fill, + tile_fuzz, + accept_remote_cmds ? "-yesremote":"-noremote", + "" + ); + + rfbUsage(); +#endif + exit(1); +} + +void xopen_display_fail_message(char *disp) { + fprintf(stderr, "\n"); + fprintf(stderr, "*** x11vnc was unable to open the X DISPLAY: \"%s\"," + " it cannot continue.\n", disp); + fprintf(stderr, "*** There may be \"Xlib:\" error messages above" + " with details about the failure.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Some tips and guidelines:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " * An X server (the one you wish to view) must" + " be running before x11vnc is\n"); + fprintf(stderr, " started: x11vnc does not start the X server.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " * You must use -display <disp>, -OR- set and" + " export your DISPLAY\n"); + fprintf(stderr, " environment variable to refer to the display of" + " the desired X server.\n"); + fprintf(stderr, " - Usually the display is simply \":0\" (in fact" + " x11vnc uses this if you forget\n"); + fprintf(stderr, " to specify it), but in some multi-user" + " situations it could be \":1\", \":2\",\n"); + fprintf(stderr, " or even \":137\". Ask your administrator" + " or a guru if you are having\n"); + fprintf(stderr, " difficulty determining what your X DISPLAY is.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " * Next, you need to have sufficient permissions" + " (Xauthority) \n"); + fprintf(stderr, " to connect to the X DISPLAY. Here are some" + " Tips:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " - Often, you just need to run x11vnc as the user" + " logged into the X session.\n"); + fprintf(stderr, " So make sure to be that user when you type" + " x11vnc.\n"); + fprintf(stderr, " - Being root is usually not enough because the" + " incorrect MIT-MAGIC-COOKIE\n"); + fprintf(stderr, " file will be accessed. The cookie file contains" + " the secret key that\n"); + fprintf(stderr, " allows x11vnc to connect to the desired" + " X DISPLAY.\n"); + fprintf(stderr, " - You can explicity indicate which MIT-MAGIC-COOKIE" + " file should be used\n"); + fprintf(stderr, " by the -auth option, e.g.:\n"); + fprintf(stderr, " x11vnc -auth /home/someuser/.Xauthority" + " -display :0\n"); + fprintf(stderr, " you must have read permission for that file.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " - If NO ONE is logged into an X session yet, but" + " there is a greeter login\n"); + fprintf(stderr, " program like \"gdm\", \"kdm\", \"xdm\", or" + " \"dtlogin\" running, you will need\n"); + fprintf(stderr, " to find and use the raw display manager" + " MIT-MAGIC-COOKIE file.\n"); + fprintf(stderr, " Some examples for various display managers:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " gdm: -auth /var/gdm/:0.Xauth\n"); + fprintf(stderr, " kdm: -auth /var/lib/kdm/A:0-crWk72\n"); + fprintf(stderr, " xdm: -auth /var/lib/xdm/authdir/authfiles/A:0-XQvaJk\n"); + fprintf(stderr, " dtlogin: -auth /var/dt/A:0-UgaaXa\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " Only root will have read permission for the" + " file, and so x11vnc must be run\n"); + fprintf(stderr, " as root. The random characters in the filenames" + " will of course change,\n"); + fprintf(stderr, " and the directory the cookie file resides in may" + " also be system dependent.\n"); + fprintf(stderr, " Sometimes the command \"ps wwaux | grep auth\"" + " can reveal the file location.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "See also: http://www.karlrunge.com/x11vnc/#faq\n"); +} + +void nopassword_warning_msg(int gotloc) { + + char str1[] = +"###############################################################\n" +"#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n" +"#@ @#\n" +"#@ ** WARNING ** WARNING ** WARNING ** WARNING ** @#\n" +"#@ @#\n" +"#@ YOU ARE RUNNING X11VNC WITHOUT A PASSWORD!! @#\n" +"#@ @#\n" +"#@ This means anyone with network access to this computer @#\n" +"#@ will be able to easily view and control your desktop. @#\n" +"#@ @#\n" +"#@ >>> If you did not mean to do this Press CTRL-C now!! <<< @#\n" +"#@ @#\n" +"#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n" +; + char str2[] = +"#@ @#\n" +"#@ You can create an x11vnc password file by running: @#\n" +"#@ @#\n" +"#@ x11vnc -storepasswd password /path/to/passfile @#\n" +"#@ @#\n" +"#@ and then starting x11vnc via: @#\n" +"#@ @#\n" +"#@ x11vnc -rfbauth /path/to/passfile @#\n" +"#@ @#\n" +"#@ an existing ~/.vnc/passwd file will work too. @#\n" +"#@ @#\n" +"#@ You can also use the -passwdfile or -passwd options. @#\n" +"#@ (note -passwd is unsafe if local users are not trusted) @#\n" +"#@ @#\n" +"#@ Make sure any -rfbauth and -passwdfile password files @#\n" +"#@ cannot be read by untrusted users. @#\n" +"#@ @#\n" +"#@ Even with a password, the subsequent VNC traffic is @#\n" +"#@ sent in the clear. Consider tunnelling via ssh(1): @#\n" +"#@ @#\n" +"#@ http://www.karlrunge.com/x11vnc/#tunnelling @#\n" +"#@ @#\n" +"#@ Please Read the documention for more info about @#\n" +"#@ passwords, security, and encryption. @#\n" +"#@ @#\n" +"#@ http://www.karlrunge.com/x11vnc/#faq-passwd @#\n" +; + char str3[] = +"#@ @#\n" +"#@ You are using the -localhost option and that is a good @#\n" +"#@ thing!! Especially if you ssh(1) into this machine and @#\n" +"#@ use port redirection. Nevertheless, without a password @#\n" +"#@ other users could possibly do redirection as well to @#\n" +"#@ gain access to your desktop. @#\n" +; + char str4[] = +"#@ @#\n" +"#@ To disable this warning use the -nopw option, or put @#\n" +"#@ the setting in your ~/.x11vncrc file. @#\n" +"#@ @#\n" +"#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n" +"###############################################################\n" +; + char str5[] = +"###############################################################\n\n" +; + if (inetd) { + return; + } + + fprintf(stderr, "%s", str1); + fflush(stderr); +#if !PASSWD_REQUIRED + usleep(2500 * 1000); +#endif + if (!quiet) { + fprintf(stderr, "%s", str2); + if (gotloc) { + fprintf(stderr, "%s", str3); + } + fprintf(stderr, "%s", str4); + } else { + fprintf(stderr, "%s", str5); + } + fflush(stderr); +#if !PASSWD_REQUIRED + usleep(500 * 1000); +#endif +} + + diff --git a/x11vnc/help.h b/x11vnc/help.h new file mode 100644 index 0000000..4c319e9 --- /dev/null +++ b/x11vnc/help.h @@ -0,0 +1,10 @@ +#ifndef _X11VNC_HELP_H +#define _X11VNC_HELP_H + +/* -- help.h -- */ + +extern void print_help(int mode); +extern void xopen_display_fail_message(char *disp); +extern void nopassword_warning_msg(int gotloc); + +#endif /* _X11VNC_HELP_H */ diff --git a/x11vnc/inet.c b/x11vnc/inet.c new file mode 100644 index 0000000..29f4992 --- /dev/null +++ b/x11vnc/inet.c @@ -0,0 +1,259 @@ +/* -- inet.c -- */ + +#include "x11vnc.h" + +/* + * Simple utility to map host name to dotted IP address. Ignores aliases. + * Up to caller to free returned string. + */ +char *host2ip(char *host); +char *raw2host(char *raw, int len); +char *raw2ip(char *raw); +char *ip2host(char *ip); +int dotted_ip(char *host); +int get_remote_port(int sock); +int get_local_port(int sock); +char *get_remote_host(int sock); +char *get_local_host(int sock); +char *ident_username(rfbClientPtr client); + + +static int get_port(int sock, int remote); +static char *get_host(int sock, int remote); + + +char *host2ip(char *host) { + struct hostent *hp; + struct sockaddr_in addr; + char *str; + + if (! host_lookup) { + return NULL; + } + + hp = gethostbyname(host); + if (!hp) { + return NULL; + } + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = *(unsigned long *)hp->h_addr; + str = strdup(inet_ntoa(addr.sin_addr)); + return str; +} + +char *raw2host(char *raw, int len) { + char *str; +#if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H + struct hostent *hp; + + if (! host_lookup) { + return strdup("unknown"); + } + + hp = gethostbyaddr(raw, len, AF_INET); + if (!hp) { + return strdup(inet_ntoa(*((struct in_addr *)raw))); + } + str = strdup(hp->h_name); +#else + str = strdup("unknown"); +#endif + return str; +} + +char *raw2ip(char *raw) { + return strdup(inet_ntoa(*((struct in_addr *)raw))); +} + +char *ip2host(char *ip) { + char *str; +#if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H + struct hostent *hp; + in_addr_t iaddr; + + if (! host_lookup) { + return strdup("unknown"); + } + + iaddr = inet_addr(ip); + if (iaddr == htonl(INADDR_NONE)) { + return strdup("unknown"); + } + + hp = gethostbyaddr((char *)&iaddr, sizeof(in_addr_t), AF_INET); + if (!hp) { + return strdup("unknown"); + } + str = strdup(hp->h_name); +#else + str = strdup("unknown"); +#endif + return str; +} + +int dotted_ip(char *host) { + char *p = host; + while (*p != '\0') { + if (*p == '.' || isdigit(*p)) { + p++; + continue; + } + return 0; + } + return 1; +} + +static int get_port(int sock, int remote) { + struct sockaddr_in saddr; + unsigned int saddr_len; + int saddr_port; + + saddr_len = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + saddr_port = -1; + if (remote) { + if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_port = ntohs(saddr.sin_port); + } + } else { + if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_port = ntohs(saddr.sin_port); + } + } + return saddr_port; +} + +int get_remote_port(int sock) { + return get_port(sock, 1); +} + +int get_local_port(int sock) { + return get_port(sock, 0); +} + +static char *get_host(int sock, int remote) { + struct sockaddr_in saddr; + unsigned int saddr_len; + int saddr_port; + char *saddr_ip_str = NULL; + + saddr_len = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + saddr_port = -1; +#if LIBVNCSERVER_HAVE_NETINET_IN_H + if (remote) { + if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_ip_str = inet_ntoa(saddr.sin_addr); + } + } else { + if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { + saddr_ip_str = inet_ntoa(saddr.sin_addr); + } + } +#endif + if (! saddr_ip_str) { + saddr_ip_str = "unknown"; + } + return strdup(saddr_ip_str); +} + +char *get_remote_host(int sock) { + return get_host(sock, 1); +} + +char *get_local_host(int sock) { + return get_host(sock, 0); +} + +char *ident_username(rfbClientPtr client) { + ClientData *cd = (ClientData *) client->clientData; + char *str, *newhost, *user = NULL, *newuser = NULL; + int len; + + if (cd) { + user = cd->username; + } + if (!user || *user == '\0') { + char msg[128]; + int n, sock, ok = 0; + + if ((sock = rfbConnectToTcpAddr(client->host, 113)) < 0) { + rfbLog("could not connect to ident: %s:%d\n", + client->host, 113); + } else { + int ret; + fd_set rfds; + struct timeval tv; + int rport = get_remote_port(client->sock); + int lport = get_local_port(client->sock); + + sprintf(msg, "%d, %d\r\n", rport, lport); + n = write(sock, msg, strlen(msg)); + + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + tv.tv_sec = 4; + tv.tv_usec = 0; + ret = select(sock+1, &rfds, NULL, NULL, &tv); + + if (ret > 0) { + int i; + char *q, *p; + for (i=0; i<128; i++) { + msg[i] = '\0'; + } + usleep(250*1000); + n = read(sock, msg, 127); + close(sock); + if (n <= 0) goto badreply; + + /* 32782 , 6000 : USERID : UNIX :runge */ + q = strstr(msg, "USERID"); + if (!q) goto badreply; + q = strstr(q, ":"); + if (!q) goto badreply; + q++; + q = strstr(q, ":"); + if (!q) goto badreply; + q++; + q = lblanks(q); + p = q; + while (*p) { + if (*p == '\r' || *p == '\n') { + *p = '\0'; + } + p++; + } + ok = 1; + if (strlen(q) > 24) { + *(q+24) = '\0'; + } + newuser = strdup(q); + + badreply: + n = 0; /* avoid syntax error */ + } else { + close(sock); + } + } + if (! ok || !newuser) { + newuser = strdup("unknown-user"); + } + if (cd) { + if (cd->username) { + free(cd->username); + } + cd->username = newuser; + } + user = newuser; + } + newhost = ip2host(client->host); + len = strlen(user) + 1 + strlen(newhost) + 1; + str = (char *) malloc(len); + sprintf(str, "%s@%s", user, newhost); + free(newhost); + return str; +} + + diff --git a/x11vnc/inet.h b/x11vnc/inet.h new file mode 100644 index 0000000..4ca7c6b --- /dev/null +++ b/x11vnc/inet.h @@ -0,0 +1,17 @@ +#ifndef _X11VNC_INET_H +#define _X11VNC_INET_H + +/* -- inet.h -- */ + +extern char *host2ip(char *host); +extern char *raw2host(char *raw, int len); +extern char *raw2ip(char *raw); +extern char *ip2host(char *ip); +extern int dotted_ip(char *host); +extern int get_remote_port(int sock); +extern int get_local_port(int sock); +extern char *get_remote_host(int sock); +extern char *get_local_host(int sock); +extern char *ident_username(rfbClientPtr client); + +#endif /* _X11VNC_INET_H */ diff --git a/x11vnc/keyboard.c b/x11vnc/keyboard.c new file mode 100644 index 0000000..1fba4c9 --- /dev/null +++ b/x11vnc/keyboard.c @@ -0,0 +1,2817 @@ +/* -- keyboard.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "xrecord.h" +#include "xinerama.h" +#include "pointer.h" +#include "userinput.h" +#include "win_utils.h" +#include "rates.h" +#include "cleanup.h" +#include "allowed_input_t.h" + +void get_keystate(int *keystate); +void clear_modifiers(int init); +int track_mod_state(rfbKeySym keysym, rfbBool down, rfbBool set); +void clear_keys(void); +int get_autorepeat_state(void); +int get_initial_autorepeat_state(void); +void autorepeat(int restore, int bequiet); +void check_add_keysyms(void); +int add_keysym(KeySym keysym); +void delete_added_keycodes(int bequiet); +void initialize_remap(char *infile); +int sloppy_key_check(int key, rfbBool down, rfbKeySym keysym, int *new); +void switch_to_xkb_if_better(void); +char *short_kmb(char *str); +void initialize_allowed_input(void); +void initialize_modtweak(void); +void initialize_keyboard_and_pointer(void); +void get_allowed_input(rfbClientPtr client, allowed_input_t *input); +double typing_rate(double time_window, int *repeating); +int skip_cr_when_scaling(char *mode); +void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); + + +static void delete_keycode(KeyCode kc, int bequiet); +static int count_added_keycodes(void); +static void add_remap(char *line); +static void add_dead_keysyms(char *str); +static void initialize_xkb_modtweak(void); +static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client); +static void tweak_mod(signed char mod, rfbBool down); +static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client); +static void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); + + +/* + * Routine to retreive current state keyboard. 1 means down, 0 up. + */ +void get_keystate(int *keystate) { + int i, k; + char keys[32]; + + /* n.b. caller decides to X_LOCK or not. */ + XQueryKeymap(dpy, keys); + for (i=0; i<32; i++) { + char c = keys[i]; + + for (k=0; k < 8; k++) { + if (c & 0x1) { + keystate[8*i + k] = 1; + } else { + keystate[8*i + k] = 0; + } + c = c >> 1; + } + } +} + +/* + * Try to KeyRelease any non-Lock modifiers that are down. + */ +void clear_modifiers(int init) { + static KeyCode keycodes[256]; + static KeySym keysyms[256]; + static char *keystrs[256]; + static int kcount = 0, first = 1; + int keystate[256]; + int i, j, minkey, maxkey, syms_per_keycode; + KeySym *keymap; + KeySym keysym; + KeyCode keycode; + + /* n.b. caller decides to X_LOCK or not. */ + if (first) { + /* + * we store results in static arrays, to aid interrupted + * case, but modifiers could have changed during session... + */ + XDisplayKeycodes(dpy, &minkey, &maxkey); + + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + for (i = minkey; i <= maxkey; i++) { + for (j = 0; j < syms_per_keycode; j++) { + keysym = keymap[ (i - minkey) * syms_per_keycode + j ]; + if (keysym == NoSymbol || ! ismodkey(keysym)) { + continue; + } + keycode = XKeysymToKeycode(dpy, keysym); + if (keycode == NoSymbol) { + continue; + } + keycodes[kcount] = keycode; + keysyms[kcount] = keysym; + keystrs[kcount] = strdup(XKeysymToString(keysym)); + kcount++; + } + } + XFree((void *) keymap); + first = 0; + } + if (init) { + return; + } + + get_keystate(keystate); + for (i=0; i < kcount; i++) { + keysym = keysyms[i]; + keycode = keycodes[i]; + + if (! keystate[(int) keycode]) { + continue; + } + + if (clear_mods) { + rfbLog("clear_modifiers: up: %-10s (0x%x) " + "keycode=0x%x\n", keystrs[i], keysym, keycode); + } + XTestFakeKeyEvent_wr(dpy, keycode, False, CurrentTime); + } + XFlush(dpy); +} + +static KeySym simple_mods[] = { + XK_Shift_L, XK_Shift_R, + XK_Control_L, XK_Control_R, + XK_Meta_L, XK_Meta_R, + XK_Alt_L, XK_Alt_R, + XK_Super_L, XK_Super_R, + XK_Hyper_L, XK_Hyper_R, + XK_Mode_switch, + NoSymbol +}; +#define NSIMPLE_MODS 13 + +int track_mod_state(rfbKeySym keysym, rfbBool down, rfbBool set) { + KeySym sym = (KeySym) keysym; + static rfbBool isdown[NSIMPLE_MODS]; + static int first = 1; + int i, cnt = 0; + + /* + * simple tracking method for the modifier state without + * contacting the Xserver. Ignores, of course what keys are + * pressed on the physical display. + * + * This is unrelated to our mod_tweak and xkb stuff. + * Just a simple thing for wireframe/scroll heuristics, + * sloppy keys etc. + */ + + if (first) { + for (i=0; i<NSIMPLE_MODS; i++) { + isdown[i] = FALSE; + } + first = 0; + } + + if (sym != NoSymbol) { + for (i=0; i<NSIMPLE_MODS; i++) { + if (sym == simple_mods[i]) { + if (set) { + isdown[i] = down; + return 1; + } else { + if (isdown[i]) { + return 1; + } else { + return 0; + } + } + break; + } + } + /* not a modifier */ + if (set) { + return 0; + } else { + return -1; + } + } + + /* called with NoSymbol: return number currently pressed: */ + for (i=0; i<NSIMPLE_MODS; i++) { + if (isdown[i]) { + cnt++; + } + } + return cnt; +} + +/* + * Attempt to set all keys to Up position. Can mess up typing at the + * physical keyboard so use with caution. + */ +void clear_keys(void) { + int k, keystate[256]; + + /* n.b. caller decides to X_LOCK or not. */ + get_keystate(keystate); + for (k=0; k<256; k++) { + if (keystate[k]) { + KeyCode keycode = (KeyCode) k; + rfbLog("clear_keys: keycode=%d\n", keycode); + XTestFakeKeyEvent_wr(dpy, keycode, False, CurrentTime); + } + } + XFlush(dpy); +} + +/* + * Kludge for -norepeat option: we turn off keystroke autorepeat in + * the X server when clients are connected. This may annoy people at + * the physical display. We do this because 'key down' and 'key up' + * user input events may be separated by 100s of ms due to screen fb + * processing or link latency, thereby inducing the X server to apply + * autorepeat when it should not. Since the *client* is likely doing + * 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; + XGetKeyboardControl(dpy, &kstate); + X_UNLOCK; + return kstate.global_auto_repeat; +} + +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; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (restore) { + if (save_auto_repeat < 0) { + return; /* nothing to restore */ + } + global_auto_repeat = get_autorepeat_state(); + X_LOCK; + /* read state and skip restore if equal (e.g. no clients) */ + if (global_auto_repeat == save_auto_repeat) { + X_UNLOCK; + return; + } + + kctrl.auto_repeat_mode = save_auto_repeat; + XChangeKeyboardControl(dpy, KBAutoRepeatMode, &kctrl); + XFlush(dpy); + X_UNLOCK; + + if (! bequiet && ! quiet) { + rfbLog("Restored X server key autorepeat to: %d\n", + save_auto_repeat); + } + } else { + global_auto_repeat = get_autorepeat_state(); + 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; + XChangeKeyboardControl(dpy, KBAutoRepeatMode, &kctrl); + XFlush(dpy); + X_UNLOCK; + + 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 (first) { + for (n=0; n < 0x100; n++) { + added_keysyms[n] = NoSymbol; + } + first = 0; + } + + if (raw_fb && ! dpy) return 0; /* raw_fb hack */ + + if (keysym == NoSymbol) { + return 0; + } + /* there can be a race before MappingNotify */ + for (n=0; n < 0x100; n++) { + if (added_keysyms[n] == keysym) { + return n; + } + } + + XDisplayKeycodes(dpy, &minkey, &maxkey); + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + for (kc = minkey+1; kc <= maxkey; kc++) { + int i, j, didmsg = 0, is_empty = 1; + char *str; + KeySym new[8]; + + for (n=0; n < syms_per_keycode; n++) { + if (keymap[ (kc-minkey) * syms_per_keycode + n] + != NoSymbol) { + is_empty = 0; + break; + } + } + if (! is_empty) { + continue; + } + + for (i=0; i<8; i++) { + new[i] = NoSymbol; + } + if (add_keysyms == 2) { + new[0] = keysym; /* XXX remove me */ + } else { + for(i=0; i < syms_per_keycode; i++) { + new[i] = keysym; + if (i >= 7) break; + } + } + + XChangeKeyboardMapping(dpy, kc, syms_per_keycode, + new, 1); + + 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; + ret = kc; + break; + } + XFree(keymap); + return ret; +} + +static void delete_keycode(KeyCode kc, int bequiet) { + int minkey, maxkey, syms_per_keycode, i; + KeySym *keymap; + KeySym ksym, new[8]; + char *str; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + XDisplayKeycodes(dpy, &minkey, &maxkey); + keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), + &syms_per_keycode); + + for (i=0; i<8; i++) { + new[i] = NoSymbol; + } + + XChangeKeyboardMapping(dpy, kc, syms_per_keycode, new, 1); + + 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); +} + +static 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, bequiet); + added_keysyms[kc] = NoSymbol; + } + } +} + +/* + * The following is for an experimental -remap option to allow the user + * to remap keystrokes. It is currently confusing wrt modifiers... + */ +typedef struct keyremap { + KeySym before; + KeySym after; + int isbutton; + struct keyremap *next; +} keyremap_t; + +static keyremap_t *keyremaps = NULL; + +static void add_remap(char *line) { + char str1[256], str2[256]; + KeySym ksym1, ksym2; + int isbtn = 0; + unsigned int i; + static keyremap_t *current = NULL; + keyremap_t *remap; + + if (sscanf(line, "%s %s", str1, str2) != 2) { + rfbLogEnable(1); + rfbLog("remap: invalid 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) { + if (sscanf(str2, "Button%u", &i) == 1) { + ksym2 = (KeySym) i; + isbtn = 1; + } + } + if (ksym1 == NoSymbol || ksym2 == NoSymbol) { + rfbLog("warning: skipping invalid 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; +} + +static 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]; + + if (keyremaps != NULL) { + /* free last remapping */ + keyremap_t *next_remap, *curr_remap = keyremaps; + while (curr_remap != NULL) { + next_remap = curr_remap->next; + free(curr_remap); + curr_remap = next_remap; + } + keyremaps = NULL; + } + if (infile == NULL || *infile == '\0') { + /* just unset remapping */ + return; + } + + in = fopen(infile, "r"); + if (in == NULL) { + /* assume cmd line key1-key2,key3-key4 */ + if (strstr(infile, "DEAD") == infile) { + ; + } else if (!strchr(infile, '-')) { + rfbLogEnable(1); + rfbLog("remap: cannot open: %s\n", infile); + rfbLogPerror("fopen"); + clean_up_exit(1); + } + if ((in = tmpfile()) == NULL) { + rfbLogEnable(1); + 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 == '-') { + fprintf(in, " "); + } else if (*p == ',' || *p == ' ' || *p == '\t') { + fprintf(in, "\n"); + } else { + fprintf(in, "%c", *p); + } + p++; + } + fprintf(in, "\n"); + fflush(in); + rewind(in); + } + + while (fgets(line, 256, in) != NULL) { + p = lblanks(line); + if (*p == '\0') { + continue; + } + if (strchr(line, '#')) { + continue; + } + + if (strstr(p, "DEAD") == p) { + add_dead_keysyms(p); + continue; + } + if ((q = strchr(line, '-')) != NULL) { + /* allow Keysym1-Keysym2 notation */ + *q = ' '; + } + add_remap(p); + } + fclose(in); +} + +/* + * preliminary support for using the Xkb (XKEYBOARD) extension for handling + * user input. inelegant, slow, and incomplete currently... but initial + * tests show it is useful for some setups. + */ +typedef struct keychar { + KeyCode code; + int group; + int level; +} keychar_t; + +/* max number of key groups and shift levels we consider */ +#define GRP 4 +#define LVL 8 +static int lvl_max, grp_max, kc_min, kc_max; +static KeySym xkbkeysyms[0x100][GRP][LVL]; +static unsigned int xkbstate[0x100][GRP][LVL]; +static unsigned int xkbignore[0x100][GRP][LVL]; +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; + +int sloppy_key_check(int key, rfbBool down, rfbKeySym keysym, int *new) { + if (!sloppy_keys) { + return 0; + } + + if (!down && !keycode_state[key] && !IsModifierKey(keysym)) { + int i, cnt = 0, downkey; + int nmods_down = track_mod_state(NoSymbol, FALSE, FALSE); + int mods_down[256]; + + if (nmods_down) { + /* tracking to skip down modifier keycodes. */ + for(i=0; i<256; i++) { + mods_down[i] = 0; + } + i = 0; + while (simple_mods[i] != NoSymbol) { + KeySym ksym = simple_mods[i]; + KeyCode k = XKeysymToKeycode(dpy, ksym); + if (k != NoSymbol && keycode_state[(int) k]) { + mods_down[(int) k] = 1; + } + + i++; + } + } + /* + * the keycode is already up... look for a single one + * (non modifier) that is down + */ + for (i=0; i<256; i++) { + if (keycode_state[i]) { + if (nmods_down && mods_down[i]) { + continue; + } + cnt++; + downkey = i; + } + } + if (cnt == 1) { + if (debug_keyboard) { + fprintf(stderr, " sloppy_keys: %d/0x%x " + "-> %d/0x%x (nmods: %d)\n", (int) key, + (int) key, downkey, downkey, nmods_down); + } + *new = downkey; + return 1; + } + } + return 0; +} + +#if !LIBVNCSERVER_HAVE_XKEYBOARD + +/* empty functions for no xkb */ +static void initialize_xkb_modtweak(void) {} +static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client) { +} +void switch_to_xkb_if_better(void) {} + +#else + +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) { + KeySym ks; + int kc, grp, lvl, k; + unsigned int state; + +/* + * Here is a guide: + +Workarounds arrays: + +multi_key[] indicates which keycodes have Multi_key (Compose) + bound to them. +mode_switch[] indicates which keycodes have Mode_switch (AltGr) + bound to them. +shift_keys[] indicates which keycodes have Shift bound to them. +skipkeycode[] indicates which keycodes are to be skipped + for any lookups from -skip_keycodes option. + +Groups and Levels, here is an example: + + ^ -------- + | L2 | A AE | + shift | | + level L1 | a ae | + -------- + G1 G2 + + group -> + +Traditionally this it all a key could do. L1 vs. L2 selected via Shift +and G1 vs. G2 selected via Mode_switch. Up to 4 Keysyms could be bound +to a key. See initialize_modtweak() for an example of using that type +of keymap from XGetKeyboardMapping(). + +Xkb gives us up to 4 groups and 63 shift levels per key, with the +situation being potentially different for each key. This is complicated, +and I don't claim to understand it all, but in the following we just think +of ranging over the group and level indices as covering all of the cases. +This gives us an accurate view of the keymap. The main tricky part +is mapping between group+level and modifier state. + +On current linux/XFree86 setups (Xkb is enabled by default) the +information from XGetKeyboardMapping() (evidently the compat map) +is incomplete and inaccurate, so we are really forced to use the +Xkb API. + +xkbkeysyms[] For a (keycode,group,level) holds the KeySym (0 for none) +xkbstate[] For a (keycode,group,level) holds the corresponding + modifier state needed to get that KeySym +xkbignore[] For a (keycode,group,level) which modifiers can be + ignored (the 0 bits can be ignored). +xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store + the modifier mask. + * + */ + + /* initialize all the arrays: */ + for (kc = 0; kc < 0x100; kc++) { + multi_key[kc] = 0; + mode_switch[kc] = 0; + skipkeycode[kc] = 0; + shift_keys[kc] = 0; + + for (grp = 0; grp < GRP; grp++) { + for (lvl = 0; lvl < LVL; lvl++) { + xkbkeysyms[kc][grp][lvl] = NoSymbol; + xkbmodifiers[kc][grp][lvl] = -1; + xkbstate[kc][grp][lvl] = -1; + } + } + } + + /* + * the array is 256*LVL*GRP, but we can make the searched region + * smaller by computing the actual ranges. + */ + lvl_max = 0; + grp_max = 0; + 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: + */ + if (debug_keyboard > 1) { + rfbLog("initialize_xkb_modtweak: XKB keycode -> keysyms " + "mapping info:\n"); + } + for (kc = 0; kc < 0x100; kc++) { + for (grp = 0; grp < GRP; grp++) { + for (lvl = 0; lvl < LVL; lvl++) { + unsigned int ms, mods; + int state_save = -1, mods_save; + KeySym ks2; + + /* look up the Keysym, if any */ + ks = XkbKeycodeToKeysym(dpy, kc, grp, lvl); + xkbkeysyms[kc][grp][lvl] = ks; + + /* if no Keysym, on to next */ + if (ks == NoSymbol) { + continue; + } + /* + * for various workarounds, note where these special + * keys are bound to. + */ + if (ks == XK_Multi_key) { + multi_key[kc] = lvl+1; + } + if (ks == XK_Mode_switch) { + mode_switch[kc] = lvl+1; + } + if (ks == XK_Shift_L || ks == XK_Shift_R) { + 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: + */ + if (grp > grp_max) { + grp_max = grp; + } + if (lvl > lvl_max) { + lvl_max = lvl; + } + if (kc > kc_max) { + kc_max = kc; + } + if (kc < kc_min) { + kc_min = kc; + } + + /* + * lookup on *keysym* (i.e. not kc, grp, lvl) + * and get the modifier mask. this is 0 for + * most keysyms, only non zero for modifiers. + */ + ms = XkbKeysymToModifiers(dpy, ks); + xkbmodifiers[kc][grp][lvl] = ms; + + /* + * Amusing heuristic (may have bugs). There are + * 8 modifier bits, so 256 possible modifier + * states. We loop over all of them for this + * keycode (simulating Key "events") and ask + * XkbLookupKeySym to tell us the Keysym. Once it + * matches the Keysym we have for this (keycode, + * group, level), gotten via XkbKeycodeToKeysym() + * above, we then (hopefully...) know that state + * of modifiers needed to generate this keysym. + * + * Yes... keep your fingers crossed. + * + * Note that many of the 256 states give the + * Keysym, we just need one, and we take the + * first one found. + */ + state = 0; + while(state < 256) { + if (XkbLookupKeySym(dpy, kc, state, &mods, + &ks2)) { + + /* save these for workaround below */ + if (state_save == -1) { + state_save = state; + mods_save = mods; + } + if (ks2 == ks) { + /* + * zero the irrelevant bits + * by anding with mods. + */ + xkbstate[kc][grp][lvl] + = state & mods; + /* + * also remember the irrelevant + * bits since it is handy. + */ + xkbignore[kc][grp][lvl] = mods; + + break; + } + } + state++; + } + if (xkbstate[kc][grp][lvl] == (unsigned int) -1 + && grp == 1) { + /* + * Hack on Solaris 9 for Mode_switch + * for Group2 characters. We force the + * Mode_switch modifier bit on. + * XXX Need to figure out better what is + * happening here. Is compat on somehow?? + */ + unsigned int ms2; + ms2 = XkbKeysymToModifiers(dpy, XK_Mode_switch); + + xkbstate[kc][grp][lvl] + = (state_save & mods_save) | ms2; + + xkbignore[kc][grp][lvl] = mods_save | ms2; + } + + if (debug_keyboard > 1) { + fprintf(stderr, " %03d G%d L%d mod=%s ", + kc, grp+1, lvl+1, bitprint(ms, 8)); + fprintf(stderr, "state=%s ", + bitprint(xkbstate[kc][grp][lvl], 8)); + fprintf(stderr, "ignore=%s ", + bitprint(xkbignore[kc][grp][lvl], 8)); + fprintf(stderr, " ks=0x%08lx \"%s\"\n", + ks, XKeysymToString(ks)); + } + } + } + } + + /* + * 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 + * ambiguities in the reverse map: Keysym -> KeyCode + Modstate, + * so if we can ignore them so much the better. Presumably the + * user can never generate them from the physical keyboard. + * There may be other reasons to deaden some keys. + */ + if (skip_keycodes != NULL) { + char *p, *str = strdup(skip_keycodes); + p = strtok(str, ", \t\n\r"); + while (p) { + k = 1; + if (sscanf(p, "%d", &k) != 1 || k < 0 || k >= 0x100) { + rfbLogEnable(1); + rfbLog("invalid skip_keycodes: %s %s\n", + skip_keycodes, p); + clean_up_exit(1); + } + skipkeycode[k] = 1; + p = strtok(NULL, ", \t\n\r"); + } + free(str); + } + if (debug_keyboard > 1) { + fprintf(stderr, "grp_max=%d lvl_max=%d\n", grp_max, lvl_max); + } +} + +/* + * Called on user keyboard input. Try to solve the reverse mapping + * problem: KeySym (from VNC client) => KeyCode(s) to press to generate + * it. The one-to-many KeySym => KeyCode mapping makes it difficult, as + * does working out what changes to the modifier keypresses are needed. + */ +static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client) { + + 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; + + if (client) {} /* unused vars warning: */ + + X_LOCK; + + if (debug_keyboard) { + char *str = XKeysymToString(keysym); + + 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"); + } + + /* + * set everything to not-yet-found. + * 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. + */ + for (i = 0; i < 0x100; i++) { + kc_f[i] = -1; + grp_f[i] = -1; + lvl_f[i] = -1; + state_f[i] = -1; + ignore_f[i] = -1; + } + found = 0; + + /* + * loop over all (keycode, group, level) triples looking for + * matching keysyms. Amazingly this isn't slow (but maybe if + * you type really fast...). Hash lookup into a linked list of + * (keycode,grp,lvl) triples would be the way to improve this + * in the future if needed. + */ + for (kc = kc_min; kc <= kc_max; kc++) { + for (grp = 0; grp < grp_max+1; grp++) { + for (lvl = 0; lvl < lvl_max+1; lvl++) { + if (keysym != xkbkeysyms[kc][grp][lvl]) { + continue; + } + /* got a keysym match */ + state = xkbstate[kc][grp][lvl]; + + if (debug_keyboard > 1) { + fprintf(stderr, " got match kc=%03d=0x%02x G%d" + " L%d ks=0x%x \"%s\" (basesym: \"%s\")\n", + kc, kc, grp+1, lvl+1, keysym, + XKeysymToString(keysym), XKeysymToString( + XKeycodeToKeysym(dpy, kc, 0))); + fprintf(stderr, " need state: %s\n", + bitprint(state, 8)); + fprintf(stderr, " ignorable : %s\n", + bitprint(xkbignore[kc][grp][lvl], 8)); + } + + /* save it if state is OK and not told to skip */ + if (state == (unsigned int) -1) { + 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 (found > 0 && kc == kc_f[found-1]) { + /* ignore repeats for same keycode */ + continue; + } + kc_f[found] = kc; + grp_f[found] = grp; + lvl_f[found] = lvl; + state_f[found] = state; + ignore_f[found] = xkbignore[kc][grp][lvl]; + found++; + } + } + } + +#define PKBSTATE \ + fprintf(stderr, " --- current mod state:\n"); \ + fprintf(stderr, " mods : %s\n", bitprint(kbstate.mods, 8)); \ + fprintf(stderr, " base_mods : %s\n", bitprint(kbstate.base_mods, 8)); \ + fprintf(stderr, " latch_mods: %s\n", bitprint(kbstate.latched_mods, 8)); \ + fprintf(stderr, " lock_mods : %s\n", bitprint(kbstate.locked_mods, 8)); \ + fprintf(stderr, " compat : %s\n", bitprint(kbstate.compat_state, 8)); + + /* + * Now get the current state of the keyboard from the X server. + * This seems to be the safest way to go as opposed to our + * keeping track of the modifier state on our own. Again, + * this is fortunately not too slow. + */ + + if (debug_keyboard > 1) { + /* get state early for debug output */ + XkbGetState(dpy, XkbUseCoreKbd, &kbstate); + got_kbstate = 1; + PKBSTATE + } + + if (!found && add_keysyms && keysym && ! IsModifierKey(keysym)) { + int new_kc = add_keysym(keysym); + if (new_kc != 0) { + found = 1; + kc_f[0] = new_kc; + grp_f[0] = 0; + lvl_f[0] = 0; + state_f[0] = 0; + } + } + + if (!found && debug_keyboard) { + char *str = XKeysymToString(keysym); + fprintf(stderr, " *** NO key found for: 0x%x %s " + "*keystroke ignored*\n", keysym, str ? str : "null"); + } + if (!found) { + X_UNLOCK; + return; + } + + /* + * 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. + */ + + 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]; + + /* + * XXX since we now internally track + * keycode_state[], we could throw that into + * the score as well. I.e. if it is already + * down, it is pointless to think we can + * press it down further! E.g. + * myscore += 1000 * keycode_state[kc_f[l]]; + * Also could watch multiple modifier + * problem, e.g. Shift+key -> Alt + * keycode = 125 on my keyboard. + */ + + 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 { + /* up */ + 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) { + int l; + /* + * If it is already down, that is + * a great hint. Use it. + * + * note: keycode_state in internal and + * ignores someone pressing keys on the + * physical display (but is updated + * periodically to clean out stale info). + */ + for (l=0; l < found; l++) { + int key = (int) kc_f[l]; + if (keycode_state[key]) { + Kc_f = kc_f[l]; + 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; + char *str; + fprintf(stderr, " *** found more than one keycode: "); + for (l = 0; l < found; l++) { + fprintf(stderr, "%03d ", kc_f[l]); + } + for (l = 0; l < found; l++) { + str = XKeysymToString(XKeycodeToKeysym(dpy,kc_f[l],0)); + fprintf(stderr, " \"%s\"", str ? str : "null"); + } + fprintf(stderr, ", picked this one: %03d (last down: %03d)\n", + Kc_f, Kc_last_down); + } + + if (sloppy_keys) { + int new_kc; + if (sloppy_key_check(Kc_f, down, keysym, &new_kc)) { + Kc_f = new_kc; + } + } + + if (down) { + /* + * need to set up the mods for tweaking and other workarounds + */ + int needmods[8], sentmods[8], Ilist[8], keystate[256]; + int involves_multi_key, shift_is_down; + int i, j, b, curr, need; + unsigned int ms; + 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; + } + + /* + * needmods[] whether or not that modifier bit needs + * something done to it. + * < 0 means no, + * 0 means needs to go up. + * 1 means needs to go down. + * + * -1, -2, -3 are used for debugging info to indicate + * why nothing needs to be done with the modifier, see below. + * + * sentmods[] is the corresponding keycode to use + * to acheive the needmods[] requirement for the bit. + */ + + for (i=0; i<8; i++) { + needmods[i] = -1; + sentmods[i] = 0; + } + + /* + * Loop over the 8 modifier bits and check if the current + * setting is what we need it to be or whether it should + * be changed (by us sending some keycode event) + * + * If nothing needs to be done to it record why: + * -1 the modifier bit is ignored. + * -2 the modifier bit is ignored, but is correct anyway. + * -3 the modifier bit is correct. + */ + + b = 0x1; + for (i=0; i<8; i++) { + curr = b & kbstate.mods; + need = b & state; + + if (! (b & xkbignore[Kc_f][Grp_f][Lvl_f])) { + /* irrelevant modifier bit */ + needmods[i] = -1; + if (curr == need) needmods[i] = -2; + } else if (curr == need) { + /* already correct */ + needmods[i] = -3; + } else if (! curr && need) { + /* need it down */ + needmods[i] = 1; + } else if (curr && ! need) { + /* need it up */ + needmods[i] = 0; + } + + b = b << 1; + } + + /* + * Again we dynamically probe the X server for information, + * this time for the state of all the keycodes. Useful + * info, and evidently is not too slow... + */ + get_keystate(keystate); + + /* + * We try to determine if Shift is down (since that can + * screw up ISO_Level3_Shift manipulations). + */ + shift_is_down = 0; + + for (kc = kc_min; kc <= kc_max; kc++) { + if (skipkeycode[kc] && debug_keyboard) { + fprintf(stderr, " xxx skipping keycode: " + "%d\n", kc); + } + if (skipkeycode[kc]) { + continue; + } + if (shift_keys[kc] && keystate[kc]) { + shift_is_down = kc; + break; + } + } + + /* + * Now loop over the modifier bits and try to deduce the + * keycode presses/release require to match the desired + * state. + */ + for (i=0; i<8; i++) { + if (needmods[i] < 0 && debug_keyboard > 1) { + int k = -needmods[i] - 1; + char *words[] = {"ignorable", + "bitset+ignorable", "bitset"}; + fprintf(stderr, " +++ needmods: mod=%d is " + "OK (%s)\n", i, words[k]); + } + if (needmods[i] < 0) { + continue; + } + + b = 1 << i; + + if (debug_keyboard > 1) { + fprintf(stderr, " +++ needmods: mod=%d %s " + "need it to be: %d %s\n", i, bitprint(b, 8), + needmods[i], needmods[i] ? "down" : "up"); + } + + /* + * 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 (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; + + kc = kc_vec[kci]; + + ms = xkbmodifiers[kc][grp][lvl]; + if (! ms || ms != (unsigned int) b) { + 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; + } + + ks = xkbkeysyms[kc][grp][lvl]; + if (! ks) { + 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"); + } + + 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++) { + /* + * reverse order is useful for tweaking + * ISO_Level3_Shift before Shift, but assumes they + * are in that order (i.e. Shift is first bit). + */ + int reverse = 1; + if (reverse) { + Ilist[i] = 7 - i; + } else { + Ilist[i] = i; + } + } + + /* + * check to see if Multi_key is bound to one of the Mods + * we have to tweak + */ + involves_multi_key = 0; + for (j=0; j<8; j++) { + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (!dn) continue; + if (multi_key[sentmods[i]]) { + involves_multi_key = i+1; + } + } + + if (involves_multi_key && shift_is_down && needmods[0] < 0) { + /* + * Workaround for Multi_key and shift. + * Assumes Shift is bit 1 (needmods[0]) + */ + if (debug_keyboard) { + fprintf(stderr, " ^^^ trying to avoid " + "inadvertent Multi_key from Shift " + "(doing %03d up now)\n", shift_is_down); + } + XTestFakeKeyEvent_wr(dpy, shift_is_down, False, + CurrentTime); + } else { + involves_multi_key = 0; + } + + for (j=0; j<8; j++) { + /* do the Mod ups */ + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (dn) continue; + XTestFakeKeyEvent_wr(dpy, sentmods[i], dn, CurrentTime); + } + for (j=0; j<8; j++) { + /* next, do the Mod downs */ + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (!dn) continue; + XTestFakeKeyEvent_wr(dpy, sentmods[i], dn, CurrentTime); + } + + if (involves_multi_key) { + /* + * Reverse workaround for Multi_key and shift. + */ + if (debug_keyboard) { + fprintf(stderr, " vvv trying to avoid " + "inadvertent Multi_key from Shift " + "(doing %03d down now)\n", shift_is_down); + } + XTestFakeKeyEvent_wr(dpy, shift_is_down, True, + CurrentTime); + } + + /* + * With the above modifier work done, send the actual keycode: + */ + XTestFakeKeyEvent_wr(dpy, Kc_f, (Bool) down, CurrentTime); + + /* + * Now undo the modifier work: + */ + for (j=7; j>=0; j--) { + /* reverse Mod downs we did */ + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (!dn) continue; + XTestFakeKeyEvent_wr(dpy, sentmods[i], !dn, + CurrentTime); + } + for (j=7; j>=0; j--) { + /* finally reverse the Mod ups we did */ + i = Ilist[j]; + if (sentmods[i] == 0) continue; + dn = (Bool) needmods[i]; + if (dn) continue; + XTestFakeKeyEvent_wr(dpy, sentmods[i], !dn, + CurrentTime); + } + + } else { /* for up case, hopefully just need to pop it up: */ + + XTestFakeKeyEvent_wr(dpy, Kc_f, (Bool) down, CurrentTime); + } + X_UNLOCK; +} +#endif + +/* + * For tweaking modifiers wrt the Alt-Graph key, etc. + */ +#define LEFTSHIFT 1 +#define RIGHTSHIFT 2 +#define ALTGR 4 +static char mod_state = 0; + +static char modifiers[0x100]; +static KeyCode keycodes[0x100]; +static KeyCode left_shift_code, right_shift_code, altgr_code, iso_level3_code; + +/* workaround for X11R5, Latin 1 only */ +#ifndef XConvertCase +#define XConvertCase(sym, lower, upper) \ +*(lower) = sym; \ +*(upper) = sym; \ +if (sym >> 8 == 0) { \ + if ((sym >= XK_A) && (sym <= XK_Z)) \ + *(lower) += (XK_a - XK_A); \ + else if ((sym >= XK_a) && (sym <= XK_z)) \ + *(upper) -= (XK_a - XK_A); \ + else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) \ + *(lower) += (XK_agrave - XK_Agrave); \ + else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis)) \ + *(upper) -= (XK_agrave - XK_Agrave); \ + else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) \ + *(lower) += (XK_oslash - XK_Ooblique); \ + else if ((sym >= XK_oslash) && (sym <= XK_thorn)) \ + *(upper) -= (XK_oslash - XK_Ooblique); \ +} +#endif + +char *short_kmb(char *str) { + int i, saw_k = 0, saw_m = 0, saw_b = 0, n = 10; + char *p, tmp[10]; + + for (i=0; i<n; i++) { + tmp[i] = '\0'; + } + + p = str; + i = 0; + while (*p) { + if ((*p == 'K' || *p == 'k') && !saw_k) { + tmp[i++] = 'K'; + saw_k = 1; + } else if ((*p == 'M' || *p == 'm') && !saw_m) { + tmp[i++] = 'M'; + saw_m = 1; + } else if ((*p == 'B' || *p == 'b') && !saw_b) { + tmp[i++] = 'B'; + saw_b = 1; + } + p++; + } + return(strdup(tmp)); +} + +void initialize_allowed_input(void) { + char *str; + + if (allowed_input_normal) { + free(allowed_input_normal); + allowed_input_normal = NULL; + } + if (allowed_input_view_only) { + free(allowed_input_view_only); + allowed_input_view_only = NULL; + } + + if (! allowed_input_str) { + allowed_input_normal = strdup("KMB"); + allowed_input_view_only = strdup(""); + } else { + char *p, *str = strdup(allowed_input_str); + p = strchr(str, ','); + if (p) { + allowed_input_view_only = strdup(p+1); + *p = '\0'; + allowed_input_normal = strdup(str); + } else { + allowed_input_normal = strdup(str); + allowed_input_view_only = strdup(""); + } + free(str); + } + + /* shorten them */ + str = short_kmb(allowed_input_normal); + free(allowed_input_normal); + allowed_input_normal = str; + + str = short_kmb(allowed_input_view_only); + free(allowed_input_view_only); + allowed_input_view_only = str; + + if (screen) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + + if (cd->input[0] == '=') { + ; /* custom setting */ + } else if (cd->login_viewonly) { + if (*allowed_input_view_only != '\0') { + cl->viewOnly = FALSE; + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_view_only, CILEN); + } else { + cl->viewOnly = TRUE; + } + } else { + if (allowed_input_normal) { + cd->input[0] = '\0'; + strncpy(cd->input, + allowed_input_normal, CILEN); + } + } + } + rfbReleaseClientIterator(iter); + } +} + +void initialize_modtweak(void) { + KeySym keysym, *keymap; + int i, j, minkey, maxkey, syms_per_keycode; + + if (use_xkb_modtweak) { + initialize_xkb_modtweak(); + return; + } + memset(modifiers, -1, sizeof(modifiers)); + for (i=0; i<0x100; i++) { + keycodes[i] = NoSymbol; + } + + X_LOCK; + 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; + } + } + for (i = minkey; i <= maxkey; i++) { + if (debug_keyboard) { + if (i == minkey) { + rfbLog("initialize_modtweak: keycode -> " + "keysyms mapping info:\n"); + } + fprintf(stderr, " %03d ", i); + } + for (j = 0; j < syms_per_keycode; j++) { + if (debug_keyboard) { + char *sym; +#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"); + } + } + if (j >= 4) { + /* + * Something wacky in the keymapping. + * Ignore these non Shift/AltGr chords + * for now... n.b. we try to automatically + * switch to -xkb for this case. + */ + continue; + } + keysym = keymap[ (i - minkey) * syms_per_keycode + j ]; + if ( keysym >= ' ' && keysym < 0x100 + && i == XKeysymToKeycode(dpy, keysym) ) { + keycodes[keysym] = i; + modifiers[keysym] = j; + } + } + } + + left_shift_code = XKeysymToKeycode(dpy, XK_Shift_L); + right_shift_code = XKeysymToKeycode(dpy, XK_Shift_R); + altgr_code = XKeysymToKeycode(dpy, XK_Mode_switch); + iso_level3_code = NoSymbol; +#ifdef XK_ISO_Level3_Shift + iso_level3_code = XKeysymToKeycode(dpy, XK_ISO_Level3_Shift); +#endif + + XFree ((void *) keymap); + + X_UNLOCK; +} + +/* + * does the actual tweak: + */ +static void tweak_mod(signed char mod, rfbBool down) { + rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT); + Bool dn = (Bool) down; + KeyCode altgr = altgr_code; + + if (mod < 0) { + if (debug_keyboard) { + rfbLog("tweak_mod: Skip: down=%d index=%d\n", down, + (int) mod); + } + return; + } + if (debug_keyboard) { + rfbLog("tweak_mod: Start: down=%d index=%d mod_state=0x%x" + " is_shift=%d\n", down, (int) mod, (int) mod_state, + is_shift); + } + + if (use_iso_level3 && iso_level3_code) { + altgr = iso_level3_code; + } + + X_LOCK; + if (is_shift && mod != 1) { + if (mod_state & LEFTSHIFT) { + XTestFakeKeyEvent_wr(dpy, left_shift_code, !dn, CurrentTime); + } + if (mod_state & RIGHTSHIFT) { + XTestFakeKeyEvent_wr(dpy, right_shift_code, !dn, CurrentTime); + } + } + if ( ! is_shift && mod == 1 ) { + XTestFakeKeyEvent_wr(dpy, left_shift_code, dn, CurrentTime); + } + if ( altgr && (mod_state & ALTGR) && mod != 2 ) { + XTestFakeKeyEvent_wr(dpy, altgr, !dn, CurrentTime); + } + if ( altgr && ! (mod_state & ALTGR) && mod == 2 ) { + XTestFakeKeyEvent_wr(dpy, altgr, dn, CurrentTime); + } + X_UNLOCK; + if (debug_keyboard) { + rfbLog("tweak_mod: Finish: down=%d index=%d mod_state=0x%x" + " is_shift=%d\n", down, (int) mod, (int) mod_state, + is_shift); + } +} + +/* + * tweak the modifier under -modtweak + */ +static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, + rfbClientPtr client) { + KeyCode k; + int tweak = 0; + + if (use_xkb_modtweak) { + xkb_tweak_keyboard(down, keysym, client); + return; + } + if (debug_keyboard) { + rfbLog("modifier_tweak_keyboard: %s keysym=0x%x\n", + down ? "down" : "up", (int) keysym); + } + +#define ADJUSTMOD(sym, state) \ + if (keysym == sym) { \ + if (down) { \ + mod_state |= state; \ + } else { \ + mod_state &= ~state; \ + } \ + } + + ADJUSTMOD(XK_Shift_L, LEFTSHIFT) + ADJUSTMOD(XK_Shift_R, RIGHTSHIFT) + ADJUSTMOD(XK_Mode_switch, ALTGR) + + if ( down && keysym >= ' ' && keysym < 0x100 ) { + tweak = 1; + tweak_mod(modifiers[keysym], True); + k = keycodes[keysym]; + } else { + X_LOCK; + k = XKeysymToKeycode(dpy, (KeySym) keysym); + X_UNLOCK; + } + if (k == NoSymbol && add_keysyms && ! IsModifierKey(keysym)) { + int new_kc = add_keysym(keysym); + if (new_kc) { + k = new_kc; + } + } + + if (sloppy_keys) { + int new_kc; + if (sloppy_key_check((int) k, down, keysym, &new_kc)) { + k = (KeyCode) new_kc; + } + } + + if (debug_keyboard) { + rfbLog("modifier_tweak_keyboard: KeySym 0x%x \"%s\" -> " + "KeyCode 0x%x%s\n", (int) keysym, XKeysymToString(keysym), + (int) k, k ? "" : " *ignored*"); + } + if ( k != NoSymbol ) { + X_LOCK; + XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime); + X_UNLOCK; + } + + if ( tweak ) { + tweak_mod(modifiers[keysym], False); + } +} + +void initialize_keyboard_and_pointer(void) { + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (use_modifier_tweak) { + initialize_modtweak(); + } + if (remap_file != NULL) { + initialize_remap(remap_file); + } + + initialize_pointer_map(pointer_remap); + + clear_modifiers(1); + if (clear_mods == 1) { + clear_modifiers(0); + } +} + +void get_allowed_input(rfbClientPtr client, allowed_input_t *input) { + ClientData *cd; + char *str; + + input->keystroke = 0; + input->motion = 0; + input->button = 0; + + if (! client) { + return; + } + + cd = (ClientData *) client->clientData; + + if (cd->input[0] != '-') { + str = cd->input; + } else if (client->viewOnly) { + if (allowed_input_view_only) { + str = allowed_input_view_only; + } else { + str = ""; + } + } else { + if (allowed_input_normal) { + str = allowed_input_normal; + } else { + str = "KMB"; + } + } + + while (*str) { + if (*str == 'K') { + input->keystroke = 1; + } else if (*str == 'M') { + input->motion = 1; + } else if (*str == 'B') { + input->button = 1; + } + str++; + } +} + +/* for -pipeinput mode */ +static void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { + int can_input = 0, uid; + allowed_input_t input; + char *name; + ClientData *cd = (ClientData *) client->clientData; + + if (pipeinput_fh == NULL) { + return; + } + + if (! view_only) { + get_allowed_input(client, &input); + if (input.motion || input.button) { + can_input = 1; /* XXX distinguish later */ + } + } + uid = cd->uid; + if (! can_input) { + uid = -uid; + } + + X_LOCK; + name = XKeysymToString(keysym); + X_UNLOCK; + + fprintf(pipeinput_fh, "Keysym %d %d %u %s %s\n", uid, down, + keysym, name, down ? "KeyPress" : "KeyRelease"); + + fflush(pipeinput_fh); + check_pipeinput(); +} + +typedef struct keyevent { + rfbKeySym sym; + rfbBool down; + double time; +} keyevent_t; + +#define KEY_HIST 256 +static int key_history_idx = -1; +static keyevent_t key_history[KEY_HIST]; + +double typing_rate(double time_window, int *repeating) { + double dt = 1.0, now = dnow(); + KeySym key = NoSymbol; + int i, idx, cnt = 0, repeat_keys = 0; + + if (key_history_idx == -1) { + if (repeating) { + *repeating = 0; + } + return 0.0; + } + if (time_window > 0.0) { + dt = time_window; + } + for (i=0; i<KEY_HIST; i++) { + idx = key_history_idx - i; + if (idx < 0) { + idx += KEY_HIST; + } + if (! key_history[idx].down) { + continue; + } + if (now > key_history[idx].time + dt) { + break; + } + cnt++; + if (key == NoSymbol) { + key = key_history[idx].sym; + repeat_keys = 1; + } else if (key == key_history[idx].sym) { + repeat_keys++; + } + } + + if (repeating) { + if (repeat_keys >= 2) { + *repeating = repeat_keys; + } else { + *repeating = 0; + } + } + + /* + * n.b. keyrate could seem very high with libvncserver buffering them + * so avoid using small dt. + */ + return ((double) cnt)/dt; +} + +int skip_cr_when_scaling(char *mode) { + int got = 0; + + if (!scaling) { + return 0; + } + + if (scaling_copyrect != scaling_copyrect0) { + /* user override via -scale: */ + if (! scaling_copyrect) { + return 1; + } else { + return 0; + } + } + if (*mode == 's') { + got = got_scrollcopyrect; + } else if (*mode == 'w') { + got = got_wirecopyrect; + } + if (scaling_copyrect || got) { + int lat, rate; + int link = link_rate(&lat, &rate); + if (link == LR_DIALUP) { + return 1; + } else if (rate < 25) { + /* the fill-in of the repair may be too slow */ + return 1; + } else { + return 0; + } + } else { + return 1; + } +} + +/* + * key event handler. See the above functions for contortions for + * running under -modtweak. + */ +static rfbClientPtr last_keyboard_client = NULL; + +void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { + KeyCode k; + int idx, isbutton = 0; + allowed_input_t input; + time_t now = time(0); + double tnow; + static int skipped_last_down; + static rfbBool last_down; + static rfbKeySym last_keysym = NoSymbol; + static rfbKeySym max_keyrepeat_last_keysym = NoSymbol; + static double max_keyrepeat_last_time = 0.0; + + dtime0(&tnow); + got_keyboard_calls++; + + if (debug_keyboard) { + char *str; + X_LOCK; + str = XKeysymToString(keysym); + rfbLog("# keyboard(%s, 0x%x \"%s\") %.4f\n", down ? "down":"up", + (int) keysym, str ? str : "null", tnow - 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 = tnow; + + last_rfb_down = down; + last_rfb_keysym = keysym; + last_rfb_keytime = tnow; + last_rfb_key_accepted = FALSE; + + if (key_history_idx == -1) { + for (idx=0; idx<KEY_HIST; idx++) { + key_history[idx].sym = NoSymbol; + key_history[idx].down = FALSE; + key_history[idx].time = 0.0; + } + } + idx = ++key_history_idx; + if (key_history_idx >= KEY_HIST) { + key_history_idx = 0; + idx = 0; + } + key_history[idx].sym = keysym; + key_history[idx].down = down; + key_history[idx].time = tnow; + + if (down && (keysym == XK_Alt_L || keysym == XK_Super_L)) { + int i, k, run = 0, ups = 0; + double delay = 1.0; + KeySym ks; + for (i=0; i<16; i++) { + k = idx - i; + if (k < 0) k += KEY_HIST; + if (!key_history[k].down) { + ups++; + continue; + } + ks = key_history[k].sym; + if (key_history[k].time < tnow - delay) { + break; + } else if (ks == keysym && ks == XK_Alt_L) { + run++; + } else if (ks == keysym && ks == XK_Super_L) { + run++; + } else { + break; + } + } + if (ups < 2) { + ; + } else if (run == 3 && keysym == XK_Alt_L) { + rfbLog("3*Alt_L, calling: refresh_screen(0)\n"); + refresh_screen(0); + } else if (run == 4 && keysym == XK_Alt_L) { + rfbLog("4*Alt_L, setting: do_copy_screen\n"); + do_copy_screen = 1; + } else if (run == 5 && keysym == XK_Alt_L) { + ; + } else if (run == 3 && keysym == XK_Super_L) { + rfbLog("3*Super_L, calling: set_xdamage_mark()\n"); + set_xdamage_mark(0, 0, dpy_x, dpy_y); + } else if (run == 4 && keysym == XK_Super_L) { + rfbLog("4*Super_L, calling: check_xrecord_reset()\n"); + check_xrecord_reset(1); + } else if (run == 5 && keysym == XK_Super_L) { + rfbLog("5*Super_L, calling: push_black_screen(0)\n"); + push_black_screen(0); + } + } + + if (!down && skipped_last_down) { + int db = debug_scroll; + if (keysym == max_keyrepeat_last_keysym) { + skipped_last_down = 0; + if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s " + "%.4f %.4f\n", keysym, down ? "down":"up ", + tnow - x11vnc_start, tnow - max_keyrepeat_last_time); + return; + } + } + if (down && max_keyrepeat_time > 0.0) { + int skip = 0; + int db = debug_scroll; + + if (max_keyrepeat_last_keysym != NoSymbol && + max_keyrepeat_last_keysym != keysym) { + ; + } else { + if (tnow < max_keyrepeat_last_time+max_keyrepeat_time) { + skip = 1; + } + } + max_keyrepeat_time = 0.0; + if (skip) { + if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s " + "%.4f %.4f\n", keysym, down ? "down":"up ", + tnow - x11vnc_start, tnow - max_keyrepeat_last_time); + max_keyrepeat_last_keysym = keysym; + skipped_last_down = 1; + return; + } else { + if (db) rfbLog("--- scroll keyrate KEEPING 0x%lx %s " + "%.4f %.4f\n", keysym, down ? "down":"up ", + tnow - x11vnc_start, tnow - max_keyrepeat_last_time); + } + } + max_keyrepeat_last_keysym = keysym; + max_keyrepeat_last_time = tnow; + skipped_last_down = 0; + last_rfb_key_accepted = TRUE; + + 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 = now; + last_keyboard_input = now; + + last_keysym = keysym; + + last_rfb_down = down; + last_rfb_keysym = keysym; + last_rfb_keytime = tnow; + + got_user_input++; + got_keyboard_input++; + } + return; + } + } + + if (view_only) { + return; + } + get_allowed_input(client, &input); + if (! input.keystroke) { + return; + } + + track_mod_state(keysym, down, TRUE); /* ignores remaps */ + + last_keyboard_client = client; + last_event = last_input = now; + last_keyboard_input = now; + + last_keysym = keysym; + + last_rfb_down = down; + last_rfb_keysym = keysym; + last_rfb_keytime = tnow; + + got_user_input++; + got_keyboard_input++; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (keyremaps) { + keyremap_t *remap = keyremaps; + while (remap != NULL) { + if (remap->before == keysym) { + keysym = remap->after; + isbutton = remap->isbutton; + if (debug_keyboard) { + X_LOCK; + rfbLog("keyboard(): remapping keysym: " + "0x%x \"%s\" -> 0x%x \"%s\"\n", + (int) remap->before, + XKeysymToString(remap->before), + (int) remap->after, + remap->isbutton ? "button" : + XKeysymToString(remap->after)); + X_UNLOCK; + } + break; + } + remap = remap->next; + } + } + + if (use_xrecord && ! xrecording && down) { + + if (!strcmp(scroll_copyrect, "never")) { + ; + } else if (!strcmp(scroll_copyrect, "mouse")) { + ; + } else if (skip_cr_when_scaling("scroll")) { + ; + } else if (! xrecord_skip_keysym(keysym)) { + snapshot_stack_list(0, 0.25); + xrecord_watch(1, SCR_KEY); + xrecord_set_by_keys = 1; + xrecord_keysym = keysym; + } else { + if (debug_scroll) { + char *str = XKeysymToString(keysym); + rfbLog("xrecord_skip_keysym: %s\n", + str ? str : "NoSymbol"); + } + } + } + + if (isbutton) { + int mask, button = (int) keysym; + if (! down) { + return; /* nothing to send */ + } + if (debug_keyboard) { + rfbLog("keyboard(): remapping keystroke to button %d" + " click\n", button); + } + dtime0(&last_key_to_button_remap_time); + + X_LOCK; + /* + * 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_modifier_tweak) { + modifier_tweak_keyboard(down, keysym, client); + X_LOCK; + XFlush(dpy); + X_UNLOCK; + return; + } + + X_LOCK; + + k = XKeysymToKeycode(dpy, (KeySym) keysym); + + if (k == NoSymbol && add_keysyms && ! IsModifierKey(keysym)) { + int new_kc = add_keysym(keysym); + if (new_kc) { + k = new_kc; + } + } + if (debug_keyboard) { + char *str = XKeysymToString(keysym); + rfbLog("keyboard(): KeySym 0x%x \"%s\" -> KeyCode 0x%x%s\n", + (int) keysym, str ? str : "null", (int) k, + k ? "" : " *ignored*"); + } + + if ( k != NoSymbol ) { + XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime); + XFlush(dpy); + } + + X_UNLOCK; +} + + diff --git a/x11vnc/keyboard.h b/x11vnc/keyboard.h new file mode 100644 index 0000000..aef9e43 --- /dev/null +++ b/x11vnc/keyboard.h @@ -0,0 +1,29 @@ +#ifndef _X11VNC_KEYBOARD_H +#define _X11VNC_KEYBOARD_H + +/* -- keyboard.h -- */ +#include "allowed_input_t.h" + +extern void get_keystate(int *keystate); +extern void clear_modifiers(int init); +extern int track_mod_state(rfbKeySym keysym, rfbBool down, rfbBool set); +extern void clear_keys(void); +extern int get_autorepeat_state(void); +extern int get_initial_autorepeat_state(void); +extern void autorepeat(int restore, int bequiet); +extern void check_add_keysyms(void); +extern int add_keysym(KeySym keysym); +extern void delete_added_keycodes(int bequiet); +extern void initialize_remap(char *infile); +extern int sloppy_key_check(int key, rfbBool down, rfbKeySym keysym, int *new); +extern void switch_to_xkb_if_better(void); +extern char *short_kmb(char *str); +extern void initialize_allowed_input(void); +extern void initialize_modtweak(void); +extern void initialize_keyboard_and_pointer(void); +extern void get_allowed_input(rfbClientPtr client, allowed_input_t *input); +extern double typing_rate(double time_window, int *repeating); +extern int skip_cr_when_scaling(char *mode); +extern void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); + +#endif /* _X11VNC_KEYBOARD_H */ diff --git a/x11vnc/options.c b/x11vnc/options.c new file mode 100644 index 0000000..6646aac --- /dev/null +++ b/x11vnc/options.c @@ -0,0 +1,310 @@ +/* -- options.c -- */ + +#define _X11VNC_OPTIONS_H +#include "x11vnc.h" + +/* + * variables for the command line options + */ +int debug = 0; + +char *use_dpy = NULL; /* -display */ +char *auth_file = NULL; /* -auth/-xauth */ +char *visual_str = NULL; /* -visual */ +char *logfile = NULL; /* -o, -logfile */ +int logfile_append = 0; +char *flagfile = NULL; /* -flag */ +char *passwdfile = NULL; /* -passwdfile */ +char *blackout_str = NULL; /* -blackout */ +int blackout_ptr = 0; +char *clip_str = NULL; /* -clip */ +int use_solid_bg = 0; /* -solid */ +char *solid_str = NULL; +char *solid_default = "cyan4"; + +char *wmdt_str = NULL; /* -wmdt */ + +char *speeds_str = NULL; /* -speeds TBD */ + +char *rc_rcfile = NULL; /* -rc */ +int rc_rcfile_default = 0; +int rc_norc = 0; +int got_norc = 0; +int opts_bg = 0; + +#ifndef VNCSHARED +int shared = 0; /* share vnc display. */ +#else +int shared = 1; +#endif +#ifndef FOREVER +int connect_once = 1; /* disconnect after first connection session. */ +#else +int connect_once = 0; +#endif +int got_connect_once = 0; +int deny_all = 0; /* global locking of new clients */ +#ifndef REMOTE_DEFAULT +#define REMOTE_DEFAULT 1 +#endif +int accept_remote_cmds = REMOTE_DEFAULT; /* -noremote */ +int query_default = 0; +int safe_remote_only = 1; /* -unsafe */ +int priv_remote = 0; /* -privremote */ +int more_safe = 0; /* -safer */ +#ifndef EXTERNAL_COMMANDS +#define EXTERNAL_COMMANDS 1 +#endif +#if EXTERNAL_COMMANDS +int no_external_cmds = 0; /* -nocmds */ +#else +int no_external_cmds = 1; /* cannot be turned back on. */ +#endif +int started_as_root = 0; +int host_lookup = 1; +char *users_list = NULL; /* -users */ +char *allow_list = NULL; /* for -allow and -localhost */ +char *listen_str = NULL; +char *allow_once = NULL; /* one time -allow */ +char *accept_cmd = NULL; /* for -accept */ +char *afteraccept_cmd = NULL; /* for -afteraccept */ +char *gone_cmd = NULL; /* for -gone */ +#ifndef VIEWONLY +#define VIEWONLY 0 +#endif +int view_only = VIEWONLY; /* clients can only watch. */ +char *allowed_input_view_only = NULL; +char *allowed_input_normal = NULL; +char *allowed_input_str = NULL; +char *viewonly_passwd = NULL; /* view only passwd. */ +char **passwd_list = NULL; /* for -passwdfile */ +int begin_viewonly = -1; +int inetd = 0; /* spawned from inetd(1) */ +#ifndef FILEXFER +#define FILEXFER 1 +#endif +int filexfer = FILEXFER; +int first_conn_timeout = 0; /* -timeout */ +int flash_cmap = 0; /* follow installed colormaps */ +int shift_cmap = 0; /* ncells < 256 and needs shift of pixel values */ +int force_indexed_color = 0; /* whether to force indexed color for 8bpp */ +int launch_gui = 0; /* -gui */ + +int use_modifier_tweak = 1; /* use the shift/altgr modifier tweak */ +int use_iso_level3 = 0; /* ISO_Level3_Shift instead of Mode_switch */ +int clear_mods = 0; /* -clear_mods (1) and -clear_keys (2) */ +int nofb = 0; /* do not send any fb updates */ +char *raw_fb_str = NULL; /* used under -rawfb */ +char *pipeinput_str = NULL; /* -pipeinput [tee,reopen,keycodes:]cmd */ +char *pipeinput_opts = NULL; +FILE *pipeinput_fh = NULL; +int pipeinput_tee = 0; + +unsigned long subwin = 0x0; /* -id, -sid */ +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_tiles = 0; +int debug_grabs = 0; + +int xtrap_input = 0; /* -xtrap for user input insertion */ +int xinerama = 0; /* -xinerama */ +int xrandr = 0; /* -xrandr */ +char *xrandr_mode = NULL; +char *pad_geometry = NULL; +time_t pad_geometry_time = 0; +int use_snapfb = 0; + +int use_xrecord = 0; +int noxrecord = 0; + +char *client_connect = NULL; /* strings for -connect option */ +char *client_connect_file = NULL; +int vnc_connect = 1; /* -vncconnect option */ + +int show_cursor = 1; /* show cursor shapes */ +int show_multiple_cursors = 0; /* show X when on root background, etc */ +char *multiple_cursors_mode = NULL; +int cursor_pos_updates = 1; /* cursor position updates -cursorpos */ +int cursor_shape_updates = 1; /* cursor shape updates -nocursorshape */ +int use_xwarppointer = 0; /* use XWarpPointer instead of XTestFake... */ +int show_dragging = 1; /* process mouse movement events */ +#ifndef WIREFRAME +#define WIREFRAME 1 +#endif +int wireframe = WIREFRAME; /* try to emulate wireframe wm moves */ +/* shade,linewidth,percent,T+B+L+R,t1+t2+t3+t4 */ +char *wireframe_str = NULL; +char *wireframe_copyrect = NULL; +#ifndef WIREFRAME_COPYRECT +#define WIREFRAME_COPYRECT 1 +#endif +#if WIREFRAME_COPYRECT +char *wireframe_copyrect_default = "always"; +#else +char *wireframe_copyrect_default = "never"; +#endif +int wireframe_in_progress = 0; + +/* T+B+L+R,tkey+presist_key,tmouse+persist_mouse */ +char *scroll_copyrect_str = NULL; +#ifndef SCROLL_COPYRECT +#define SCROLL_COPYRECT 1 +#endif +char *scroll_copyrect = NULL; +#if SCROLL_COPYRECT +#if 1 +char *scroll_copyrect_default = "always"; /* -scrollcopyrect */ +#else +char *scroll_copyrect_default = "keys"; +#endif +#else +char *scroll_copyrect_default = "never"; +#endif +char *scroll_key_list_str = NULL; +KeySym *scroll_key_list = NULL; + +#ifndef SCALING_COPYRECT +#define SCALING_COPYRECT 1 +#endif +int scaling_copyrect0 = SCALING_COPYRECT; +int scaling_copyrect = SCALING_COPYRECT; + +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; +double max_keyrepeat_time = 0.0; +char *max_keyrepeat_str = NULL; +char *max_keyrepeat_str0 = "4-20"; +int max_keyrepeat_lo = 1, max_keyrepeat_hi = 40; + +char **scroll_good_all = NULL; +char **scroll_good_key = NULL; +char **scroll_good_mouse = NULL; +char *scroll_good_str = NULL; +char *scroll_good_str0 = "##Nomatch"; +/* "##Firefox-bin," */ +/* "##Gnome-terminal," */ +/* "##XTerm", */ + +char **scroll_skip_all = NULL; +char **scroll_skip_key = NULL; +char **scroll_skip_mouse = NULL; +char *scroll_skip_str = NULL; +char *scroll_skip_str0 = "##Soffice.bin,##StarOffice"; +/* "##Konsole," * no problems, known heuristics do not work */ + +char **scroll_term = NULL; +char *scroll_term_str = NULL; +char *scroll_term_str0 = "term"; + +char* screen_fixup_str = NULL; +double screen_fixup_V = 0.0; +double screen_fixup_C = 0.0; +double screen_fixup_X = 0.0; + +#ifndef NOREPEAT +#define NOREPEAT 1 +#endif +int no_autorepeat = NOREPEAT; /* turn off autorepeat with clients */ +int no_repeat_countdown = 2; +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 0 +#endif +int skip_duplicate_key_events = SKIPDUPS; +char *skip_keycodes = NULL; +int sloppy_keys = 0; +#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; +/* use the various ways of updating pointer */ +#ifndef POINTER_MODE_DEFAULT +#define POINTER_MODE_DEFAULT 2 +#endif +int pointer_mode = POINTER_MODE_DEFAULT; +int pointer_mode_max = 4; +int single_copytile = 0; /* use the old way copy_tiles() */ +int single_copytile_orig = 0; +int single_copytile_count = 0; +int tile_shm_count = 0; + +int using_shm = 1; /* whether mit-shm is used */ +int flip_byte_order = 0; /* sometimes needed when using_shm = 0 */ +/* + * waitms is the msec to wait between screen polls. Not too old h/w shows + * poll times of 10-35ms, so maybe this value cuts the idle load by 2 or so. + */ +int waitms = 30; +double wait_ui = 2.0; +double slow_fb = 0.0; +int wait_bog = 1; +int defer_update = 30; /* deferUpdateTime ms to wait before sends. */ +int got_defer = 0; +int got_deferupdate = 0; + +int screen_blank = 60; /* number of seconds of no activity to throttle */ + /* down the screen polls. zero to disable. */ +int no_fbu_blank = 30; /* nap if no client updates in this many secs. */ +int take_naps = 1; /* -nap/-nonap */ +int naptile = 4; /* tile change threshold per poll to take a nap */ +int napfac = 4; /* time = napfac*waitms, cut load with extra waits */ +int napmax = 1500; /* longest nap in ms. */ +int ui_skip = 10; /* see watchloop. negative means ignore input */ + +int watch_selection = 1; /* normal selection/cutbuffer maintenance */ +int watch_primary = 1; /* more dicey, poll for changes in PRIMARY */ +char *sel_direction = NULL; /* "send" or "recv" for one-way */ + +char *sigpipe = NULL; /* skip, ignore, exit */ + +/* visual stuff for -visual override or -overlay */ +VisualID visual_id = (VisualID) 0; +int visual_depth = 0; + +/* for -overlay mode on Solaris/IRIX. X server draws cursor correctly. */ +int overlay = 0; +int overlay_cursor = 1; + +/* tile heuristics: */ +double fs_frac = 0.75; /* threshold tile fraction to do fullscreen updates. */ +int tile_fuzz = 2; /* tolerance for suspecting changed tiles touching */ + /* a known changed tile. */ +int grow_fill = 3; /* do the grow islands heuristic with this width. */ +int gaps_fill = 4; /* do a final pass to try to fill gaps between tiles. */ + +int debug_pointer = 0; +int debug_keyboard = 0; + +int quiet = 0; + +/* threaded vs. non-threaded (default) */ +#if LIBVNCSERVER_HAVE_LIBPTHREAD && defined(X11VNC_THREADED) +int use_threads = 1; +#else +int use_threads = 0; +#endif + +/* info about command line opts */ +int got_rfbport = 0; +int got_alwaysshared = 0; +int got_nevershared = 0; +int got_cursorpos = 0; +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; + diff --git a/x11vnc/options.h b/x11vnc/options.h new file mode 100644 index 0000000..ae5ef43 --- /dev/null +++ b/x11vnc/options.h @@ -0,0 +1,231 @@ +#ifndef _X11VNC_OPTIONS_H +#define _X11VNC_OPTIONS_H + +/* -- options.h -- */ + +/* + * variables for the command line options + */ +extern int debug; + +extern char *use_dpy; +extern char *auth_file; +extern char *visual_str; +extern char *logfile; +extern int logfile_append; +extern char *flagfile; +extern char *passwdfile; +extern char *blackout_str; +extern int blackout_ptr; +extern char *clip_str; +extern int use_solid_bg; +extern char *solid_str; +extern char *solid_default; + +extern char *wmdt_str; + +extern char *speeds_str; +extern char *rc_rcfile; +extern int rc_rcfile_default; +extern int rc_norc; +extern int got_norc; +extern int opts_bg; + +extern int shared; +extern int connect_once; +extern int got_connect_once; +extern int deny_all; +extern int accept_remote_cmds; +extern int query_default; +extern int safe_remote_only; +extern int priv_remote; +extern int more_safe; +extern int no_external_cmds; +extern int started_as_root; +extern int host_lookup; +extern char *users_list; +extern char *allow_list; +extern char *listen_str; +extern char *allow_once; +extern char *accept_cmd; +extern char *afteraccept_cmd; +extern char *gone_cmd; +extern int view_only; +extern char *allowed_input_view_only; +extern char *allowed_input_normal; +extern char *allowed_input_str; +extern char *viewonly_passwd; +extern char **passwd_list; +extern int begin_viewonly; +extern int inetd; +extern int filexfer; +extern int first_conn_timeout; +extern int flash_cmap; +extern int shift_cmap; +extern int force_indexed_color; +extern int launch_gui; + +extern int use_modifier_tweak; +extern int use_iso_level3; +extern int clear_mods; +extern int nofb; +extern char *raw_fb_str; +extern char *pipeinput_str; +extern char *pipeinput_opts; +extern FILE *pipeinput_fh; +extern int pipeinput_tee; + +extern unsigned long subwin; +extern int subwin_wait_mapped; + +extern int debug_xevents; +extern int debug_xdamage; +extern int debug_wireframe; +extern int debug_tiles; +extern int debug_grabs; + +extern int xtrap_input; +extern int xinerama; +extern int xrandr; +extern char *xrandr_mode; +extern char *pad_geometry; +extern time_t pad_geometry_time; +extern int use_snapfb; + +extern int use_xrecord; +extern int noxrecord; + +extern char *client_connect; +extern char *client_connect_file; +extern int vnc_connect; + +extern int show_cursor; +extern int show_multiple_cursors; +extern char *multiple_cursors_mode; +extern int cursor_pos_updates; +extern int cursor_shape_updates; +extern int use_xwarppointer; +extern int show_dragging; +extern int wireframe; + +extern char *wireframe_str; +extern char *wireframe_copyrect; +extern char *wireframe_copyrect_default; +extern int wireframe_in_progress; + +extern char *scroll_copyrect_str; +extern char *scroll_copyrect; +extern char *scroll_copyrect_default; +extern char *scroll_key_list_str; +extern KeySym *scroll_key_list; + +extern int scaling_copyrect0; +extern int scaling_copyrect; + +extern int scrollcopyrect_min_area; +extern int debug_scroll; +extern double pointer_flush_delay; +extern double last_scroll_event; +extern int max_scroll_keyrate; +extern double max_keyrepeat_time; +extern char *max_keyrepeat_str; +extern char *max_keyrepeat_str0; +extern int max_keyrepeat_lo, max_keyrepeat_hi; + +extern char **scroll_good_all; +extern char **scroll_good_key; +extern char **scroll_good_mouse; +extern char *scroll_good_str; +extern char *scroll_good_str0; + +extern char **scroll_skip_all; +extern char **scroll_skip_key; +extern char **scroll_skip_mouse; +extern char *scroll_skip_str; +extern char *scroll_skip_str0; + +extern char **scroll_term; +extern char *scroll_term_str; +extern char *scroll_term_str0; + +extern char* screen_fixup_str; +extern double screen_fixup_V; +extern double screen_fixup_C; +extern double screen_fixup_X; + +extern int no_autorepeat; +extern int no_repeat_countdown; +extern int watch_bell; +extern int sound_bell; +extern int xkbcompat; +extern int use_xkb_modtweak; +extern int skip_duplicate_key_events; +extern char *skip_keycodes; +extern int sloppy_keys; +extern int add_keysyms; + +extern char *remap_file; +extern char *pointer_remap; +extern int pointer_mode; +extern int pointer_mode_max; +extern int single_copytile; +extern int single_copytile_orig; +extern int single_copytile_count; +extern int tile_shm_count; + +extern int using_shm; +extern int flip_byte_order; +extern int waitms; +extern double wait_ui; +extern double slow_fb; +extern int wait_bog; +extern int defer_update; +extern int got_defer; +extern int got_deferupdate; + +extern int screen_blank; + +extern int no_fbu_blank; +extern int take_naps; +extern int naptile; +extern int napfac; +extern int napmax; +extern int ui_skip; + +extern int watch_selection; +extern int watch_primary; +extern char *sel_direction; + +extern char *sigpipe; + +extern VisualID visual_id; +extern int visual_depth; + +extern int overlay; +extern int overlay_cursor; + +extern double fs_frac; +extern int tile_fuzz; + +extern int grow_fill; +extern int gaps_fill; + +extern int debug_pointer; +extern int debug_keyboard; + +extern int quiet; + +extern int use_threads; + +extern int got_rfbport; +extern int got_alwaysshared; +extern int got_nevershared; +extern int got_cursorpos; +extern int got_pointer_mode; +extern int got_noviewonly; +extern int got_wirecopyrect; +extern int got_scrollcopyrect; +extern int got_noxkb; +extern int got_nomodtweak; + +#endif /* _X11VNC_OPTIONS_H */ diff --git a/x11vnc/params.h b/x11vnc/params.h new file mode 100644 index 0000000..fca526b --- /dev/null +++ b/x11vnc/params.h @@ -0,0 +1,33 @@ +#ifndef _X11VNC_PARAMS_H +#define _X11VNC_PARAMS_H + +/* -- params.h -- */ + +#define ICON_MODE_SOCKS 16 + +#ifndef WIREFRAME_PARMS +#define WIREFRAME_PARMS "0xff,3,0,32+8+8+8,all,0.15+0.30+5.0+0.125" +#endif + +#ifndef SCROLL_COPYRECT_PARMS +#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 + +#define LATENCY0 20 /* 20ms */ +#define NETRATE0 20 /* 20KB/sec */ + +#define POINTER_MODE_NOFB 2 + +/* scan pattern jitter from x0rfbserver */ +#define NSCAN 32 + +#define FB_COPY 0x1 +#define FB_MOD 0x2 +#define FB_REQ 0x4 + +#define VNC_CONNECT_MAX 16384 +#define PROP_MAX (131072L) + +#define MAXN 256 + +#endif /* _X11VNC_PARAMS_H */ diff --git a/x11vnc/pointer.c b/x11vnc/pointer.c new file mode 100644 index 0000000..6ff92d4 --- /dev/null +++ b/x11vnc/pointer.c @@ -0,0 +1,1029 @@ +/* -- pointer.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "keyboard.h" +#include "xinerama.h" +#include "xrecord.h" +#include "win_utils.h" +#include "cursor.h" +#include "userinput.h" +#include "connections.h" +#include "cleanup.h" + +int pointer_queued_sent = 0; + +void initialize_pointer_map(char *pointer_remap); +void do_button_mask_change(int mask, int button); +void pointer(int mask, int x, int y, rfbClientPtr client); +void initialize_pipeinput(void); +int check_pipeinput(void); + + +static void buttonparse(int from, char **s); +static void update_x11_pointer_position(int x, int y); +static void update_x11_pointer_mask(int mask); +static void pipe_pointer(int mask, int x, int y, rfbClientPtr client); + + +/* + * pointer event (motion and button click) handling routines. + */ +typedef struct ptrremap { + KeySym keysym; + KeyCode keycode; + int end; + int button; + int down; + int up; +} prtremap_t; + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +MUTEX(pointerMutex); +#endif +#define MAX_BUTTONS 5 +#define MAX_BUTTON_EVENTS 50 +static prtremap_t pointer_map[MAX_BUTTONS+1][MAX_BUTTON_EVENTS]; + +/* + * For parsing the -buttonmap sections, e.g. "4" or ":Up+Up+Up:" + */ +static void buttonparse(int from, char **s) { + char *q; + int to, i; + int modisdown[256]; + + q = *s; + + for (i=0; i<256; i++) { + modisdown[i] = 0; + } + + if (*q == ':') { + /* :sym1+sym2+...+symN: format */ + int l = 0, n = 0; + char list[1000]; + char *t, *kp = q + 1; + KeyCode kcode; + + while (*(kp+l) != ':' && *(kp+l) != '\0') { + /* loop to the matching ':' */ + l++; + if (l >= 1000) { + rfbLog("buttonparse: keysym list too long: " + "%s\n", q); + break; + } + } + *(kp+l) = '\0'; + strncpy(list, kp, l); + list[l] = '\0'; + rfbLog("remap button %d using \"%s\"\n", from, list); + + /* loop over tokens separated by '+' */ + t = strtok(list, "+"); + while (t) { + KeySym ksym; + unsigned int ui; + int i; + if (n >= MAX_BUTTON_EVENTS - 20) { + rfbLog("buttonparse: too many button map " + "events: %s\n", list); + break; + } + if (sscanf(t, "0x%x", &ui) == 1) { + ksym = (KeySym) ui; /* hex value */ + } else { + X_LOCK; + ksym = XStringToKeysym(t); /* string value */ + X_UNLOCK; + } + if (ksym == NoSymbol) { + /* see if Button<N> "keysym" was used: */ + if (sscanf(t, "Button%d", &i) == 1) { + rfbLog(" event %d: button %d\n", + from, n+1, i); + if (i == 0) i = -1; /* bah */ + pointer_map[from][n].keysym = NoSymbol; + pointer_map[from][n].keycode = NoSymbol; + pointer_map[from][n].button = i; + pointer_map[from][n].end = 0; + pointer_map[from][n].down = 0; + pointer_map[from][n].up = 0; + } else { + rfbLog("buttonparse: ignoring unknown " + "keysym: %s\n", t); + n--; + } + } else { + /* + * XXX may not work with -modtweak or -xkb + */ + X_LOCK; + kcode = XKeysymToKeycode(dpy, ksym); + + pointer_map[from][n].keysym = ksym; + pointer_map[from][n].keycode = kcode; + pointer_map[from][n].button = 0; + pointer_map[from][n].end = 0; + if (! ismodkey(ksym) ) { + /* do both down then up */ + pointer_map[from][n].down = 1; + pointer_map[from][n].up = 1; + } else { + if (modisdown[kcode]) { + pointer_map[from][n].down = 0; + pointer_map[from][n].up = 1; + modisdown[kcode] = 0; + } else { + pointer_map[from][n].down = 1; + pointer_map[from][n].up = 0; + modisdown[kcode] = 1; + } + } + rfbLog(" event %d: keysym %s (0x%x) -> " + "keycode 0x%x down=%d up=%d\n", n+1, + XKeysymToString(ksym), ksym, kcode, + pointer_map[from][n].down, + pointer_map[from][n].up); + X_UNLOCK; + } + t = strtok(NULL, "+"); + n++; + } + + /* we must release any modifiers that are still down: */ + for (i=0; i<256; i++) { + kcode = (KeyCode) i; + if (n >= MAX_BUTTON_EVENTS) { + rfbLog("buttonparse: too many button map " + "events: %s\n", list); + break; + } + if (modisdown[kcode]) { + pointer_map[from][n].keysym = NoSymbol; + pointer_map[from][n].keycode = kcode; + pointer_map[from][n].button = 0; + pointer_map[from][n].end = 0; + pointer_map[from][n].down = 0; + pointer_map[from][n].up = 1; + modisdown[kcode] = 0; + n++; + } + } + + /* advance the source pointer position */ + (*s) += l+2; + } else { + /* single digit format */ + char str[2]; + str[0] = *q; + str[1] = '\0'; + + to = atoi(str); + if (to < 1) { + rfbLog("skipping invalid remap button \"%d\" for button" + " %d from string \"%s\"\n", + to, from, str); + } else { + rfbLog("remap button %d using \"%s\"\n", from, str); + rfbLog(" button: %d -> %d\n", from, to); + pointer_map[from][0].keysym = NoSymbol; + pointer_map[from][0].keycode = NoSymbol; + pointer_map[from][0].button = to; + pointer_map[from][0].end = 0; + pointer_map[from][0].down = 0; + pointer_map[from][0].up = 0; + } + /* advance the source pointer position */ + (*s)++; + } +} + +/* + * process the -buttonmap string + */ +void initialize_pointer_map(char *pointer_remap) { + unsigned char map[MAX_BUTTONS]; + int i, k; + /* + * This routine counts the number of pointer buttons on the X + * server (to avoid problems, even crashes, if a client has more + * buttons). And also initializes any pointer button remapping + * from -buttonmap option. + */ + + X_LOCK; + num_buttons = XGetPointerMapping(dpy, map, MAX_BUTTONS); + X_UNLOCK; + + if (num_buttons < 0) { + num_buttons = 0; + } + + /* FIXME: should use info in map[] */ + for (i=1; i<= MAX_BUTTONS; i++) { + for (k=0; k < MAX_BUTTON_EVENTS; k++) { + pointer_map[i][k].end = 1; + } + pointer_map[i][0].keysym = NoSymbol; + pointer_map[i][0].keycode = NoSymbol; + pointer_map[i][0].button = i; + pointer_map[i][0].end = 0; + pointer_map[i][0].down = 0; + pointer_map[i][0].up = 0; + } + + if (pointer_remap && *pointer_remap != '\0') { + /* -buttonmap, format is like: 12-21=2 */ + char *p, *q, *remap = strdup(pointer_remap); + int n; + + if ((p = strchr(remap, '=')) != NULL) { + /* undocumented max button number */ + n = atoi(p+1); + *p = '\0'; + if (n < num_buttons || num_buttons == 0) { + num_buttons = n; + } else { + rfbLog("warning: increasing number of mouse " + "buttons from %d to %d\n", num_buttons, n); + num_buttons = n; + } + } + if ((q = strchr(remap, '-')) != NULL) { + /* + * The '-' separates the 'from' and 'to' lists, + * then it is kind of like tr(1). + */ + char str[2]; + int from; + + rfbLog("remapping pointer buttons using string:\n"); + rfbLog(" \"%s\"\n", remap); + + p = remap; + q++; + i = 0; + str[1] = '\0'; + while (*p != '-') { + str[0] = *p; + from = atoi(str); + buttonparse(from, &q); + p++; + } + } + free(remap); + } +} + +/* + * Send a pointer position event to the X server. + */ +static void update_x11_pointer_position(int x, int y) { + int rc; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + X_LOCK; + if (use_xwarppointer) { + /* + * off_x and off_y not needed with XWarpPointer since + * window is used: + */ + XWarpPointer(dpy, None, window, 0, 0, 0, 0, x + coff_x, + y + coff_y); + } else { + XTestFakeMotionEvent_wr(dpy, scr, x + off_x + coff_x, + y + off_y + coff_y, CurrentTime); + } + X_UNLOCK; + + if (cursor_x != x || cursor_y != y) { + last_pointer_motion_time = dnow(); + } + + cursor_x = x; + cursor_y = y; + + /* record the x, y position for the rfb screen as well. */ + cursor_position(x, y); + + /* change the cursor shape if necessary */ + rc = set_cursor(x, y, get_which_cursor()); + cursor_changes += rc; + + 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 snapped, xr_mouse = 1, i; + + last_event = last_input = last_pointer_input = time(0); + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (mask != button_mask) { + last_pointer_click_time = dnow(); + } + + if (nofb) { + xr_mouse = 0; + } else if (!strcmp(scroll_copyrect, "never")) { + xr_mouse = 0; + } else if (!strcmp(scroll_copyrect, "keys")) { + xr_mouse = 0; + } else if (skip_cr_when_scaling("scroll")) { + xr_mouse = 0; + } else if (xrecord_skip_button(mask, button_mask)) { + xr_mouse = 0; + } + + if (mask && use_xrecord && ! xrecording && xr_mouse) { + 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, &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 { + got_wm_frame = 0; + } + } + 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; + int btn4 = (1<<3); + int btn5 = (1<<4); + + if (near_scrollbar_edge(x, y, w, h, px, py)) { + ok = 1; + } + if (mask & (btn4|btn5)) { + /* scroll wheel mouse */ + ok = 1; + } + if (mwin != None) { + /* skinny internal window */ + 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) { + xrecord_watch(1, SCR_MOUSE); + snapshot_stack_list(0, 0.50); + snapped = 1; + if (button_mask) { + xrecord_set_by_mouse = 1; + } else { + update_stack_list(); + xrecord_set_by_mouse = 2; + } + } + } + + if (mask && !button_mask) { + /* button down, snapshot the stacking list before flushing */ + if (wireframe && !wireframe_in_progress && + strcmp(wireframe_copyrect, "never")) { + if (! snapped) { + snapshot_stack_list(0, 0.0); + } + } + } + + 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)) ) { + if (debug_pointer) { + rfbLog("pointer(): mask change: mask: 0x%x -> " + "0x%x button: %d\n", button_mask, mask,i+1); + } + do_button_mask_change(mask, i+1); /* button # is i+1 */ + } + } + + X_UNLOCK; + + /* + * Remember the button state for next time and also for the + * -nodragging case: + */ + button_mask_prev = button_mask; + button_mask = mask; +} + +/* for -pipeinput */ + + +static void pipe_pointer(int mask, int x, int y, rfbClientPtr client) { + int can_input = 0, uid; + allowed_input_t input; + ClientData *cd = (ClientData *) client->clientData; + char hint[MAX_BUTTONS * 20]; + + if (pipeinput_fh == NULL) { + return; + } + + if (! view_only) { + get_allowed_input(client, &input); + if (input.motion || input.button) { + can_input = 1; /* XXX distinguish later */ + } + } + uid = cd->uid; + if (! can_input) { + uid = -uid; + } + + hint[0] = '\0'; + if (mask == button_mask) { + strcat(hint, "None"); + } else { + int i, old, new, m = 1, cnt = 0; + for (i=0; i<MAX_BUTTONS; i++) { + char s[20]; + + old = button_mask & m; + new = mask & m; + m = m << 1; + + if (old == new) { + continue; + } + if (hint[0] != '\0') { + strcat(hint, ","); + } + if (new && ! old) { + sprintf(s, "ButtonPress-%d", i+1); + cnt++; + } else if (! new && old) { + sprintf(s, "ButtonRelease-%d", i+1); + cnt++; + } + strcat(hint, s); + } + if (! cnt) { + strcpy(hint, "None"); + } + } + + fprintf(pipeinput_fh, "Pointer %d %d %d %d %s\n", uid, x, y, + mask, hint); + fflush(pipeinput_fh); + check_pipeinput(); +} + +/* + * Actual callback from libvncserver when it gets a pointer event. + * This may queue pointer events rather than sending them immediately + * to the X server. (see update_x11_pointer*()) + */ +void pointer(int mask, int x, int y, rfbClientPtr client) { + allowed_input_t input; + int sent = 0, buffer_it = 0; + double now; + + if (mask >= 0) { + got_pointer_calls++; + } + + 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; + } else { + 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) " + "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) { + /* map from rfb size to X11 size: */ + x = ((double) x / scaled_x) * dpy_x; + x = nfix(x, dpy_x); + y = ((double) y / scaled_y) * dpy_y; + y = nfix(y, dpy_y); + } + + if (pipeinput_fh != NULL && mask >= 0) { + pipe_pointer(mask, x, y, client); + if (! pipeinput_tee) { + if (! view_only || raw_fb) { /* raw_fb hack */ + got_user_input++; + got_keyboard_input++; + last_pointer_client = client; + } + if (view_only && raw_fb) { + /* raw_fb hack track button state */ + button_mask_prev = button_mask; + button_mask = mask; + } + return; + } + } + + if (view_only) { + return; + } + + now = dnow(); + + 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; + + last_pointer_time = now; + + if (blackout_ptr && blackouts) { + int b, ok = 1; + /* see if it goes into the blacked out region */ + for (b=0; b < blackouts; b++) { + if (x < blackr[b].x1 || x > blackr[b].x2) { + continue; + } + if (y < blackr[b].y1 || y > blackr[b].y2) { + continue; + } + /* x1 <= x <= x2 and y1 <= y <= y2 */ + ok = 0; + break; + } + if (! ok) { + if (debug_pointer) { + rfbLog("pointer(): blackout_ptr skipping " + "x=%d y=%d in rectangle %d,%d %d,%d\n", x, y, + blackr[b].x1, blackr[b].y1, + blackr[b].x2, blackr[b].y2); + } + return; + } + } + } + + /* + * The following is hopefully an improvement wrt response during + * pointer user input (window drags) for the threaded case. + * See check_user_input() for the more complicated things we do + * in the non-threaded case. + */ + if ((use_threads && pointer_mode != 1) || pointer_flush_delay > 0.0) { +# define NEV 32 + /* storage for the event queue */ + static int mutex_init = 0; + static int nevents = 0; + static int ev[NEV][3]; + int i; + /* timer things */ + static double dt = 0.0, tmr = 0.0, maxwait = 0.4; + + if (! mutex_init) { + INIT_MUTEX(pointerMutex); + 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); + + /* + * If the framebuffer is being copied in another thread + * (scan_for_updates()), we will queue up to 32 pointer + * events for later. The idea is by delaying these input + * events, the screen is less likely to change during the + * copying period, and so will give rise to less window + * "tearing". + * + * Tearing is not completely eliminated because we do + * not suspend work in the other libvncserver threads. + * Maybe that is a possibility with a mutex... + */ + if (buffer_it) { + /* + * mask = -1 is an all-clear signal from + * scan_for_updates(). + * + * dt is a timer in seconds; we only queue for so long. + */ + dt += dtime(&tmr); + + if (nevents < NEV && dt < maxwait) { + i = nevents++; + ev[i][0] = mask; + ev[i][1] = x; + ev[i][2] = y; + if (! input.button) { + ev[i][0] = -1; + } + if (! input.motion) { + ev[i][1] = -1; + ev[i][2] = -1; + } + UNLOCK(pointerMutex); + if (debug_pointer) { + rfbLog("pointer(): deferring event %d" + " %.4f\n", i, tmr - x11vnc_start); + } + return; + } + } + + /* 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 %.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) { + 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; + } + } + } + nevents = 0; /* reset everything */ + dt = 0.0; + dtime0(&tmr); + + UNLOCK(pointerMutex); + } + if (mask < 0) { /* -1 just means flush the event queue */ + if (debug_pointer) { + rfbLog("pointer(): flush only. %.4f\n", + dnow() - x11vnc_start); + } + return; + } + + /* update the X display with the event: */ + if (input.motion) { + update_x11_pointer_position(x, y); + sent = 1; + } + if (input.button) { + if (mask != button_mask) { + button_change_x = cursor_x; + button_change_y = cursor_y; + } + update_x11_pointer_mask(mask); + sent = 1; + } + + if (nofb && sent) { + /* + * nofb is for, e.g. Win2VNC, where fastest pointer + * updates are desired. + */ + 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; + } +} + +void initialize_pipeinput(void) { + char *p; + + if (pipeinput_fh != NULL) { + rfbLog("closing pipeinput stream: %p\n", pipeinput_fh); + pclose(pipeinput_fh); + pipeinput_fh = NULL; + } + + pipeinput_tee = 0; + if (pipeinput_opts) { + free(pipeinput_opts); + pipeinput_opts = NULL; + } + + if (! pipeinput_str) { + return; + } + + /* look for options: tee, reopen, ... */ + p = strchr(pipeinput_str, ':'); + if (p != NULL) { + char *str, *opt, *q; + int got = 0; + *p = '\0'; + str = strdup(pipeinput_str); + opt = strdup(pipeinput_str); + *p = ':'; + q = strtok(str, ","); + while (q) { + if (!strcmp(q, "key") || !strcmp(q, "keycodes")) { + got = 1; + } + if (!strcmp(q, "reopen")) { + got = 1; + } + if (!strcmp(q, "tee")) { + pipeinput_tee = 1; + got = 1; + } + q = strtok(NULL, ","); + } + if (got) { + pipeinput_opts = opt; + } else { + free(opt); + } + free(str); + p++; + } else { + p = pipeinput_str; + } + + set_child_info(); + if (no_external_cmds) { + rfbLogEnable(1); + rfbLog("cannot run external commands in -nocmds mode:\n"); + rfbLog(" \"%s\"\n", p); + rfbLog(" exiting.\n"); + clean_up_exit(1); + } + rfbLog("pipeinput: starting: \"%s\"...\n", p); + pipeinput_fh = popen(p, "w"); + + if (! pipeinput_fh) { + rfbLog("popen(\"%s\", \"w\") failed.\n", p); + rfbLogPerror("popen"); + rfbLog("Disabling -pipeinput mode.\n"); + return; + } + + fprintf(pipeinput_fh, "%s", +"# \n" +"# Format of the -pipeinput stream:\n" +"# --------------------------------\n" +"#\n" +"# Lines like these beginning with '#' are to be ignored.\n" +"#\n" +"# Pointer events (mouse motion and button clicks) come in the form:\n" +"#\n" +"#\n" +"# Pointer <client#> <x> <y> <mask> <hint>\n" +"#\n" +"#\n" +"# The <client#> is a decimal integer uniquely identifying the client\n" +"# that generated the event. If it is negative that means this event\n" +"# would have been discarded since the client was viewonly.\n" +"#\n" +"# <x> and <y> are decimal integers reflecting the position on the screen\n" +"# the event took place at.\n" +"#\n" +"# <mask> is the button mask indicating the button press state, as normal\n" +"# 0 means no buttons pressed, 1 means button 1 is down 3 (11) means buttons\n" +"# 1 and 2 are down, etc.\n" +"#\n" +"# <hint> is a string containing no spaces and may be ignored.\n" +"# It contains some interpretation about what has happened.\n" +"# It can be:\n" +"#\n" +"# None (nothing to report)\n" +"# ButtonPress-N (this event will cause button-1 to be pressed) \n" +"# ButtonRelease-N (this event will cause button-1 to be released) \n" +"#\n" +"# if two more more buttons change state in one event they are listed\n" +"# separated by commas.\n" +"#\n" +"# One might parse a Pointer line with:\n" +"#\n" +"# int client, x, y, mask; char *hint;\n" +"# sscanf(line, \"Pointer %d %d %d %s\", &client, &x, &y, &mask, &hint);\n" +"#\n" +"#\n" +"# Keysym events (keyboard presses and releases) come in the form:\n" +"#\n" +"#\n" +"# Keysym <client#> <down> <keysym#> <keysym-name> <hint>\n" +"#\n" +"#\n" +"# The <client#> is as with Pointer.\n" +"#\n" +"# <down> is a decimal either 1 or 0 indicating KeyPress or KeyRelease,\n" +"# respectively.\n" +"#\n" +"# <keysym#> is a decimal integer incidating the Keysym of the event.\n" +"#\n" +"# <keysym-name> is the corresponding Keysym name.\n" +"#\n" +"# See the file /usr/include/X11/keysymdef.h for the mappings.\n" +"# You basically remove the leading 'XK_' prefix from the macro name in\n" +"# that file to get the Keysym name.\n" +"#\n" +"# One might parse a Keysym line with:\n" +"#\n" +"# int client, down, keysym; char *name, *hint;\n" +"# sscanf(line, \"Keysym %d %d %s %s\", &client, &down, &keysym, &name, &hint);\n" +"#\n" +"# The <hint> value is currently just None, KeyPress, or KeyRelease.\n" +"#\n" +"# In the future <hint> will provide a hint for the sequence of KeyCodes\n" +"# (i.e. keyboard scancodes) that x11vnc would inject to an X display to\n" +"# simulate the Keysym.\n" +"#\n" +"# You see, some Keysyms will require more than one injected Keycode to\n" +"# generate the symbol. E.g. the Keysym \"ampersand\" going down usually\n" +"# requires a Shift key going down, then the key with the \"&\" on it going\n" +"# down, and, perhaps, the Shift key going up (that is how x11vnc does it).\n" +"#\n" +"# The Keysym => Keycode(s) stuff gets pretty messy. Hopefully the Keysym\n" +"# info will be enough for most purposes (having identical keyboards on\n" +"# both sides helps).\n" +"#\n" +"# Here comes your stream. The following token will always indicate the\n" +"# end of this informational text:\n" +"# END_OF_TOP\n" +); + fflush(pipeinput_fh); + if (raw_fb_str) { + /* the pipe program may actually create the fb */ + sleep(1); + } +} + +int check_pipeinput(void) { + if (! pipeinput_fh) { + return 1; + } + if (ferror(pipeinput_fh)) { + rfbLog("pipeinput pipe has ferror. %p\n", pipeinput_fh); + + if (pipeinput_opts && strstr(pipeinput_opts, "reopen")) { + rfbLog("restarting -pipeinput pipe...\n"); + initialize_pipeinput(); + if (pipeinput_fh) { + return 1; + } else { + return 0; + } + } else { + rfbLog("closing -pipeinput pipe...\n"); + pclose(pipeinput_fh); + pipeinput_fh = NULL; + return 0; + } + } + return 1; +} + + diff --git a/x11vnc/pointer.h b/x11vnc/pointer.h new file mode 100644 index 0000000..7034631 --- /dev/null +++ b/x11vnc/pointer.h @@ -0,0 +1,14 @@ +#ifndef _X11VNC_POINTER_H +#define _X11VNC_POINTER_H + +/* -- pointer.h -- */ + +extern int pointer_queued_sent; + +extern void initialize_pointer_map(char *pointer_remap); +extern void do_button_mask_change(int mask, int button); +extern void pointer(int mask, int x, int y, rfbClientPtr client); +extern int check_pipeinput(void); +extern void initialize_pipeinput(void); + +#endif /* _X11VNC_POINTER_H */ diff --git a/x11vnc/rates.c b/x11vnc/rates.c new file mode 100644 index 0000000..b6516b2 --- /dev/null +++ b/x11vnc/rates.c @@ -0,0 +1,595 @@ +/* -- rates.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "scan.h" + +int measure_speeds = 1; +int speeds_net_rate = 0; +int speeds_net_rate_measured = 0; +int speeds_net_latency = 0; +int speeds_net_latency_measured = 0; +int speeds_read_rate = 0; +int speeds_read_rate_measured = 0; + + +int get_cmp_rate(void); +int get_raw_rate(void); +void initialize_speeds(void); +int get_read_rate(void); +int link_rate(int *latency, int *netrate); +int get_net_rate(void); +int get_net_latency(void); +void measure_send_rates(int init); + + +static void measure_display_hook(rfbClientPtr cl); +static int get_rate(int which); +static int get_latency(void); + + +static void measure_display_hook(rfbClientPtr cl) { + ClientData *cd = (ClientData *) cl->clientData; + dtime0(&cd->timer); +} + +static int get_rate(int which) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int irate, irate_min = 1; /* 1 KB/sec */ + int irate_max = 100000; /* 100 MB/sec */ + int count = 0; + double slowest = -1.0, rate; + static double save_rate = 1000 * NETRATE0; + + if (!screen) { + return 0; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + + if (cl->state != RFB_NORMAL) { + continue; + } + if (cd->send_cmp_rate == 0.0 || cd->send_raw_rate == 0.0) { + continue; + } + count++; + + if (which == 0) { + rate = cd->send_cmp_rate; + } else { + rate = cd->send_raw_rate; + } + if (slowest == -1.0 || rate < slowest) { + slowest = rate; + } + + } + rfbReleaseClientIterator(iter); + + if (! count) { + return NETRATE0; + } + + if (slowest == -1.0) { + slowest = save_rate; + } else { + save_rate = slowest; + } + + irate = (int) (slowest/1000.0); + if (irate < irate_min) { + irate = irate_min; + } + if (irate > irate_max) { + irate = irate_max; + } +if (0) fprintf(stderr, "get_rate(%d) %d %.3f/%.3f\n", which, irate, save_rate, slowest); + + return irate; +} + +static int get_latency(void) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int ilat, ilat_min = 1; /* 1 ms */ + int ilat_max = 2000; /* 2 sec */ + double slowest = -1.0, lat; + static double save_lat = ((double) LATENCY0)/1000.0; + int count = 0; + + if (!screen) { + return 0; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + + if (cl->state != RFB_NORMAL) { + continue; + } + if (cd->latency == 0.0) { + continue; + } + count++; + + lat = cd->latency; + if (slowest == -1.0 || lat > slowest) { + slowest = lat; + } + } + rfbReleaseClientIterator(iter); + + if (! count) { + return LATENCY0; + } + + if (slowest == -1.0) { + slowest = save_lat; + } else { + save_lat = slowest; + } + + ilat = (int) (slowest * 1000.0); + if (ilat < ilat_min) { + ilat = ilat_min; + } + if (ilat > ilat_max) { + ilat = ilat_max; + } + + return ilat; +} + +int get_cmp_rate(void) { + return get_rate(0); +} + +int get_raw_rate(void) { + return get_rate(1); +} + +void initialize_speeds(void) { + char *s, *s_in, *p; + int i; + + speeds_read_rate = 0; + speeds_net_rate = 0; + speeds_net_latency = 0; + if (! speeds_str || *speeds_str == '\0') { + s_in = strdup(""); + } else { + s_in = strdup(speeds_str); + } + + if (!strcmp(s_in, "modem")) { + s = strdup("6,4,200"); + } else if (!strcmp(s_in, "dsl")) { + s = strdup("6,100,50"); + } else if (!strcmp(s_in, "lan")) { + s = strdup("6,5000,1"); + } else { + s = strdup(s_in); + } + + p = strtok(s, ","); + i = 0; + while (p) { + double val; + if (*p != '\0') { + val = atof(p); + if (i==0) { + speeds_read_rate = (int) 1000000 * val; + } else if (i==1) { + speeds_net_rate = (int) 1000 * val; + } else if (i==2) { + speeds_net_latency = (int) val; + } + } + i++; + p = strtok(NULL, ","); + } + free(s); + free(s_in); + + if (! speeds_read_rate) { + int n = 0; + double dt, timer; + dtime0(&timer); + if (fullscreen) { + copy_image(fullscreen, 0, 0, 0, 0); + n = fullscreen->bytes_per_line * fullscreen->height; + } else if (scanline) { + copy_image(scanline, 0, 0, 0, 0); + n = scanline->bytes_per_line * scanline->height; + } + dt = dtime(&timer); + if (n && dt > 0.0) { + double rate = ((double) n) / dt; + speeds_read_rate_measured = (int) (rate/1000000.0); + if (speeds_read_rate_measured < 1) { + speeds_read_rate_measured = 1; + } else { + rfbLog("fb read rate: %d MB/sec\n", + speeds_read_rate_measured); + } + } + } +} + +int get_read_rate(void) { + if (speeds_read_rate) { + return speeds_read_rate; + } + if (speeds_read_rate_measured) { + return speeds_read_rate_measured; + } + 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 (! spm || spm == NETRATE0) { + speeds_net_rate_measured = get_cmp_rate(); + } + if (speeds_net_rate_measured) { + return speeds_net_rate_measured; + } + return 0; +} + +int get_net_latency(void) { + int spm = speeds_net_latency_measured; + if (speeds_net_latency) { + return speeds_net_latency; + } + if (! spm || spm == LATENCY0) { + speeds_net_latency_measured = get_latency(); + } + if (speeds_net_latency_measured) { + return speeds_net_latency_measured; + } + return 0; +} + +void measure_send_rates(int init) { + double cmp_rate, raw_rate; + static double now, start = 0.0; + static rfbDisplayHookPtr orig_display_hook = NULL; + double cmp_max = 1.0e+08; /* 100 MB/sec */ + double cmp_min = 1000.0; /* 9600baud */ + double lat_max = 5.0; /* 5 sec */ + double lat_min = .0005; /* 0.5 ms */ + int min_cmp = 10000, nclients; + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int db = 0, msg = 0; + +db = 0; + + if (! measure_speeds) { + return; + } + if (speeds_net_rate && speeds_net_latency) { + return; + } + + if (! orig_display_hook) { + orig_display_hook = screen->displayHook; + } + + if (start == 0.0) { + dtime(&start); + } + dtime0(&now); + now = now - start; + + nclients = 0; + + if (!screen) { + return; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + int defer, i, cbs, rbs; + char *httpdir; + double dt, dt1 = 0.0, dt2, dt3; + double tm, spin_max = 15.0, spin_lat_max = 1.5; + int got_t2 = 0, got_t3 = 0; + ClientData *cd = (ClientData *) cl->clientData; + + if (cd->send_cmp_rate > 0.0) { + continue; + } + nclients++; + + cbs = 0; + for (i=0; i<MAX_ENCODINGS; i++) { + cbs += cl->bytesSent[i]; + } + rbs = cl->rawBytesEquivalent; + + if (init) { + +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); + + cd->timer = dnow(); + cd->cmp_bytes_sent = cbs; + cd->raw_bytes_sent = rbs; + continue; + } + + /* first part of the bulk transfer of initial screen */ + dt1 = dtime(&cd->timer); + +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); + + if (dt1 <= 0.0) { + continue; + } + + cbs = cbs - cd->cmp_bytes_sent; + rbs = rbs - cd->raw_bytes_sent; + + if (cbs < min_cmp) { + continue; + } + + rfbPE(1000); + if (sraRgnCountRects(cl->modifiedRegion)) { + rfbPE(1000); + } + + defer = screen->deferUpdateTime; + httpdir = screen->httpDir; + screen->deferUpdateTime = 0; + screen->httpDir = NULL; + + /* mark a small rectangle: */ + mark_rect_as_modified(0, 0, 16, 16, 1); + + dtime0(&tm); + + dt2 = 0.0; + dt3 = 0.0; + + if (dt1 < 0.25) { + /* try to cut it down to avoid long pauses. */ + spin_max = 5.0; + } + + /* when req1 = 1 mod1 == 0, end of 2nd part of bulk transfer */ + while (1) { + int req0, req1, mod0, mod1; + req0 = sraRgnCountRects(cl->requestedRegion); + mod0 = sraRgnCountRects(cl->modifiedRegion); + if (use_threads) { + usleep(1000); + } else { + if (mod0) { + rfbPE(1000); + } else { + rfbCFD(1000); + } + } + dt = dtime(&tm); + dt2 += dt; + if (dt2 > spin_max) { + break; + } + req1 = sraRgnCountRects(cl->requestedRegion); + mod1 = sraRgnCountRects(cl->modifiedRegion); + +if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d " + "fbu-sent: %d dt: %.4f dt2: %.4f tm: %.4f\n", + req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt2, tm); + if (req1 != 0 && mod1 == 0) { + got_t2 = 1; + break; + } + } + + if (! got_t2) { + dt2 = 0.0; + } else { + int tr, trm = 3; + double dts[10]; + + /* + * Note: since often select(2) cannot sleep + * less than 1/HZ (e.g. 10ms), the resolution + * of the latency may be messed up by something + * of this order. Effect may occur on both ends, + * i.e. the viewer may not respond immediately. + */ + + for (tr = 0; tr < trm; tr++) { + usleep(5000); + + /* mark a 2nd small rectangle: */ + mark_rect_as_modified(0, 0, 16, 16, 1); + i = 0; + dtime0(&tm); + dt3 = 0.0; + + /* + * when req1 > 0 and mod1 == 0, we say + * that is the "ping" time. + */ + while (1) { + int req0, req1, mod0, mod1; + + req0 = sraRgnCountRects( + cl->requestedRegion); + mod0 = sraRgnCountRects( + cl->modifiedRegion); + + if (i == 0) { + rfbPE(0); + } else { + if (use_threads) { + usleep(1000); + } else { + /* try to get it all */ + rfbCFD(1000*1000); + } + } + dt = dtime(&tm); + i++; + + dt3 += dt; + if (dt3 > spin_lat_max) { + break; + } + req1 = sraRgnCountRects( + cl->requestedRegion); + + mod1 = sraRgnCountRects( + cl->modifiedRegion); + +if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d " + "fbu-sent: %d dt: %.4f dt3: %.4f tm: %.4f\n", + req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt3, tm); + + if (req1 != 0 && mod1 == 0) { + dts[got_t3++] = dt3; + break; + } + } + } + + if (! got_t3) { + dt3 = 0.0; + } else { + if (got_t3 == 1) { + dt3 = dts[0]; + } else if (got_t3 == 2) { + dt3 = dts[1]; + } else { + if (dts[2] >= 0.0) { + double rat = dts[1]/dts[2]; + if (rat > 0.5 && rat < 2.0) { + dt3 = dts[1]+dts[2]; + dt3 *= 0.5; + } else { + dt3 = dts[1]; + } + } else { + dt3 = dts[1]; + } + } + } + } + + screen->deferUpdateTime = defer; + screen->httpDir = httpdir; + + dt = dt1 + dt2; + + + if (dt3 <= dt2/2.0) { + /* guess only 1/2 a ping for reply... */ + dt = dt - dt3/2.0; + } + + cmp_rate = cbs/dt; + raw_rate = rbs/dt; + + if (cmp_rate > cmp_max) { + cmp_rate = cmp_max; + } + if (cmp_rate <= cmp_min) { + cmp_rate = cmp_min; + } + + cd->send_cmp_rate = cmp_rate; + cd->send_raw_rate = raw_rate; + + if (dt3 > lat_max) { + dt3 = lat_max; + } + if (dt3 <= lat_min) { + dt3 = lat_min; + } + + cd->latency = dt3; + + rfbLog("client %d network rate %.1f KB/sec (%.1f eff KB/sec)\n", + cd->uid, cmp_rate/1000.0, raw_rate/1000.0); + 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; + } + } else { + screen->displayHook = orig_display_hook; + } +} + diff --git a/x11vnc/rates.h b/x11vnc/rates.h new file mode 100644 index 0000000..f3d4a79 --- /dev/null +++ b/x11vnc/rates.h @@ -0,0 +1,23 @@ +#ifndef _X11VNC_RATES_H +#define _X11VNC_RATES_H + +/* -- rates.h -- */ + +extern int measure_speeds; +extern int speeds_net_rate; +extern int speeds_net_rate_measured; +extern int speeds_net_latency; +extern int speeds_net_latency_measured; +extern int speeds_read_rate; +extern int speeds_read_rate_measured; + +extern int get_cmp_rate(void); +extern int get_raw_rate(void); +extern void initialize_speeds(void); +extern int get_read_rate(void); +extern int link_rate(int *latency, int *netrate); +extern int get_net_rate(void); +extern int get_net_latency(void); +extern void measure_send_rates(int init); + +#endif /* _X11VNC_RATES_H */ diff --git a/x11vnc/remote.c b/x11vnc/remote.c new file mode 100644 index 0000000..2a82ca8 --- /dev/null +++ b/x11vnc/remote.c @@ -0,0 +1,3766 @@ +/* -- remote.c -- */ + +#include "x11vnc.h" +#include "inet.h" +#include "xwrappers.h" +#include "xevents.h" +#include "xinerama.h" +#include "xrandr.h" +#include "xdamage.h" +#include "xrecord.h" +#include "xkb_bell.h" +#include "win_utils.h" +#include "screen.h" +#include "cleanup.h" +#include "gui.h" +#include "solid.h" +#include "user.h" +#include "rates.h" +#include "scan.h" +#include "connections.h" +#include "pointer.h" +#include "cursor.h" +#include "userinput.h" +#include "keyboard.h" + +int send_remote_cmd(char *cmd, int query, int wait); +int do_remote_query(char *remote_cmd, char *query_cmd, int remote_sync, + int qdefault); +void check_black_fb(void); +int check_httpdir(void); +void http_connections(int on); +int remote_control_access_ok(void); +char *process_remote_cmd(char *cmd, int stringonly); + + +static char *add_item(char *instr, char *item); +static char *delete_item(char *instr, char *item); +static void if_8bpp_do_new_fb(void); +static void reset_httpport(int old, int new); +static void reset_rfbport(int old, int new) ; + + +/* + * for the wild-n-crazy -remote/-R interface. + */ +int send_remote_cmd(char *cmd, int query, int wait) { + FILE *in = NULL; + + if (client_connect_file) { + in = fopen(client_connect_file, "w"); + if (in == NULL) { + fprintf(stderr, "send_remote_cmd: could not open " + "connect file \"%s\" for writing\n", + client_connect_file); + perror("fopen"); + return 1; + } + } else if (vnc_connect_prop == None) { + initialize_vnc_connect_prop(); + if (vnc_connect_prop == None) { + fprintf(stderr, "send_remote_cmd: could not obtain " + "VNC_CONNECT X property\n"); + return 1; + } + } + + if (in != NULL) { + fprintf(stderr, ">>> sending remote command: \"%s\"\n via" + " connect file: %s\n", cmd, client_connect_file); + fprintf(in, "%s\n", cmd); + fclose(in); + } else { + fprintf(stderr, ">>> sending remote command: \"%s\" via" + " VNC_CONNECT X property.\n", cmd); + set_vnc_connect_prop(cmd); + XFlush(dpy); + } + + if (query || wait) { + char line[VNC_CONNECT_MAX]; + int rc=1, i=0, max=70, ms_sl=50; + + if (!strcmp(cmd, "cmd=stop")) { + max = 20; + } + for (i=0; i<max; i++) { + usleep(ms_sl * 1000); + if (client_connect_file) { + char *q; + in = fopen(client_connect_file, "r"); + if (in == NULL) { + fprintf(stderr, "send_remote_cmd: could" + " not open connect file \"%s\" for" + " writing\n", client_connect_file); + perror("fopen"); + return 1; + } + fgets(line, VNC_CONNECT_MAX, in); + fclose(in); + q = line; + while (*q != '\0') { + if (*q == '\n') *q = '\0'; + q++; + } + } else { + read_vnc_connect_prop(); + strncpy(line, vnc_connect_str, VNC_CONNECT_MAX); + } + if (strcmp(cmd, line)){ + if (query) { + fprintf(stdout, "%s\n", line); + fflush(stdout); + } + rc = 0; + break; + } + } + if (rc) { + fprintf(stderr, "error: could not connect to " + "an x11vnc server at %s (rc=%d)\n", + client_connect_file ? client_connect_file + : DisplayString(dpy), rc); + } + return rc; + } + return 0; +} + +int do_remote_query(char *remote_cmd, char *query_cmd, int remote_sync, + int qdefault) { + char *rcmd = NULL, *qcmd = NULL; + int rc = 1; + + if (qdefault && !query_cmd) { + query_cmd = remote_cmd; + remote_cmd = NULL; + } + + if (remote_cmd) { + rcmd = (char *) malloc(strlen(remote_cmd) + 5); + strcpy(rcmd, "cmd="); + strcat(rcmd, remote_cmd); + } + if (query_cmd) { + qcmd = (char *) malloc(strlen(query_cmd) + 5); + strcpy(qcmd, "qry="); + strcat(qcmd, query_cmd); + } + if (qdefault) { + char *res; + if (!qcmd) { + return 1; + } + res = process_remote_cmd(qcmd, 1); + fprintf(stdout, "%s\n", res); + fflush(stdout); + return 0; + } + + if (rcmd && qcmd) { + rc = send_remote_cmd(rcmd, 0, 1); + if (rc) { + free(rcmd); + free(qcmd); + return(rc); + } + rc = send_remote_cmd(qcmd, 1, 1); + } else if (rcmd) { + rc = send_remote_cmd(rcmd, 0, remote_sync); + free(rcmd); + } else if (qcmd) { + rc = send_remote_cmd(qcmd, 1, 1); + free(qcmd); + } + return rc; +} + +static char *add_item(char *instr, char *item) { + char *p, *str; + int len, saw_item = 0; + + if (! instr || *instr == '\0') { + str = strdup(item); + return str; + } + len = strlen(instr) + 1 + strlen(item) + 1; + str = (char *) malloc(len); + str[0] = '\0'; + + /* n.b. instr will be modified; caller replaces with returned string */ + p = strtok(instr, ","); + while (p) { + if (!strcmp(p, item)) { + if (saw_item) { + p = strtok(NULL, ","); + continue; + } + saw_item = 1; + } else if (*p == '\0') { + p = strtok(NULL, ","); + continue; + } + if (str[0]) { + strcat(str, ","); + } + strcat(str, p); + p = strtok(NULL, ","); + } + if (! saw_item) { + if (str[0]) { + strcat(str, ","); + } + strcat(str, item); + } + return str; +} + +static char *delete_item(char *instr, char *item) { + char *p, *str; + int len; + + if (! instr || *instr == '\0') { + str = strdup(""); + return str; + } + len = strlen(instr) + 1; + str = (char *) malloc(len); + str[0] = '\0'; + + /* n.b. instr will be modified; caller replaces with returned string */ + p = strtok(instr, ","); + while (p) { + if (!strcmp(p, item) || *p == '\0') { + p = strtok(NULL, ","); + continue; + } + if (str[0]) { + strcat(str, ","); + } + strcat(str, p); + p = strtok(NULL, ","); + } + return str; +} + +static void if_8bpp_do_new_fb(void) { + if (bpp == 8) { + do_new_fb(0); + } else { + rfbLog(" bpp(%d) is not 8bpp, not resetting fb\n", bpp); + } +} + +void check_black_fb(void) { + if (!screen) { + return; + } + if (new_fb_size_clients(screen) != client_count) { + rfbLog("trying to send a black fb for non-newfbsize" + " clients %d != %d\n", client_count, + new_fb_size_clients(screen)); + push_black_screen(4); + } +} + +int check_httpdir(void) { + if (http_dir) { + return 1; + } else { + char *prog = NULL, *httpdir, *q; + struct stat sbuf; + int len; + + rfbLog("check_httpdir: trying to guess httpdir...\n"); + if (program_name[0] == '/') { + prog = strdup(program_name); + } else { + char cwd[1024]; + getcwd(cwd, 1024); + len = strlen(cwd) + 1 + strlen(program_name) + 1; + prog = (char *) malloc(len); + snprintf(prog, len, "%s/%s", cwd, program_name); + if (stat(prog, &sbuf) != 0) { + char *path = strdup(getenv("PATH")); + char *p, *base; + base = strrchr(program_name, '/'); + if (base) { + base++; + } else { + base = program_name; + } + + p = strtok(path, ":"); + while(p) { + free(prog); + len = strlen(p) + 1 + strlen(base) + 1; + prog = (char *) malloc(len); + snprintf(prog, len, "%s/%s", p, base); + if (stat(prog, &sbuf) == 0) { + break; + } + p = strtok(NULL, ":"); + } + free(path); + } + } + /* + * /path/to/bin/x11vnc + * /path/to/bin/../share/x11vnc/classes + * 12345678901234567 + */ + if ((q = strrchr(prog, '/')) == NULL) { + rfbLog("check_httpdir: bad program path: %s\n", prog); + free(prog); + return 0; + } + + len = strlen(prog) + 17 + 1; + *q = '\0'; + httpdir = (char *) malloc(len); + snprintf(httpdir, len, "%s/../share/x11vnc/classes", prog); + free(prog); + + if (stat(httpdir, &sbuf) == 0) { + /* good enough for me */ + rfbLog("check_httpdir: guessed directory:\n"); + rfbLog(" %s\n", httpdir); + http_dir = httpdir; + return 1; + } else { + /* try some hardwires: */ + if (stat("/usr/local/share/x11vnc/classes", + &sbuf) == 0) { + http_dir = + strdup("/usr/local/share/x11vnc/classes"); + return 1; + } + if (stat("/usr/share/x11vnc/classes", &sbuf) == 0) { + http_dir = strdup("/usr/share/x11vnc/classes"); + return 1; + } + rfbLog("check_httpdir: bad guess:\n"); + rfbLog(" %s\n", httpdir); + return 0; + } + } +} + +void http_connections(int on) { + if (!screen) { + return; + } + if (on) { + rfbLog("http_connections: turning on http service.\n"); + screen->httpInitDone = FALSE; + screen->httpDir = http_dir; + if (check_httpdir()) { + rfbHttpInitSockets(screen); + } + } else { + rfbLog("http_connections: turning off http service.\n"); + if (screen->httpListenSock > -1) { + close(screen->httpListenSock); + } + screen->httpListenSock = -1; + screen->httpDir = NULL; + } +} + +static void reset_httpport(int old, int new) { + int hp = new; + if (hp < 0) { + rfbLog("reset_httpport: invalid httpport: %d\n", hp); + } else if (hp == old) { + rfbLog("reset_httpport: unchanged httpport: %d\n", hp); + } else if (inetd) { + rfbLog("reset_httpport: cannot set httpport: %d" + " in inetd.\n", hp); + } else if (screen) { + screen->httpPort = hp; + screen->httpInitDone = FALSE; + if (screen->httpListenSock > -1) { + close(screen->httpListenSock); + } + rfbLog("reset_httpport: setting httpport %d -> %d.\n", + old == -1 ? hp : old, hp); + rfbHttpInitSockets(screen); + } +} + +static void reset_rfbport(int old, int new) { + int rp = new; + if (rp < 0) { + rfbLog("reset_rfbport: invalid rfbport: %d\n", rp); + } else if (rp == old) { + rfbLog("reset_rfbport: unchanged rfbport: %d\n", rp); + } else if (inetd) { + rfbLog("reset_rfbport: cannot set rfbport: %d" + " in inetd.\n", rp); + } else if (screen) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int maxfd; + if (rp == 0) { + screen->autoPort = TRUE; + } else { + screen->autoPort = FALSE; + } + screen->port = rp; + screen->socketState = RFB_SOCKET_INIT; + + if (screen->listenSock > -1) { + close(screen->listenSock); + } + + rfbLog("reset_rfbport: setting rfbport %d -> %d.\n", + old == -1 ? rp : old, rp); + rfbInitSockets(screen); + + maxfd = screen->maxFd; + if (screen->udpSock > 0 && screen->udpSock > maxfd) { + maxfd = screen->udpSock; + } + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->sock > -1) { + FD_SET(cl->sock, &(screen->allFds)); + if (cl->sock > maxfd) { + maxfd = cl->sock; + } + } + } + rfbReleaseClientIterator(iter); + + screen->maxFd = maxfd; + + set_vnc_desktop_name(); + } +} + +/* + * Do some sanity checking of the permissions on the XAUTHORITY and the + * -connect file. This is -privremote. What should be done is check + * for an empty host access list, currently we lazily do not bring in + * libXau yet. + */ +int remote_control_access_ok(void) { + struct stat sbuf; + + if (client_connect_file) { + if (stat(client_connect_file, &sbuf) == 0) { + if (sbuf.st_mode & S_IWOTH) { + rfbLog("connect file is writable by others.\n"); + rfbLog(" %s\n", client_connect_file); + return 0; + } + if (sbuf.st_mode & S_IWGRP) { + rfbLog("connect file is writable by group.\n"); + rfbLog(" %s\n", client_connect_file); + return 0; + } + } + } + + if (dpy) { + char tmp[1000]; + char *home, *xauth; + char *dpy_str = DisplayString(dpy); + Display *dpy2; + XHostAddress *xha; + Bool enabled; + int n; + + home = get_home_dir(); + if (getenv("XAUTHORITY") != NULL) { + xauth = getenv("XAUTHORITY"); + } else if (home) { + int len = 1000 - strlen("/.Xauthority") - 1; + strncpy(tmp, home, len); + strcat(tmp, "/.Xauthority"); + xauth = tmp; + } else { + rfbLog("cannot determine default XAUTHORITY.\n"); + return 0; + } + if (home) { + free(home); + } + if (stat(xauth, &sbuf) == 0) { + if (sbuf.st_mode & S_IWOTH) { + rfbLog("XAUTHORITY is writable by others!!\n"); + rfbLog(" %s\n", xauth); + return 0; + } + if (sbuf.st_mode & S_IWGRP) { + rfbLog("XAUTHORITY is writable by group!!\n"); + rfbLog(" %s\n", xauth); + return 0; + } + if (sbuf.st_mode & S_IROTH) { + rfbLog("XAUTHORITY is readable by others.\n"); + rfbLog(" %s\n", xauth); + return 0; + } + if (sbuf.st_mode & S_IRGRP) { + rfbLog("XAUTHORITY is readable by group.\n"); + rfbLog(" %s\n", xauth); + return 0; + } + } + + xha = XListHosts(dpy, &n, &enabled); + if (! enabled) { + rfbLog("X access control is disabled, X clients can\n"); + rfbLog(" connect from any host. Run 'xhost -'\n"); + return 0; + } + if (xha) { + int i; + rfbLog("The following hosts can connect w/o X11 " + "auth:\n"); + for (i=0; i<n; i++) { + if (xha[i].family == FamilyInternet) { + char *str = raw2host(xha[i].address, + xha[i].length); + char *ip = raw2ip(xha[i].address); + rfbLog(" %s/%s\n", str, ip); + free(str); + free(ip); + } else { + rfbLog(" unknown-%d\n", i+1); + } + } + XFree(xha); + return 0; + } + + if (getenv("XAUTHORITY")) { + xauth = strdup(getenv("XAUTHORITY")); + } else { + xauth = NULL; + } + set_env("XAUTHORITY", "/impossible/xauthfile"); + + fprintf(stderr, "\nChecking if display %s requires " + "XAUTHORITY\n", dpy_str); + fprintf(stderr, " -- (ignore any Xlib: errors that" + " follow) --\n"); + dpy2 = XOpenDisplay(dpy_str); + fflush(stderr); + fprintf(stderr, " -- (done checking) --\n\n"); + + if (xauth) { + set_env("XAUTHORITY", xauth); + free(xauth); + } else { + xauth = getenv("XAUTHORITY"); + if (xauth) { + *(xauth-2) = '_'; /* yow */ + } + } + if (dpy2) { + rfbLog("XAUTHORITY is not required on display.\n"); + rfbLog(" %s\n", DisplayString(dpy)); + XCloseDisplay(dpy2); + return 0; + } + + } + return 1; +} + +static int hack_val = 0; + +/* + * Huge, ugly switch to handle all remote commands and queries + * -remote/-R and -query/-Q. + */ +char *process_remote_cmd(char *cmd, int stringonly) { +#if REMOTE_CONTROL + char *p = cmd; + char *co = ""; + char buf[VNC_CONNECT_MAX]; + int bufn = VNC_CONNECT_MAX; + int query = 0; + static char *prev_cursors_mode = NULL; + + if (!query_default && !accept_remote_cmds) { + rfbLog("remote commands disabled: %s\n", cmd); + return NULL; + } + + if (!query_default && priv_remote) { + if (! remote_control_access_ok()) { + rfbLog("** Disabling remote commands in -privremote " + "mode.\n"); + accept_remote_cmds = 0; + return NULL; + } + } + + strcpy(buf, ""); + if (strstr(cmd, "cmd=") == cmd) { + p += strlen("cmd="); + } else if (strstr(cmd, "qry=") == cmd) { + query = 1; + if (strchr(cmd, ',')) { + /* comma separated batch mode */ + char *s, *q, *res; + char tmp[512]; + strcpy(buf, ""); + s = strdup(cmd + strlen("qry=")); + q = strtok(s, ","); + while (q) { + strcpy(tmp, "qry="); + strncat(tmp, q, 500); + res = process_remote_cmd(tmp, 1); + if (res && strlen(buf)+strlen(res) + >= VNC_CONNECT_MAX - 1) { + rfbLog("overflow in process_remote_cmd:" + " %s -- %s\n", buf, res); + free(res); + break; + } + if (res) { + strcat(buf, res); + free(res); + } + q = strtok(NULL, ","); + if (q) { + strcat(buf, ","); + } + } + free(s); + goto qry; + } + p += strlen("qry="); + } else { + rfbLog("ignoring malformed command: %s\n", cmd); + goto done; + } + + /* allow var=val usage */ + if (!strchr(p, ':')) { + char *q = strchr(p, '='); + if (q) *q = ':'; + } + + /* always call like: COLON_CHECK("foobar:") */ +#define COLON_CHECK(str) \ + if (strstr(p, str) != p) { \ + co = ":"; \ + if (! query) { \ + goto done; \ + } \ + } else { \ + char *q = strchr(p, ':'); \ + if (query && q != NULL) { \ + *(q+1) = '\0'; \ + } \ + } + +#define NOTAPP \ + if (query) { \ + if (strchr(p, ':')) { \ + snprintf(buf, bufn, "ans=%sN/A", p); \ + } else { \ + snprintf(buf, bufn, "ans=%s:N/A", p); \ + } \ + goto qry; \ + } + +#define NOTAPPRO \ + if (query) { \ + if (strchr(p, ':')) { \ + snprintf(buf, bufn, "aro=%sN/A", p); \ + } else { \ + snprintf(buf, bufn, "aro=%s:N/A", p); \ + } \ + goto qry; \ + } + +/* + * Maybe add: passwdfile logfile bg rfbauth passwd... + */ + if (!strcmp(p, "stop") || !strcmp(p, "quit") || + !strcmp(p, "exit") || !strcmp(p, "shutdown")) { + NOTAPP + close_all_clients(); + rfbLog("remote_cmd: setting shut_down flag\n"); + shut_down = 1; + + } else if (!strcmp(p, "ping")) { + query = 1; + if (rfb_desktop_name) { + snprintf(buf, bufn, "ans=%s:%s", p, rfb_desktop_name); + } else { + snprintf(buf, bufn, "ans=%s:%s", p, "unknown"); + } + goto qry; + + } else if (!strcmp(p, "blacken") || !strcmp(p, "zero")) { + NOTAPP + push_black_screen(4); + } else if (!strcmp(p, "refresh")) { + NOTAPP + refresh_screen(1); + } else if (!strcmp(p, "reset")) { + NOTAPP + do_new_fb(1); + } else if (strstr(p, "zero:") == p) { /* skip-cmd-list */ + int x1, y1, x2, y2; + NOTAPP + p += strlen("zero:"); + if (sscanf(p, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4) { + int mark = 1; + rfbLog("zeroing rect: %s\n", p); + if (x1 < 0 || x2 < 0) { + x1 = nabs(x1); + x2 = nabs(x2); + mark = 0; /* hack for testing */ + } + + zero_fb(x1, y1, x2, y2); + if (mark) { + mark_rect_as_modified(x1, y1, x2, y2, 0); + } + push_sleep(4); + } + } else if (strstr(p, "damagefb:") == p) { /* skip-cmd-list */ + int delay; + NOTAPP + p += strlen("damagefb:"); + if (sscanf(p, "%d", &delay) == 1) { + rfbLog("damaging client fb's for %d secs " + "(by not marking rects.)\n", delay); + damage_time = time(0); + damage_delay = delay; + } + + } else if (strstr(p, "close") == p) { + NOTAPP + COLON_CHECK("close:") + p += strlen("close:"); + close_clients(p); + } else if (strstr(p, "disconnect") == p) { + NOTAPP + COLON_CHECK("disconnect:") + p += strlen("disconnect:"); + close_clients(p); + + } else if (strstr(p, "id") == p) { + int ok = 0; + Window twin; + COLON_CHECK("id:") + if (query) { + snprintf(buf, bufn, "ans=%s%s0x%lx", p, co, + rootshift ? 0 : subwin); + goto qry; + } + p += strlen("id:"); + if (*p == '\0' || !strcmp("root", p)) { + /* back to root win */ + twin = 0x0; + ok = 1; + } else if (!strcmp("pick", p)) { + twin = 0x0; + if (safe_remote_only) { + rfbLog("unsafe: '-id pick'\n"); + } else if (pick_windowid(&twin)) { + ok = 1; + } + } else if (! scan_hexdec(p, &twin)) { + rfbLog("-id: skipping incorrect hex/dec number:" + " %s\n", p); + } else { + ok = 1; + } + if (ok) { + if (twin && ! valid_window(twin, NULL, 0)) { + rfbLog("skipping invalid sub-window: 0x%lx\n", + twin); + } else { + subwin = twin; + rootshift = 0; + check_black_fb(); + do_new_fb(1); + } + } + } else if (strstr(p, "sid") == p) { + int ok = 0; + Window twin; + COLON_CHECK("sid:") + if (query) { + snprintf(buf, bufn, "ans=%s%s0x%lx", p, co, + !rootshift ? 0 : subwin); + goto qry; + } + p += strlen("sid:"); + if (*p == '\0' || !strcmp("root", p)) { + /* back to root win */ + twin = 0x0; + ok = 1; + } else if (!strcmp("pick", p)) { + twin = 0x0; + if (safe_remote_only) { + rfbLog("unsafe: '-sid pick'\n"); + } else if (pick_windowid(&twin)) { + ok = 1; + } + } else if (! scan_hexdec(p, &twin)) { + rfbLog("-sid: skipping incorrect hex/dec number: %s\n", p); + } else { + ok = 1; + } + if (ok) { + if (twin && ! valid_window(twin, NULL, 0)) { + rfbLog("skipping invalid sub-window: 0x%lx\n", + twin); + } else { + subwin = twin; + rootshift = 1; + check_black_fb(); + do_new_fb(1); + } + } + } else if (strstr(p, "waitmapped") == p) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + subwin_wait_mapped); + goto qry; + } + subwin_wait_mapped = 1; + } else if (strstr(p, "nowaitmapped") == p) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !subwin_wait_mapped); + goto qry; + } + subwin_wait_mapped = 0; + + } 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, + NONUL(clip_str)); + goto qry; + } + p += strlen("clip:"); + if (clip_str) free(clip_str); + clip_str = strdup(p); + + /* OK, this requires a new fb... */ + do_new_fb(1); + + } else if (!strcmp(p, "flashcmap")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, flash_cmap); + goto qry; + } + rfbLog("remote_cmd: turning on flashcmap mode.\n"); + flash_cmap = 1; + } else if (!strcmp(p, "noflashcmap")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !flash_cmap); + goto qry; + } + rfbLog("remote_cmd: turning off flashcmap mode.\n"); + flash_cmap = 0; + + } else if (strstr(p, "shiftcmap") == p) { + COLON_CHECK("shiftcmap:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, shift_cmap); + goto qry; + } + p += strlen("shiftcmap:"); + shift_cmap = atoi(p); + rfbLog("remote_cmd: set -shiftcmap %d\n", shift_cmap); + do_new_fb(1); + + } else if (!strcmp(p, "truecolor")) { + int orig = force_indexed_color; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !force_indexed_color); + goto qry; + } + rfbLog("remote_cmd: turning off notruecolor mode.\n"); + force_indexed_color = 0; + if (orig != force_indexed_color) { + if_8bpp_do_new_fb(); + } + } else if (!strcmp(p, "notruecolor")) { + int orig = force_indexed_color; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + force_indexed_color); + goto qry; + } + rfbLog("remote_cmd: turning on notruecolor mode.\n"); + force_indexed_color = 1; + if (orig != force_indexed_color) { + if_8bpp_do_new_fb(); + } + + } else if (!strcmp(p, "overlay")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, overlay); + goto qry; + } + rfbLog("remote_cmd: turning on -overlay mode.\n"); + if (!overlay_present) { + rfbLog("skipping: overlay extension not present.\n"); + } else if (overlay) { + rfbLog("skipping: already in -overlay mode.\n"); + } else { + int reset_mem = 0; + /* here we go... */ + if (using_shm) { + rfbLog("setting -noshm mode.\n"); + using_shm = 0; + reset_mem = 1; + } + overlay = 1; + do_new_fb(reset_mem); + } + } else if (!strcmp(p, "nooverlay")) { + int orig = overlay; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !overlay); + goto qry; + } + rfbLog("remote_cmd: turning off overlay mode\n"); + overlay = 0; + if (!overlay_present) { + rfbLog("warning: overlay extension not present.\n"); + } else if (!orig) { + rfbLog("skipping: already not in -overlay mode.\n"); + } else { + /* here we go... */ + do_new_fb(0); + } + + } else if (!strcmp(p, "overlay_cursor") || + !strcmp(p, "overlay_yescursor") || + !strcmp(p, "nooverlay_nocursor")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, overlay_cursor); + goto qry; + } + rfbLog("remote_cmd: turning on overlay_cursor mode.\n"); + overlay_cursor = 1; + if (!overlay_present) { + rfbLog("warning: overlay extension not present.\n"); + } else if (!overlay) { + rfbLog("warning: not in -overlay mode.\n"); + } else { + rfbLog("You may want to run -R noshow_cursor or\n"); + rfbLog(" -R cursor:none to disable any extra " + "cursors.\n"); + } + } else if (!strcmp(p, "nooverlay_cursor") || + !strcmp(p, "nooverlay_yescursor") || + !strcmp(p, "overlay_nocursor")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !overlay_cursor); + goto qry; + } + rfbLog("remote_cmd: turning off overlay_cursor mode\n"); + overlay_cursor = 0; + if (!overlay_present) { + rfbLog("warning: overlay extension not present.\n"); + } else if (!overlay) { + rfbLog("warning: not in -overlay mode.\n"); + } else { + rfbLog("You may want to run -R show_cursor or\n"); + rfbLog(" -R cursor:... to re-enable any cursors.\n"); + } + + } else if (strstr(p, "visual") == p) { + COLON_CHECK("visual:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(visual_str)); + goto qry; + } + p += strlen("visual:"); + if (visual_str) free(visual_str); + visual_str = strdup(p); + + /* OK, this requires a new fb... */ + do_new_fb(0); + + } else if (!strcmp(p, "scale") || + strstr(p, "scale:") == p) { /* skip-cmd-list */ + COLON_CHECK("scale:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(scale_str)); + goto qry; + } + p += strlen("scale:"); + if (scale_str) free(scale_str); + scale_str = strdup(p); + + /* OK, this requires a new fb... */ + check_black_fb(); + do_new_fb(0); + + } else if (!strcmp(p, "scale_cursor") || + strstr(p, "scale_cursor:") == p) { /* skip-cmd-list */ + COLON_CHECK("scale_cursor:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(scale_cursor_str)); + goto qry; + } + p += strlen("scale_cursor:"); + if (scale_cursor_str) free(scale_cursor_str); + if (*p == '\0') { + scale_cursor_str = NULL; + } else { + scale_cursor_str = strdup(p); + } + setup_cursors_and_push(); + + } else if (!strcmp(p, "viewonly")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, view_only); + goto qry; + } + rfbLog("remote_cmd: enable viewonly mode.\n"); + view_only = 1; + } else if (!strcmp(p, "noviewonly")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !view_only); + goto qry; + } + rfbLog("remote_cmd: disable viewonly mode.\n"); + view_only = 0; + if (raw_fb) set_raw_fb_params(0); + + } else if (!strcmp(p, "shared")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, shared); goto qry; + } + rfbLog("remote_cmd: enable sharing.\n"); + shared = 1; + if (screen) { + screen->alwaysShared = TRUE; + screen->neverShared = FALSE; + } + } else if (!strcmp(p, "noshared")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !shared); goto qry; + } + rfbLog("remote_cmd: disable sharing.\n"); + shared = 0; + if (screen) { + screen->alwaysShared = FALSE; + screen->neverShared = TRUE; + } + + } else if (!strcmp(p, "forever")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, 1-connect_once); + goto qry; + } + rfbLog("remote_cmd: enable -forever mode.\n"); + connect_once = 0; + } else if (!strcmp(p, "noforever") || !strcmp(p, "once")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, connect_once); + goto qry; + } + rfbLog("remote_cmd: disable -forever mode.\n"); + connect_once = 1; + + } else if (strstr(p, "timeout") == p) { + int to; + COLON_CHECK("timeout:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + first_conn_timeout); + goto qry; + } + p += strlen("timeout:"); + to = atoi(p); + if (to > 0 ) { + to = -to; + } + first_conn_timeout = to; + rfbLog("remote_cmd: set -timeout to %d\n", -to); + +#if 0 + } else if (!strcmp(p, "filexfer")) { + /* does this work after rfbInitServer? */ + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, filexfer); + goto qry; + } + rfbLog("remote_cmd: enabling -filexfer.\n"); + filexfer = 1; + rfbRegisterTightVNCFileTransferExtension(); +#endif + + } else if (!strcmp(p, "deny") || !strcmp(p, "lock")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, deny_all); + goto qry; + } + rfbLog("remote_cmd: denying new connections.\n"); + deny_all = 1; + } else if (!strcmp(p, "nodeny") || !strcmp(p, "unlock")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !deny_all); + goto qry; + } + rfbLog("remote_cmd: allowing new connections.\n"); + deny_all = 0; + + } else if (strstr(p, "connect") == p) { + NOTAPP + COLON_CHECK("connect:") + p += strlen("connect:"); + /* this is a reverse connection */ + reverse_connect(p); + + } else if (strstr(p, "allowonce") == p) { + COLON_CHECK("allowonce:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(allow_once)); + goto qry; + } + p += strlen("allowonce:"); + allow_once = strdup(p); + rfbLog("remote_cmd: set allow_once %s\n", allow_once); + + } else if (strstr(p, "allow") == p) { + char *before, *old; + COLON_CHECK("allow:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(allow_list)); + goto qry; + } + p += strlen("allow:"); + if (allow_list && strchr(allow_list, '/')) { + rfbLog("remote_cmd: cannot use allow:host\n"); + rfbLog("in '-allow %s' mode.\n", allow_list); + goto done; + } + if (allow_list) { + before = strdup(allow_list); + } else { + before = strdup(""); + } + + old = allow_list; + if (*p == '+') { + p++; + allow_list = add_item(allow_list, p); + } else if (*p == '-') { + p++; + allow_list = delete_item(allow_list, p); + } else { + allow_list = strdup(p); + } + + if (strcmp(before, allow_list)) { + rfbLog("remote_cmd: modified allow_list:\n"); + rfbLog(" from: \"%s\"\n", before); + rfbLog(" to: \"%s\"\n", allow_list); + } + if (old) free(old); + free(before); + + } else if (!strcmp(p, "localhost")) { + char *before, *old; + if (query) { + int state = 0; + char *s = allow_list; + if (s && (!strcmp(s, "127.0.0.1") || + !strcmp(s, "localhost"))) { + state = 1; + } + snprintf(buf, bufn, "ans=%s:%d", p, state); + goto qry; + } + if (allow_list) { + before = strdup(allow_list); + } else { + before = strdup(""); + } + old = allow_list; + + allow_list = strdup("127.0.0.1"); + + if (strcmp(before, allow_list)) { + rfbLog("remote_cmd: modified allow_list:\n"); + rfbLog(" from: \"%s\"\n", before); + rfbLog(" to: \"%s\"\n", allow_list); + } + if (old) free(old); + free(before); + + if (listen_str) { + free(listen_str); + } + listen_str = strdup("localhost"); + + screen->listenInterface = htonl(INADDR_LOOPBACK); + rfbLog("listening on loopback network only.\n"); + rfbLog("allow list is: '%s'\n", NONUL(allow_list)); + reset_rfbport(-1, screen->port); + if (screen->httpListenSock > -1) { + reset_httpport(-1, screen->httpPort); + } + } else if (!strcmp(p, "nolocalhost")) { + char *before, *old; + if (query) { + int state = 0; + char *s = allow_list; + if (s && (!strcmp(s, "127.0.0.1") || + !strcmp(s, "localhost"))) { + state = 1; + } + snprintf(buf, bufn, "ans=%s:%d", p, !state); + goto qry; + } + if (allow_list) { + before = strdup(allow_list); + } else { + before = strdup(""); + } + old = allow_list; + + allow_list = strdup(""); + + if (strcmp(before, allow_list)) { + rfbLog("remote_cmd: modified allow_list:\n"); + rfbLog(" from: \"%s\"\n", before); + rfbLog(" to: \"%s\"\n", allow_list); + } + if (old) free(old); + free(before); + + if (listen_str) { + free(listen_str); + } + listen_str = NULL; + + screen->listenInterface = htonl(INADDR_ANY); + rfbLog("listening on ALL network interfaces.\n"); + rfbLog("allow list is: '%s'\n", NONUL(allow_list)); + reset_rfbport(-1, screen->port); + if (screen->httpListenSock > -1) { + reset_httpport(-1, screen->httpPort); + } + + } else if (strstr(p, "listen") == p) { + char *before; + int ok, mod = 0; + + COLON_CHECK("listen:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(listen_str)); + goto qry; + } + if (listen_str) { + before = strdup(listen_str); + } else { + before = strdup(""); + } + p += strlen("listen:"); + + listen_str = strdup(p); + + if (strcmp(before, listen_str)) { + rfbLog("remote_cmd: modified listen_str:\n"); + rfbLog(" from: \"%s\"\n", before); + rfbLog(" to: \"%s\"\n", listen_str); + mod = 1; + } + + ok = 1; + if (listen_str == NULL || *listen_str == '\0' || + !strcmp(listen_str, "any")) { + screen->listenInterface = htonl(INADDR_ANY); + } else if (!strcmp(listen_str, "localhost")) { + screen->listenInterface = htonl(INADDR_LOOPBACK); + } else { + struct hostent *hp; + in_addr_t iface = inet_addr(listen_str); + if (iface == htonl(INADDR_NONE)) { + if (!host_lookup) { + ok = 0; + } else if (!(hp = gethostbyname(listen_str))) { + ok = 0; + } else { + iface = *(unsigned long *)hp->h_addr; + } + } + if (ok) { + screen->listenInterface = iface; + } + } + + if (ok && mod) { + int is_loopback = 0; + in_addr_t iface = screen->listenInterface; + + if (allow_list) { + if (!strcmp(allow_list, "127.0.0.1") || + !strcmp(allow_list, "localhost")) { + is_loopback = 1; + } + } + if (iface != htonl(INADDR_LOOPBACK)) { + if (is_loopback) { + rfbLog("re-setting -allow list to all " + "hosts for non-loopback listening.\n"); + if (allow_list) { + free(allow_list); + } + allow_list = NULL; + } + } else { + if (!is_loopback) { + if (allow_list) { + free(allow_list); + } + rfbLog("setting -allow list to 127.0.0.1\n"); + allow_list = strdup("127.0.0.1"); + } + } + } + if (ok) { + rfbLog("allow list is: '%s'\n", NONUL(allow_list)); + reset_rfbport(-1, screen->port); + if (screen->httpListenSock > -1) { + reset_httpport(-1, screen->httpPort); + } + free(before); + } else { + rfbLog("invalid listen string: %s\n", listen_str); + free(listen_str); + listen_str = before; + } + } else if (!strcmp(p, "lookup")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, host_lookup); + goto qry; + } + rfbLog("remote_cmd: enabling hostname lookup.\n"); + host_lookup = 1; + } else if (!strcmp(p, "nolookup")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !host_lookup); + goto qry; + } + rfbLog("remote_cmd: disabling hostname lookup.\n"); + host_lookup = 0; + + } else if (strstr(p, "accept") == p) { + int doit = 1; + COLON_CHECK("accept:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(accept_cmd)); + goto qry; + } + p += strlen("accept:"); + if (safe_remote_only) { + if (icon_mode && !strcmp(p, "")) { /* skip-cmd-list */ + ; + } else if (icon_mode && !strcmp(p, "popup")) { /* skip-cmd-list */ + ; + } else { + rfbLog("unsafe: %s\n", p); + doit = 0; + } + } + + if (doit) { + if (accept_cmd) free(accept_cmd); + accept_cmd = strdup(p); + } + + } else if (strstr(p, "afteraccept") == p) { + COLON_CHECK("afteraccept:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(afteraccept_cmd)); + goto qry; + } + if (safe_remote_only) { + rfbLog("unsafe: %s\n", p); + } else { + p += strlen("afteraccept:"); + if (afteraccept_cmd) free(afteraccept_cmd); + afteraccept_cmd = strdup(p); + } + + } else if (strstr(p, "gone") == p) { + COLON_CHECK("gone:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(gone_cmd)); + goto qry; + } + if (safe_remote_only) { + rfbLog("unsafe: %s\n", p); + } else { + p += strlen("gone:"); + if (gone_cmd) free(gone_cmd); + gone_cmd = strdup(p); + } + + } else if (!strcmp(p, "shm")) { + int orig = using_shm; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, using_shm); + goto qry; + } + rfbLog("remote_cmd: turning off noshm mode.\n"); + using_shm = 1; + if (raw_fb) set_raw_fb_params(0); + + if (orig != using_shm) { + do_new_fb(1); + } else { + rfbLog(" already in shm mode.\n"); + } + } else if (!strcmp(p, "noshm")) { + int orig = using_shm; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !using_shm); + goto qry; + } + rfbLog("remote_cmd: turning on noshm mode.\n"); + using_shm = 0; + if (orig != using_shm) { + do_new_fb(1); + } else { + rfbLog(" already in noshm mode.\n"); + } + + } else if (!strcmp(p, "flipbyteorder")) { + int orig = flip_byte_order; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, flip_byte_order); + goto qry; + } + rfbLog("remote_cmd: turning on flipbyteorder mode.\n"); + flip_byte_order = 1; + if (orig != flip_byte_order) { + if (! using_shm) { + do_new_fb(1); + } else { + rfbLog(" using shm, not resetting fb\n"); + } + } + } else if (!strcmp(p, "noflipbyteorder")) { + int orig = flip_byte_order; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !flip_byte_order); + goto qry; + } + rfbLog("remote_cmd: turning off flipbyteorder mode.\n"); + flip_byte_order = 0; + if (orig != flip_byte_order) { + if (! using_shm) { + do_new_fb(1); + } else { + rfbLog(" using shm, not resetting fb\n"); + } + } + + } else if (!strcmp(p, "onetile")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, single_copytile); + goto qry; + } + rfbLog("remote_cmd: enable -onetile mode.\n"); + single_copytile = 1; + } else if (!strcmp(p, "noonetile")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !single_copytile); + goto qry; + } + rfbLog("remote_cmd: disable -onetile mode.\n"); + if (tile_shm_count < ntiles_x) { + rfbLog(" this has no effect: tile_shm_count=%d" + " ntiles_x=%d\n", tile_shm_count, ntiles_x); + + } + single_copytile = 0; + + } else if (strstr(p, "solid_color") == p) { + /* + * n.b. this solid stuff perhaps should reflect + * safe_remote_only but at least the command names + * are fixed. + */ + char *new; + int doit = 1; + COLON_CHECK("solid_color:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(solid_str)); + goto qry; + } + p += strlen("solid_color:"); + if (*p != '\0') { + new = strdup(p); + } else { + new = strdup(solid_default); + } + rfbLog("remote_cmd: solid %s -> %s\n", NONUL(solid_str), new); + + if (solid_str) { + if (!strcmp(solid_str, new)) { + doit = 0; + } + free(solid_str); + } + solid_str = new; + use_solid_bg = 1; + if (raw_fb) set_raw_fb_params(0); + + if (doit && client_count) { + solid_bg(0); + } + } else if (!strcmp(p, "solid")) { + int orig = use_solid_bg; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, use_solid_bg); + goto qry; + } + rfbLog("remote_cmd: enable -solid mode\n"); + if (! solid_str) { + solid_str = strdup(solid_default); + } + use_solid_bg = 1; + if (raw_fb) set_raw_fb_params(0); + if (client_count && !orig) { + solid_bg(0); + } + } else if (!strcmp(p, "nosolid")) { + int orig = use_solid_bg; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !use_solid_bg); + goto qry; + } + rfbLog("remote_cmd: disable -solid mode\n"); + use_solid_bg = 0; + if (client_count && orig) { + solid_bg(1); + } + + } else if (strstr(p, "blackout") == p) { + char *before, *old; + COLON_CHECK("blackout:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(blackout_str)); + goto qry; + } + p += strlen("blackout:"); + if (blackout_str) { + before = strdup(blackout_str); + } else { + before = strdup(""); + } + old = blackout_str; + if (*p == '+') { + p++; + blackout_str = add_item(blackout_str, p); + } else if (*p == '-') { + p++; + blackout_str = delete_item(blackout_str, p); + } else { + blackout_str = strdup(p); + } + if (strcmp(before, blackout_str)) { + rfbLog("remote_cmd: changing -blackout\n"); + rfbLog(" from: %s\n", before); + rfbLog(" to: %s\n", blackout_str); + if (0 && !strcmp(blackout_str, "") && + single_copytile_orig != single_copytile) { + rfbLog("resetting single_copytile to: %d\n", + single_copytile_orig); + single_copytile = single_copytile_orig; + } + initialize_blackouts_and_xinerama(); + } + if (old) free(old); + free(before); + + } else if (!strcmp(p, "xinerama")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, xinerama); + goto qry; + } + rfbLog("remote_cmd: enable xinerama mode. (if applicable).\n"); + xinerama = 1; + initialize_blackouts_and_xinerama(); + } else if (!strcmp(p, "noxinerama")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !xinerama); + goto qry; + } + rfbLog("remote_cmd: disable xinerama mode. (if applicable).\n"); + xinerama = 0; + initialize_blackouts_and_xinerama(); + + } else if (!strcmp(p, "xtrap")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, xtrap_input); + goto qry; + } + rfbLog("remote_cmd: enable xtrap input mode." + "(if applicable).\n"); + if (! xtrap_input) { + xtrap_input = 1; + disable_grabserver(dpy, 1); + } + + } else if (!strcmp(p, "noxtrap")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !xtrap_input); + goto qry; + } + rfbLog("remote_cmd: disable xtrap input mode." + "(if applicable).\n"); + if (xtrap_input) { + xtrap_input = 0; + disable_grabserver(dpy, 1); + } + + } else if (!strcmp(p, "xrandr")) { + int orig = xrandr; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, xrandr); goto qry; + } + if (xrandr_present) { + rfbLog("remote_cmd: enable xrandr mode.\n"); + xrandr = 1; + if (raw_fb) set_raw_fb_params(0); + if (! xrandr_mode) { + xrandr_mode = strdup("default"); + } + if (orig != xrandr) { + initialize_xrandr(); + } + } else { + rfbLog("remote_cmd: XRANDR ext. not present.\n"); + } + } else if (!strcmp(p, "noxrandr")) { + int orig = xrandr; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !xrandr); goto qry; + } + xrandr = 0; + if (xrandr_present) { + rfbLog("remote_cmd: disable xrandr mode.\n"); + if (orig != xrandr) { + initialize_xrandr(); + } + } else { + rfbLog("remote_cmd: XRANDR ext. not present.\n"); + } + } else if (strstr(p, "xrandr_mode") == p) { + int orig = xrandr; + COLON_CHECK("xrandr_mode:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(xrandr_mode)); + goto qry; + } + p += strlen("xrandr_mode:"); + if (!strcmp("none", p)) { + xrandr = 0; + } else { + if (known_xrandr_mode(p)) { + if (xrandr_mode) free(xrandr_mode); + xrandr_mode = strdup(p); + } else { + rfbLog("skipping unknown xrandr mode: %s\n", p); + goto done; + } + xrandr = 1; + } + if (xrandr_present) { + if (xrandr) { + rfbLog("remote_cmd: enable xrandr mode.\n"); + } else { + rfbLog("remote_cmd: disable xrandr mode.\n"); + } + if (! xrandr_mode) { + xrandr_mode = strdup("default"); + } + if (orig != xrandr) { + initialize_xrandr(); + } + } else { + rfbLog("remote_cmd: XRANDR ext. not present.\n"); + } + + } else if (strstr(p, "padgeom") == p) { + COLON_CHECK("padgeom:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(pad_geometry)); + goto qry; + } + p += strlen("padgeom:"); + if (!strcmp("force", p) || !strcmp("do",p) || !strcmp("go",p)) { + rfbLog("remote_cmd: invoking install_padded_fb()\n"); + install_padded_fb(pad_geometry); + } else { + if (pad_geometry) free(pad_geometry); + pad_geometry = strdup(p); + rfbLog("remote_cmd: set padgeom to: %s\n", + pad_geometry); + } + + } else if (!strcmp(p, "quiet") || !strcmp(p, "q")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, quiet); goto qry; + } + rfbLog("remote_cmd: turning on quiet mode.\n"); + quiet = 1; + } else if (!strcmp(p, "noquiet")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !quiet); goto qry; + } + rfbLog("remote_cmd: turning off quiet mode.\n"); + quiet = 0; + + } else if (!strcmp(p, "modtweak")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, use_modifier_tweak); + goto qry; + } + rfbLog("remote_cmd: enabling -modtweak mode.\n"); + if (! use_modifier_tweak) { + use_modifier_tweak = 1; + initialize_modtweak(); + } + use_modifier_tweak = 1; + + } else if (!strcmp(p, "nomodtweak")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !use_modifier_tweak); + goto qry; + } + rfbLog("remote_cmd: enabling -nomodtweak mode.\n"); + got_nomodtweak = 1; + use_modifier_tweak = 0; + + } else if (!strcmp(p, "xkb")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, use_xkb_modtweak); + goto qry; + } + if (! xkb_present) { + rfbLog("remote_cmd: cannot enable -xkb " + "modtweak mode (not supported on X display)\n"); + goto done; + } + rfbLog("remote_cmd: enabling -xkb modtweak mode" + " (if supported).\n"); + if (! use_modifier_tweak || ! use_xkb_modtweak) { + use_modifier_tweak = 1; + use_xkb_modtweak = 1; + initialize_modtweak(); + } + use_modifier_tweak = 1; + use_xkb_modtweak = 1; + + } else if (!strcmp(p, "noxkb")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !use_xkb_modtweak); + goto qry; + } + if (! xkb_present) { + rfbLog("remote_cmd: cannot disable -xkb " + "modtweak mode (not supported on X display)\n"); + goto done; + } + 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:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(skip_keycodes)); + goto qry; + } + p += strlen("skip_keycodes:"); + rfbLog("remote_cmd: setting xkb -skip_keycodes" + " to:\n\t'%s'\n", p); + if (! xkb_present) { + rfbLog("remote_cmd: warning xkb not present\n"); + } else if (! use_xkb_modtweak) { + rfbLog("remote_cmd: turning on xkb.\n"); + use_xkb_modtweak = 1; + if (! use_modifier_tweak) { + rfbLog("remote_cmd: turning on modtweak.\n"); + use_modifier_tweak = 1; + } + } + if (skip_keycodes) free(skip_keycodes); + skip_keycodes = strdup(p); + initialize_modtweak(); + + } else if (!strcmp(p, "sloppy_keys")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, sloppy_keys); + goto qry; + } + sloppy_keys += 1; + rfbLog("remote_cmd: set sloppy_keys to: %d\n", sloppy_keys); + } else if (!strcmp(p, "nosloppy_keys")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !sloppy_keys); + goto qry; + } + sloppy_keys = 0; + rfbLog("remote_cmd: set sloppy_keys to: %d\n", sloppy_keys); + + } 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); + goto qry; + } + rfbLog("remote_cmd: enabling -add_keysyms mode.\n"); + add_keysyms = 1; + + } else if (!strcmp(p, "noadd_keysyms")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !add_keysyms); + goto qry; + } + rfbLog("remote_cmd: disabling -add_keysyms mode.\n"); + add_keysyms = 0; + + } else if (!strcmp(p, "clear_mods")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, clear_mods == 1); + goto qry; + } + rfbLog("remote_cmd: enabling -clear_mods mode.\n"); + clear_mods = 1; + clear_modifiers(0); + + } else if (!strcmp(p, "noclear_mods")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !(clear_mods == 1)); + goto qry; + } + rfbLog("remote_cmd: disabling -clear_mods mode.\n"); + clear_mods = 0; + + } else if (!strcmp(p, "clear_keys")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + clear_mods == 2); + goto qry; + } + rfbLog("remote_cmd: enabling -clear_keys mode.\n"); + clear_mods = 2; + clear_keys(); + + } else if (!strcmp(p, "noclear_keys")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !(clear_mods == 2)); + goto qry; + } + rfbLog("remote_cmd: disabling -clear_keys mode.\n"); + clear_mods = 0; + + } else if (strstr(p, "remap") == p) { + char *before, *old; + COLON_CHECK("remap:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(remap_file)); + goto qry; + } + p += strlen("remap:"); + if ((*p == '+' || *p == '-') && remap_file && + strchr(remap_file, '/')) { + rfbLog("remote_cmd: cannot use remap:+/-\n"); + rfbLog("in '-remap %s' mode.\n", remap_file); + goto done; + } + if (remap_file) { + before = strdup(remap_file); + } else { + before = strdup(""); + } + old = remap_file; + if (*p == '+') { + p++; + remap_file = add_item(remap_file, p); + } else if (*p == '-') { + p++; + remap_file = delete_item(remap_file, p); + if (! strchr(remap_file, '-')) { + *remap_file = '\0'; + } + } else { + remap_file = strdup(p); + } + if (strcmp(before, remap_file)) { + rfbLog("remote_cmd: changed -remap\n"); + rfbLog(" from: %s\n", before); + rfbLog(" to: %s\n", remap_file); + initialize_remap(remap_file); + } + if (old) free(old); + free(before); + + } else if (!strcmp(p, "repeat")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !no_autorepeat); + goto qry; + } + rfbLog("remote_cmd: enabling -repeat mode.\n"); + autorepeat(1, 0); /* restore initial setting */ + no_autorepeat = 0; + + } else if (!strcmp(p, "norepeat")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, no_autorepeat); + goto qry; + } + rfbLog("remote_cmd: enabling -norepeat mode.\n"); + no_autorepeat = 1; + if (no_repeat_countdown >= 0) { + no_repeat_countdown = 2; + } + if (client_count && ! view_only) { + autorepeat(0, 0); /* disable if any clients */ + } + + } else if (!strcmp(p, "fb")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !nofb); + goto qry; + } + if (nofb) { + rfbLog("remote_cmd: disabling nofb mode.\n"); + rfbLog(" you may need to these turn back on:\n"); + rfbLog(" xfixes, xdamage, solid, flashcmap\n"); + rfbLog(" overlay, shm, noonetile, nap, cursor\n"); + rfbLog(" cursorpos, cursorshape, bell.\n"); + nofb = 0; + set_nofb_params(1); + do_new_fb(1); + } + } else if (!strcmp(p, "nofb")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, nofb); + goto qry; + } + if (!nofb) { + rfbLog("remote_cmd: enabling nofb mode.\n"); + if (main_fb) { + push_black_screen(4); + } + nofb = 1; + sound_bell = 0; + initialize_watch_bell(); + set_nofb_params(0); + do_new_fb(1); + } + + } else if (!strcmp(p, "bell")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, sound_bell); + goto qry; + } + rfbLog("remote_cmd: enabling bell (if supported).\n"); + initialize_watch_bell(); + sound_bell = 1; + + } else if (!strcmp(p, "nobell")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !sound_bell); + goto qry; + } + rfbLog("remote_cmd: disabling bell.\n"); + initialize_watch_bell(); + sound_bell = 0; + + } else if (!strcmp(p, "sel")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, watch_selection); + goto qry; + } + rfbLog("remote_cmd: enabling watch selection+primary.\n"); + watch_selection = 1; + watch_primary = 1; + + } else if (!strcmp(p, "nosel")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !watch_selection); + goto qry; + } + rfbLog("remote_cmd: disabling watch selection+primary.\n"); + watch_selection = 0; + watch_primary = 0; + + } else if (!strcmp(p, "primary")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, watch_primary); + goto qry; + } + rfbLog("remote_cmd: enabling watch_primary.\n"); + watch_primary = 1; + + } else if (!strcmp(p, "noprimary")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !watch_primary); + goto qry; + } + rfbLog("remote_cmd: disabling watch_primary.\n"); + watch_primary = 0; + + } else if (strstr(p, "seldir") == p) { + COLON_CHECK("seldir:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(sel_direction)); + goto qry; + } + p += strlen("seldir:"); + rfbLog("remote_cmd: setting -seldir to %s\n", p); + if (sel_direction) free(sel_direction); + sel_direction = strdup(p); + + } else if (!strcmp(p, "set_no_cursor")) { /* skip-cmd-list */ + rfbLog("remote_cmd: calling set_no_cursor()\n"); + set_no_cursor(); + + } else if (!strcmp(p, "cursorshape")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + cursor_shape_updates); + goto qry; + } + rfbLog("remote_cmd: turning on cursorshape mode.\n"); + + set_no_cursor(); + cursor_shape_updates = 1; + restore_cursor_shape_updates(screen); + first_cursor(); + } else if (!strcmp(p, "nocursorshape")) { + int i, max = 5; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !cursor_shape_updates); + goto qry; + } + rfbLog("remote_cmd: turning off cursorshape mode.\n"); + + set_no_cursor(); + for (i=0; i<max; i++) { + /* XXX: try to force empty cursor back to client */ + rfbPE(-1); + } + cursor_shape_updates = 0; + disable_cursor_shape_updates(screen); + first_cursor(); + + } else if (!strcmp(p, "cursorpos")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + cursor_pos_updates); + goto qry; + } + rfbLog("remote_cmd: turning on cursorpos mode.\n"); + cursor_pos_updates = 1; + } else if (!strcmp(p, "nocursorpos")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, + !cursor_pos_updates); + goto qry; + } + rfbLog("remote_cmd: turning off cursorpos mode.\n"); + cursor_pos_updates = 0; + + } else if (strstr(p, "cursor") == p) { + COLON_CHECK("cursor:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(multiple_cursors_mode)); + goto qry; + } + p += strlen("cursor:"); + if (multiple_cursors_mode) { + if (prev_cursors_mode) free(prev_cursors_mode); + prev_cursors_mode = strdup(multiple_cursors_mode); + free(multiple_cursors_mode); + } + multiple_cursors_mode = strdup(p); + + rfbLog("remote_cmd: changed -cursor mode " + "to: %s\n", multiple_cursors_mode); + + if (strcmp(multiple_cursors_mode, "none") && !show_cursor) { + show_cursor = 1; + rfbLog("remote_cmd: changed show_cursor " + "to: %d\n", show_cursor); + } + initialize_cursors_mode(); + first_cursor(); + + } else if (!strcmp(p, "show_cursor")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, show_cursor); + goto qry; + } + rfbLog("remote_cmd: enabling show_cursor.\n"); + show_cursor = 1; + if (multiple_cursors_mode && !strcmp(multiple_cursors_mode, + "none")) { + free(multiple_cursors_mode); + if (prev_cursors_mode) { + multiple_cursors_mode = + strdup(prev_cursors_mode); + } else { + multiple_cursors_mode = strdup("default"); + } + rfbLog("remote_cmd: changed -cursor mode " + "to: %s\n", multiple_cursors_mode); + } + initialize_cursors_mode(); + first_cursor(); + } else if (!strcmp(p, "noshow_cursor") || !strcmp(p, "nocursor")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !show_cursor); + goto qry; + } + if (prev_cursors_mode) free(prev_cursors_mode); + prev_cursors_mode = strdup(multiple_cursors_mode); + + rfbLog("remote_cmd: disabling show_cursor.\n"); + show_cursor = 0; + initialize_cursors_mode(); + first_cursor(); + + } else if (strstr(p, "arrow") == p) { + COLON_CHECK("arrow:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, alt_arrow); + goto qry; + } + p += strlen("arrow:"); + alt_arrow = atoi(p); + rfbLog("remote_cmd: setting alt_arrow: %d.\n", alt_arrow); + setup_cursors_and_push(); + + } else if (!strcmp(p, "xfixes")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, use_xfixes); + goto qry; + } + if (! xfixes_present) { + rfbLog("remote_cmd: cannot enable xfixes " + "(not supported on X display)\n"); + goto done; + } + rfbLog("remote_cmd: enabling -xfixes" + " (if supported).\n"); + use_xfixes = 1; + initialize_xfixes(); + first_cursor(); + } else if (!strcmp(p, "noxfixes")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !use_xfixes); + goto qry; + } + if (! xfixes_present) { + rfbLog("remote_cmd: disabling xfixes " + "(but not supported on X display)\n"); + goto done; + } + rfbLog("remote_cmd: disabling -xfixes.\n"); + use_xfixes = 0; + initialize_xfixes(); + first_cursor(); + + } else if (!strcmp(p, "xdamage")) { + int orig = use_xdamage; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, use_xdamage); + goto qry; + } + if (! xdamage_present) { + rfbLog("remote_cmd: cannot enable xdamage hints " + "(not supported on X display)\n"); + goto done; + } + rfbLog("remote_cmd: enabling xdamage hints" + " (if supported).\n"); + use_xdamage = 1; + if (use_xdamage != orig) { + initialize_xdamage(); + create_xdamage_if_needed(); + } + } else if (!strcmp(p, "noxdamage")) { + int orig = use_xdamage; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !use_xdamage); + goto qry; + } + if (! xdamage_present) { + rfbLog("remote_cmd: disabling xdamage hints " + "(but not supported on X display)\n"); + goto done; + } + rfbLog("remote_cmd: disabling xdamage hints.\n"); + use_xdamage = 0; + if (use_xdamage != orig) { + initialize_xdamage(); + destroy_xdamage_if_needed(); + } + + } else if (strstr(p, "xd_area") == p) { + int a; + COLON_CHECK("xd_area:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + xdamage_max_area); + goto qry; + } + p += strlen("xd_area:"); + a = atoi(p); + if (a >= 0) { + rfbLog("remote_cmd: setting xdamage_max_area " + "%d -> %d.\n", xdamage_max_area, a); + xdamage_max_area = a; + } + } else if (strstr(p, "xd_mem") == p) { + double a; + COLON_CHECK("xd_mem:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%.3f", p, co, + xdamage_memory); + goto qry; + } + p += strlen("xd_mem:"); + a = atof(p); + if (a >= 0.0) { + rfbLog("remote_cmd: setting xdamage_memory " + "%.3f -> %.3f.\n", xdamage_memory, a); + xdamage_memory = a; + } + + } else if (strstr(p, "alphacut") == p) { + int a; + COLON_CHECK("alphacut:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + alpha_threshold); + goto qry; + } + p += strlen("alphacut:"); + a = atoi(p); + if (a < 0) a = 0; + if (a > 256) a = 256; /* allow 256 for testing. */ + if (alpha_threshold != a) { + rfbLog("remote_cmd: setting alphacut " + "%d -> %d.\n", alpha_threshold, a); + if (a == 256) { + rfbLog("note: alphacut=256 leads to completely" + " transparent cursors.\n"); + } + alpha_threshold = a; + setup_cursors_and_push(); + } + } else if (strstr(p, "alphafrac") == p) { + double a; + COLON_CHECK("alphafrac:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%f", p, co, + alpha_frac); + goto qry; + } + p += strlen("alphafrac:"); + a = atof(p); + if (a < 0.0) a = 0.0; + if (a > 1.0) a = 1.0; + if (alpha_frac != a) { + rfbLog("remote_cmd: setting alphafrac " + "%f -> %f.\n", alpha_frac, a); + alpha_frac = a; + setup_cursors_and_push(); + } + } else if (strstr(p, "alpharemove") == p) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, alpha_remove); + goto qry; + } + if (!alpha_remove) { + rfbLog("remote_cmd: enable alpharemove\n"); + alpha_remove = 1; + setup_cursors_and_push(); + } + } else if (strstr(p, "noalpharemove") == p) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !alpha_remove); + goto qry; + } + if (alpha_remove) { + rfbLog("remote_cmd: disable alpharemove\n"); + alpha_remove = 0; + setup_cursors_and_push(); + } + } else if (strstr(p, "alphablend") == p) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, alpha_blend); + goto qry; + } + if (!alpha_blend) { + rfbLog("remote_cmd: enable alphablend\n"); + alpha_remove = 0; + alpha_blend = 1; + setup_cursors_and_push(); + } + } else if (strstr(p, "noalphablend") == p) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !alpha_blend); + goto qry; + } + if (alpha_blend) { + rfbLog("remote_cmd: disable alphablend\n"); + alpha_blend = 0; + setup_cursors_and_push(); + } + + } 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, "noxwarppointer") == p || + strstr(p, "noxwarp") == p) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !use_xwarppointer); + goto qry; + } + rfbLog("remote_cmd: turning off xwarppointer mode.\n"); + use_xwarppointer = 0; + + } else if (strstr(p, "buttonmap") == p) { + COLON_CHECK("buttonmap:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(pointer_remap)); + goto qry; + } + p += strlen("buttonmap:"); + if (pointer_remap) free(pointer_remap); + pointer_remap = strdup(p); + + rfbLog("remote_cmd: setting -buttonmap to:\n\t'%s'\n", p); + initialize_pointer_map(p); + + } else if (!strcmp(p, "dragging")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, show_dragging); + goto qry; + } + rfbLog("remote_cmd: enabling mouse dragging mode.\n"); + show_dragging = 1; + } else if (!strcmp(p, "nodragging")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !show_dragging); + goto qry; + } + rfbLog("remote_cmd: enabling mouse nodragging mode.\n"); + show_dragging = 0; + + } else if (strstr(p, "wireframe_mode") == p) { + COLON_CHECK("wireframe_mode:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + wireframe_str ? wireframe_str : WIREFRAME_PARMS); + goto qry; + } + p += strlen("wireframe_mode:"); + if (*p) { + if (wireframe_str) { + free(wireframe_str); + } + wireframe_str = strdup(p); + parse_wireframe(); + } + rfbLog("remote_cmd: enabling -wireframe mode.\n"); + wireframe = 1; + } else if (strstr(p, "wireframe:") == p) { /* skip-cmd-list */ + COLON_CHECK("wireframe:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, wireframe); + goto qry; + } + p += strlen("wireframe:"); + if (*p) { + if (wireframe_str) { + free(wireframe_str); + } + wireframe_str = strdup(p); + parse_wireframe(); + } + rfbLog("remote_cmd: enabling -wireframe mode.\n"); + wireframe = 1; + } else if (strstr(p, "wf:") == p) { /* skip-cmd-list */ + COLON_CHECK("wf:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, wireframe); + goto qry; + } + p += strlen("wf:"); + if (*p) { + if (wireframe_str) { + free(wireframe_str); + } + wireframe_str = strdup(p); + parse_wireframe(); + } + rfbLog("remote_cmd: enabling -wireframe mode.\n"); + wireframe = 1; + } else if (!strcmp(p, "wireframe") || !strcmp(p, "wf")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, wireframe); + goto qry; + } + rfbLog("remote_cmd: enabling -wireframe mode.\n"); + wireframe = 1; + } else if (!strcmp(p, "nowireframe") || !strcmp(p, "nowf")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !wireframe); + goto qry; + } + rfbLog("remote_cmd: enabling -nowireframe mode.\n"); + wireframe = 0; + + } else if (strstr(p, "wirecopyrect") == p) { + COLON_CHECK("wirecopyrect:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(wireframe_copyrect)); + goto qry; + } + p += strlen("wirecopyrect:"); + + set_wirecopyrect_mode(p); + rfbLog("remote_cmd: changed -wirecopyrect mode " + "to: %s\n", NONUL(wireframe_copyrect)); + got_wirecopyrect = 1; + } else if (strstr(p, "wcr") == p) { + COLON_CHECK("wcr:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(wireframe_copyrect)); + goto qry; + } + p += strlen("wcr:"); + + set_wirecopyrect_mode(p); + rfbLog("remote_cmd: changed -wirecopyrect mode " + "to: %s\n", NONUL(wireframe_copyrect)); + got_wirecopyrect = 1; + } else if (!strcmp(p, "nowirecopyrect") || !strcmp(p, "nowcr")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%s", p, + NONUL(wireframe_copyrect)); + goto qry; + } + + set_wirecopyrect_mode("never"); + rfbLog("remote_cmd: changed -wirecopyrect mode " + "to: %s\n", NONUL(wireframe_copyrect)); + + } else if (strstr(p, "scr_area") == p) { + COLON_CHECK("scr_area:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + scrollcopyrect_min_area); + goto qry; + } + p += strlen("scr_area:"); + + scrollcopyrect_min_area = atoi(p); + 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_term") == p) { + char *s = scroll_term_str; + if (!s || *s == '\0') s = scroll_term_str0; + COLON_CHECK("scr_term:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s)); + goto qry; + } + p += strlen("scr_term:"); + if (scroll_term_str) { + free(scroll_term_str); + } + + scroll_term_str = strdup(p); + rfbLog("remote_cmd: changed -scr_term to: %s\n", + scroll_term_str); + initialize_scroll_term(); + + } else if (strstr(p, "scr_keyrepeat") == p) { + char *s = max_keyrepeat_str; + if (!s || *s == '\0') s = max_keyrepeat_str0; + COLON_CHECK("scr_keyrepeat:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s)); + goto qry; + } + p += strlen("scr_keyrepeat:"); + if (max_keyrepeat_str) { + free(max_keyrepeat_str); + } + + max_keyrepeat_str = strdup(p); + rfbLog("remote_cmd: changed -scr_keyrepeat to: %s\n", + max_keyrepeat_str); + initialize_max_keyrepeat(); + + } 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) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(scroll_copyrect)); + goto qry; + } + p += strlen("scrollcopyrect:"); + + set_scrollcopyrect_mode(p); + rfbLog("remote_cmd: changed -scrollcopyrect mode " + "to: %s\n", NONUL(scroll_copyrect)); + got_scrollcopyrect = 1; + } 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, + NONUL(scroll_copyrect)); + goto qry; + } + p += strlen("scr:"); + + set_scrollcopyrect_mode(p); + rfbLog("remote_cmd: changed -scrollcopyrect mode " + "to: %s\n", NONUL(scroll_copyrect)); + got_scrollcopyrect = 1; + } else if (!strcmp(p, "noscrollcopyrect") || !strcmp(p, "noscr")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%s", p, + NONUL(scroll_copyrect)); + goto qry; + } + + set_scrollcopyrect_mode("never"); + rfbLog("remote_cmd: changed -scrollcopyrect mode " + "to: %s\n", NONUL(scroll_copyrect)); + + } else if (strstr(p, "fixscreen") == p) { + COLON_CHECK("fixscreen:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(screen_fixup_str)); + goto qry; + } + p += strlen("fixscreen:"); + if (screen_fixup_str) { + free(screen_fixup_str); + } + screen_fixup_str = strdup(p); + parse_fixscreen(); + rfbLog("remote_cmd: set -fixscreen %s.\n", + NONUL(screen_fixup_str)); + + } else if (!strcmp(p, "noxrecord")) { + int orig = noxrecord; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, noxrecord); + goto qry; + } + noxrecord = 1; + rfbLog("set noxrecord to: %d\n", noxrecord); + if (orig != noxrecord) { + shutdown_xrecord(); + } + } else if (!strcmp(p, "xrecord")) { + int orig = noxrecord; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !noxrecord); + goto qry; + } + noxrecord = 0; + rfbLog("set noxrecord to: %d\n", noxrecord); + if (orig != noxrecord) { + initialize_xrecord(); + } + } else if (!strcmp(p, "reset_record")) { + NOTAPP + if (use_xrecord) { + rfbLog("resetting RECORD\n"); + check_xrecord_reset(1); + } else { + rfbLog("RECORD is disabled, not resetting.\n"); + } + + } else if (strstr(p, "pointer_mode") == p) { + int pm; + COLON_CHECK("pointer_mode:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, pointer_mode); + goto qry; + } + p += strlen("pointer_mode:"); + pm = atoi(p); + if (pm < 0 || pm > pointer_mode_max) { + rfbLog("remote_cmd: pointer_mode out of range:" + " 1-%d: %d\n", pointer_mode_max, pm); + } else { + rfbLog("remote_cmd: setting pointer_mode %d\n", pm); + pointer_mode = pm; + } + } else if (strstr(p, "pm") == p) { + int pm; + COLON_CHECK("pm:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, pointer_mode); + goto qry; + } + p += strlen("pm:"); + pm = atoi(p); + if (pm < 0 || pm > pointer_mode_max) { + rfbLog("remote_cmd: pointer_mode out of range:" + " 1-%d: %d\n", pointer_mode_max, pm); + } else { + rfbLog("remote_cmd: setting pointer_mode %d\n", pm); + pointer_mode = pm; + } + + } else if (strstr(p, "input_skip") == p) { + int is; + COLON_CHECK("input_skip:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, ui_skip); + goto qry; + } + p += strlen("input_skip:"); + is = atoi(p); + rfbLog("remote_cmd: setting input_skip %d\n", is); + ui_skip = is; + + } else if (strstr(p, "input") == p) { + int doit = 1; + COLON_CHECK("input:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(allowed_input_str)); + goto qry; + } + p += strlen("input:"); + if (allowed_input_str && !strcmp(p, allowed_input_str)) { + doit = 0; + } + rfbLog("remote_cmd: setting input %s\n", p); + if (allowed_input_str) free(allowed_input_str); + if (*p == '\0') { + allowed_input_str = NULL; + } else { + allowed_input_str = strdup(p); + } + if (doit) { + initialize_allowed_input(); + } + } else if (strstr(p, "client_input") == p) { + NOTAPP + COLON_CHECK("client_input:") + p += strlen("client_input:"); + set_client_input(p); + + } else if (strstr(p, "speeds") == p) { + COLON_CHECK("speeds:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(speeds_str)); + goto qry; + } + p += strlen("speeds:"); + if (speeds_str) free(speeds_str); + speeds_str = strdup(p); + + rfbLog("remote_cmd: setting -speeds to:\n\t'%s'\n", p); + initialize_speeds(); + + } else if (strstr(p, "wmdt") == p) { + COLON_CHECK("wmdt:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(wmdt_str)); + goto qry; + } + p += strlen("wmdt:"); + if (wmdt_str) free(wmdt_str); + wmdt_str = strdup(p); + + rfbLog("remote_cmd: setting -wmdt to: %s\n", p); + + } else if (!strcmp(p, "debug_pointer") || !strcmp(p, "dp")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_pointer); + goto qry; + } + rfbLog("remote_cmd: turning on debug_pointer.\n"); + debug_pointer = 1; + } else if (!strcmp(p, "nodebug_pointer") || !strcmp(p, "nodp")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_pointer); + goto qry; + } + rfbLog("remote_cmd: turning off debug_pointer.\n"); + debug_pointer = 0; + + } else if (!strcmp(p, "debug_keyboard") || !strcmp(p, "dk")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_keyboard); + goto qry; + } + rfbLog("remote_cmd: turning on debug_keyboard.\n"); + debug_keyboard = 1; + } else if (!strcmp(p, "nodebug_keyboard") || !strcmp(p, "nodk")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_keyboard); + goto qry; + } + rfbLog("remote_cmd: turning off debug_keyboard.\n"); + debug_keyboard = 0; + + } else if (strstr(p, "deferupdate") == p) { + int d; + COLON_CHECK("deferupdate:") + if (query) { + if (!screen) { + d = defer_update; + } else { + d = screen->deferUpdateTime; + } + snprintf(buf, bufn, "ans=%s%s%d", p, co, d); + goto qry; + } + p += strlen("deferupdate:"); + d = atoi(p); + if (d < 0) d = 0; + rfbLog("remote_cmd: setting defer to %d ms.\n", d); + screen->deferUpdateTime = d; + got_defer = 1; + + } else if (strstr(p, "defer") == p) { + int d; + COLON_CHECK("defer:") + if (query) { + if (!screen) { + d = defer_update; + } else { + d = screen->deferUpdateTime; + } + snprintf(buf, bufn, "ans=%s%s%d", p, co, d); + goto qry; + } + p += strlen("defer:"); + d = atoi(p); + if (d < 0) d = 0; + rfbLog("remote_cmd: setting defer to %d ms.\n", d); + screen->deferUpdateTime = d; + got_defer = 1; + + } else if (strstr(p, "wait_ui") == p) { + double w; + COLON_CHECK("wait_ui:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%.2f", p, co, wait_ui); + goto qry; + } + p += strlen("wait_ui:"); + w = atof(p); + if (w <= 0) w = 1.0; + rfbLog("remote_cmd: setting wait_ui factor %.2f -> %.2f\n", + wait_ui, w); + wait_ui = w; + + } else if (!strcmp(p, "wait_bog")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, wait_bog); + goto qry; + } + wait_bog = 1; + rfbLog("remote_cmd: setting wait_bog to %d\n", wait_bog); + } else if (!strcmp(p, "nowait_bog")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !wait_bog); + goto qry; + } + wait_bog = 0; + rfbLog("remote_cmd: setting wait_bog to %d\n", wait_bog); + + } else if (strstr(p, "slow_fb") == p) { + double w; + COLON_CHECK("slow_fb:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%.2f", p, co, slow_fb); + goto qry; + } + p += strlen("slow_fb:"); + w = atof(p); + if (w <= 0) w = 0.0; + rfbLog("remote_cmd: setting slow_fb factor %.2f -> %.2f\n", + slow_fb, w); + slow_fb = w; + + } else if (strstr(p, "wait") == p) { + int w; + COLON_CHECK("wait:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, waitms); + goto qry; + } + p += strlen("wait:"); + w = atoi(p); + if (w < 0) w = 0; + rfbLog("remote_cmd: setting wait %d -> %d ms.\n", waitms, w); + waitms = w; + + } else if (strstr(p, "readtimeout") == p) { + int w, orig = rfbMaxClientWait; + COLON_CHECK("readtimeout:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + rfbMaxClientWait/1000); + goto qry; + } + p += strlen("readtimeout:"); + w = atoi(p) * 1000; + if (w <= 0) w = 0; + rfbLog("remote_cmd: setting rfbMaxClientWait %d -> " + "%d msec.\n", orig, w); + rfbMaxClientWait = w; + + } else if (!strcmp(p, "nap")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, take_naps); + goto qry; + } + rfbLog("remote_cmd: turning on nap mode.\n"); + take_naps = 1; + } else if (!strcmp(p, "nonap")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !take_naps); + goto qry; + } + rfbLog("remote_cmd: turning off nap mode.\n"); + take_naps = 0; + + } else if (strstr(p, "sb") == p) { + int w; + COLON_CHECK("sb:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, screen_blank); + goto qry; + } + p += strlen("sb:"); + w = atoi(p); + if (w < 0) w = 0; + rfbLog("remote_cmd: setting screen_blank %d -> %d sec.\n", + screen_blank, w); + screen_blank = w; + } else if (strstr(p, "screen_blank") == p) { + int w; + COLON_CHECK("screen_blank:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, screen_blank); + goto qry; + } + p += strlen("screen_blank:"); + w = atoi(p); + if (w < 0) w = 0; + rfbLog("remote_cmd: setting screen_blank %d -> %d sec.\n", + screen_blank, w); + screen_blank = w; + + } else if (strstr(p, "fs") == p) { + COLON_CHECK("fs:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%f", p, co, fs_frac); + goto qry; + } + p += strlen("fs:"); + fs_frac = atof(p); + rfbLog("remote_cmd: setting -fs frac to %f\n", fs_frac); + + } else if (strstr(p, "gaps") == p) { + int g; + COLON_CHECK("gaps:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, gaps_fill); + goto qry; + } + p += strlen("gaps:"); + g = atoi(p); + if (g < 0) g = 0; + rfbLog("remote_cmd: setting gaps_fill %d -> %d.\n", + gaps_fill, g); + gaps_fill = g; + } else if (strstr(p, "grow") == p) { + int g; + COLON_CHECK("grow:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, grow_fill); + goto qry; + } + p += strlen("grow:"); + g = atoi(p); + if (g < 0) g = 0; + rfbLog("remote_cmd: setting grow_fill %d -> %d.\n", + grow_fill, g); + grow_fill = g; + } else if (strstr(p, "fuzz") == p) { + int f; + COLON_CHECK("fuzz:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, tile_fuzz); + goto qry; + } + p += strlen("fuzz:"); + f = atoi(p); + if (f < 0) f = 0; + rfbLog("remote_cmd: setting tile_fuzz %d -> %d.\n", + tile_fuzz, f); + grow_fill = f; + + } else if (!strcmp(p, "snapfb")) { + int orig = use_snapfb; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, use_snapfb); + goto qry; + } + rfbLog("remote_cmd: turning on snapfb mode.\n"); + use_snapfb = 1; + if (orig != use_snapfb) { + do_new_fb(1); + } + } else if (!strcmp(p, "nosnapfb")) { + int orig = use_snapfb; + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !use_snapfb); + goto qry; + } + rfbLog("remote_cmd: turning off snapfb mode.\n"); + use_snapfb = 0; + if (orig != use_snapfb) { + do_new_fb(1); + } + + } else if (strstr(p, "rawfb") == p) { + COLON_CHECK("rawfb:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(raw_fb_str)); + goto qry; + } + p += strlen("rawfb:"); + if (raw_fb_str) free(raw_fb_str); + raw_fb_str = strdup(p); + if (safe_remote_only && strstr(p, "setup:") == p) { /* skip-cmd-list */ + /* n.b. we still allow filename, shm, of rawfb */ + fprintf(stderr, "unsafe rawfb setup: %s\n", p); + exit(1); + } + + rfbLog("remote_cmd: setting -rawfb to:\n\t'%s'\n", p); + + if (*raw_fb_str == '\0') { + free(raw_fb_str); + raw_fb_str = NULL; + rfbLog("restoring per-rawfb settings...\n"); + set_raw_fb_params(1); + } + rfbLog("hang on tight, here we go...\n"); + do_new_fb(1); + + } else if (strstr(p, "progressive") == p) { + int f; + COLON_CHECK("progressive:") + if (query) { + if (!screen) { + f = 0; + } else { + f = screen->progressiveSliceHeight; + } + snprintf(buf, bufn, "ans=%s%s%d", p, co, f); + goto qry; + } + p += strlen("progressive:"); + f = atoi(p); + if (f < 0) f = 0; + rfbLog("remote_cmd: setting progressive %d -> %d.\n", + screen->progressiveSliceHeight, f); + screen->progressiveSliceHeight = f; + + } else if (strstr(p, "rfbport") == p) { + int rp, orig = screen ? screen->port : 5900; + COLON_CHECK("rfbport:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, orig); + goto qry; + } + p += strlen("rfbport:"); + rp = atoi(p); + reset_rfbport(orig, rp); + + } else if (!strcmp(p, "http")) { + if (query) { + int ls = screen ? screen->httpListenSock : -1; + snprintf(buf, bufn, "ans=%s:%d", p, (ls > -1)); + goto qry; + } + if (screen->httpListenSock > -1) { + rfbLog("already listening for http connections.\n"); + } else { + rfbLog("turning on listening for http connections.\n"); + if (check_httpdir()) { + http_connections(1); + } + } + } else if (!strcmp(p, "nohttp")) { + if (query) { + int ls = screen ? screen->httpListenSock : -1; + snprintf(buf, bufn, "ans=%s:%d", p, !(ls > -1)); + goto qry; + } + if (screen->httpListenSock < 0) { + rfbLog("already not listening for http connections.\n"); + } else { + rfbLog("turning off listening for http connections.\n"); + if (check_httpdir()) { + http_connections(0); + } + } + + } else if (strstr(p, "httpport") == p) { + int hp, orig = screen ? screen->httpPort : 0; + COLON_CHECK("httpport:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, orig); + goto qry; + } + p += strlen("httpport:"); + hp = atoi(p); + reset_httpport(orig, hp); + + } else if (strstr(p, "httpdir") == p) { + COLON_CHECK("httpdir:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%s", p, co, + NONUL(http_dir)); + goto qry; + } + p += strlen("httpdir:"); + if (http_dir && !strcmp(http_dir, p)) { + rfbLog("no change in httpdir: %s\n", http_dir); + } else { + if (http_dir) { + free(http_dir); + } + http_dir = strdup(p); + http_connections(0); + if (*p != '\0') { + http_connections(1); + } + } + + } else if (!strcmp(p, "enablehttpproxy")) { + if (query) { + int ht = screen ? screen->httpEnableProxyConnect : 0; + snprintf(buf, bufn, "ans=%s:%d", p, ht != 0); + goto qry; + } + rfbLog("turning on enablehttpproxy.\n"); + screen->httpEnableProxyConnect = 1; + } else if (!strcmp(p, "noenablehttpproxy")) { + if (query) { + int ht = screen ? screen->httpEnableProxyConnect : 0; + snprintf(buf, bufn, "ans=%s:%d", p, ht == 0); + goto qry; + } + rfbLog("turning off enablehttpproxy.\n"); + screen->httpEnableProxyConnect = 0; + + } else if (!strcmp(p, "alwaysshared")) { + if (query) { + int t = screen ? screen->alwaysShared : 0; + snprintf(buf, bufn, "ans=%s:%d", p, t != 0); + goto qry; + } + rfbLog("turning on alwaysshared.\n"); + screen->alwaysShared = 1; + } else if (!strcmp(p, "noalwaysshared")) { + if (query) { + int t = screen ? screen->alwaysShared : 0; + snprintf(buf, bufn, "ans=%s:%d", p, t == 0); + goto qry; + } + rfbLog("turning off alwaysshared.\n"); + screen->alwaysShared = 0; + + } else if (!strcmp(p, "nevershared")) { + if (query) { + int t = screen ? screen->neverShared : 1; + snprintf(buf, bufn, "ans=%s:%d", p, t != 0); + goto qry; + } + rfbLog("turning on nevershared.\n"); + screen->neverShared = 1; + } else if (!strcmp(p, "noalwaysshared")) { + if (query) { + int t = screen ? screen->neverShared : 1; + snprintf(buf, bufn, "ans=%s:%d", p, t == 0); + goto qry; + } + rfbLog("turning off nevershared.\n"); + screen->neverShared = 0; + + } else if (!strcmp(p, "dontdisconnect")) { + if (query) { + int t = screen ? screen->dontDisconnect : 1; + snprintf(buf, bufn, "ans=%s:%d", p, t != 0); + goto qry; + } + rfbLog("turning on dontdisconnect.\n"); + screen->dontDisconnect = 1; + } else if (!strcmp(p, "nodontdisconnect")) { + if (query) { + int t = screen ? screen->dontDisconnect : 1; + snprintf(buf, bufn, "ans=%s:%d", p, t == 0); + goto qry; + } + rfbLog("turning off dontdisconnect.\n"); + screen->dontDisconnect = 0; + + } 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, + NONUL(rfb_desktop_name)); + goto qry; + } + p += strlen("desktop:"); + if (rfb_desktop_name) { + free(rfb_desktop_name); + } + rfb_desktop_name = strdup(p); + screen->desktopName = rfb_desktop_name; + rfbLog("remote_cmd: setting desktop name to %s\n", + rfb_desktop_name); + + } else if (!strcmp(p, "debug_xevents")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_xevents); + goto qry; + } + debug_xevents = 1; + rfbLog("set debug_xevents to: %d\n", debug_xevents); + } else if (!strcmp(p, "nodebug_xevents")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_xevents); + goto qry; + } + debug_xevents = 0; + rfbLog("set debug_xevents to: %d\n", debug_xevents); + } else if (strstr(p, "debug_xevents") == p) { + COLON_CHECK("debug_xevents:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, debug_xevents); + goto qry; + } + p += strlen("debug_xevents:"); + debug_xevents = atoi(p); + rfbLog("set debug_xevents to: %d\n", debug_xevents); + + } else if (!strcmp(p, "debug_xdamage")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_xdamage); + goto qry; + } + debug_xdamage = 1; + rfbLog("set debug_xdamage to: %d\n", debug_xdamage); + } else if (!strcmp(p, "nodebug_xdamage")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_xdamage); + goto qry; + } + debug_xdamage = 0; + rfbLog("set debug_xdamage to: %d\n", debug_xdamage); + } else if (strstr(p, "debug_xdamage") == p) { + COLON_CHECK("debug_xdamage:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, debug_xdamage); + goto qry; + } + p += strlen("debug_xdamage:"); + debug_xdamage = atoi(p); + rfbLog("set debug_xdamage to: %d\n", debug_xdamage); + + } else if (!strcmp(p, "debug_wireframe")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_wireframe); + goto qry; + } + debug_wireframe = 1; + rfbLog("set debug_wireframe to: %d\n", debug_wireframe); + } else if (!strcmp(p, "nodebug_wireframe")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_wireframe); + goto qry; + } + debug_wireframe = 0; + rfbLog("set debug_wireframe to: %d\n", debug_wireframe); + } else if (strstr(p, "debug_wireframe") == p) { + COLON_CHECK("debug_wireframe:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + debug_wireframe); + goto qry; + } + p += strlen("debug_wireframe:"); + debug_wireframe = atoi(p); + rfbLog("set debug_wireframe to: %d\n", debug_wireframe); + + } else if (!strcmp(p, "debug_scroll")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_scroll); + goto qry; + } + debug_scroll = 1; + rfbLog("set debug_scroll to: %d\n", debug_scroll); + } else if (!strcmp(p, "nodebug_scroll")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_scroll); + goto qry; + } + debug_scroll = 0; + rfbLog("set debug_scroll to: %d\n", debug_scroll); + } else if (strstr(p, "debug_scroll") == p) { + COLON_CHECK("debug_scroll:") + if (query) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, + debug_scroll); + goto qry; + } + p += strlen("debug_scroll:"); + 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, "debug_grabs")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, debug_grabs); + goto qry; + } + debug_grabs = 1; + rfbLog("set debug_grabs to: %d\n", debug_grabs); + } else if (!strcmp(p, "nodebug_grabs")) { + if (query) { + snprintf(buf, bufn, "ans=%s:%d", p, !debug_grabs); + goto qry; + } + debug_grabs = 0; + rfbLog("set debug_grabs to: %d\n", debug_grabs); + + } 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) { + snprintf(buf, bufn, "ans=%s%s%d", p, co, hack_val); + goto qry; + } + p += strlen("hack:"); + 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 (strstr(p, "client_info_sock") == p) { /* skip-cmd-list */ + NOTAPP + p += strlen("client_info_sock:"); + if (*p != '\0') { + start_client_info_sock(p); + } + + } else if (strstr(p, "noop") == p) { + NOTAPP + rfbLog("remote_cmd: noop\n"); + + } else if (icon_mode && !query && strstr(p, "passwd") == p) { /* skip-cmd-list */ + char **passwds_new = (char **) malloc(3*sizeof(char *)); + char **passwds_old = (char **) screen->authPasswdData; + + COLON_CHECK("passwd:") + p += strlen("passwd:"); + + passwds_new[0] = strdup(p); + + if (screen->authPasswdData && + screen->passwordCheck == rfbCheckPasswordByList) { + passwds_new[1] = passwds_old[1]; + } else { + passwds_new[1] = NULL; + screen->passwordCheck = rfbCheckPasswordByList; + } + passwds_new[2] = NULL; + + screen->authPasswdData = (void*) passwds_new; + if (*p == '\0') { + screen->authPasswdData = (void*) NULL; + } + rfbLog("remote_cmd: changed full access passwd.\n"); + + } else if (icon_mode && !query && strstr(p, "viewpasswd") == p) { /* skip-cmd-list */ + char **passwds_new = (char **) malloc(3*sizeof(char *)); + char **passwds_old = (char **) screen->authPasswdData; + + COLON_CHECK("viewpasswd:") + p += strlen("viewpasswd:"); + + passwds_new[1] = strdup(p); + + if (screen->authPasswdData && + screen->passwordCheck == rfbCheckPasswordByList) { + passwds_new[0] = passwds_old[0]; + } else { + char *tmp = (char *) malloc(4 + CHALLENGESIZE); + rfbRandomBytes((unsigned char*)tmp); + passwds_new[0] = tmp; + screen->passwordCheck = rfbCheckPasswordByList; + } + passwds_new[2] = NULL; + + if (*p == '\0') { + passwds_new[1] = NULL; + } + + screen->authPasswdData = (void*) passwds_new; + rfbLog("remote_cmd: changed view only passwd.\n"); + + } else if (strstr(p, "trayembed") == p) { /* skip-cmd-list */ + unsigned long id; + NOTAPP + + COLON_CHECK("trayembed:") + p += strlen("trayembed:"); + if (scan_hexdec(p, &id)) { + tray_request = (Window) id; + tray_unembed = 0; + rfbLog("remote_cmd: will try to embed 0x%x in" + " the system tray.\n", id); + } + } else if (strstr(p, "trayunembed") == p) { /* skip-cmd-list */ + unsigned long id; + NOTAPP + + COLON_CHECK("trayunembed:") + p += strlen("trayunembed:"); + if (scan_hexdec(p, &id)) { + tray_request = (Window) id; + tray_unembed = 1; + rfbLog("remote_cmd: will try to unembed 0x%x out" + " of the system tray.\n", id); + } + + + } else if (query) { + /* read-only variables that can only be queried: */ + + if (!strcmp(p, "display")) { + if (raw_fb) { + snprintf(buf, bufn, "aro=%s:rawfb:%p", + p, raw_fb_addr); + } else if (! dpy) { + snprintf(buf, bufn, "aro=%s:", p); + } else { + char *d; + d = DisplayString(dpy); + if (! d) d = "unknown"; + if (*d == ':') { + snprintf(buf, bufn, "aro=%s:%s%s", p, + this_host(), d); + } else { + snprintf(buf, bufn, "aro=%s:%s", p, d); + } + } + } else if (!strcmp(p, "vncdisplay")) { + snprintf(buf, bufn, "aro=%s:%s", p, + NONUL(vnc_desktop_name)); + } else if (!strcmp(p, "desktopname")) { + snprintf(buf, bufn, "aro=%s:%s", p, + NONUL(rfb_desktop_name)); + } else if (!strcmp(p, "guess_desktop")) { + snprintf(buf, bufn, "aro=%s:%s", p, + NONUL(guess_desktop())); + } else if (!strcmp(p, "http_url")) { + if (!screen) { + snprintf(buf, bufn, "aro=%s:", p); + } else if (screen->httpListenSock > -1) { + snprintf(buf, bufn, "aro=%s:http://%s:%d", p, + NONUL(screen->thisHost), screen->httpPort); + } else { + snprintf(buf, bufn, "aro=%s:%s", p, + "http_not_active"); + } + } else if (!strcmp(p, "auth") || !strcmp(p, "xauth")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(auth_file)); + } else if (!strcmp(p, "users")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(users_list)); + } else if (!strcmp(p, "rootshift")) { + snprintf(buf, bufn, "aro=%s:%d", p, rootshift); + } else if (!strcmp(p, "clipshift")) { + snprintf(buf, bufn, "aro=%s:%d", p, clipshift); + } else if (!strcmp(p, "scale_str")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(scale_str)); + } else if (!strcmp(p, "scaled_x")) { + snprintf(buf, bufn, "aro=%s:%d", p, scaled_x); + } else if (!strcmp(p, "scaled_y")) { + snprintf(buf, bufn, "aro=%s:%d", p, scaled_y); + } else if (!strcmp(p, "scale_numer")) { + snprintf(buf, bufn, "aro=%s:%d", p, scale_numer); + } else if (!strcmp(p, "scale_denom")) { + snprintf(buf, bufn, "aro=%s:%d", p, scale_denom); + } else if (!strcmp(p, "scale_fac")) { + snprintf(buf, bufn, "aro=%s:%f", p, scale_fac); + } else if (!strcmp(p, "scaling_blend")) { + snprintf(buf, bufn, "aro=%s:%d", p, scaling_blend); + } else if (!strcmp(p, "scaling_nomult4")) { + snprintf(buf, bufn, "aro=%s:%d", p, scaling_nomult4); + } else if (!strcmp(p, "scaling_pad")) { + snprintf(buf, bufn, "aro=%s:%d", p, scaling_pad); + } else if (!strcmp(p, "scaling_interpolate")) { + snprintf(buf, bufn, "aro=%s:%d", p, + scaling_interpolate); + } else if (!strcmp(p, "inetd")) { + snprintf(buf, bufn, "aro=%s:%d", p, inetd); + } else if (!strcmp(p, "privremote")) { + snprintf(buf, bufn, "aro=%s:%d", p, priv_remote); + } else if (!strcmp(p, "unsafe")) { + snprintf(buf, bufn, "aro=%s:%d", p, !safe_remote_only); + } else if (!strcmp(p, "safer")) { + snprintf(buf, bufn, "aro=%s:%d", p, more_safe); + } else if (!strcmp(p, "nocmds")) { + snprintf(buf, bufn, "aro=%s:%d", p, no_external_cmds); + } else if (!strcmp(p, "passwdfile")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(passwdfile)); + } else if (!strcmp(p, "using_shm")) { + snprintf(buf, bufn, "aro=%s:%d", p, !using_shm); + } else if (!strcmp(p, "logfile") || !strcmp(p, "o")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(logfile)); + } else if (!strcmp(p, "flag")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(flagfile)); + } else if (!strcmp(p, "rc")) { + char *s = rc_rcfile; + if (rc_rcfile_default) { + s = NULL; + } + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(s)); + } else if (!strcmp(p, "norc")) { + snprintf(buf, bufn, "aro=%s:%d", p, got_norc); + } else if (!strcmp(p, "h") || !strcmp(p, "help") || + !strcmp(p, "V") || !strcmp(p, "version") || + !strcmp(p, "lastmod")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(lastmod)); + } else if (!strcmp(p, "bg")) { + snprintf(buf, bufn, "aro=%s:%d", p, opts_bg); + } else if (!strcmp(p, "sigpipe")) { + snprintf(buf, bufn, "aro=%s:%s", p, NONUL(sigpipe)); + } else if (!strcmp(p, "threads")) { + snprintf(buf, bufn, "aro=%s:%d", p, use_threads); + } else if (!strcmp(p, "readrate")) { + snprintf(buf, bufn, "aro=%s:%d", p, get_read_rate()); + } else if (!strcmp(p, "netrate")) { + snprintf(buf, bufn, "aro=%s:%d", p, get_net_rate()); + } else if (!strcmp(p, "netlatency")) { + snprintf(buf, bufn, "aro=%s:%d", p, get_net_latency()); + } else if (!strcmp(p, "pipeinput")) { + snprintf(buf, bufn, "aro=%s:%s", p, + NONUL(pipeinput_str)); + } else if (!strcmp(p, "clients")) { + char *str = list_clients(); + snprintf(buf, bufn, "aro=%s:%s", p, str); + free(str); + } else if (!strcmp(p, "client_count")) { + snprintf(buf, bufn, "aro=%s:%d", p, client_count); + } else if (!strcmp(p, "pid")) { + snprintf(buf, bufn, "aro=%s:%d", p, (int) getpid()); + } else if (!strcmp(p, "ext_xtest")) { + snprintf(buf, bufn, "aro=%s:%d", p, xtest_present); + } else if (!strcmp(p, "ext_xtrap")) { + snprintf(buf, bufn, "aro=%s:%d", p, xtrap_present); + } else if (!strcmp(p, "ext_xrecord")) { + snprintf(buf, bufn, "aro=%s:%d", p, xrecord_present); + } else if (!strcmp(p, "ext_xkb")) { + snprintf(buf, bufn, "aro=%s:%d", p, xkb_present); + } else if (!strcmp(p, "ext_xshm")) { + snprintf(buf, bufn, "aro=%s:%d", p, xshm_present); + } else if (!strcmp(p, "ext_xinerama")) { + snprintf(buf, bufn, "aro=%s:%d", p, xinerama_present); + } else if (!strcmp(p, "ext_overlay")) { + snprintf(buf, bufn, "aro=%s:%d", p, overlay_present); + } else if (!strcmp(p, "ext_xfixes")) { + snprintf(buf, bufn, "aro=%s:%d", p, xfixes_present); + } else if (!strcmp(p, "ext_xdamage")) { + snprintf(buf, bufn, "aro=%s:%d", p, xdamage_present); + } else if (!strcmp(p, "ext_xrandr")) { + snprintf(buf, bufn, "aro=%s:%d", p, xrandr_present); + } else if (!strcmp(p, "rootwin")) { + snprintf(buf, bufn, "aro=%s:0x%x", p, + (unsigned int) rootwin); + } else if (!strcmp(p, "num_buttons")) { + snprintf(buf, bufn, "aro=%s:%d", p, num_buttons); + } else if (!strcmp(p, "button_mask")) { + snprintf(buf, bufn, "aro=%s:%d", p, button_mask); + } else if (!strcmp(p, "mouse_x")) { + snprintf(buf, bufn, "aro=%s:%d", p, cursor_x); + } else if (!strcmp(p, "mouse_y")) { + snprintf(buf, bufn, "aro=%s:%d", p, cursor_y); + } else if (!strcmp(p, "bpp")) { + snprintf(buf, bufn, "aro=%s:%d", p, bpp); + } else if (!strcmp(p, "depth")) { + snprintf(buf, bufn, "aro=%s:%d", p, depth); + } else if (!strcmp(p, "indexed_color")) { + snprintf(buf, bufn, "aro=%s:%d", p, indexed_color); + } else if (!strcmp(p, "dpy_x")) { + snprintf(buf, bufn, "aro=%s:%d", p, dpy_x); + } else if (!strcmp(p, "dpy_y")) { + snprintf(buf, bufn, "aro=%s:%d", p, dpy_y); + } else if (!strcmp(p, "wdpy_x")) { + snprintf(buf, bufn, "aro=%s:%d", p, wdpy_x); + } else if (!strcmp(p, "wdpy_y")) { + snprintf(buf, bufn, "aro=%s:%d", p, wdpy_y); + } else if (!strcmp(p, "off_x")) { + snprintf(buf, bufn, "aro=%s:%d", p, off_x); + } else if (!strcmp(p, "off_y")) { + snprintf(buf, bufn, "aro=%s:%d", p, off_y); + } else if (!strcmp(p, "cdpy_x")) { + snprintf(buf, bufn, "aro=%s:%d", p, cdpy_x); + } else if (!strcmp(p, "cdpy_y")) { + snprintf(buf, bufn, "aro=%s:%d", p, cdpy_y); + } else if (!strcmp(p, "coff_x")) { + snprintf(buf, bufn, "aro=%s:%d", p, coff_x); + } else if (!strcmp(p, "coff_y")) { + snprintf(buf, bufn, "aro=%s:%d", p, coff_y); + } else if (!strcmp(p, "rfbauth")) { + NOTAPPRO + } else if (!strcmp(p, "passwd")) { + NOTAPPRO + } else if (!strcmp(p, "viewpasswd")) { + NOTAPPRO + } else { + NOTAPP + } + goto qry; + } else { + char tmp[100]; + NOTAPP + rfbLog("remote_cmd: warning unknown\n"); + strncpy(tmp, p, 90); + rfbLog("command \"%s\"\n", tmp); + goto done; + } + + done: + + if (*buf == '\0') { + sprintf(buf, "ack=1"); + } + + qry: + + if (stringonly) { + return strdup(buf); + } else if (client_connect_file) { + FILE *out = fopen(client_connect_file, "w"); + if (out != NULL) { + fprintf(out, "%s\n", buf); + fclose(out); + usleep(20*1000); + } + } else { + if (dpy) { /* raw_fb hack */ + set_vnc_connect_prop(buf); + XFlush(dpy); + } + } +#endif + return NULL; +} + + diff --git a/x11vnc/remote.h b/x11vnc/remote.h new file mode 100644 index 0000000..225f418 --- /dev/null +++ b/x11vnc/remote.h @@ -0,0 +1,15 @@ +#ifndef _X11VNC_REMOTE_H +#define _X11VNC_REMOTE_H + +/* -- remote.h -- */ + +extern int send_remote_cmd(char *cmd, int query, int wait); +extern int do_remote_query(char *remote_cmd, char *query_cmd, int remote_sync, + int qdefault); +extern void check_black_fb(void); +extern int check_httpdir(void); +extern void http_connections(int on); +extern int remote_control_access_ok(void); +extern char *process_remote_cmd(char *cmd, int stringonly); + +#endif /* _X11VNC_REMOTE_H */ diff --git a/x11vnc/scan.c b/x11vnc/scan.c new file mode 100644 index 0000000..ad5f032 --- /dev/null +++ b/x11vnc/scan.c @@ -0,0 +1,2622 @@ +/* -- scan.c -- */ + +#include "x11vnc.h" +#include "xinerama.h" +#include "xwrappers.h" +#include "xdamage.h" +#include "xrandr.h" +#include "win_utils.h" +#include "screen.h" +#include "pointer.h" +#include "cleanup.h" + +/* + * routines for scanning and reading the X11 display for changes, and + * for doing all the tile work (shm, etc). + */ +void initialize_tiles(void); +void free_tiles(void); +void shm_delete(XShmSegmentInfo *shm); +void shm_clean(XShmSegmentInfo *shm, XImage *xim); +void initialize_polling_images(void); +void scale_rect(double factor, int blend, int interpolate, int Bpp, + char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line, + int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark); +void scale_and_mark_rect(int X1, int Y1, int X2, int Y2); +void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force); +int copy_screen(void); +int copy_snap(void); +void nap_sleep(int ms, int split); +void set_offset(void); +int scan_for_updates(int count_only); + + +static void set_fs_factor(int max); +static char *flip_ximage_byte_order(XImage *xim); +static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, + char *name); +static void create_tile_hint(int x, int y, int tw, int th, hint_t *hint); +static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint); +static void save_hint(hint_t hint, int loc); +static void hint_updates(void); +static void mark_hint(hint_t hint); +static int copy_tiles(int tx, int ty, int nt); +static int copy_all_tiles(void); +static int copy_all_tile_runs(void); +static int copy_tiles_backward_pass(void); +static int copy_tiles_additional_pass(void); +static int gap_try(int x, int y, int *run, int *saw, int along_x); +static int fill_tile_gaps(void); +static int island_try(int x, int y, int u, int v, int *run); +static int grow_islands(void); +static void blackout_regions(void); +static void nap_set(int tile_cnt); +static void nap_check(int tile_cnt); +static void ping_clients(int tile_cnt); +static int blackout_line_skip(int n, int x, int y, int rescan, + int *tile_count); +static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src, + int w, int pixelsize); +static int scan_display(int ystart, int rescan); + + +/* array to hold the hints: */ +static hint_t *hint_list; + +/* nap state */ +int nap_ok = 0; +static int nap_diff_count = 0; + +static int scan_count = 0; /* indicates which scan pattern we are on */ +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; +} region_t; + +/* array to hold the tiles region_t-s. */ +static region_t *tile_region; + + + + +/* + * setup tile numbers and allocate the tile and hint arrays: + */ +void initialize_tiles(void) { + + ntiles_x = (dpy_x - 1)/tile_x + 1; + ntiles_y = (dpy_y - 1)/tile_y + 1; + ntiles = ntiles_x * ntiles_y; + + tile_has_diff = (unsigned char *) + malloc((size_t) (ntiles * sizeof(unsigned char))); + tile_has_xdamage_diff = (unsigned char *) + malloc((size_t) (ntiles * sizeof(unsigned char))); + tile_row_has_xdamage_diff = (unsigned char *) + malloc((size_t) (ntiles_y * sizeof(unsigned char))); + tile_tried = (unsigned char *) + malloc((size_t) (ntiles * sizeof(unsigned char))); + tile_copied = (unsigned char *) + malloc((size_t) (ntiles * sizeof(unsigned char))); + tile_blackout = (tile_blackout_t *) + malloc((size_t) (ntiles * sizeof(tile_blackout_t))); + tile_region = (region_t *) malloc((size_t) (ntiles * sizeof(region_t))); + + tile_row = (XImage **) + malloc((size_t) ((ntiles_x + 1) * sizeof(XImage *))); + tile_row_shm = (XShmSegmentInfo *) + malloc((size_t) ((ntiles_x + 1) * sizeof(XShmSegmentInfo))); + + /* there will never be more hints than tiles: */ + hint_list = (hint_t *) malloc((size_t) (ntiles * sizeof(hint_t))); +} + +void free_tiles(void) { + if (tile_has_diff) { + free(tile_has_diff); + tile_has_diff = NULL; + } + if (tile_has_xdamage_diff) { + free(tile_has_xdamage_diff); + tile_has_xdamage_diff = NULL; + } + if (tile_row_has_xdamage_diff) { + free(tile_row_has_xdamage_diff); + tile_row_has_xdamage_diff = NULL; + } + if (tile_tried) { + free(tile_tried); + tile_tried = NULL; + } + if (tile_copied) { + free(tile_copied); + tile_copied = NULL; + } + if (tile_blackout) { + free(tile_blackout); + tile_blackout = NULL; + } + if (tile_region) { + free(tile_region); + tile_region = NULL; + } + if (tile_row) { + free(tile_row); + tile_row = NULL; + } + if (tile_row_shm) { + free(tile_row_shm); + tile_row_shm = NULL; + } + if (hint_list) { + free(hint_list); + hint_list = NULL; + } +} + +/* + * silly function to factor dpy_y until fullscreen shm is not bigger than max. + * should always work unless dpy_y is a large prime or something... under + * failure fs_factor remains 0 and no fullscreen updates will be tried. + */ +static int fs_factor = 0; + +static void set_fs_factor(int max) { + int f, fac = 1, n = dpy_y; + + fs_factor = 0; + if ((bpp/8) * dpy_x * dpy_y <= max) { + fs_factor = 1; + return; + } + for (f=2; f <= 101; f++) { + while (n % f == 0) { + n = n / f; + fac = fac * f; + if ( (bpp/8) * dpy_x * (dpy_y/fac) <= max ) { + fs_factor = fac; + return; + } + } + } +} + +static char *flip_ximage_byte_order(XImage *xim) { + char *order; + if (xim->byte_order == LSBFirst) { + order = "MSBFirst"; + xim->byte_order = MSBFirst; + xim->bitmap_bit_order = MSBFirst; + } else { + order = "LSBFirst"; + xim->byte_order = LSBFirst; + xim->bitmap_bit_order = LSBFirst; + } + return order; +} + +/* + * set up an XShm image, or if not using shm just create the XImage. + */ +static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, + char *name) { + + XImage *xim; + static int reported_flip = 0; + + shm->shmid = -1; + shm->shmaddr = (char *) -1; + *ximg_ptr = NULL; + + if (nofb) { + return 1; + } + + X_LOCK; + + if (! using_shm) { + /* we only need the XImage created */ + xim = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, + 0, NULL, w, h, raw_fb ? 32 : BitmapPad(dpy), 0); + + X_UNLOCK; + + if (xim == NULL) { + rfbErr("XCreateImage(%s) failed.\n", name); + if (quiet) { + fprintf(stderr, "XCreateImage(%s) failed.\n", + name); + } + return 0; + } + xim->data = (char *) malloc(xim->bytes_per_line * xim->height); + if (xim->data == NULL) { + rfbErr("XCreateImage(%s) data malloc failed.\n", name); + if (quiet) { + fprintf(stderr, "XCreateImage(%s) data malloc" + " failed.\n", name); + } + return 0; + } + if (flip_byte_order) { + char *order = flip_ximage_byte_order(xim); + if (! reported_flip && ! quiet) { + rfbLog("Changing XImage byte order" + " to %s\n", order); + reported_flip = 1; + } + } + + *ximg_ptr = xim; + return 1; + } + + xim = XShmCreateImage_wr(dpy, default_visual, depth, ZPixmap, NULL, + shm, w, h); + + if (xim == NULL) { + rfbErr("XShmCreateImage(%s) failed.\n", name); + if (quiet) { + fprintf(stderr, "XShmCreateImage(%s) failed.\n", name); + } + X_UNLOCK; + return 0; + } + + *ximg_ptr = xim; + +#if LIBVNCSERVER_HAVE_XSHM + shm->shmid = shmget(IPC_PRIVATE, + xim->bytes_per_line * xim->height, IPC_CREAT | 0777); + + if (shm->shmid == -1) { + rfbErr("shmget(%s) failed.\n", name); + rfbLogPerror("shmget"); + + XDestroyImage(xim); + *ximg_ptr = NULL; + + X_UNLOCK; + return 0; + } + + shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0); + + if (shm->shmaddr == (char *)-1) { + rfbErr("shmat(%s) failed.\n", name); + rfbLogPerror("shmat"); + + XDestroyImage(xim); + *ximg_ptr = NULL; + + shmctl(shm->shmid, IPC_RMID, 0); + shm->shmid = -1; + + X_UNLOCK; + return 0; + } + + shm->readOnly = False; + + if (! XShmAttach_wr(dpy, shm)) { + rfbErr("XShmAttach(%s) failed.\n", name); + XDestroyImage(xim); + *ximg_ptr = NULL; + + shmdt(shm->shmaddr); + shm->shmaddr = (char *) -1; + + shmctl(shm->shmid, IPC_RMID, 0); + shm->shmid = -1; + + X_UNLOCK; + return 0; + } +#endif + + X_UNLOCK; + return 1; +} + +void shm_delete(XShmSegmentInfo *shm) { +#if LIBVNCSERVER_HAVE_XSHM + if (shm != NULL && shm->shmaddr != (char *) -1) { + shmdt(shm->shmaddr); + } + if (shm != NULL && shm->shmid != -1) { + shmctl(shm->shmid, IPC_RMID, 0); + } +#endif +} + +void shm_clean(XShmSegmentInfo *shm, XImage *xim) { + + X_LOCK; +#if LIBVNCSERVER_HAVE_XSHM + if (shm != NULL && shm->shmid != -1 && dpy) { /* raw_fb hack */ + XShmDetach_wr(dpy, shm); + } +#endif + if (xim != NULL) { + XDestroyImage(xim); + xim = NULL; + } + X_UNLOCK; + + shm_delete(shm); +} + +void initialize_polling_images(void) { + int i, MB = 1024 * 1024; + + /* set all shm areas to "none" before trying to create any */ + scanline_shm.shmid = -1; + scanline_shm.shmaddr = (char *) -1; + scanline = NULL; + fullscreen_shm.shmid = -1; + fullscreen_shm.shmaddr = (char *) -1; + fullscreen = NULL; + snaprect_shm.shmid = -1; + snaprect_shm.shmaddr = (char *) -1; + snaprect = NULL; + for (i=1; i<=ntiles_x; i++) { + tile_row_shm[i].shmid = -1; + tile_row_shm[i].shmaddr = (char *) -1; + tile_row[i] = NULL; + } + + /* the scanline (e.g. 1280x1) shared memory area image: */ + + if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) { + clean_up_exit(1); + } + + /* + * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image: + * (we cut down the size of the shm area to try avoid and shm segment + * limits, e.g. the default 1MB on Solaris) + */ + if (UT.sysname && strstr(UT.sysname, "Linux")) { + set_fs_factor(10 * MB); + } else { + set_fs_factor(1 * MB); + } + if (fs_frac >= 1.0) { + fs_frac = 1.1; + fs_factor = 0; + } + if (! fs_factor) { + rfbLog("warning: fullscreen updates are disabled.\n"); + } else { + if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x, + dpy_y/fs_factor, "fullscreen")) { + clean_up_exit(1); + } + } + if (use_snapfb) { + if (! fs_factor) { + rfbLog("warning: disabling -snapfb mode.\n"); + use_snapfb = 0; + } else if (! shm_create(&snaprect_shm, &snaprect, dpy_x, + dpy_y/fs_factor, "snaprect")) { + clean_up_exit(1); + } + } + + /* + * for copy_tiles we need a lot of shared memory areas, one for + * each possible run length of changed tiles. 32 for 1024x768 + * and 40 for 1280x1024, etc. + */ + + tile_shm_count = 0; + for (i=1; i<=ntiles_x; i++) { + if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i, + tile_y, "tile_row")) { + if (i == 1) { + clean_up_exit(1); + } + rfbLog("shm: Error creating shared memory tile-row for" + " len=%d,\n", i); + rfbLog("shm: reverting to -onetile mode. If this" + " problem persists\n"); + rfbLog("shm: try using the -onetile or -noshm options" + " to limit\n"); + rfbLog("shm: shared memory usage, or run ipcrm(1)" + " to manually\n"); + rfbLog("shm: delete unattached shm segments.\n"); + single_copytile_count = i; + single_copytile = 1; + } + tile_shm_count++; + if (single_copytile && i >= 1) { + /* only need 1x1 tiles */ + break; + } + } + if (!quiet) { + if (using_shm) { + rfbLog("created %d tile_row shm polling images.\n", + tile_shm_count); + } else { + rfbLog("created %d tile_row polling images.\n", + tile_shm_count); + } + } +} + +/* + * A hint is a rectangular region built from 1 or more adjacent tiles + * 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 tw, int th, hint_t *hint) { + int w = dpy_x - x; + int h = dpy_y - y; + + if (w > tw) { + w = tw; + } + if (h > th) { + h = th; + } + + hint->x = x; + hint->y = y; + hint->w = w; + hint->h = h; +} + +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 > tw) { + w = tw; + } + if (h > th) { + h = th; + } + + if (hint->x > x) { /* extend to the left */ + hint->w += hint->x - x; + hint->x = x; + } + if (hint->y > y) { /* extend upward */ + hint->h += hint->y - y; + hint->y = y; + } + + if (hint->x + hint->w < x + w) { /* extend to the right */ + hint->w = x + w - hint->x; + } + if (hint->y + hint->h < y + h) { /* extend downward */ + hint->h = y + h - hint->y; + } +} + +static void save_hint(hint_t hint, int loc) { + /* simply copy it to the global array for later use. */ + hint_list[loc].x = hint.x; + hint_list[loc].y = hint.y; + hint_list[loc].w = hint.w; + hint_list[loc].h = hint.h; +} + +/* + * Glue together horizontal "runs" of adjacent changed tiles into one big + * rectangle change "hint" to be passed to the vnc machinery. + */ +static void hint_updates(void) { + hint_t hint; + int x, y, i, n, ty, th, tx, tw; + int hint_count = 0, in_run = 0; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + + 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 + tx, + y * tile_y + ty, tw, th, &hint); + in_run = 1; + } else { + extend_tile_hint( x * tile_x + tx, + y * tile_y + ty, tw, th, &hint); + } + } else { + if (in_run) { + /* end of a row run of altered tiles: */ + save_hint(hint, hint_count++); + in_run = 0; + } + } + } + if (in_run) { /* save the last row run */ + save_hint(hint, hint_count++); + in_run = 0; + } + } + + for (i=0; i < hint_count; i++) { + /* pass update info to vnc: */ + mark_hint(hint_list[i]); + } +} + +/* + * kludge, simple ceil+floor for non-negative doubles: + */ +#define CEIL(x) ( (double) ((int) (x)) == (x) ? \ + (double) ((int) (x)) : (double) ((int) (x) + 1) ) +#define FLOOR(x) ( (double) ((int) (x)) ) + +/* + * Scaling: + * + * For shrinking, a destination (scaled) pixel will correspond to more + * than one source (i.e. main fb) pixel. Think of an x-y plane made with + * graph paper. Each unit square in the graph paper (i.e. collection of + * points (x,y) such that N < x < N+1 and M < y < M+1, N and M integers) + * corresponds to one pixel in the unscaled fb. There is a solid + * color filling the inside of such a square. A scaled pixel has width + * 1/scale_fac, e.g. for "-scale 3/4" the width of the scaled pixel + * is 1.333. The area of this scaled pixel is 1.333 * 1.333 (so it + * obviously overlaps more than one source pixel, each which have area 1). + * + * We take the weight an unscaled pixel (source) contributes to a + * scaled pixel (destination) as simply proportional to the overlap area + * between the two pixels. One can then think of the value of the scaled + * pixel as an integral over the portion of the graph paper it covers. + * The thing being integrated is the color value of the unscaled source. + * That color value is constant over a graph paper square (source pixel), + * and changes discontinuously from one unit square to the next. + * + +Here is an example for -scale 3/4, the solid lines are the source pixels +(graph paper unit squares), while the dotted lines denote the scaled +pixels (destination pixels): + + 0 1 4/3 2 8/3 3 4=12/3 + |---------|--.------|------.--|---------|. + | | . | . | |. + | A | . B | . | |. + | | . | . | |. + | | . | . | |. + 1 |---------|--.------|------.--|---------|. + 4/3|.........|.........|.........|.........|. + | | . | . | |. + | C | . D | . | |. + | | . | . | |. + 2 |---------|--.------|------.--|---------|. + | | . | . | |. + | | . | . | |. + 8/3|.........|.........|.........|.........|. + | | . | . | |. + 3 |---------|--.------|------.--|---------|. + +So we see the first scaled pixel (0 < x < 4/3 and 0 < y < 4/3) mostly +overlaps with unscaled source pixel "A". The integration (averaging) +weights for this scaled pixel are: + + A 1 + B 1/3 + C 1/3 + D 1/9 + + * + * The Red, Green, and Blue color values must be averaged over separately + * otherwise you can get a complete mess (except in solid regions), + * because high order bits are averaged differently from the low order bits. + * + * So the algorithm is roughly: + * + * - Given as input a rectangle in the unscaled source fb with changes, + * find the rectangle of pixels this affects in the scaled destination fb. + * + * - For each of the affected scaled (dest) pixels, determine all of the + * unscaled (source) pixels it overlaps with. + * + * - Average those unscaled source values together, weighted by the area + * overlap with the destination pixel. Average R, G, B separately. + * + * - Take this average value and convert to a valid pixel value if + * necessary (e.g. rounding, shifting), and then insert it into the + * destination framebuffer as the pixel value. + * + * - On to the next destination pixel... + * + * ======================================================================== + * + * For expanding, e.g. -scale 1.1 (which we don't think people will do + * very often... or at least so we hope, the framebuffer can become huge) + * the situation is reversed and the destination pixel is smaller than a + * "graph paper" unit square (source pixel). Some destination pixels + * will be completely within a single unscaled source pixel. + * + * What we do here is a simple 4 point interpolation scheme: + * + * Let P00 be the source pixel closest to the destination pixel but with + * x and y values less than or equal to those of the destination pixel. + * (for simplicity, think of the upper left corner of a pixel defining the + * x,y location of the pixel, the center would work just as well). So it + * is the source pixel immediately to the upper left of the destination + * pixel. Let P10 be the source pixel one to the right of P00. Let P01 + * be one down from P00. And let P11 be one down and one to the right + * of P00. They form a 2x2 square we will interpolate inside of. + * + * Let V00, V10, V01, and V11 be the color values of those 4 source + * pixels. Let dx be the displacement along x the destination pixel is + * from P00. Note: 0 <= dx < 1 by definition of P00. Similarly let + * dy be the displacement along y. The weighted average for the + * interpolation is: + * + * V_ave = V00 * (1 - dx) * (1 - dy) + * + V10 * dx * (1 - dy) + * + V01 * (1 - dx) * dy + * + V11 * dx * dy + * + * Note that the weights (1-dx)*(1-dy) + dx*(1-dy) + (1-dx)*dy + dx*dy + * automatically add up to 1. It is also nice that all the weights are + * positive (unsigned char stays unsigned char). The above formula can + * be motivated by doing two 1D interpolations along x: + * + * VA = V00 * (1 - dx) + V10 * dx + * VB = V01 * (1 - dx) + V11 * dx + * + * and then interpolating VA and VB along y: + * + * V_ave = VA * (1 - dy) + VB * dy + * + * VA + * v |<-dx->| + * -- V00 ------ V10 + * dy | | + * -- | o...|... "o" denotes the position of the desired + * ^ | . | . destination pixel relative to the P00 + * | . | . source pixel. + * V10 ----.- V11 . + * ........ + * | + * VB + * + * + * Of course R, G, B averages are done separately as in the shrinking + * case. This gives reasonable results, and the implementation for + * shrinking can simply be used with different choices for weights for + * the loop over the 4 pixels. + */ + +void scale_rect(double factor, int blend, int interpolate, int Bpp, + char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line, + int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark) { +/* + * Notation: + * "i" an x pixel index in the destination (scaled) framebuffer + * "j" a y pixel index in the destination (scaled) framebuffer + * "I" an x pixel index in the source (un-scaled, i.e. main) framebuffer + * "J" a y pixel index in the source (un-scaled, i.e. main) framebuffer + * + * Similarly for nx, ny, Nx, Ny, etc. Lowercase: dest, Uppercase: source. + */ + int i, j, i1, i2, j1, j2; /* indices for scaled fb (dest) */ + int I, J, I1, I2, J1, J2; /* indices for main fb (source) */ + + double w, wx, wy, wtot; /* pixel weights */ + + double x1, y1, x2, y2; /* x-y coords for destination pixels edges */ + double dx, dy; /* size of destination pixel */ + double ddx, ddy; /* for interpolation expansion */ + + char *src, *dest; /* pointers to the two framebuffers */ + + + unsigned short us; + unsigned char uc; + unsigned int ui; + + int use_noblend_shortcut = 1; + int shrink; /* whether shrinking or expanding */ + static int constant_weights = -1, mag_int = -1; + static int last_Nx = -1, last_Ny = -1, cnt = 0; + static double last_factor = -1.0; + int b, k; + double pixave[4]; /* for averaging pixel values */ + + if (factor <= 1.0) { + shrink = 1; + } else { + shrink = 0; + } + + /* + * N.B. width and height (real numbers) of a scaled pixel. + * both are > 1 (e.g. 1.333 for -scale 3/4) + * they should also be equal but we don't assume it. + * + * This new way is probably the best we can do, take the inverse + * of the scaling factor to double precision. + */ + dx = 1.0/factor; + dy = 1.0/factor; + + /* + * There is some speedup if the pixel weights are constant, so + * let's special case these. + * + * If scale = 1/n and n divides Nx and Ny, the pixel weights + * are constant (e.g. 1/2 => equal on 2x2 square). + */ + if (factor != last_factor || Nx != last_Nx || Ny != last_Ny) { + constant_weights = -1; + mag_int = -1; + last_Nx = Nx; + last_Ny = Ny; + last_factor = factor; + } + + if (constant_weights < 0) { + int n = 0; + + constant_weights = 0; + mag_int = 0; + + for (i = 2; i<=128; i++) { + double test = ((double) 1)/ i; + double diff, eps = 1.0e-7; + diff = factor - test; + if (-eps < diff && diff < eps) { + n = i; + break; + } + } + if (! blend || ! shrink || interpolate) { + ; + } else if (n != 0) { + if (Nx % n == 0 && Ny % n == 0) { + static int didmsg = 0; + if (mark && ! didmsg) { + didmsg = 1; + rfbLog("scale_and_mark_rect: using " + "constant pixel weight speedup " + "for 1/%d\n", n); + } + constant_weights = 1; + } + } + + n = 0; + for (i = 2; i<=32; i++) { + double test = (double) i; + double diff, eps = 1.0e-7; + diff = factor - test; + if (-eps < diff && diff < eps) { + n = i; + break; + } + } + if (! blend && factor > 1.0 && n) { + mag_int = n; + } + } + + if (mark && factor > 1.0 && blend) { + /* + * kludge: correct for interpolating blurring leaking + * up or left 1 destination pixel. + */ + if (X1 > 0) X1--; + if (Y1 > 0) Y1--; + } + + /* + * find the extent of the change the input rectangle induces in + * the scaled framebuffer. + */ + + /* Left edges: find largest i such that i * dx <= X1 */ + i1 = FLOOR(X1/dx); + + /* Right edges: find smallest i such that (i+1) * dx >= X2+1 */ + i2 = CEIL( (X2+1)/dx ) - 1; + + /* To be safe, correct any overflows: */ + i1 = nfix(i1, nx); + i2 = nfix(i2, nx) + 1; /* add 1 to make a rectangle upper boundary */ + + /* Repeat above for y direction: */ + j1 = FLOOR(Y1/dy); + j2 = CEIL( (Y2+1)/dy ) - 1; + + j1 = nfix(j1, ny); + j2 = nfix(j2, ny) + 1; + + /* special case integer magnification with no blending */ + if (mark && ! blend && mag_int && Bpp != 3) { + int jmin, jmax, imin, imax; + + /* outer loop over *source* pixels */ + for (J=Y1; J < Y2; J++) { + jmin = J * mag_int; + jmax = jmin + mag_int; + for (I=X1; I < X2; I++) { + /* extract value */ + src = src_fb + J*src_bytes_per_line + I*Bpp; + if (Bpp == 4) { + ui = *((unsigned int *)src); + } else if (Bpp == 2) { + us = *((unsigned short *)src); + } else if (Bpp == 1) { + uc = *((unsigned char *)src); + } + imin = I * mag_int; + imax = imin + mag_int; + /* inner loop over *dest* pixels */ + for (j=jmin; j<jmax; j++) { + dest = dst_fb + j*dst_bytes_per_line + imin*Bpp; + for (i=imin; i<imax; i++) { + if (Bpp == 4) { + *((unsigned int *)dest) = ui; + } else if (Bpp == 2) { + *((unsigned short *)dest) = us; + } else if (Bpp == 1) { + *((unsigned char *)dest) = uc; + } + dest += Bpp; + } + } + } + } + goto markit; + } + + /* set these all to 1.0 to begin with */ + wx = 1.0; + wy = 1.0; + w = 1.0; + + /* + * Loop over destination pixels in scaled fb: + */ + for (j=j1; j<j2; j++) { + y1 = j * dy; /* top edge */ + if (y1 > Ny - 1) { + /* can go over with dy = 1/scale_fac */ + y1 = Ny - 1; + } + y2 = y1 + dy; /* bottom edge */ + + /* Find main fb indices covered by this dest pixel: */ + J1 = (int) FLOOR(y1); + J1 = nfix(J1, Ny); + + if (shrink && ! interpolate) { + J2 = (int) CEIL(y2) - 1; + J2 = nfix(J2, Ny); + } else { + J2 = J1 + 1; /* simple interpolation */ + ddy = y1 - J1; + } + + /* destination char* pointer: */ + dest = dst_fb + j*dst_bytes_per_line + i1*Bpp; + + for (i=i1; i<i2; i++) { + + x1 = i * dx; /* left edge */ + if (x1 > Nx - 1) { + /* can go over with dx = 1/scale_fac */ + x1 = Nx - 1; + } + x2 = x1 + dx; /* right edge */ + + cnt++; + + /* Find main fb indices covered by this dest pixel: */ + I1 = (int) FLOOR(x1); + if (I1 >= Nx) I1 = Nx - 1; + + if (! blend && use_noblend_shortcut) { + /* + * The noblend case involves no weights, + * and 1 pixel, so just copy the value + * directly. + */ + src = src_fb + J1*src_bytes_per_line + I1*Bpp; + if (Bpp == 4) { + *((unsigned int *)dest) + = *((unsigned int *)src); + } else if (Bpp == 2) { + *((unsigned short *)dest) + = *((unsigned short *)src); + } else if (Bpp == 1) { + *(dest) = *(src); + } else if (Bpp == 3) { + /* rare case */ + for (k=0; k<=2; k++) { + *(dest+k) = *(src+k); + } + } + dest += Bpp; + continue; + } + + if (shrink && ! interpolate) { + I2 = (int) CEIL(x2) - 1; + if (I2 >= Nx) I2 = Nx - 1; + } else { + I2 = I1 + 1; /* simple interpolation */ + ddx = x1 - I1; + } + + /* Zero out accumulators for next pixel average: */ + for (b=0; b<4; b++) { + pixave[b] = 0.0; /* for RGB weighted sums */ + } + + /* + * wtot is for accumulating the total weight. + * It should always sum to 1/(scale_fac * scale_fac). + */ + wtot = 0.0; + + /* + * Loop over source pixels covered by this dest pixel. + * + * These "extra" loops over "J" and "I" make + * the cache/cacheline performance unclear. + * For example, will the data brought in from + * src for j, i, and J=0 still be in the cache + * after the J > 0 data have been accessed and + * we are at j, i+1, J=0? The stride in J is + * main_bytes_per_line, and so ~4 KB. + * + * Typical case when shrinking are 2x2 loop, so + * just two lines to worry about. + */ + for (J=J1; J<=J2; J++) { + /* see comments for I, x1, x2, etc. below */ + if (constant_weights) { + ; + } else if (! blend) { + if (J != J1) { + continue; + } + wy = 1.0; + + /* interpolation scheme: */ + } else if (! shrink || interpolate) { + if (J >= Ny) { + continue; + } else if (J == J1) { + wy = 1.0 - ddy; + } else if (J != J1) { + wy = ddy; + } + + /* integration scheme: */ + } else if (J < y1) { + wy = J+1 - y1; + } else if (J+1 > y2) { + wy = y2 - J; + } else { + wy = 1.0; + } + + src = src_fb + J*src_bytes_per_line + I1*Bpp; + + for (I=I1; I<=I2; I++) { + + /* Work out the weight: */ + + if (constant_weights) { + ; + } else if (! blend) { + /* + * Ugh, PseudoColor colormap is + * bad news, to avoid random + * colors just take the first + * pixel. Or user may have + * specified :nb to fraction. + * The :fb will force blending + * for this case. + */ + if (I != I1) { + continue; + } + wx = 1.0; + + /* interpolation scheme: */ + } else if (! shrink || interpolate) { + if (I >= Nx) { + continue; /* off edge */ + } else if (I == I1) { + wx = 1.0 - ddx; + } else if (I != I1) { + wx = ddx; + } + + /* integration scheme: */ + } else if (I < x1) { + /* + * source left edge (I) to the + * left of dest left edge (x1): + * fractional weight + */ + wx = I+1 - x1; + } else if (I+1 > x2) { + /* + * source right edge (I+1) to the + * right of dest right edge (x2): + * fractional weight + */ + wx = x2 - I; + } else { + /* + * source edges (I and I+1) completely + * inside dest edges (x1 and x2): + * full weight + */ + wx = 1.0; + } + + w = wx * wy; + wtot += w; + + /* + * We average the unsigned char value + * instead of char value: otherwise + * the minimum (char 0) is right next + * to the maximum (char -1)! This way + * they are spread between 0 and 255. + */ + if (Bpp == 4) { + /* unroll the loops, can give 20% */ + pixave[0] += w * + ((unsigned char) *(src )); + pixave[1] += w * + ((unsigned char) *(src+1)); + pixave[2] += w * + ((unsigned char) *(src+2)); + pixave[3] += w * + ((unsigned char) *(src+3)); + } else if (Bpp == 2) { + /* + * 16bpp: trickier with green + * split over two bytes, so we + * use the masks: + */ + us = *((unsigned short *) src); + pixave[0] += w*(us & main_red_mask); + pixave[1] += w*(us & main_green_mask); + pixave[2] += w*(us & main_blue_mask); + } else if (Bpp == 1) { + pixave[0] += w * + ((unsigned char) *(src)); + } else { + for (b=0; b<Bpp; b++) { + pixave[b] += w * + ((unsigned char) *(src+b)); + } + } + src += Bpp; + } + } + + if (wtot <= 0.0) { + wtot = 1.0; + } + wtot = 1.0/wtot; /* normalization factor */ + + /* place weighted average pixel in the scaled fb: */ + if (Bpp == 4) { + *(dest ) = (char) (wtot * pixave[0]); + *(dest+1) = (char) (wtot * pixave[1]); + *(dest+2) = (char) (wtot * pixave[2]); + *(dest+3) = (char) (wtot * pixave[3]); + } else if (Bpp == 2) { + /* 16bpp / 565 case: */ + pixave[0] *= wtot; + pixave[1] *= wtot; + pixave[2] *= wtot; + us = (main_red_mask & (int) pixave[0]) + | (main_green_mask & (int) pixave[1]) + | (main_blue_mask & (int) pixave[2]); + *( (unsigned short *) dest ) = us; + } else if (Bpp == 1) { + *(dest) = (char) (wtot * pixave[0]); + } else { + for (b=0; b<Bpp; b++) { + *(dest+b) = (char) (wtot * pixave[b]); + } + } + dest += Bpp; + } + } + markit: + if (mark) { + mark_rect_as_modified(i1, j1, i2, j2, 1); + } +} + +void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { + + if (!screen || !rfb_fb || !main_fb) { + return; + } + if (! screen->serverFormat.trueColour) { + /* + * PseudoColor colormap... blending leads to random colors. + * User can override with ":fb" + */ + if (scaling_blend == 1) { + /* :fb option sets it to 2 */ + if (default_visual->class == StaticGray) { + /* + * StaticGray can be blended OK, otherwise + * user can disable with :nb + */ + ; + } else { + scaling_blend = 0; + } + } + } + + scale_rect(scale_fac, scaling_blend, scaling_interpolate, bpp/8, + main_fb, main_bytes_per_line, rfb_fb, rfb_bytes_per_line, + dpy_x, dpy_y, scaled_x, scaled_y, X1, Y1, X2, Y2, 1); +} + +void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force) { + + if (damage_time != 0) { + /* + * This is not XDAMAGE, rather a hack for testing + * where we allow the framebuffer to be corrupted for + * damage_delay seconds. + */ + int debug = 0; + if (time(0) > damage_time + damage_delay) { + if (! quiet) { + rfbLog("damaging turned off.\n"); + } + damage_time = 0; + damage_delay = 0; + } else { + if (debug) { + rfbLog("damaging viewer fb by not marking " + "rect: %d,%d,%d,%d\n", x1, y1, x2, y2); + } + return; + } + } + + if (rfb_fb == main_fb || force) { + rfbMarkRectAsModified(screen, x1, y1, x2, y2); + } else if (scaling) { + scale_and_mark_rect(x1, y1, x2, y2); + } +} + +/* + * Notifies libvncserver of a changed hint rectangle. + */ +static void mark_hint(hint_t hint) { + int x = hint.x; + int y = hint.y; + int w = hint.w; + int h = hint.h; + + mark_rect_as_modified(x, y, x + w, y + h, 0); +} + +/* + * copy_tiles() gives a slight improvement over copy_tile() since + * adjacent runs of tiles are done all at once there is some savings + * due to contiguous memory access. Not a great speedup, but in some + * cases it can be up to 2X. Even more on a SunRay or ShadowFB where + * no graphics hardware is involved in the read. Generally, graphics + * devices are optimized for write, not read, so we are limited by the + * read bandwidth, sometimes only 5 MB/sec on otherwise fast hardware. + */ +static int *first_line = NULL, *last_line; +static unsigned short *left_diff, *right_diff; + +static int copy_tiles(int tx, int ty, int nt) { + int x, y, line; + int size_x, size_y, width1, width2; + int off, len, n, dw, dx, t; + 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; + if (! first_line) { + /* allocate arrays first time in. */ + int n = ntiles_x + 1; + first_line = (int *) malloc((size_t) (n * sizeof(int))); + last_line = (int *) malloc((size_t) (n * sizeof(int))); + left_diff = (unsigned short *) + malloc((size_t) (n * sizeof(unsigned short))); + right_diff = (unsigned short *) + malloc((size_t) (n * sizeof(unsigned short))); + } + + x = tx * tile_x; + y = ty * tile_y; + + size_x = dpy_x - x; + if ( size_x > tile_x * nt ) { + size_x = tile_x * nt; + width1 = tile_x; + width2 = tile_x; + } else { + /* short tile */ + width1 = tile_x; /* internal tile */ + width2 = size_x - (nt - 1) * tile_x; /* right hand tile */ + } + + size_y = dpy_y - y; + if ( size_y > tile_y ) { + size_y = tile_y; + } + + n = tx + ty * ntiles_x; /* number of the first tile */ + + if (blackouts && tile_blackout[n].cover == 2) { + /* + * If there are blackouts and this tile is completely covered + * no need to poll screen or do anything else.. + * n.b. we are in single copy_tile mode: nt=1 + */ + tile_has_diff[n] = 0; + return(0); + } + + X_LOCK; + XRANDR_SET_TRAP_RET(-1, "copy_tile-set"); + /* read in the whole tile run at once: */ + copy_image(tile_row[nt], x, y, size_x, size_y); + XRANDR_CHK_TRAP_RET(-1, "copy_tile-chk"); + + X_UNLOCK; + + if (blackouts && tile_blackout[n].cover == 1) { + /* + * If there are blackouts and this tile is partially covered + * we should re-black-out the portion. + * n.b. we are in single copy_tile mode: nt=1 + */ + int x1, x2, y1, y2, b; + int w, s, fill = 0; + + for (b=0; b < tile_blackout[n].count; b++) { + char *b_dst = tile_row[nt]->data; + + x1 = tile_blackout[n].bo[b].x1 - x; + y1 = tile_blackout[n].bo[b].y1 - y; + x2 = tile_blackout[n].bo[b].x2 - x; + y2 = tile_blackout[n].bo[b].y2 - y; + + w = (x2 - x1) * pixelsize; + s = x1 * pixelsize; + + for (line = 0; line < size_y; line++) { + if (y1 <= line && line < y2) { + memset(b_dst + s, fill, (size_t) w); + } + b_dst += tile_row[nt]->bytes_per_line; + } + } + } + + src = tile_row[nt]->data; + dst = main_fb + y * main_bytes_per_line + x * pixelsize; + + s_src = src; + s_dst = dst; + + for (t=1; t <= nt; t++) { + first_line[t] = -1; + } + + /* find the first line with difference: */ + w1 = width1 * pixelsize; + w2 = width2 * pixelsize; + + /* foreach line: */ + for (line = 0; line < size_y; line++) { + /* foreach horizontal tile: */ + for (t=1; t <= nt; t++) { + if (first_line[t] != -1) { + continue; + } + + off = (t-1) * w1; + if (t == nt) { + len = w2; /* possible short tile */ + } else { + len = w1; + } + + if (memcmp(s_dst + off, s_src + off, len)) { + first_line[t] = line; + } + } + s_src += tile_row[nt]->bytes_per_line; + s_dst += main_bytes_per_line; + } + + /* see if there were any differences for any tile: */ + first_min = -1; + for (t=1; t <= nt; t++) { + tile_tried[n+(t-1)] = 1; + if (first_line[t] != -1) { + if (first_min == -1 || first_line[t] < first_min) { + first_min = first_line[t]; + } + } + } + if (first_min == -1) { + /* no tile has a difference, note this and get out: */ + for (t=1; t <= nt; t++) { + tile_has_diff[n+(t-1)] = 0; + } + return(0); + } else { + /* + * at least one tile has a difference. make sure info + * is recorded (e.g. sometimes we guess tiles and they + * came in with tile_has_diff 0) + */ + for (t=1; t <= nt; t++) { + if (first_line[t] == -1) { + tile_has_diff[n+(t-1)] = 0; + } else { + tile_has_diff[n+(t-1)] = 1; + } + } + } + + m_src = src + (tile_row[nt]->bytes_per_line * size_y); + m_dst = dst + (main_bytes_per_line * size_y); + + for (t=1; t <= nt; t++) { + last_line[t] = first_line[t]; + } + + /* find the last line with difference: */ + w1 = width1 * pixelsize; + w2 = width2 * pixelsize; + + /* foreach line: */ + for (line = size_y - 1; line > first_min; line--) { + + m_src -= tile_row[nt]->bytes_per_line; + m_dst -= main_bytes_per_line; + + /* foreach tile: */ + for (t=1; t <= nt; t++) { + if (first_line[t] == -1 + || last_line[t] != first_line[t]) { + /* tile has no changes or already done */ + continue; + } + + off = (t-1) * w1; + if (t == nt) { + len = w2; /* possible short tile */ + } else { + len = w1; + } + if (memcmp(m_dst + off, m_src + off, len)) { + last_line[t] = line; + } + } + } + + /* + * determine the farthest down last changed line + * will be used below to limit our memcpy() to the framebuffer. + */ + last_max = -1; + for (t=1; t <= nt; t++) { + if (first_line[t] == -1) { + continue; + } + if (last_max == -1 || last_line[t] > last_max) { + last_max = last_line[t]; + } + } + + /* look for differences on left and right hand edges: */ + for (t=1; t <= nt; t++) { + left_diff[t] = 0; + right_diff[t] = 0; + } + + h_src = src; + h_dst = dst; + + w1 = width1 * pixelsize; + w2 = width2 * pixelsize; + + dx1 = (width1 - tile_fuzz) * pixelsize; + dx2 = (width2 - tile_fuzz) * pixelsize; + dw = tile_fuzz * pixelsize; + + /* foreach line: */ + for (line = 0; line < size_y; line++) { + /* foreach tile: */ + for (t=1; t <= nt; t++) { + if (first_line[t] == -1) { + /* tile has no changes at all */ + continue; + } + + off = (t-1) * w1; + if (t == nt) { + dx = dx2; /* possible short tile */ + if (dx <= 0) { + break; + } + } else { + dx = dx1; + } + + if (! left_diff[t] && memcmp(h_dst + off, + h_src + off, dw)) { + left_diff[t] = 1; + } + if (! right_diff[t] && memcmp(h_dst + off + dx, + h_src + off + dx, dw) ) { + right_diff[t] = 1; + } + } + h_src += tile_row[nt]->bytes_per_line; + h_dst += main_bytes_per_line; + } + + /* now finally copy the difference to the rfb framebuffer: */ + s_src = src + tile_row[nt]->bytes_per_line * first_min; + s_dst = dst + main_bytes_per_line * first_min; + + 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 to limit + * the size of the hint. could help for a slow + * link. Unfortunately we spent a lot of time + * reading in the many tiles. + * + * BTW, 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) + * so this inner loop is not as bad as it seems. + */ + 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; + } + + /* record all the info in the region array for this tile: */ + for (t=1; t <= nt; t++) { + int s = t - 1; + + if (first_line[t] == -1) { + /* tile unchanged */ + continue; + } + 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 ) { + tile_region[n+s].top_diff = 1; + } + if ( last_line[t] > (size_y - 1) - tile_fuzz ) { + tile_region[n+s].bot_diff = 1; + } + + tile_region[n+s].left_diff = left_diff[t]; + tile_region[n+s].right_diff = right_diff[t]; + + tile_copied[n+s] = 1; + } + + return(1); +} + +/* + * The copy_tile() call in the loop below copies the changed tile into + * the rfb framebuffer. Note that copy_tile() sets the tile_region + * struct to have info about the y-range of the changed region and also + * whether the tile edges contain diffs (within distance tile_fuzz). + * + * We use this tile_region info to try to guess if the downward and right + * tiles will have diffs. These tiles will be checked later in the loop + * (since y+1 > y and x+1 > x). + * + * See copy_tiles_backward_pass() for analogous checking upward and + * left tiles. + */ +static int copy_all_tiles(void) { + int x, y, n, m; + int diffs = 0, ct; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + + if (tile_has_diff[n]) { + ct = copy_tiles(x, y, 1); + if (ct < 0) return ct; /* fatal */ + } + if (! tile_has_diff[n]) { + /* + * n.b. copy_tiles() may have detected + * no change and reset tile_has_diff to 0. + */ + continue; + } + diffs++; + + /* neighboring tile downward: */ + if ( (y+1) < ntiles_y && tile_region[n].bot_diff) { + m = x + (y+1) * ntiles_x; + if (! tile_has_diff[m]) { + tile_has_diff[m] = 2; + } + } + /* neighboring tile to right: */ + if ( (x+1) < ntiles_x && tile_region[n].right_diff) { + m = (x+1) + y * ntiles_x; + if (! tile_has_diff[m]) { + tile_has_diff[m] = 2; + } + } + } + } + return diffs; +} + +/* + * Routine analogous to copy_all_tiles() above, but for horizontal runs + * of adjacent changed tiles. + */ +static int copy_all_tile_runs(void) { + int x, y, n, m, i; + int diffs = 0, ct; + int in_run = 0, run = 0; + int ntave = 0, ntcnt = 0; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x + 1; x++) { + n = x + y * ntiles_x; + + if (x != ntiles_x && tile_has_diff[n]) { + in_run = 1; + run++; + } else { + if (! in_run) { + in_run = 0; + run = 0; + continue; + } + ct = copy_tiles(x - run, y, run); + if (ct < 0) return ct; /* fatal */ + + ntcnt++; + ntave += run; + diffs += run; + + /* neighboring tile downward: */ + for (i=1; i <= run; i++) { + if ((y+1) < ntiles_y + && tile_region[n-i].bot_diff) { + m = (x-i) + (y+1) * ntiles_x; + if (! tile_has_diff[m]) { + tile_has_diff[m] = 2; + } + } + } + + /* neighboring tile to right: */ + if (((x-1)+1) < ntiles_x + && tile_region[n-1].right_diff) { + m = ((x-1)+1) + y * ntiles_x; + if (! tile_has_diff[m]) { + tile_has_diff[m] = 2; + } + + /* note that this starts a new run */ + in_run = 1; + run = 1; + } else { + in_run = 0; + run = 0; + } + } + } + /* + * Could some activity go here, to emulate threaded + * behavior by servicing some libvncserver tasks? + */ + } + return diffs; +} + +/* + * Here starts a bunch of heuristics to guess/detect changed tiles. + * They are: + * copy_tiles_backward_pass, fill_tile_gaps/gap_try, grow_islands/island_try + */ + +/* + * Try to predict whether the upward and/or leftward tile has been modified. + * copy_all_tiles() has already done downward and rightward tiles. + */ +static int copy_tiles_backward_pass(void) { + int x, y, n, m; + int diffs = 0, ct; + + for (y = ntiles_y - 1; y >= 0; y--) { + for (x = ntiles_x - 1; x >= 0; x--) { + n = x + y * ntiles_x; /* number of this tile */ + + if (! tile_has_diff[n]) { + continue; + } + + m = x + (y-1) * ntiles_x; /* neighboring tile upward */ + + if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) { + if (! tile_tried[m]) { + tile_has_diff[m] = 2; + ct = copy_tiles(x, y-1, 1); + if (ct < 0) return ct; /* fatal */ + } + } + + m = (x-1) + y * ntiles_x; /* neighboring tile to left */ + + if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) { + if (! tile_tried[m]) { + tile_has_diff[m] = 2; + ct = copy_tiles(x-1, y, 1); + if (ct < 0) return ct; /* fatal */ + } + } + } + } + for (n=0; n < ntiles; n++) { + if (tile_has_diff[n]) { + diffs++; + } + } + return diffs; +} + +static int copy_tiles_additional_pass(void) { + int x, y, n; + int diffs = 0, ct; + + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; /* number of this tile */ + + if (! tile_has_diff[n]) { + continue; + } + if (tile_copied[n]) { + continue; + } + + ct = copy_tiles(x, y, 1); + if (ct < 0) return ct; /* fatal */ + } + } + for (n=0; n < ntiles; n++) { + if (tile_has_diff[n]) { + diffs++; + } + } + return diffs; +} + +static int gap_try(int x, int y, int *run, int *saw, int along_x) { + int n, m, i, xt, yt, ct; + + n = x + y * ntiles_x; + + if (! tile_has_diff[n]) { + if (*saw) { + (*run)++; /* extend the gap run. */ + } + return 0; + } + if (! *saw || *run == 0 || *run > gaps_fill) { + *run = 0; /* unacceptable run. */ + *saw = 1; + return 0; + } + + for (i=1; i <= *run; i++) { /* iterate thru the run. */ + if (along_x) { + xt = x - i; + yt = y; + } else { + xt = x; + yt = y - i; + } + + m = xt + yt * ntiles_x; + if (tile_tried[m]) { /* do not repeat tiles */ + continue; + } + + ct = copy_tiles(xt, yt, 1); + if (ct < 0) return ct; /* fatal */ + } + *run = 0; + *saw = 1; + return 1; +} + +/* + * Look for small gaps of unchanged tiles that may actually contain changes. + * E.g. when paging up and down in a web broswer or terminal there can + * be a distracting delayed filling in of such gaps. gaps_fill is the + * tweak parameter that sets the width of the gaps that are checked. + * + * BTW, grow_islands() is actually pretty successful at doing this too... + */ +static int fill_tile_gaps(void) { + int x, y, run, saw; + int n, diffs = 0, ct; + + /* horizontal: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + saw = 0; + for (x=0; x < ntiles_x; x++) { + ct = gap_try(x, y, &run, &saw, 1); + if (ct < 0) return ct; /* fatal */ + } + } + + /* vertical: */ + for (x=0; x < ntiles_x; x++) { + run = 0; + saw = 0; + for (y=0; y < ntiles_y; y++) { + ct = gap_try(x, y, &run, &saw, 0); + if (ct < 0) return ct; /* fatal */ + } + } + + for (n=0; n < ntiles; n++) { + if (tile_has_diff[n]) { + diffs++; + } + } + return diffs; +} + +static int island_try(int x, int y, int u, int v, int *run) { + int n, m, ct; + + n = x + y * ntiles_x; + m = u + v * ntiles_x; + + if (tile_has_diff[n]) { + (*run)++; + } else { + *run = 0; + } + + if (tile_has_diff[n] && ! tile_has_diff[m]) { + /* found a discontinuity */ + + if (tile_tried[m]) { + return 0; + } else if (*run < grow_fill) { + return 0; + } + + ct = copy_tiles(u, v, 1); + if (ct < 0) return ct; /* fatal */ + } + return 1; +} + +/* + * Scan looking for discontinuities in tile_has_diff[]. Try to extend + * the boundary of the discontinuity (i.e. make the island larger). + * Vertical scans are skipped since they do not seem to yield much... + */ +static int grow_islands(void) { + int x, y, n, run; + int diffs = 0, ct; + + /* + * n.b. the way we scan here should keep an extension going, + * and so also fill in gaps effectively... + */ + + /* left to right: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + for (x=0; x <= ntiles_x - 2; x++) { + ct = island_try(x, y, x+1, y, &run); + if (ct < 0) return ct; /* fatal */ + } + } + /* right to left: */ + for (y=0; y < ntiles_y; y++) { + run = 0; + for (x = ntiles_x - 1; x >= 1; x--) { + ct = island_try(x, y, x-1, y, &run); + if (ct < 0) return ct; /* fatal */ + } + } + for (n=0; n < ntiles; n++) { + if (tile_has_diff[n]) { + diffs++; + } + } + return diffs; +} + +/* + * Fill the framebuffer with zeros for each blackout region + */ +static void blackout_regions(void) { + int i; + for (i=0; i < blackouts; i++) { + zero_fb(blackr[i].x1, blackr[i].y1, blackr[i].x2, blackr[i].y2); + } +} + +/* + * copy the whole X screen to the rfb framebuffer. For a large enough + * number of changed tiles, this is faster than tiles scheme at retrieving + * the info from the X server. Bandwidth to client and compression time + * are other issues... use -fs 1.0 to disable. + */ +int copy_screen(void) { + int pixelsize = bpp/8; + char *fbp; + int i, y, block_size; + + if (! fs_factor) { + return 0; + } + + block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); + + if (! main_fb) { + return 0; + } + fbp = main_fb; + y = 0; + + X_LOCK; + + /* screen may be too big for 1 shm area, so broken into fs_factor */ + for (i=0; i < fs_factor; i++) { + XRANDR_SET_TRAP_RET(-1, "copy_screen-set"); + copy_image(fullscreen, 0, y, 0, 0); + XRANDR_CHK_TRAP_RET(-1, "copy_screen-chk"); + + memcpy(fbp, fullscreen->data, (size_t) block_size); + + y += dpy_y / fs_factor; + fbp += block_size; + } + + X_UNLOCK; + + if (blackouts) { + blackout_regions(); + } + + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + return 0; +} + +int copy_snap(void) { + int pixelsize = bpp/8; + char *fbp; + int i, y, block_size; + double dt; + static int first = 1; + + if (! fs_factor) { + return 0; + } + + block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); + + if (! snap_fb || ! snap || ! snaprect) { + return 0; + } + fbp = snap_fb; + y = 0; + + dtime0(&dt); + X_LOCK; + + /* screen may be too big for 1 shm area, so broken into fs_factor */ + for (i=0; i < fs_factor; i++) { + XRANDR_SET_TRAP_RET(-1, "copy_snap-set"); + copy_image(snaprect, 0, y, 0, 0); + XRANDR_CHK_TRAP_RET(-1, "copy_snap-chk"); + + memcpy(fbp, snaprect->data, (size_t) block_size); + + y += dpy_y / fs_factor; + fbp += block_size; + } + + X_UNLOCK; + dt = dtime(&dt); + if (first) { + rfbLog("copy_snap: time for -snapfb snapshot: %.3f sec\n", dt); + first = 0; + } + + return 0; +} + + +/* + * Utilities for managing the "naps" to cut down on amount of polling. + */ +static void nap_set(int tile_cnt) { + int nap_in = nap_ok; + time_t now = time(0); + + if (scan_count == 0) { + /* roll up check for all NSCAN scans */ + nap_ok = 0; + if (naptile && nap_diff_count < 2 * NSCAN * naptile) { + /* "2" is a fudge to permit a bit of bg drawing */ + nap_ok = 1; + } + nap_diff_count = 0; + } + if (nap_ok && ! nap_in && use_xdamage) { + if (XD_skip > 0.8 * XD_tot) { + /* X DAMAGE is keeping load low, so skip nap */ + nap_ok = 0; + } + } + if (! nap_ok && client_count) { + if(now > last_fb_bytes_sent + no_fbu_blank) { + if (debug_tiles > 1) { + printf("nap_set: nap_ok=1: now: %d last: %d\n", + (int) now, (int) last_fb_bytes_sent); + } + nap_ok = 1; + } + } + + if (show_cursor) { + /* kludge for the up to 4 tiles the mouse patch could occupy */ + if ( tile_cnt > 4) { + last_event = now; + } + } else if (tile_cnt != 0) { + last_event = now; + } +} + +/* + * split up a long nap to improve the wakeup time + */ +void nap_sleep(int ms, int split) { + int i, input = got_user_input; + + for (i=0; i<split; i++) { + usleep(ms * 1000 / split); + if (! use_threads && i != split - 1) { + rfbPE(-1); + } + if (input != got_user_input) { + break; + } + } +} + +/* + * see if we should take a nap of some sort between polls + */ +static void nap_check(int tile_cnt) { + time_t now; + + nap_diff_count += tile_cnt; + + if (! take_naps) { + return; + } + + now = time(0); + + if (screen_blank > 0) { + int dt_ev, dt_fbu, ms = 2000; + + /* if no activity, pause here for a second or so. */ + dt_ev = (int) (now - last_event); + dt_fbu = (int) (now - last_fb_bytes_sent); + if (dt_fbu > screen_blank) { + /* sleep longer for no fb requests */ + nap_sleep(2 * ms, 16); + return; + } + if (dt_ev > screen_blank) { + nap_sleep(ms, 8); + return; + } + } + if (naptile && nap_ok && tile_cnt < naptile) { + int ms = napfac * waitms; + ms = ms > napmax ? napmax : ms; + if (now - last_input <= 2) { + nap_ok = 0; + } else { + nap_sleep(ms, 1); + } + } +} + +/* + * This is called to avoid a ~20 second timeout in libvncserver. + * May no longer be needed. + */ +static void ping_clients(int tile_cnt) { + static time_t last_send = 0; + time_t now = time(0); + + if (rfbMaxClientWait < 20000) { + rfbMaxClientWait = 20000; + rfbLog("reset rfbMaxClientWait to %d msec.\n", + rfbMaxClientWait); + } + if (tile_cnt) { + last_send = now; + } else if (now - last_send > 1) { + /* Send small heartbeat to client */ + mark_rect_as_modified(0, 0, 1, 1, 1); + last_send = now; + } +} + +/* + * scan_display() wants to know if this tile can be skipped due to + * blackout regions: (no data compare is done, just a quick geometric test) + */ +static int blackout_line_skip(int n, int x, int y, int rescan, + int *tile_count) { + + if (tile_blackout[n].cover == 2) { + tile_has_diff[n] = 0; + return 1; /* skip it */ + + } else if (tile_blackout[n].cover == 1) { + int w, x1, y1, x2, y2, b, hit = 0; + if (x + NSCAN > dpy_x) { + w = dpy_x - x; + } else { + w = NSCAN; + } + + for (b=0; b < tile_blackout[n].count; b++) { + + /* n.b. these coords are in full display space: */ + x1 = tile_blackout[n].bo[b].x1; + x2 = tile_blackout[n].bo[b].x2; + y1 = tile_blackout[n].bo[b].y1; + y2 = tile_blackout[n].bo[b].y2; + + if (x2 - x1 < w) { + /* need to cover full width */ + continue; + } + if (y1 <= y && y < y2) { + hit = 1; + break; + } + } + if (hit) { + if (! rescan) { + tile_has_diff[n] = 0; + } else { + *tile_count += tile_has_diff[n]; + } + return 1; /* skip */ + } + } + return 0; /* do not skip */ +} + +static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src, + int w, int pixelsize) { + + int i, x1, y1, x2, y2, b, hit = 0; + int beg = -1, end = -1; + + if (tile_blackout[n].cover == 0) { + return 0; /* 0 means do not skip it. */ + } else if (tile_blackout[n].cover == 2) { + return 1; /* 1 means skip it. */ + } + + /* tile has partial coverage: */ + + for (i=0; i < w * pixelsize; i++) { + if (*(dst+i) != *(src+i)) { + beg = i/pixelsize; /* beginning difference */ + break; + } + } + for (i = w * pixelsize - 1; i >= 0; i--) { + if (*(dst+i) != *(src+i)) { + end = i/pixelsize; /* ending difference */ + break; + } + } + if (beg < 0 || end < 0) { + /* problem finding range... */ + return 0; + } + + /* loop over blackout rectangles: */ + for (b=0; b < tile_blackout[n].count; b++) { + + /* y in full display space: */ + y1 = tile_blackout[n].bo[b].y1; + y2 = tile_blackout[n].bo[b].y2; + + /* x relative to tile origin: */ + x1 = tile_blackout[n].bo[b].x1 - x; + x2 = tile_blackout[n].bo[b].x2 - x; + + if (y1 > y || y >= y2) { + continue; + } + if (x1 <= beg && end <= x2) { + hit = 1; + break; + } + } + if (hit) { + return 1; + } else { + return 0; + } +} + +/* + * For the subwin case follows the window if it is moved. + */ +void set_offset(void) { + Window w; + if (! subwin) { + return; + } + X_LOCK; + xtranslate(window, rootwin, 0, 0, &off_x, &off_y, &w, 0); + X_UNLOCK; +} + +/* + * Loop over 1-pixel tall horizontal scanlines looking for changes. + * Record the changes in tile_has_diff[]. Scanlines in the loop are + * equally spaced along y by NSCAN pixels, but have a slightly random + * starting offset ystart ( < NSCAN ) from scanlines[]. + */ +static int scan_display(int ystart, int rescan) { + char *src, *dst; + int pixelsize = bpp/8; + int x, y, w, n; + int tile_count = 0; + int nodiffs = 0, diff_hint; + + y = ystart; + + if (! main_fb) { + rfbLog("scan_display: no main_fb!\n"); + return 0; + } + + while (y < dpy_y) { + + if (use_xdamage) { + XD_tot++; + if (xdamage_hint_skip(y)) { + XD_skip++; + y += NSCAN; + continue; + } + } + + /* grab the horizontal scanline from the display: */ + X_LOCK; + XRANDR_SET_TRAP_RET(-1, "scan_display-set"); + copy_image(scanline, 0, y, 0, 0); + XRANDR_CHK_TRAP_RET(-1, "scan_display-chk"); + X_UNLOCK; + + /* for better memory i/o try the whole line at once */ + src = scanline->data; + dst = main_fb + y * main_bytes_per_line; + + if (! memcmp(dst, src, main_bytes_per_line)) { + /* no changes anywhere in scan line */ + nodiffs = 1; + if (! rescan) { + y += NSCAN; + continue; + } + } + + x = 0; + while (x < dpy_x) { + n = (x/tile_x) + (y/tile_y) * ntiles_x; + diff_hint = 0; + + if (blackouts) { + if (blackout_line_skip(n, x, y, rescan, + &tile_count)) { + x += NSCAN; + continue; + } + } + + if (rescan) { + if (nodiffs || tile_has_diff[n]) { + tile_count += tile_has_diff[n]; + x += NSCAN; + continue; + } + } else if (xdamage_tile_count && + tile_has_xdamage_diff[n]) { + tile_has_xdamage_diff[n] = 2; + diff_hint = 1; + } + + /* set ptrs to correspond to the x offset: */ + src = scanline->data + x * pixelsize; + dst = main_fb + y * main_bytes_per_line + x * pixelsize; + + /* compute the width of data to be compared: */ + if (x + NSCAN > dpy_x) { + w = dpy_x - x; + } else { + w = NSCAN; + } + + if (diff_hint || memcmp(dst, src, w * pixelsize)) { + /* found a difference, record it: */ + if (! blackouts) { + tile_has_diff[n] = 1; + tile_count++; + } else { + if (blackout_line_cmpskip(n, x, y, + dst, src, w, pixelsize)) { + tile_has_diff[n] = 0; + } else { + tile_has_diff[n] = 1; + tile_count++; + } + } + } + x += NSCAN; + } + y += NSCAN; + } + return tile_count; +} + + +static int scanlines[NSCAN] = { + 0, 16, 8, 24, 4, 20, 12, 28, + 10, 26, 18, 2, 22, 6, 30, 14, + 1, 17, 9, 25, 7, 23, 15, 31, + 19, 3, 27, 11, 29, 13, 5, 21 +}; + +/* + * toplevel for the scanning, rescanning, and applying the heuristics. + * returns number of changed tiles. + */ +int scan_for_updates(int count_only) { + int i, tile_count, tile_diffs; + int old_copy_tile; + double frac1 = 0.1; /* tweak parameter to try a 2nd scan_display() */ + double frac2 = 0.35; /* or 3rd */ + double frac3 = 0.02; /* do scan_display() again after copy_tiles() */ + static double last_poll = 0.0; + + if (slow_fb > 0.0) { + double now = dnow(); + if (now < last_poll + slow_fb) { + return 0; + } + last_poll = now; + } + + for (i=0; i < ntiles; i++) { + tile_has_diff[i] = 0; + tile_has_xdamage_diff[i] = 0; + tile_tried[i] = 0; + tile_copied[i] = 0; + } + for (i=0; i < ntiles_y; i++) { + /* could be useful, currently not used */ + tile_row_has_xdamage_diff[i] = 0; + } + xdamage_tile_count = 0; + + /* + * n.b. this program has only been tested so far with + * tile_x = tile_y = NSCAN = 32! + */ + + if (!count_only) { + scan_count++; + scan_count %= NSCAN; + + /* some periodic maintenance */ + if (subwin) { + set_offset(); /* follow the subwindow */ + } + if (indexed_color && scan_count % 4 == 0) { + /* check for changed colormap */ + set_colormap(0); + } + if (use_xdamage) { + /* first pass collecting DAMAGE events: */ + collect_xdamage(scan_count, 0); + } + } + +#define SCAN_FATAL(x) \ + if (x < 0) { \ + scan_in_progress = 0; \ + fb_copy_in_progress = 0; \ + return 0; \ + } + + /* scan with the initial y to the jitter value from scanlines: */ + scan_in_progress = 1; + tile_count = scan_display(scanlines[scan_count], 0); + SCAN_FATAL(tile_count); + + /* + * we do the XDAMAGE here too since after scan_display() + * there is a better chance we have received the events from + * the X server (otherwise the DAMAGE events will be processed + * in the *next* call, usually too late and wasteful since + * the unchanged tiles are read in again). + */ + if (use_xdamage) { + collect_xdamage(scan_count, 1); + } + if (count_only) { + scan_in_progress = 0; + fb_copy_in_progress = 0; + return tile_count; + } + + if (xdamage_tile_count) { + /* pick up "known" damaged tiles we missed in scan_display() */ + for (i=0; i < ntiles; i++) { + if (tile_has_diff[i]) { + continue; + } + if (tile_has_xdamage_diff[i]) { + tile_has_diff[i] = 1; + if (tile_has_xdamage_diff[i] == 1) { + tile_has_xdamage_diff[i] = 2; + tile_count++; + } + } + } + } + + nap_set(tile_count); + + if (fs_factor && frac1 >= fs_frac) { + /* make frac1 < fs_frac if fullscreen updates are enabled */ + frac1 = fs_frac/2.0; + } + + if (tile_count > frac1 * ntiles) { + /* + * many tiles have changed, so try a rescan (since it should + * be short compared to the many upcoming copy_tiles() calls) + */ + + /* this check is done to skip the extra scan_display() call */ + if (! fs_factor || tile_count <= fs_frac * ntiles) { + int cp, tile_count_old = tile_count; + + /* choose a different y shift for the 2nd scan: */ + cp = (NSCAN - scan_count) % NSCAN; + + tile_count = scan_display(scanlines[cp], 1); + SCAN_FATAL(tile_count); + + if (tile_count >= (1 + frac2) * tile_count_old) { + /* on a roll... do a 3rd scan */ + cp = (NSCAN - scan_count + 7) % NSCAN; + tile_count = scan_display(scanlines[cp], 1); + SCAN_FATAL(tile_count); + } + } + scan_in_progress = 0; + + /* + * At some number of changed tiles it is better to just + * copy the full screen at once. I.e. time = c1 + m * r1 + * where m is number of tiles, r1 is the copy_tiles() + * time, and c1 is the scan_display() time: for some m + * it crosses the full screen update time. + * + * We try to predict that crossover with the fs_frac + * fudge factor... seems to be about 1/2 the total number + * of tiles. n.b. this ignores network bandwidth, + * compression time etc... + * + * Use -fs 1.0 to disable on slow links. + */ + if (fs_factor && tile_count > fs_frac * ntiles) { + int cs; + fb_copy_in_progress = 1; + cs = copy_screen(); + fb_copy_in_progress = 0; + SCAN_FATAL(cs); + if (use_threads && pointer_mode != 1) { + pointer(-1, 0, 0, NULL); + } + nap_check(tile_count); + return tile_count; + } + } + scan_in_progress = 0; + + /* copy all tiles with differences from display to rfb framebuffer: */ + fb_copy_in_progress = 1; + + if (single_copytile || tile_shm_count < ntiles_x) { + /* + * Old way, copy I/O one tile at a time. + */ + old_copy_tile = 1; + } else { + /* + * New way, does runs of horizontal tiles at once. + * Note that below, for simplicity, the extra tile finding + * (e.g. copy_tiles_backward_pass) is done the old way. + */ + old_copy_tile = 0; + } + if (old_copy_tile) { + tile_diffs = copy_all_tiles(); + } else { + tile_diffs = copy_all_tile_runs(); + } + SCAN_FATAL(tile_diffs); + + /* + * This backward pass for upward and left tiles complements what + * was done in copy_all_tiles() for downward and right tiles. + */ + tile_diffs = copy_tiles_backward_pass(); + SCAN_FATAL(tile_diffs); + + if (tile_diffs > frac3 * ntiles) { + /* + * we spent a lot of time in those copy_tiles, run + * another scan, maybe more of the screen changed. + */ + int cp = (NSCAN - scan_count + 13) % NSCAN; + + scan_in_progress = 1; + tile_count = scan_display(scanlines[cp], 1); + SCAN_FATAL(tile_count); + scan_in_progress = 0; + + tile_diffs = copy_tiles_additional_pass(); + SCAN_FATAL(tile_diffs); + } + + /* Given enough tile diffs, try the islands: */ + if (grow_fill && tile_diffs > 4) { + tile_diffs = grow_islands(); + } + SCAN_FATAL(tile_diffs); + + /* Given enough tile diffs, try the gaps: */ + if (gaps_fill && tile_diffs > 4) { + tile_diffs = fill_tile_gaps(); + } + SCAN_FATAL(tile_diffs); + + fb_copy_in_progress = 0; + if (use_threads && pointer_mode != 1) { + /* + * tell the pointer handler it can process any queued + * pointer events: + */ + pointer(-1, 0, 0, NULL); + } + + if (blackouts) { + /* ignore any diffs in completely covered tiles */ + int x, y, n; + for (y=0; y < ntiles_y; y++) { + for (x=0; x < ntiles_x; x++) { + n = x + y * ntiles_x; + if (tile_blackout[n].cover == 2) { + tile_has_diff[n] = 0; + } + } + } + } + + hint_updates(); /* use x0rfbserver hints algorithm */ + + /* Work around threaded rfbProcessClientMessage() calls timeouts */ + if (use_threads) { + ping_clients(tile_diffs); + } + + + nap_check(tile_diffs); + return tile_diffs; +} + + diff --git a/x11vnc/scan.h b/x11vnc/scan.h new file mode 100644 index 0000000..55f45f1 --- /dev/null +++ b/x11vnc/scan.h @@ -0,0 +1,24 @@ +#ifndef _X11VNC_SCAN_H +#define _X11VNC_SCAN_H + +/* -- scan.h -- */ + +extern int nap_ok; + +extern void initialize_tiles(void); +extern void free_tiles(void); +extern void shm_delete(XShmSegmentInfo *shm); +extern void shm_clean(XShmSegmentInfo *shm, XImage *xim); +extern void initialize_polling_images(void); +extern void scale_rect(double factor, int blend, int interpolate, int Bpp, + char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line, + int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark); +extern void scale_and_mark_rect(int X1, int Y1, int X2, int Y2); +extern void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force); +extern int copy_screen(void); +extern int copy_snap(void); +extern void nap_sleep(int ms, int split); +extern void set_offset(void); +extern int scan_for_updates(int count_only); + +#endif /* _X11VNC_SCAN_H */ diff --git a/x11vnc/screen.c b/x11vnc/screen.c new file mode 100644 index 0000000..ac4bf7b --- /dev/null +++ b/x11vnc/screen.c @@ -0,0 +1,1929 @@ +/* -- screen.c -- */ + +#include "x11vnc.h" +#include "xevents.h" +#include "xwrappers.h" +#include "xinerama.h" +#include "xdamage.h" +#include "win_utils.h" +#include "cleanup.h" +#include "userinput.h" +#include "scan.h" +#include "user.h" +#include "rates.h" +#include "pointer.h" +#include "keyboard.h" +#include "cursor.h" +#include "connections.h" +#include "remote.h" + +void set_colormap(int reset); +void set_nofb_params(int restore); +void set_raw_fb_params(int restore); +void do_new_fb(int reset_mem); +void check_padded_fb(void); +void install_padded_fb(char *geom); +XImage *initialize_xdisplay_fb(void); +void parse_scale_string(char *str, double *factor, int *scaling, int *blend, + int *nomult4, int *pad, int *interpolate, int *numer, int *denom); +int scale_round(int len, double fac); +void initialize_screen(int *argc, char **argv, XImage *fb); +void set_vnc_desktop_name(void); + + +static void debug_colormap(XImage *fb); +static void set_visual(char *str); +static void nofb_hook(rfbClientPtr cl); +static void remove_fake_fb(void); +static void install_fake_fb(int w, int h, int bpp); +static void initialize_snap_fb(void); +static XImage *initialize_raw_fb(void); +static void initialize_clipshift(void); +static int wait_until_mapped(Window win); +static void setup_scaling(int *width_in, int *height_in); + + +/* + * X11 and rfb display/screen related routines + */ + +/* + * Some handling of 8bpp PseudoColor colormaps. Called for initializing + * the clients and dynamically if -flashcmap is specified. + */ +#define NCOLOR 256 +void set_colormap(int reset) { + static int first = 1; + static XColor color[NCOLOR], prev[NCOLOR]; + Colormap cmap; + Visual *vis; + int i, ncells, diffs = 0; + + if (reset) { + first = 1; + if (screen->colourMap.data.shorts) { + free(screen->colourMap.data.shorts); + screen->colourMap.data.shorts = NULL; + } + } + + if (first) { + screen->colourMap.count = NCOLOR; + screen->serverFormat.trueColour = FALSE; + screen->colourMap.is16 = TRUE; + screen->colourMap.data.shorts = (unsigned short *) + malloc(3*sizeof(unsigned short) * NCOLOR); + } + + for (i=0; i < NCOLOR; i++) { + prev[i].red = color[i].red; + prev[i].green = color[i].green; + prev[i].blue = color[i].blue; + } + + X_LOCK; + + cmap = DefaultColormap(dpy, scr); + ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr)); + vis = default_visual; + + if (subwin) { + XWindowAttributes attr; + + if (XGetWindowAttributes(dpy, window, &attr)) { + cmap = attr.colormap; + vis = attr.visual; + ncells = vis->map_entries; + } + } + + if (ncells != NCOLOR) { + if (first && ! quiet) { + rfbLog("set_colormap: number of cells is %d " + "instead of %d.\n", ncells, NCOLOR); + } + if (! shift_cmap) { + screen->colourMap.count = ncells; + } + } + + if (flash_cmap && ! first) { + XWindowAttributes attr; + Window c; + int tries = 0; + + c = window; + while (c && tries++ < 16) { + c = query_pointer(c); + if (valid_window(c, &attr, 0)) { + if (attr.colormap && attr.map_installed) { + cmap = attr.colormap; + vis = attr.visual; + ncells = vis->map_entries; + break; + } + } else { + break; + } + } + } + if (ncells > NCOLOR && ! quiet) { + rfbLog("set_colormap: big problem: ncells=%d > %d\n", + ncells, NCOLOR); + } + + if (vis->class == TrueColor || vis->class == DirectColor) { + /* + * Kludge to make 8bpp TrueColor & DirectColor be like + * the StaticColor map. The ncells = 8 is "8 per subfield" + * mentioned in xdpyinfo. Looks OK... perhaps fortuitously. + */ + if (ncells == 8 && ! shift_cmap) { + ncells = NCOLOR; + } + } + + for (i=0; i < ncells; i++) { + color[i].pixel = i; + color[i].pad = 0; + } + + XQueryColors(dpy, cmap, color, ncells); + + X_UNLOCK; + + for(i = ncells - 1; i >= 0; i--) { + int k = i + shift_cmap; + + screen->colourMap.data.shorts[i*3+0] = color[i].red; + screen->colourMap.data.shorts[i*3+1] = color[i].green; + screen->colourMap.data.shorts[i*3+2] = color[i].blue; + + if (prev[i].red != color[i].red || + prev[i].green != color[i].green || + prev[i].blue != color[i].blue ) { + diffs++; + } + + if (shift_cmap && k >= 0 && k < NCOLOR) { + /* kludge to copy the colors to higher pixel values */ + screen->colourMap.data.shorts[k*3+0] = color[i].red; + screen->colourMap.data.shorts[k*3+1] = color[i].green; + screen->colourMap.data.shorts[k*3+2] = color[i].blue; + } + } + + if (diffs && ! first) { + if (! all_clients_initialized()) { + rfbLog("set_colormap: warning: sending cmap " + "with uninitialized clients.\n"); + } + if (shift_cmap) { + rfbSetClientColourMaps(screen, 0, NCOLOR); + } else { + rfbSetClientColourMaps(screen, 0, ncells); + } + } + + first = 0; +} + +static void debug_colormap(XImage *fb) { + static int debug_cmap = -1; + int i, k, histo[NCOLOR]; + + if (debug_cmap < 0) { + if (getenv("DEBUG_CMAP") != NULL) { + debug_cmap = 1; + } else { + debug_cmap = 0; + } + } + if (! debug_cmap) { + return; + } + if (! fb) { + return; + } + if (fb->bits_per_pixel > 8) { + return; + } + + for (i=0; i < NCOLOR; i++) { + histo[i] = 0; + } + for (k = 0; k < fb->width * fb->height; k++) { + unsigned char n; + char c = *(fb->data + k); + + n = (unsigned char) c; + histo[n]++; + } + fprintf(stderr, "\nColormap histogram for current screen contents:\n"); + for (i=0; i < NCOLOR; i++) { + unsigned short r = screen->colourMap.data.shorts[i*3+0]; + unsigned short g = screen->colourMap.data.shorts[i*3+1]; + unsigned short b = screen->colourMap.data.shorts[i*3+2]; + + fprintf(stderr, " %03d: %7d %04x/%04x/%04x", i, histo[i], + r, g, b); + if ((i+1) % 2 == 0) { + fprintf(stderr, "\n"); + } + } + fprintf(stderr, "\n"); +} + +/* + * Experimental mode to force the visual of the window instead of querying + * it. Used for testing, overriding some rare cases (win2vnc), and for + * -overlay . Input string can be a decimal or 0x hex or something like + * TrueColor or TrueColor:24 to force a depth as well. + * + * visual_id and possibly visual_depth are set. + */ +static void set_visual(char *str) { + int vis, vdepth, defdepth = DefaultDepth(dpy, scr); + XVisualInfo vinfo; + char *p, *vstring = strdup(str); + + visual_id = (VisualID) 0; + visual_depth = 0; + + if (!strcmp(vstring, "ignore") || !strcmp(vstring, "default") + || !strcmp(vstring, "")) { + free(vstring); + return; + } + + /* set visual depth */ + if ((p = strchr(vstring, ':')) != NULL) { + visual_depth = atoi(p+1); + *p = '\0'; + vdepth = visual_depth; + } else { + vdepth = defdepth; + } + if (! quiet) { + fprintf(stderr, "\nVisual Info:\n"); + fprintf(stderr, " set_visual(\"%s\")\n", str); + fprintf(stderr, " visual_depth: %d\n", vdepth); + } + + /* set visual id number */ + if (strcmp(vstring, "StaticGray") == 0) { + vis = StaticGray; + } else if (strcmp(vstring, "GrayScale") == 0) { + vis = GrayScale; + } else if (strcmp(vstring, "StaticColor") == 0) { + vis = StaticColor; + } else if (strcmp(vstring, "PseudoColor") == 0) { + vis = PseudoColor; + } else if (strcmp(vstring, "TrueColor") == 0) { + vis = TrueColor; + } else if (strcmp(vstring, "DirectColor") == 0) { + vis = DirectColor; + } else { + unsigned int v_in; + if (sscanf(vstring, "0x%x", &v_in) != 1) { + if (sscanf(vstring, "%u", &v_in) == 1) { + visual_id = (VisualID) v_in; + return; + } + rfbLogEnable(1); + rfbLog("invalid -visual arg: %s\n", vstring); + X_UNLOCK; + clean_up_exit(1); + } + visual_id = (VisualID) v_in; + free(vstring); + return; + } + + if (! quiet) fprintf(stderr, " visual: %d\n", vis); + if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) { + ; + } else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) { + ; + } else { + rfbLogEnable(1); + rfbLog("could not find visual: %s\n", vstring); + X_UNLOCK; + clean_up_exit(1); + } + free(vstring); + + /* set numerical visual id. */ + visual_id = vinfo.visualid; +} + +void set_nofb_params(int restore) { + static int first = 1; + static int save[100]; + int i = 0; + + if (first) { + first = 0; + save[i++] = use_xfixes; + save[i++] = use_xdamage; + save[i++] = use_xrecord; + save[i++] = wireframe; + save[i++] = use_solid_bg; + save[i++] = overlay; + save[i++] = overlay_cursor; + save[i++] = using_shm; + save[i++] = single_copytile; + save[i++] = take_naps; + save[i++] = measure_speeds; + save[i++] = grab_buster; + save[i++] = show_cursor; + save[i++] = cursor_shape_updates; + save[i++] = cursor_pos_updates; + } + if (restore) { + i = 0; + use_xfixes = save[i++]; + use_xdamage = save[i++]; + use_xrecord = save[i++]; + wireframe = save[i++]; + use_solid_bg = save[i++]; + overlay = save[i++]; + overlay_cursor = save[i++]; + using_shm = save[i++]; + single_copytile = save[i++]; + take_naps = save[i++]; + measure_speeds = save[i++]; + grab_buster = save[i++]; + show_cursor = save[i++]; + cursor_shape_updates = save[i++]; + cursor_pos_updates = save[i++]; + + if (cursor_shape_updates) { + restore_cursor_shape_updates(screen); + } + initialize_cursors_mode(); + + return; + } + + use_xfixes = 0; + use_xdamage = 0; + use_xrecord = 0; + wireframe = 0; + + use_solid_bg = 0; + overlay = 0; + overlay_cursor = 0; + + using_shm = 0; + single_copytile = 1; + + take_naps = 0; + measure_speeds = 0; + + /* got_grab_buster? */ + grab_buster = 0; + + show_cursor = 0; + show_multiple_cursors = 0; + cursor_shape_updates = 0; + if (! got_cursorpos) { + cursor_pos_updates = 0; + } + + if (! quiet) { + rfbLog("disabling: xfixes, xdamage, solid, overlay, shm,\n"); + rfbLog(" wireframe, scrollcopyrect,\n"); + rfbLog(" noonetile, nap, cursor, %scursorshape\n", + got_cursorpos ? "" : "cursorpos, " ); + rfbLog(" in -nofb mode.\n"); + } +} + +static char *raw_fb_orig_dpy = NULL; + +void set_raw_fb_params(int restore) { + static int first = 1; + static int vo0, us0, sm0, ws0, wp0, wb0, na0, tn0; + static int xr0, sb0; + static char *mc0; + + /* + * set turn off a bunch of parameters not compatible with + * -rawfb mode: 1) ignoring the X server 2) ignoring user input. + */ + + if (first) { + /* at least save the initial settings... */ + vo0 = view_only; + ws0 = watch_selection; + wp0 = watch_primary; + wb0 = watch_bell; + na0 = no_autorepeat; + sb0 = use_solid_bg; + + us0 = use_snapfb; + sm0 = using_shm; + tn0 = take_naps; + xr0 = xrandr; + mc0 = multiple_cursors_mode; + + first = 0; + } + + if (restore) { + view_only = vo0; + watch_selection = ws0; + watch_primary = wp0; + watch_bell = wb0; + no_autorepeat = na0; + use_solid_bg = sb0; + + use_snapfb = us0; + using_shm = sm0; + take_naps = tn0; + xrandr = xr0; + multiple_cursors_mode = mc0; + + if (! dpy && raw_fb_orig_dpy) { + dpy = XOpenDisplay(raw_fb_orig_dpy); + if (dpy) { + if (! quiet) rfbLog("reopened DISPLAY: %s\n", + raw_fb_orig_dpy); + } else { + if (! quiet) rfbLog("WARNING: failed to reopen " + "DISPLAY: %s\n", raw_fb_orig_dpy); + } + } + return; + } + + if (! quiet) { + rfbLog("set_raw_fb_params: modifying settings for " + "-rawfb mode.\n"); + } + + if (got_noviewonly) { + /* + * The user input parameters are not unset under + * -noviewonly... this usage should be very rare + * (i.e. rawfb but also send user input to the X + * display, most likely using /dev/fb0 for some reason...) + */ + if (! quiet) { + rfbLog("rawfb: -noviewonly mode: still sending mouse and\n"); + rfbLog("rawfb: keyboard input to the X DISPLAY!!\n"); + } + } else { + /* Normal case: */ + if (! view_only) { + if (! quiet) rfbLog("rawfb: setting view_only\n"); + view_only = 1; + } + if (watch_selection) { + if (! quiet) rfbLog("rawfb: turning off " + "watch_selection\n"); + watch_selection = 0; + } + if (watch_primary) { + if (! quiet) rfbLog("rawfb: turning off " + "watch_primary\n"); + watch_primary = 0; + } + if (watch_bell) { + if (! quiet) rfbLog("rawfb: turning off watch_bell\n"); + watch_bell = 0; + } + if (no_autorepeat) { + if (! quiet) rfbLog("rawfb: turning off " + "no_autorepeat\n"); + no_autorepeat = 0; + } + if (use_solid_bg) { + if (! quiet) rfbLog("rawfb: turning off " + "use_solid_bg\n"); + use_solid_bg = 0; + } + multiple_cursors_mode = strdup("arrow"); + } + if (use_snapfb) { + if (! quiet) rfbLog("rawfb: turning off use_snapfb\n"); + use_snapfb = 0; + } + if (using_shm) { + if (! quiet) rfbLog("rawfb: turning off using_shm\n"); + using_shm = 0; + } + if (take_naps) { + if (! quiet) rfbLog("rawfb: turning off take_naps\n"); + take_naps = 0; + } + if (xrandr) { + if (! quiet) rfbLog("rawfb: turning off xrandr\n"); + xrandr = 0; + } +} + +/* + * Presumably under -nofb the clients will never request the framebuffer. + * However, we have gotten such a request... so let's just give them + * the current view on the display. n.b. x2vnc and perhaps win2vnc + * requests a 1x1 pixel for some workaround so sadly this evidently + * nearly always happens. + */ +static void nofb_hook(rfbClientPtr cl) { + XImage *fb; + rfbLog("framebuffer requested in -nofb mode by client %s\n", cl->host); + /* ignore xrandr */ + fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); + main_fb = fb->data; + rfb_fb = main_fb; + screen->frameBuffer = rfb_fb; + screen->displayHook = NULL; +} + +void do_new_fb(int reset_mem) { + XImage *fb; + char *old_main = main_fb; + char *old_rfb = rfb_fb; + + /* for threaded we really should lock libvncserver out. */ + if (use_threads) { + rfbLog("warning: changing framebuffers while threaded may\n"); + rfbLog(" not work, do not use -threads if problems arise.\n"); + } + + if (reset_mem == 1) { + /* reset_mem == 2 is a hack for changing users... */ + clean_shm(0); + free_tiles(); + } + + fb = initialize_xdisplay_fb(); + + initialize_screen(NULL, NULL, fb); + + if (reset_mem) { + initialize_tiles(); + initialize_blackouts_and_xinerama(); + initialize_polling_images(); + } + + if (old_main != old_rfb && old_main) { + free(old_main); + } + if (old_rfb) { + free(old_rfb); + } + fb0 = fb; +} + +static void remove_fake_fb(void) { + if (! screen) { + return; + } + rfbLog("removing fake fb: 0x%x\n", fake_fb); + + do_new_fb(1); + + /* + * fake_fb is freed in do_new_fb(), but we set to NULL here to + * indicate it is gone. + */ + fake_fb = NULL; +} + +static void install_fake_fb(int w, int h, int bpp) { + int bpc; + if (! screen) { + return; + } + if (fake_fb) { + free(fake_fb); + } + fake_fb = (char *) calloc(w*h*bpp/8, 1); + if (! fake_fb) { + rfbLog("could not create fake fb: %dx%d %d\n", w, h, bpp); + return; + } + bpc = guess_bits_per_color(bpp); + rfbLog("installing fake fb: %dx%d %d\n", w, h, bpp); + rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n", + screen, fake_fb, w, h, bpc, 1, bpp/8); + + rfbNewFramebuffer(screen, fake_fb, w, h, bpc, 1, bpp/8); +} + +void check_padded_fb(void) { + if (! fake_fb) { + return; + } + if (time(0) > pad_geometry_time+1 && all_clients_initialized()) { + remove_fake_fb(); + } +} + +void install_padded_fb(char *geom) { + int w, h; + int ok = 1; + if (! geom || *geom == '\0') { + ok = 0; + } else if (sscanf(geom, "%dx%d", &w, &h) != 2) { + ok = 0; + } + w = nabs(w); + h = nabs(h); + + if (w < 5) w = 5; + if (h < 5) h = 5; + + if (!ok) { + rfbLog("skipping invalid pad geometry: '%s'\n", NONUL(geom)); + return; + } + install_fake_fb(w, h, bpp); + pad_geometry_time = time(0); +} + +static void initialize_snap_fb(void) { + if (snap_fb) { + free(snap_fb); + } + snap = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, + ZPixmap); + snap_fb = snap->data; +} + +static XImage *initialize_raw_fb(void) { + char *str, *q; + int w, h, b, shmid = 0; + unsigned long rm = 0, gm = 0, bm = 0; + static XImage ximage_struct; /* n.b.: not (XImage *) */ + int closedpy = 1, i, m; + + if (raw_fb_addr || raw_fb_seek) { + if (raw_fb_shm) { + shmdt(raw_fb_addr); +#if LIBVNCSERVER_HAVE_MMAP + } else if (raw_fb_mmap) { + munmap(raw_fb_addr, raw_fb_mmap); + if (raw_fb_fd >= 0) { + close(raw_fb_fd); + } +#endif + } else if (raw_fb_seek) { + if (raw_fb_fd >= 0) { + close(raw_fb_fd); + } + } + raw_fb_addr = NULL; + } + if (! raw_fb_str) { + return NULL; + } + + + if ( (q = strstr(raw_fb_str, "setup:")) == raw_fb_str) { + FILE *pipe; + char line[1024], *t; + + set_child_info(); + q += strlen("setup:"); + if (no_external_cmds) { + rfbLogEnable(1); + rfbLog("cannot run external commands in -nocmds " + "mode:\n"); + rfbLog(" \"%s\"\n", q); + rfbLog(" exiting.\n"); + clean_up_exit(1); + } + rfbLog("running command to setup rawfb: %s\n", q); + pipe = popen(q, "r"); + if (! pipe) { + rfbLogEnable(1); + rfbLog("popen of setup command failed.\n"); + rfbLogPerror("popen"); + clean_up_exit(1); + } + line[0] = '\0'; + if (fgets(line, 1024, pipe) == NULL) { + rfbLogEnable(1); + rfbLog("read of setup command failed.\n"); + clean_up_exit(1); + } + pclose(pipe); + str = strdup(line); + t = str; + while (*t != '\0') { + if (*t == '\n') { + *t = '\0'; + } + t++; + } + rfbLog("setup command returned: %s\n", str); + + } else { + str = strdup(raw_fb_str); + } + + /* + * uppercase means do not close the display (e.g. for remote control) + */ + if (strstr(str, "SHM:") == str) { + closedpy = 0; + str[0] = 's'; str[1] = 'h'; str[2] = 'm'; + } else if (strstr(str, "MAP:") == str) { + closedpy = 0; + str[0] = 'm'; str[1] = 'a'; str[2] = 'p'; + } else if (strstr(str, "MMAP:") == str) { + closedpy = 0; + str[0] = 'm'; str[1] = 'm'; str[2] = 'a'; str[3] = 'p'; + } else if (strstr(str, "FILE:") == str) { + str[0] = 'f'; str[1] = 'i'; str[2] = 'l'; str[3] = 'e'; + closedpy = 0; + } + + if (closedpy && !view_only && got_noviewonly) { + rfbLog("not closing X DISPLAY under -noviewonly option.\n"); + closedpy = 0; + if (! window) { + window = rootwin; + } + } + + if (! raw_fb_orig_dpy && dpy) { + raw_fb_orig_dpy = strdup(DisplayString(dpy)); + } +#ifndef BOLDLY_CLOSE_DISPLAY +#define BOLDLY_CLOSE_DISPLAY 1 +#endif +#if BOLDLY_CLOSE_DISPLAY + if (closedpy) { + if (dpy) { + rfbLog("closing X DISPLAY: %s in rawfb mode.\n", + DisplayString(dpy)); + XCloseDisplay(dpy); /* yow! */ + } + dpy = NULL; + } +#endif + + /* + * -rawfb shm:163938442@640x480x32:ff/ff00/ff0000+3000 + * -rawfb map:/path/to/file@640x480x32:ff/ff00/ff0000 + * -rawfb file:/path/to/file@640x480x32:ff/ff00/ff0000 + */ + + raw_fb_offset = 0; + + /* +O offset */ + if ((q = strrchr(str, '+')) != NULL) { + if (sscanf(q, "+%d", &raw_fb_offset) == 1) { + *q = '\0'; + } else { + raw_fb_offset = 0; + } + } + /* :R/G/B masks */ + if ((q = strrchr(str, ':')) != NULL) { + if (sscanf(q, ":%lx/%lx/%lx", &rm, &gm, &bm) == 3) { + *q = '\0'; + } else if (sscanf(q, ":0x%lx/0x%lx/0x%lx", &rm, &gm, &bm)== 3) { + *q = '\0'; + } else if (sscanf(q, ":%lu/%lu/%lu", &rm, &gm, &bm) == 3) { + *q = '\0'; + } else { + rm = 0; + gm = 0; + bm = 0; + } + } + if ((q = strrchr(str, '@')) == NULL) { + rfbLogEnable(1); + rfbLog("invalid rawfb str: %s\n", str); + clean_up_exit(1); + } + /* @WxHxB */ + if (sscanf(q, "@%dx%dx%d", &w, &h, &b) != 3) { + rfbLogEnable(1); + rfbLog("invalid rawfb str: %s\n", str); + clean_up_exit(1); + } + *q = '\0'; + + if (strstr(str, "shm:") != str && strstr(str, "mmap:") != str && + strstr(str, "map:") != str && strstr(str, "file:") != str) { + /* hmmm, not following directions, see if map: applies */ + struct stat sbuf; + if (stat(str, &sbuf) == 0) { + char *new; + int len = strlen("map:") + strlen(str) + 1; + rfbLog("no type prefix: %s\n", raw_fb_str); + rfbLog(" but file exists, so assuming: map:%s\n", + raw_fb_str); + new = (char *) malloc(len); + strcpy(new, "map:"); + strcat(new, str); + free(str); + str = new; + } + } + + dpy_x = wdpy_x = w; + dpy_y = wdpy_y = h; + off_x = 0; + off_y = 0; + + raw_fb_shm = 0; + raw_fb_mmap = 0; + raw_fb_seek = 0; + raw_fb_fd = -1; + raw_fb_addr = NULL; + + if (sscanf(str, "shm:%d", &shmid) == 1) { + /* shm:N */ +#if LIBVNCSERVER_HAVE_XSHM + raw_fb_addr = (char *) shmat(shmid, 0, SHM_RDONLY); + if (! raw_fb_addr) { + rfbLogEnable(1); + rfbLog("failed to attach to shm: %d, %s\n", shmid, str); + rfbLogPerror("shmat"); + clean_up_exit(1); + } + raw_fb_shm = 1; + rfbLog("rawfb: shm: %d W: %d H: %d B: %d addr: %p\n", + shmid, w, h, b, raw_fb_addr); +#else + rfbLogEnable(1); + rfbLog("x11vnc was compiled without shm support.\n"); + rfbLogPerror("shmat"); + clean_up_exit(1); +#endif + } else if (strstr(str, "map:") == str || strstr(str, "mmap:") == str + || strstr(str, "file:") == str) { + /* map:/path/... or file:/path */ + int fd, do_mmap = 1, size; + struct stat sbuf; + + if (*str == 'f') { + do_mmap = 0; + } + q = strchr(str, ':'); + q++; + + fd = open(q, O_RDONLY); + if (fd < 0) { + rfbLogEnable(1); + rfbLog("failed to open file: %s, %s\n", q, str); + rfbLogPerror("open"); + clean_up_exit(1); + } + raw_fb_fd = fd; + + size = w*h*b/8 + raw_fb_offset; + if (fstat(fd, &sbuf) == 0) { + if (S_ISREG(sbuf.st_mode)) { + if (0) size = sbuf.st_size; + } else { + rfbLog("raw fb is non-regular file: %s\n", q); + } + } + + if (do_mmap) { +#if LIBVNCSERVER_HAVE_MMAP + raw_fb_addr = mmap(0, size, PROT_READ, MAP_SHARED, + fd, 0); + + if (raw_fb_addr == MAP_FAILED || raw_fb_addr == NULL) { + rfbLogEnable(1); + rfbLog("failed to mmap file: %s, %s\n", q, str); + rfbLog(" raw_fb_addr: %p\n", raw_fb_addr); + rfbLogPerror("mmap"); + clean_up_exit(1); + } + raw_fb_mmap = size; + + rfbLog("rawfb: mmap file: %s\n", q); + rfbLog(" w: %d h: %d b: %d addr: %p sz: %d\n", w, h, + b, raw_fb_addr, size); +#else + rfbLog("mmap(2) not supported on system, using" + " slower lseek(2)\n"); + raw_fb_seek = size; +#endif + } else { + raw_fb_seek = size; + + rfbLog("rawfb: seek file: %s\n", q); + rfbLog(" W: %d H: %d B: %d sz: %d\n", w, h, b, size); + } + } else { + rfbLogEnable(1); + rfbLog("invalid rawfb str: %s\n", str); + clean_up_exit(1); + } + + if (! raw_fb_image) { + raw_fb_image = &ximage_struct; + } + + initialize_clipshift(); + + raw_fb = (char *) malloc(dpy_x * dpy_y * b/8); + raw_fb_image->data = raw_fb; + raw_fb_image->format = ZPixmap; + raw_fb_image->width = dpy_x; + raw_fb_image->height = dpy_y; + raw_fb_image->bits_per_pixel = b; + raw_fb_image->bytes_per_line = dpy_x*b/8; + + if (rm == 0 && gm == 0 && bm == 0) { + /* guess masks... */ + if (b == 24 || b == 32) { + rm = 0xff0000; + gm = 0x00ff00; + bm = 0x0000ff; + } else if (b == 16) { + rm = 0xf800; + gm = 0x07e0; + bm = 0x001f; + } else if (b == 8) { + rm = 0x07; + gm = 0x38; + bm = 0xc0; + } + } + + raw_fb_image->red_mask = rm; + raw_fb_image->green_mask = gm; + raw_fb_image->blue_mask = bm; + + raw_fb_image->depth = 0; + m = 1; + for (i=0; i<32; i++) { + if (rm & m) { + raw_fb_image->depth++; + } + if (gm & m) { + raw_fb_image->depth++; + } + if (bm & m) { + raw_fb_image->depth++; + } + m = m << 1; + } + if (! raw_fb_image->depth) { + raw_fb_image->depth = (b == 32) ? 24 : b; + } + + if (clipshift) { + memset(raw_fb, 0xff, dpy_x * dpy_y * b/8); + } else if (raw_fb_addr) { + memcpy(raw_fb, raw_fb_addr + raw_fb_offset, dpy_x*dpy_y*b/8); + } else { + memset(raw_fb, 0xff, dpy_x * dpy_y * b/8); + } + + rfbLog("rawfb: raw_fb %p\n", raw_fb); + + free(str); + + return raw_fb_image; +} + +static void initialize_clipshift(void) { + clipshift = 0; + cdpy_x = cdpy_y = coff_x = coff_y = 0; + if (clip_str) { + int w, h, x, y, bad = 0; + if (parse_geom(clip_str, &w, &h, &x, &y, wdpy_x, wdpy_y)) { + if (x < 0) { + x = 0; + } + if (y < 0) { + y = 0; + } + if (x + w > wdpy_x) { + w = wdpy_x - x; + } + if (y + h > wdpy_y) { + h = wdpy_y - y; + } + if (w <= 0 || h <= 0) { + bad = 1; + } + } else { + bad = 1; + } + if (bad) { + rfbLog("skipping invalid -clip WxH+X+Y: %s\n", + clip_str); + } else { + /* OK, change geom behind everyone's back... */ + cdpy_x = w; + cdpy_y = h; + coff_x = x; + coff_y = y; + + clipshift = 1; + + dpy_x = cdpy_x; + dpy_y = cdpy_y; + } + } +} + +static int wait_until_mapped(Window win) { + int ms = 50, waittime = 30; + time_t start = time(0); + XWindowAttributes attr; + + while (1) { + if (! valid_window(win, NULL, 0)) { + if (time(0) > start + waittime) { + break; + } + usleep(ms * 1000); + continue; + } + if (! XGetWindowAttributes(dpy, win, &attr)) { + return 0; + } + if (attr.map_state == IsViewable) { + return 1; + } + usleep(ms * 1000); + } + return 0; +} + +/* + * initialize a fb for the X display + */ +XImage *initialize_xdisplay_fb(void) { + XImage *fb; + char *vis_str = visual_str; + int try = 0, subwin_tries = 3; + XErrorHandler old_handler; + int subwin_bs; + + if (raw_fb_str) { + return initialize_raw_fb(); + } + + X_LOCK; + if (subwin) { + if (subwin_wait_mapped) { + wait_until_mapped(subwin); + } + if (!valid_window((Window) subwin, NULL, 0)) { + rfbLogEnable(1); + rfbLog("invalid sub-window: 0x%lx\n", subwin); + X_UNLOCK; + clean_up_exit(1); + } + } + + if (overlay) { + /* + * ideally we'd like to not have to cook up the + * visual variables but rather let it all come out + * of XReadScreen(), however there is no way to get + * a default visual out of it, so we pretend -visual + * TrueColor:NN was supplied with NN usually 24. + */ + char str[32]; + Window twin = subwin ? subwin : rootwin; + XImage *xi; + + xi = xreadscreen(dpy, twin, 0, 0, 8, 8, False); + sprintf(str, "TrueColor:%d", xi->depth); + if (xi->depth != 24 && ! quiet) { + rfbLog("warning: overlay image has depth %d " + "instead of 24.\n", xi->depth); + } + XDestroyImage(xi); + if (visual_str != NULL && ! quiet) { + rfbLog("warning: replacing '-visual %s' by '%s' " + "for use with -overlay\n", visual_str, str); + } + vis_str = strdup(str); + } + + if (vis_str != NULL) { + set_visual(vis_str); + if (vis_str != visual_str) { + free(vis_str); + } + } + + /* set up parameters for subwin or non-subwin cases: */ + + if (! subwin) { + /* full screen */ + window = rootwin; + dpy_x = wdpy_x = DisplayWidth(dpy, scr); + dpy_y = wdpy_y = DisplayHeight(dpy, scr); + off_x = 0; + off_y = 0; + /* this may be overridden via visual_id below */ + default_visual = DefaultVisual(dpy, scr); + } else { + /* single window */ + XWindowAttributes attr; + + window = (Window) subwin; + if (! XGetWindowAttributes(dpy, window, &attr)) { + rfbLogEnable(1); + rfbLog("invalid window: 0x%lx\n", window); + X_UNLOCK; + clean_up_exit(1); + } + dpy_x = wdpy_x = attr.width; + dpy_y = wdpy_y = attr.height; + + subwin_bs = attr.backing_store; + + /* this may be overridden via visual_id below */ + default_visual = attr.visual; + + X_UNLOCK; + set_offset(); + X_LOCK; + } + + initialize_clipshift(); + + /* initialize depth to reasonable value, visual_id may override */ + depth = DefaultDepth(dpy, scr); + + if (visual_id) { + int n; + XVisualInfo vinfo_tmpl, *vinfo; + + /* + * we are in here from -visual or -overlay options + * visual_id and visual_depth were set in set_visual(). + */ + + vinfo_tmpl.visualid = visual_id; + vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n); + if (vinfo == NULL || n == 0) { + rfbLogEnable(1); + rfbLog("could not match visual_id: 0x%x\n", + (int) visual_id); + X_UNLOCK; + clean_up_exit(1); + } + default_visual = vinfo->visual; + depth = vinfo->depth; + if (visual_depth) { + /* force it from -visual MooColor:NN */ + depth = visual_depth; + } + if (! quiet) { + fprintf(stderr, " initialize_xdisplay_fb()\n"); + fprintf(stderr, " Visual*: %p\n", + (void *) vinfo->visual); + fprintf(stderr, " visualid: 0x%x\n", + (int) vinfo->visualid); + fprintf(stderr, " screen: %d\n", vinfo->screen); + fprintf(stderr, " depth: %d\n", vinfo->depth); + fprintf(stderr, " class: %d\n", vinfo->class); + fprintf(stderr, " red_mask: 0x%08lx %s\n", + vinfo->red_mask, bitprint(vinfo->red_mask, 32)); + fprintf(stderr, " green_mask: 0x%08lx %s\n", + vinfo->green_mask, bitprint(vinfo->green_mask, 32)); + fprintf(stderr, " blue_mask: 0x%08lx %s\n", + vinfo->blue_mask, bitprint(vinfo->blue_mask, 32)); + fprintf(stderr, " cmap_size: %d\n", + vinfo->colormap_size); + fprintf(stderr, " bits b/rgb: %d\n", + vinfo->bits_per_rgb); + fprintf(stderr, "\n"); + } + XFree(vinfo); + } + + if (! quiet) { + rfbLog("Default visual ID: 0x%x\n", + (int) XVisualIDFromVisual(default_visual)); + } + + again: + if (subwin) { + int shift = 0; + int subwin_x, subwin_y; + int disp_x = DisplayWidth(dpy, scr); + int disp_y = DisplayHeight(dpy, scr); + Window twin; + /* subwins can be a dicey if they are changing size... */ + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + XTranslateCoordinates(dpy, window, rootwin, 0, 0, &subwin_x, + &subwin_y, &twin); + + if (subwin_x + wdpy_x > disp_x) { + shift = 1; + subwin_x = disp_x - wdpy_x - 3; + } + if (subwin_y + wdpy_y > disp_y) { + shift = 1; + subwin_y = disp_y - wdpy_y - 3; + } + if (subwin_x < 0) { + shift = 1; + subwin_x = 1; + } + if (subwin_y < 0) { + shift = 1; + subwin_y = 1; + } + + if (shift) { + XMoveWindow(dpy, window, subwin_x, subwin_y); + } + XMapRaised(dpy, window); + XRaiseWindow(dpy, window); + XFlush(dpy); + } + try++; + + if (nofb) { + /* + * For -nofb we do not allocate the framebuffer, so we + * can save a few MB of memory. + */ + fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, + 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); + + } else if (visual_id) { + /* + * we need to call XCreateImage to supply the visual + */ + fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, + 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); + fb->data = (char *) malloc(fb->bytes_per_line * fb->height); + + } else { + fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, + ZPixmap); + if (! quiet) { + rfbLog("Read initial data from X display into" + " framebuffer.\n"); + } + } + + if (subwin) { + XSetErrorHandler(old_handler); + if (trapped_xerror) { + rfbLog("trapped GetImage at SUBWIN creation.\n"); + if (try < subwin_tries) { + usleep(250 * 1000); + if (!get_window_size(window, &wdpy_x, &wdpy_y)) { + rfbLogEnable(1); + rfbLog("could not get size of subwin " + "0x%lx\n", subwin); + X_UNLOCK; + clean_up_exit(1); + } + goto again; + } + } + trapped_xerror = 0; + + } else if (! fb && try == 1) { + /* try once more */ + usleep(250 * 1000); + goto again; + } + if (use_snapfb) { + initialize_snap_fb(); + } + X_UNLOCK; + + if (fb->bits_per_pixel == 24 && ! quiet) { + rfbLog("warning: 24 bpp may have poor performance.\n"); + } + return fb; +} + +void parse_scale_string(char *str, double *factor, int *scaling, int *blend, + int *nomult4, int *pad, int *interpolate, int *numer, int *denom) { + + int m, n; + char *p, *tstr; + double f; + + *factor = 1.0; + *scaling = 0; + *blend = 1; + *nomult4 = 0; + *pad = 0; + *interpolate = 0; + *numer = 0, *denom = 0; + + if (str == NULL || str[0] == '\0') { + return; + } + tstr = strdup(str); + + if ( (p = strchr(tstr, ':')) != NULL) { + /* options */ + if (strstr(p+1, "nb") != NULL) { + *blend = 0; + } + if (strstr(p+1, "fb") != NULL) { + *blend = 2; + } + if (strstr(p+1, "n4") != NULL) { + *nomult4 = 1; + } + if (strstr(p+1, "in") != NULL) { + *interpolate = 1; + } + if (strstr(p+1, "pad") != NULL) { + *pad = 1; + } + if (strstr(p+1, "nocr") != NULL) { + /* global */ + scaling_copyrect = 0; + } else if (strstr(p+1, "cr") != NULL) { + /* global */ + scaling_copyrect = 1; + } + *p = '\0'; + } + if (strchr(tstr, '.') != NULL) { + double test, diff, eps = 1.0e-7; + if (sscanf(tstr, "%lf", &f) != 1) { + rfbLogEnable(1); + rfbLog("invalid -scale arg: %s\n", tstr); + clean_up_exit(1); + } + *factor = (double) f; + /* look for common fractions from small ints: */ + for (n=2; n<=10; n++) { + for (m=1; m<n; m++) { + test = ((double) m)/ n; + diff = *factor - test; + if (-eps < diff && diff < eps) { + *numer = m; + *denom = n; + break; + + } + } + if (*denom) { + break; + } + } + if (*factor < 0.01) { + rfbLogEnable(1); + rfbLog("-scale factor too small: %f\n", scale_fac); + clean_up_exit(1); + } + } else { + if (sscanf(tstr, "%d/%d", &m, &n) != 2) { + if (sscanf(tstr, "%d", &m) != 1) { + rfbLogEnable(1); + rfbLog("invalid -scale arg: %s\n", tstr); + clean_up_exit(1); + } else { + /* e.g. -scale 1 or -scale 2 */ + n = 1; + } + } + if (n <= 0 || m <=0) { + rfbLogEnable(1); + rfbLog("invalid -scale arg: %s\n", tstr); + clean_up_exit(1); + } + *factor = ((double) m)/ n; + if (*factor < 0.01) { + rfbLogEnable(1); + rfbLog("-scale factor too small: %f\n", *factor); + clean_up_exit(1); + } + *numer = m; + *denom = n; + } + if (*factor == 1.0) { + if (! quiet) { + rfbLog("scaling disabled for factor %f\n", *factor); + } + } else { + *scaling = 1; + } + free(tstr); +} + +int scale_round(int len, double fac) { + double eps = 0.000001; + + len = (int) (len * fac + eps); + if (len < 1) { + len = 1; + } + return len; +} + +static void setup_scaling(int *width_in, int *height_in) { + int width = *width_in; + int height = *height_in; + + parse_scale_string(scale_str, &scale_fac, &scaling, &scaling_blend, + &scaling_nomult4, &scaling_pad, &scaling_interpolate, + &scale_numer, &scale_denom); + + if (scaling) { + width = scale_round(width, scale_fac); + height = scale_round(height, scale_fac); + if (scale_denom && scaling_pad) { + /* it is not clear this padding is useful anymore */ + rfbLog("width %% denom: %d %% %d = %d\n", width, + scale_denom, width % scale_denom); + rfbLog("height %% denom: %d %% %d = %d\n", height, + scale_denom, height % scale_denom); + if (width % scale_denom != 0) { + int w = width; + w += scale_denom - (w % scale_denom); + if (!scaling_nomult4 && w % 4 != 0) { + /* need to make mult of 4 as well */ + int c = 0; + while (w % 4 != 0 && c++ <= 5) { + w += scale_denom; + } + } + width = w; + rfbLog("padded width to: %d (mult of %d%s\n", + width, scale_denom, !scaling_nomult4 ? + " and 4)" : ")"); + } + if (height % scale_denom != 0) { + height += scale_denom - (height % scale_denom); + rfbLog("padded height to: %d (mult of %d)\n", + height, scale_denom); + } + } + if (!scaling_nomult4 && width % 4 != 0 && width > 2) { + /* reset width to be multiple of 4 */ + int width0 = width; + if ((width+1) % 4 == 0) { + width = width+1; + } else if ((width-1) % 4 == 0) { + width = width-1; + } else if ((width+2) % 4 == 0) { + width = width+2; + } + rfbLog("reset scaled width %d -> %d to be a multiple of" + " 4 (to\n", width0, width); + rfbLog("make vncviewers happy). use -scale m/n:n4 to " + "disable.\n"); + } + scaled_x = width; + scaled_y = height; + + *width_in = width; + *height_in = height; + } +} + +/* + * initialize the rfb framebuffer/screen + */ +void initialize_screen(int *argc, char **argv, XImage *fb) { + int have_masks = 0; + int width = fb->width; + int height = fb->height; + int create_screen = screen ? 0 : 1; + int bits_per_color; + + main_bytes_per_line = fb->bytes_per_line; + + setup_scaling(&width, &height); + + + if (scaling) { + rfbLog("scaling screen: %dx%d -> %dx%d scale_fac=%.5f\n", + fb->width, fb->height, scaled_x, scaled_y, scale_fac); + + rfb_bytes_per_line = (main_bytes_per_line / fb->width) * width; + } else { + rfb_bytes_per_line = main_bytes_per_line; + } + + /* + * These are just hints wrt pixel format just to let + * rfbGetScreen/rfbNewFramebuffer proceed with reasonable + * defaults. We manually set them in painful detail below. + */ + bits_per_color = guess_bits_per_color(fb->bits_per_pixel); + + /* n.b. samplesPerPixel (set = 1 here) seems to be unused. */ + if (create_screen) { + screen = rfbGetScreen(argc, argv, width, height, + bits_per_color, 1, (int) fb->bits_per_pixel/8); + if (screen && http_dir) { + http_connections(1); + } + } else { + /* set set frameBuffer member below. */ + rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n", + screen, NULL, width, height, + bits_per_color, 1, fb->bits_per_pixel/8); + + /* these are probably overwritten, but just to be safe: */ + screen->bitsPerPixel = fb->bits_per_pixel; + screen->depth = fb->depth; + + rfbNewFramebuffer(screen, NULL, width, height, + bits_per_color, 1, (int) fb->bits_per_pixel/8); + } + if (! screen) { + int i; + rfbLogEnable(1); + rfbLog("\n"); + rfbLog("failed to create rfb screen.\n"); + for (i=0; i< *argc; i++) { + rfbLog("\t[%d] %s\n", i, argv[i]); + } + clean_up_exit(1); + } + + if (create_screen && *argc != 1) { + int i; + rfbLogEnable(1); + rfbLog("*** unrecognized option(s) ***\n"); + for (i=1; i< *argc; i++) { + rfbLog("\t[%d] %s\n", i, argv[i]); + } + rfbLog("For a list of options run: x11vnc -opts\n"); + rfbLog("or for the full help: x11vnc -help\n"); + rfbLog("\n"); + rfbLog("Here is a list of removed or obsolete" + " options:\n"); + rfbLog("\n"); + rfbLog("removed: -hints, -nohints\n"); + rfbLog("removed: -cursorposall\n"); + rfbLog("\n"); + rfbLog("renamed: -old_copytile, use -onetile\n"); + rfbLog("renamed: -mouse, use -cursor\n"); + rfbLog("renamed: -mouseX, use -cursor X\n"); + rfbLog("renamed: -X, use -cursor X\n"); + rfbLog("renamed: -nomouse, use -nocursor\n"); + rfbLog("renamed: -old_pointer, use -pointer_mode 1\n"); + + clean_up_exit(1); + } + + /* set up format from scratch: */ + screen->paddedWidthInBytes = rfb_bytes_per_line; + screen->serverFormat.bitsPerPixel = fb->bits_per_pixel; + screen->serverFormat.depth = fb->depth; + screen->serverFormat.trueColour = TRUE; + + screen->serverFormat.redShift = 0; + screen->serverFormat.greenShift = 0; + screen->serverFormat.blueShift = 0; + screen->serverFormat.redMax = 0; + screen->serverFormat.greenMax = 0; + screen->serverFormat.blueMax = 0; + + /* these main_* formats are used generally. */ + main_red_shift = 0; + main_green_shift = 0; + main_blue_shift = 0; + main_red_max = 0; + main_green_max = 0; + main_blue_max = 0; + main_red_mask = fb->red_mask; + main_green_mask = fb->green_mask; + main_blue_mask = fb->blue_mask; + + + have_masks = ((fb->red_mask|fb->green_mask|fb->blue_mask) != 0); + if (force_indexed_color) { + have_masks = 0; + } + + if (! have_masks && screen->serverFormat.bitsPerPixel == 8 + && dpy && CellsOfScreen(ScreenOfDisplay(dpy, scr))) { + /* indexed color */ + if (!quiet) { + rfbLog("X display %s is 8bpp indexed color\n", + DisplayString(dpy)); + if (! flash_cmap && ! overlay) { + rfbLog("\n"); + rfbLog("In 8bpp PseudoColor mode if you " + "experience color\n"); + rfbLog("problems you may want to enable " + "following the\n"); + rfbLog("changing colormap by using the " + "-flashcmap option.\n"); + rfbLog("\n"); + } + } + screen->serverFormat.trueColour = FALSE; + indexed_color = 1; + set_colormap(1); + debug_colormap(fb); + } else { + /* + * general case, we call it truecolor, but could be direct + * color, static color, etc.... + */ + if (! quiet) { + if (raw_fb) { + rfbLog("Raw fb at addr %p is %dbpp depth=%d " + "true color\n", raw_fb_addr, + fb->bits_per_pixel, fb->depth); + } else { + rfbLog("X display %s is %dbpp depth=%d true " + "color\n", DisplayString(dpy), + fb->bits_per_pixel, fb->depth); + } + } + + indexed_color = 0; + + /* convert masks to bit shifts and max # colors */ + if (fb->red_mask) { + while (! (fb->red_mask + & (1 << screen->serverFormat.redShift))) { + screen->serverFormat.redShift++; + } + } + if (fb->green_mask) { + while (! (fb->green_mask + & (1 << screen->serverFormat.greenShift))) { + screen->serverFormat.greenShift++; + } + } + if (fb->blue_mask) { + while (! (fb->blue_mask + & (1 << screen->serverFormat.blueShift))) { + screen->serverFormat.blueShift++; + } + } + screen->serverFormat.redMax + = fb->red_mask >> screen->serverFormat.redShift; + screen->serverFormat.greenMax + = fb->green_mask >> screen->serverFormat.greenShift; + screen->serverFormat.blueMax + = fb->blue_mask >> screen->serverFormat.blueShift; + + main_red_max = screen->serverFormat.redMax; + main_green_max = screen->serverFormat.greenMax; + main_blue_max = screen->serverFormat.blueMax; + + main_red_shift = screen->serverFormat.redShift; + main_green_shift = screen->serverFormat.greenShift; + main_blue_shift = screen->serverFormat.blueShift; + } + +#if !SMALL_FOOTPRINT + if (!quiet) { + fprintf(stderr, "\n"); + fprintf(stderr, "FrameBuffer Info:\n"); + fprintf(stderr, " width: %d\n", fb->width); + fprintf(stderr, " height: %d\n", fb->height); + fprintf(stderr, " scaled_width: %d\n", width); + fprintf(stderr, " scaled_height: %d\n", height); + fprintf(stderr, " indexed_color: %d\n", indexed_color); + fprintf(stderr, " bits_per_pixel: %d\n", fb->bits_per_pixel); + fprintf(stderr, " depth: %d\n", fb->depth); + fprintf(stderr, " red_mask: 0x%08lx %s\n", fb->red_mask, + bitprint(fb->red_mask, 32)); + fprintf(stderr, " green_mask: 0x%08lx %s\n", fb->green_mask, + bitprint(fb->green_mask, 32)); + fprintf(stderr, " blue_mask: 0x%08lx %s\n", fb->blue_mask, + bitprint(fb->blue_mask, 32)); + fprintf(stderr, " red: max: %3d shift: %2d\n", + main_red_max, main_red_shift); + fprintf(stderr, " green: max: %3d shift: %2d\n", + main_green_max, main_green_shift); + fprintf(stderr, " blue: max: %3d shift: %2d\n", + main_blue_max, main_blue_shift); + fprintf(stderr, " mainfb_bytes_per_line: %d\n", + main_bytes_per_line); + fprintf(stderr, " rfb_fb_bytes_per_line: %d\n", + rfb_bytes_per_line); + switch(fb->format) { + case XYBitmap: + fprintf(stderr, " format: XYBitmap\n"); break; + case XYPixmap: + fprintf(stderr, " format: XYPixmap\n"); break; + case ZPixmap: + fprintf(stderr, " format: ZPixmap\n"); break; + default: + fprintf(stderr, " format: %d\n", fb->format); break; + } + switch(fb->byte_order) { + case LSBFirst: + fprintf(stderr, " byte_order: LSBFirst\n"); break; + case MSBFirst: + fprintf(stderr, " byte_order: MSBFirst\n"); break; + default: + fprintf(stderr, " byte_order: %d\n", fb->byte_order); + break; + } + fprintf(stderr, " bitmap_pad: %d\n", fb->bitmap_pad); + fprintf(stderr, " bitmap_unit: %d\n", fb->bitmap_unit); + switch(fb->bitmap_bit_order) { + case LSBFirst: + fprintf(stderr, " bitmap_bit_order: LSBFirst\n"); break; + case MSBFirst: + fprintf(stderr, " bitmap_bit_order: MSBFirst\n"); break; + default: + fprintf(stderr, " bitmap_bit_order: %d\n", + fb->bitmap_bit_order); break; + } + } + if (overlay && ! quiet) { + rfbLog("\n"); + rfbLog("Overlay mode enabled: If you experience color\n"); + rfbLog("problems when popup menus are on the screen, try\n"); + rfbLog("disabling SaveUnders in your X server, one way is\n"); + rfbLog("to start the X server with the '-su' option, e.g.:\n"); + rfbLog("Xsun -su ... see Xserver(1), xinit(1) for more info.\n"); + rfbLog("\n"); + } +#endif + /* nofb is for pointer/keyboard only handling. */ + if (nofb) { + main_fb = NULL; + rfb_fb = main_fb; + screen->displayHook = nofb_hook; + } else { + main_fb = fb->data; + if (scaling) { + rfb_fb = (char *) malloc(rfb_bytes_per_line * height); + memset(rfb_fb, 0, rfb_bytes_per_line * height); + } else { + rfb_fb = main_fb; + } + } + screen->frameBuffer = rfb_fb; + if (!quiet) { + fprintf(stderr, " main_fb: %p\n", main_fb); + fprintf(stderr, " rfb_fb: %p\n", rfb_fb); + fprintf(stderr, "\n"); + } + + bpp = screen->serverFormat.bitsPerPixel; + depth = screen->serverFormat.depth; + + /* may need, bpp, main_red_max, etc. */ + parse_wireframe(); + parse_scroll_copyrect(); + + setup_cursors_and_push(); + + if (scaling) { + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + } + + if (! create_screen) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + /* + * since bits_per_color above may have been approximate, + * try to reset the individual translation tables... + * we do not seem to need this with rfbGetScreen()... + */ + if (!quiet) rfbLog("calling setTranslateFunction()...\n"); + iter = rfbGetClientIterator(screen); + while ((cl = rfbClientIteratorNext(iter)) != NULL) { + screen->setTranslateFunction(cl); + } + rfbReleaseClientIterator(iter); + if (!quiet) rfbLog(" done.\n"); + do_copy_screen = 1; + + /* done for framebuffer change case */ + return; + } + + /* + * the rest is screen server initialization, etc, only needed + * at screen creation time. + */ + + /* called from inetd, we need to treat stdio as our socket */ + if (inetd) { + int fd = dup(0); + if (fd < 0) { + rfbLogEnable(1); + rfbErr("dup(0) = %d failed.\n", fd); + rfbLogPerror("dup"); + clean_up_exit(1); + } + fclose(stdin); + fclose(stdout); + /* we keep stderr for logging */ + screen->inetdSock = fd; + screen->port = 0; + + } else if (! got_rfbport) { + screen->autoPort = TRUE; + } + + if (! got_nevershared && ! got_alwaysshared) { + if (shared) { + screen->alwaysShared = TRUE; + } else { + screen->neverShared = TRUE; + } + screen->dontDisconnect = TRUE; + } + if (! got_deferupdate) { + screen->deferUpdateTime = defer_update; + } + + /* event callbacks: */ + screen->newClientHook = new_client; + screen->kbdAddEvent = keyboard; + screen->ptrAddEvent = pointer; + screen->setXCutText = xcut_receive; + + rfbInitServer(screen); + + install_passwds(); +} + +void set_vnc_desktop_name(void) { + int sz = 256; + sprintf(vnc_desktop_name, "unknown"); + if (inetd) { + sprintf(vnc_desktop_name, "inetd-no-further-clients"); + } + if (screen->port) { + char *host = this_host(); + int lport = screen->port; + char *iface = listen_str; + + if (iface != NULL && *iface != '\0' && strcmp(iface, "any")) { + host = iface; + } + if (host != NULL) { + /* note that vncviewer special cases 5900-5999 */ + if (inetd) { + ; /* should not occur (port) */ + } else if (quiet) { + if (lport >= 5900) { + snprintf(vnc_desktop_name, sz, "%s:%d", + host, lport - 5900); + fprintf(stderr, "The VNC desktop is " + "%s\n", vnc_desktop_name); + } else { + snprintf(vnc_desktop_name, sz, "%s:%d", + host, lport); + fprintf(stderr, "The VNC desktop is " + "%s\n", vnc_desktop_name); + } + } else if (lport >= 5900) { + snprintf(vnc_desktop_name, sz, "%s:%d", + host, lport - 5900); + rfbLog("\n"); + rfbLog("The VNC desktop is %s\n", + vnc_desktop_name); + if (lport >= 6000) { + rfbLog("possible aliases: %s:%d, " + "%s::%d\n", host, lport, + host, lport); + } + } else { + snprintf(vnc_desktop_name, sz, "%s:%d", + host, lport); + rfbLog("\n"); + rfbLog("The VNC desktop is %s\n", + vnc_desktop_name); + rfbLog("possible alias: %s::%d\n", + host, lport); + } + } + fflush(stderr); + if (inetd) { + ; /* should not occur (port != 0) */ + } else { + fprintf(stdout, "PORT=%d\n", screen->port); + fflush(stdout); + if (flagfile) { + FILE *flag = fopen(flagfile, "w"); + if (flag) { + fprintf(flag, "PORT=%d\n",screen->port); + fflush(flag); + fclose(flag); + } else { + rfbLog("could not open flag file: %s\n", + flagfile); + } + } + } + fflush(stdout); + } +} + + diff --git a/x11vnc/screen.h b/x11vnc/screen.h new file mode 100644 index 0000000..9bd8fd7 --- /dev/null +++ b/x11vnc/screen.h @@ -0,0 +1,19 @@ +#ifndef _X11VNC_SCREEN_H +#define _X11VNC_SCREEN_H + +/* -- screen.h -- */ + +extern void set_colormap(int reset); +extern void set_nofb_params(int restore); +extern void set_raw_fb_params(int restore); +extern void do_new_fb(int reset_mem); +extern void check_padded_fb(void); +extern void install_padded_fb(char *geom); +extern XImage *initialize_xdisplay_fb(void); +extern void parse_scale_string(char *str, double *factor, int *scaling, int *blend, + int *nomult4, int *pad, int *interpolate, int *numer, int *denom); +extern int scale_round(int len, double fac); +extern void initialize_screen(int *argc, char **argv, XImage *fb); +extern void set_vnc_desktop_name(void); + +#endif /* _X11VNC_SCREEN_H */ diff --git a/x11vnc/scrollevent_t.h b/x11vnc/scrollevent_t.h new file mode 100644 index 0000000..4bb1292 --- /dev/null +++ b/x11vnc/scrollevent_t.h @@ -0,0 +1,15 @@ +#ifndef _X11VNC_SCROLLEVENT_T_H +#define _X11VNC_SCROLLEVENT_T_H + +/* -- scrollevent_t.h -- */ + +typedef struct scroll_event { + Window win, frame; + int dx, dy; + 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; + +#endif /* _X11VNC_SCROLLEVENT_T_H */ diff --git a/x11vnc/selection.c b/x11vnc/selection.c new file mode 100644 index 0000000..28b172c --- /dev/null +++ b/x11vnc/selection.c @@ -0,0 +1,298 @@ +/* -- selection.c -- */ + +#include "x11vnc.h" +#include "cleanup.h" +#include "connections.h" + +/* + * Selection/Cutbuffer/Clipboard handlers. + */ + +int own_selection = 0; /* whether we currently own PRIMARY or not */ +int set_cutbuffer = 0; /* to avoid bouncing the CutText right back */ +int sel_waittime = 15; /* some seconds to skip before first send */ +Window selwin; /* special window for our selection */ + +/* + * This is where we keep our selection: the string sent TO us from VNC + * clients, and the string sent BY us to requesting X11 clients. + */ +char *xcut_str = NULL; + + +void selection_request(XEvent *ev); +int check_sel_direction(char *dir, char *label, char *sel, int len); +void cutbuffer_send(void); +void selection_send(XEvent *ev); + + +/* + * Our callbacks instruct us to check for changes in the cutbuffer + * and PRIMARY selection on the local X11 display. + * + * We store the new cutbuffer and/or PRIMARY selection data in this + * constant sized array selection_str[]. + * TODO: check if malloc does not cause performance issues (esp. WRT + * SelectionNotify handling). + */ +static char selection_str[PROP_MAX+1]; + +/* + * An X11 (not VNC) client on the local display has requested the selection + * from us (because we are the current owner). + * + * n.b.: our caller already has the X_LOCK. + */ +void selection_request(XEvent *ev) { + XSelectionEvent notify_event; + XSelectionRequestEvent *req_event; + XErrorHandler old_handler; + unsigned int length; + unsigned char *data; +#ifndef XA_LENGTH + unsigned long XA_LENGTH = XInternAtom(dpy, "LENGTH", True); +#endif + + req_event = &(ev->xselectionrequest); + notify_event.type = SelectionNotify; + notify_event.display = req_event->display; + notify_event.requestor = req_event->requestor; + notify_event.selection = req_event->selection; + notify_event.target = req_event->target; + notify_event.time = req_event->time; + + if (req_event->property == None) { + notify_event.property = req_event->target; + } else { + notify_event.property = req_event->property; + } + if (xcut_str) { + length = strlen(xcut_str); + } else { + length = 0; + } + + /* the window may have gone away, so trap errors */ + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + + if (ev->xselectionrequest.target == XA_LENGTH) { + /* length request */ + + XChangeProperty(ev->xselectionrequest.display, + ev->xselectionrequest.requestor, + ev->xselectionrequest.property, + ev->xselectionrequest.target, 32, PropModeReplace, + (unsigned char *) &length, sizeof(unsigned int)); + + } else { + /* data request */ + + data = (unsigned char *)xcut_str; + + XChangeProperty(ev->xselectionrequest.display, + ev->xselectionrequest.requestor, + ev->xselectionrequest.property, + ev->xselectionrequest.target, 8, PropModeReplace, + data, length); + } + + if (! trapped_xerror) { + XSendEvent(req_event->display, req_event->requestor, False, 0, + (XEvent *)¬ify_event); + } + if (trapped_xerror) { + rfbLog("selection_request: ignored XError while sending " + "PRIMARY selection to 0x%x.\n", req_event->requestor); + } + XSetErrorHandler(old_handler); + trapped_xerror = 0; + + XFlush(dpy); +} + +int check_sel_direction(char *dir, char *label, char *sel, int len) { + int db = 0, ok = 1; + if (sel_direction) { + if (strstr(sel_direction, "debug")) { + db = 1; + } + if (strcmp(sel_direction, "debug")) { + if (strstr(sel_direction, dir) == NULL) { + ok = 0; + } + } + } + if (db) { + char str[40]; + int n = 40; + strncpy(str, sel, n); + str[n-1] = '\0'; + if (len < n) { + str[len] = '\0'; + } + rfbLog("%s: %s...\n", label, str); + if (ok) { + rfbLog("%s: %s-ing it.\n", label, dir); + } else { + rfbLog("%s: NOT %s-ing it.\n", label, dir); + } + } + return ok; +} + +/* + * CUT_BUFFER0 property on the local display has changed, we read and + * store it and send it out to any connected VNC clients. + * + * n.b.: our caller already has the X_LOCK. + */ +void cutbuffer_send(void) { + Atom type; + int format, slen, dlen, len; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + + selection_str[0] = '\0'; + slen = 0; + + /* read the property value into selection_str: */ + do { + if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), + XA_CUT_BUFFER0, nitems/4, PROP_MAX/16, False, + AnyPropertyType, &type, &format, &nitems, &bytes_after, + &data) == Success) { + + dlen = nitems * (format/8); + if (slen + dlen > PROP_MAX) { + /* too big */ + rfbLog("warning: truncating large CUT_BUFFER0" + " selection > %d bytes.\n", PROP_MAX); + XFree(data); + break; + } + memcpy(selection_str+slen, data, dlen); + slen += dlen; + selection_str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); + + selection_str[PROP_MAX] = '\0'; + + if (! all_clients_initialized()) { + rfbLog("cutbuffer_send: no send: uninitialized clients\n"); + return; /* some clients initializing, cannot send */ + } + + /* now send it to any connected VNC clients (rfbServerCutText) */ + if (!screen) { + return; + } + len = strlen(selection_str); + if (check_sel_direction("send", "cutbuffer_send", selection_str, len)) { + rfbSendServerCutText(screen, selection_str, len); + } +} + +/* + * "callback" for our SelectionNotify polling. We try to determine if + * the PRIMARY selection has changed (checking length and first CHKSZ bytes) + * and if it has we store it and send it off to any connected VNC clients. + * + * n.b.: our caller already has the X_LOCK. + * + * TODO: if we were willing to use libXt, we could perhaps get selection + * timestamps to speed up the checking... XtGetSelectionValue(). + * + * Also: XFIXES has XFixesSelectSelectionInput(). + */ +#define CHKSZ 32 +void selection_send(XEvent *ev) { + Atom type; + int format, slen, dlen, oldlen, newlen, toobig = 0, len; + static int err = 0, sent_one = 0; + char before[CHKSZ], after[CHKSZ]; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + + /* + * remember info about our last value of PRIMARY (or CUT_BUFFER0) + * so we can check for any changes below. + */ + oldlen = strlen(selection_str); + strncpy(before, selection_str, CHKSZ); + + selection_str[0] = '\0'; + slen = 0; + + /* read in the current value of PRIMARY: */ + do { + if (XGetWindowProperty(dpy, ev->xselection.requestor, + ev->xselection.property, nitems/4, PROP_MAX/16, True, + AnyPropertyType, &type, &format, &nitems, &bytes_after, + &data) == Success) { + + dlen = nitems * (format/8); + if (slen + dlen > PROP_MAX) { + /* too big */ + toobig = 1; + XFree(data); + if (err) { /* cut down on messages */ + break; + } else { + err = 5; + } + rfbLog("warning: truncating large PRIMARY" + " selection > %d bytes.\n", PROP_MAX); + break; + } + memcpy(selection_str+slen, data, dlen); + slen += dlen; + selection_str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); + + if (! toobig) { + err = 0; + } else if (err) { + err--; + } + + if (! sent_one) { + /* try to force a send first time in */ + oldlen = -1; + sent_one = 1; + } + + /* look for changes in the new value */ + newlen = strlen(selection_str); + strncpy(after, selection_str, CHKSZ); + + if (oldlen == newlen && strncmp(before, after, CHKSZ) == 0) { + /* evidently no change */ + return; + } + if (newlen == 0) { + /* do not bother sending a null string out */ + return; + } + + if (! all_clients_initialized()) { + rfbLog("selection_send: no send: uninitialized clients\n"); + return; /* some clients initializing, cannot send */ + } + + /* now send it to any connected VNC clients (rfbServerCutText) */ + if (!screen) { + return; + } + + len = newlen; + if (check_sel_direction("send", "selection_send", selection_str, len)) { + rfbSendServerCutText(screen, selection_str, len); + } +} + + diff --git a/x11vnc/selection.h b/x11vnc/selection.h new file mode 100644 index 0000000..5ffab44 --- /dev/null +++ b/x11vnc/selection.h @@ -0,0 +1,17 @@ +#ifndef _X11VNC_SELECTION_H +#define _X11VNC_SELECTION_H + +/* -- selection.h -- */ + +extern char *xcut_str; +extern int own_selection; +extern int set_cutbuffer; +extern int sel_waittime; +extern Window selwin; + +extern void selection_request(XEvent *ev); +extern int check_sel_direction(char *dir, char *label, char *sel, int len); +extern void cutbuffer_send(void); +extern void selection_send(XEvent *ev); + +#endif /* _X11VNC_SELECTION_H */ diff --git a/x11vnc/solid.c b/x11vnc/solid.c new file mode 100644 index 0000000..5e91bf6 --- /dev/null +++ b/x11vnc/solid.c @@ -0,0 +1,722 @@ +/* -- solid.c -- */ + +#include "x11vnc.h" +#include "win_utils.h" + +char *guess_desktop(void); +void solid_bg(int restore); + + +static void usr_bin_path(int restore); +static int dt_cmd(char *cmd); +static char *cmd_output(char *cmd); +static void solid_root(char *color); +static void solid_cde(char *color); +static void solid_gnome(char *color); +static void solid_kde(char *color); + + +static void usr_bin_path(int restore) { + static char *oldpath = NULL; + char *newpath; + char addpath[] = "/usr/bin:/bin:"; + + if (restore) { + if (oldpath) { + set_env("PATH", oldpath); + free(oldpath); + oldpath = NULL; + } + return; + } + + if (getenv("PATH")) { + oldpath = strdup(getenv("PATH")); + } else { + oldpath = strdup("/usr/bin"); + } + newpath = (char *) malloc(strlen(oldpath) + strlen(addpath) + 1); + newpath[0] = '\0'; + strcat(newpath, addpath); + strcat(newpath, oldpath); + set_env("PATH", newpath); + free(newpath); +} + +static int dt_cmd(char *cmd) { + int rc; + + if (!cmd || *cmd == '\0') { + return 0; + } + + if (no_external_cmds) { + rfbLog("cannot run external commands in -nocmds mode:\n"); + rfbLog(" \"%s\"\n", cmd); + rfbLog(" dt_cmd: returning 1\n"); + return 1; + } + + if (getenv("DISPLAY") == NULL) { + set_env("DISPLAY", DisplayString(dpy)); + } + + rfbLog("running command:\n %s\n", cmd); + usr_bin_path(0); + rc = system(cmd); + usr_bin_path(1); + + if (rc >= 256) { + rc = rc/256; + } + return rc; +} + +static char *cmd_output(char *cmd) { + FILE *p; + static char output[50000]; + char line[1024]; + int rc; + + if (!cmd || *cmd == '\0') { + return ""; + } + + if (no_external_cmds) { + rfbLog("cannot run external commands in -nocmds mode:\n"); + rfbLog(" \"%s\"\n", cmd); + rfbLog(" cmd_output: null string.\n"); + return ""; + } + + rfbLog("running pipe:\n %s\n", cmd); + usr_bin_path(0); + p = popen(cmd, "r"); + usr_bin_path(1); + + output[0] = '\0'; + + while (fgets(line, 1024, p) != NULL) { + if (strlen(output) + strlen(line) + 1 < 50000) { + strcat(output, line); + } + } + rc = pclose(p); + return(output); +} + +static void solid_root(char *color) { + Window expose; + static XImage *image = NULL; + Pixmap pixmap; + XGCValues gcv; + GC gc; + XSetWindowAttributes swa; + Visual visual; + unsigned long mask, pixel; + XColor cdef; + Colormap cmap; + + if (subwin || window != rootwin) { + rfbLog("cannot set subwin to solid color, must be rootwin\n"); + return; + } + + /* create the "clear" window just for generating exposures */ + swa.override_redirect = True; + swa.backing_store = NotUseful; + swa.save_under = False; + swa.background_pixmap = None; + visual.visualid = CopyFromParent; + mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap); + expose = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, depth, + InputOutput, &visual, mask, &swa); + + if (! color) { + /* restore the root window from the XImage snapshot */ + pixmap = XCreatePixmap(dpy, window, wdpy_x, wdpy_y, depth); + + if (! image) { + /* whoops */ + XDestroyWindow(dpy, expose); + rfbLog("no root snapshot available.\n"); + return; + } + + + /* draw the image to a pixmap: */ + gcv.function = GXcopy; + gcv.plane_mask = AllPlanes; + gc = XCreateGC(dpy, window, GCFunction|GCPlaneMask, &gcv); + + XPutImage(dpy, pixmap, gc, image, 0, 0, 0, 0, wdpy_x, wdpy_y); + + gcv.foreground = gcv.background = BlackPixel(dpy, scr); + gc = XCreateGC(dpy, window, GCForeground|GCBackground, &gcv); + + rfbLog("restoring root snapshot...\n"); + /* set the pixmap as the bg: */ + XSetWindowBackgroundPixmap(dpy, window, pixmap); + XFreePixmap(dpy, pixmap); + XClearWindow(dpy, window); + XFlush(dpy); + + /* generate exposures */ + XMapWindow(dpy, expose); + XSync(dpy, False); + XDestroyWindow(dpy, expose); + return; + } + + if (! image) { + /* need to retrieve a snapshot of the root background: */ + Window iwin; + XSetWindowAttributes iswa; + + /* create image window: */ + iswa.override_redirect = True; + iswa.backing_store = NotUseful; + iswa.save_under = False; + iswa.background_pixmap = ParentRelative; + + iwin = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, + depth, InputOutput, &visual, mask, &iswa); + + rfbLog("snapshotting background...\n"); + + XMapWindow(dpy, iwin); + XSync(dpy, False); + image = XGetImage(dpy, iwin, 0, 0, wdpy_x, wdpy_y, AllPlanes, + ZPixmap); + XSync(dpy, False); + XDestroyWindow(dpy, iwin); + } + + /* use black for low colors or failure */ + pixel = BlackPixel(dpy, scr); + if (depth > 8 || strcmp(color, solid_default)) { + cmap = DefaultColormap (dpy, scr); + if (XParseColor(dpy, cmap, color, &cdef) && + XAllocColor(dpy, cmap, &cdef)) { + pixel = cdef.pixel; + } else { + rfbLog("error parsing/allocing color: %s\n", color); + } + } + + rfbLog("setting solid background...\n"); + XSetWindowBackground(dpy, window, pixel); + XMapWindow(dpy, expose); + XSync(dpy, False); + XDestroyWindow(dpy, expose); +} + +static void solid_cde(char *color) { + int wsmax = 16; + static XImage *image[16]; + static Window ws_wins[16]; + static int nws = -1; + + Window expose; + Pixmap pixmap; + XGCValues gcv; + GC gc; + XSetWindowAttributes swa; + Visual visual; + unsigned long mask, pixel; + XColor cdef; + Colormap cmap; + int n; + + if (subwin || window != rootwin) { + rfbLog("cannot set subwin to solid color, must be rootwin\n"); + return; + } + + /* create the "clear" window just for generating exposures */ + swa.override_redirect = True; + swa.backing_store = NotUseful; + swa.save_under = False; + swa.background_pixmap = None; + visual.visualid = CopyFromParent; + mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap); + expose = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, depth, + InputOutput, &visual, mask, &swa); + + if (! color) { + /* restore the backdrop windows from the XImage snapshots */ + + for (n=0; n < nws; n++) { + Window twin; + + if (! image[n]) { + continue; + } + + twin = ws_wins[n]; + if (! twin) { + twin = rootwin; + } + if (! valid_window(twin, NULL, 0)) { + continue; + } + + pixmap = XCreatePixmap(dpy, twin, wdpy_x, wdpy_y, + depth); + + /* draw the image to a pixmap: */ + gcv.function = GXcopy; + gcv.plane_mask = AllPlanes; + gc = XCreateGC(dpy, twin, GCFunction|GCPlaneMask, &gcv); + + XPutImage(dpy, pixmap, gc, image[n], 0, 0, 0, 0, + wdpy_x, wdpy_y); + + gcv.foreground = gcv.background = BlackPixel(dpy, scr); + gc = XCreateGC(dpy, twin, GCForeground|GCBackground, + &gcv); + + rfbLog("restoring CDE ws%d snapshot to 0x%lx\n", + n, twin); + /* set the pixmap as the bg: */ + XSetWindowBackgroundPixmap(dpy, twin, pixmap); + XFreePixmap(dpy, pixmap); + XClearWindow(dpy, twin); + XFlush(dpy); + } + + /* generate exposures */ + XMapWindow(dpy, expose); + XSync(dpy, False); + XDestroyWindow(dpy, expose); + return; + } + + if (nws < 0) { + /* need to retrieve snapshots of the ws backgrounds: */ + Window iwin, wm_win; + XSetWindowAttributes iswa; + Atom dt_list, wm_info, type; + int format; + unsigned long length, after; + unsigned char *data; + unsigned int * dp; + + nws = 0; + + /* extract the hidden wm properties about backdrops: */ + + wm_info = XInternAtom(dpy, "_MOTIF_WM_INFO", True); + if (wm_info == None) { + return; + } + + XGetWindowProperty(dpy, rootwin, wm_info, 0L, 10L, False, + AnyPropertyType, &type, &format, &length, &after, &data); + + /* + * xprop -notype -root _MOTIF_WM_INFO + * _MOTIF_WM_INFO = 0x2, 0x580028 + */ + + if (length < 2 || format != 32 || after != 0) { + return; + } + + dp = (unsigned int *) data; + wm_win = (Window) *(dp+1); /* 2nd item. */ + + + dt_list = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True); + if (dt_list == None) { + return; + } + + XGetWindowProperty(dpy, wm_win, dt_list, 0L, 10L, False, + AnyPropertyType, &type, &format, &length, &after, &data); + + nws = length; + + if (nws > wsmax) { + nws = wsmax; + } + if (nws < 0) { + nws = 0; + } + + rfbLog("special CDE win: 0x%lx, %d workspaces\n", wm_win, nws); + if (nws == 0) { + return; + } + + for (n=0; n<nws; n++) { + Atom ws_atom; + char tmp[32]; + Window twin; + XWindowAttributes attr; + int i, cnt; + + image[n] = NULL; + ws_wins[n] = 0x0; + + sprintf(tmp, "_DT_WORKSPACE_INFO_ws%d", n); + ws_atom = XInternAtom(dpy, tmp, False); + if (ws_atom == None) { + continue; + } + XGetWindowProperty(dpy, wm_win, ws_atom, 0L, 100L, + False, AnyPropertyType, &type, &format, &length, + &after, &data); + + if (format != 8 || after != 0) { + continue; + } + /* + * xprop -notype -id wm_win + * _DT_WORKSPACE_INFO_ws0 = "One", "3", "0x2f2f4a", + * "0x63639c", "0x103", "1", "0x58044e" + */ + + cnt = 0; + twin = 0x0; + for (i=0; i< (int) length; i++) { + if (*(data+i) != '\0') { + continue; + } + cnt++; /* count nulls to indicate field */ + if (cnt == 6) { + /* one past the null: */ + char *q = (char *) (data+i+1); + unsigned long in; + if (sscanf(q, "0x%lx", &in) == 1) { + twin = (Window) in; + break; + } + } + } + ws_wins[n] = twin; + + if (! twin) { + twin = rootwin; + } + + XGetWindowAttributes(dpy, twin, &attr); + if (twin != rootwin) { + if (attr.map_state != IsViewable) { + XMapWindow(dpy, twin); + } + XRaiseWindow(dpy, twin); + } + XSync(dpy, False); + + /* create image window: */ + iswa.override_redirect = True; + iswa.backing_store = NotUseful; + iswa.save_under = False; + iswa.background_pixmap = ParentRelative; + visual.visualid = CopyFromParent; + + iwin = XCreateWindow(dpy, twin, 0, 0, wdpy_x, wdpy_y, + 0, depth, InputOutput, &visual, mask, &iswa); + + rfbLog("snapshotting CDE backdrop ws%d 0x%lx -> " + "0x%lx ...\n", n, twin, iwin); + XMapWindow(dpy, iwin); + XSync(dpy, False); + + image[n] = XGetImage(dpy, iwin, 0, 0, wdpy_x, wdpy_y, + AllPlanes, ZPixmap); + XSync(dpy, False); + XDestroyWindow(dpy, iwin); + if (twin != rootwin) { + XLowerWindow(dpy, twin); + if (attr.map_state != IsViewable) { + XUnmapWindow(dpy, twin); + } + } + } + } + if (nws == 0) { + return; + } + + /* use black for low colors or failure */ + pixel = BlackPixel(dpy, scr); + if (depth > 8 || strcmp(color, solid_default)) { + cmap = DefaultColormap (dpy, scr); + if (XParseColor(dpy, cmap, color, &cdef) && + XAllocColor(dpy, cmap, &cdef)) { + pixel = cdef.pixel; + } else { + rfbLog("error parsing/allocing color: %s\n", color); + } + } + + rfbLog("setting solid backgrounds...\n"); + + for (n=0; n < nws; n++) { + Window twin = ws_wins[n]; + if (image[n] == NULL) { + continue; + } + if (! twin) { + twin = rootwin; + } + XSetWindowBackground(dpy, twin, pixel); + } + XMapWindow(dpy, expose); + XSync(dpy, False); + XDestroyWindow(dpy, expose); +} + +static void solid_gnome(char *color) { + char get_color[] = "gconftool-2 --get " + "/desktop/gnome/background/primary_color"; + char set_color[] = "gconftool-2 --set " + "/desktop/gnome/background/primary_color --type string '%s'"; + char get_option[] = "gconftool-2 --get " + "/desktop/gnome/background/picture_options"; + char set_option[] = "gconftool-2 --set " + "/desktop/gnome/background/picture_options --type string '%s'"; + static char *orig_color = NULL; + static char *orig_option = NULL; + char *cmd; + + if (! color) { + if (! orig_color) { + orig_color = strdup("#FFFFFF"); + } + if (! orig_option) { + orig_option = strdup("stretched"); + } + if (strstr(orig_color, "'") != NULL) { + rfbLog("invalid color: %s\n", orig_color); + return; + } + if (strstr(orig_option, "'") != NULL) { + rfbLog("invalid option: %s\n", orig_option); + return; + } + cmd = (char *) malloc(strlen(set_option) - 2 + + strlen(orig_option) + 1); + sprintf(cmd, set_option, orig_option); + dt_cmd(cmd); + free(cmd); + cmd = (char *) malloc(strlen(set_color) - 2 + + strlen(orig_color) + 1); + sprintf(cmd, set_color, orig_color); + dt_cmd(cmd); + free(cmd); + return; + } + + if (! orig_color) { + char *q; + orig_color = strdup(cmd_output(get_color)); + if (*orig_color == '\0') { + orig_color = strdup("#FFFFFF"); + } + if ((q = strchr(orig_color, '\n')) != NULL) { + *q = '\0'; + } + } + if (! orig_option) { + char *q; + orig_option = strdup(cmd_output(get_option)); + if (*orig_option == '\0') { + orig_option = strdup("stretched"); + } + if ((q = strchr(orig_option, '\n')) != NULL) { + *q = '\0'; + } + } + if (strstr(color, "'") != NULL) { + rfbLog("invalid color: %s\n", color); + return; + } + cmd = (char *) malloc(strlen(set_color) + strlen(color) + 1); + sprintf(cmd, set_color, color); + dt_cmd(cmd); + free(cmd); + + cmd = (char *) malloc(strlen(set_option) + strlen("none") + 1); + sprintf(cmd, set_option, "none"); + dt_cmd(cmd); + free(cmd); +} + +static void solid_kde(char *color) { + char set_color[] = + "dcop --user '%s' kdesktop KBackgroundIface setColor '%s' 1"; + char bg_off[] = + "dcop --user '%s' kdesktop KBackgroundIface setBackgroundEnabled 0"; + char bg_on[] = + "dcop --user '%s' kdesktop KBackgroundIface setBackgroundEnabled 1"; + char *cmd, *user = NULL; + int len; + + user = get_user_name(); + if (strstr(user, "'") != NULL) { + rfbLog("invalid user: %s\n", user); + free(user); + return; + } + + if (! color) { + len = strlen(bg_on) + strlen(user) + 1; + cmd = (char *) malloc(len); + sprintf(cmd, bg_on, user); + dt_cmd(cmd); + free(cmd); + free(user); + + return; + } + + if (strstr(color, "'") != NULL) { + rfbLog("invalid color: %s\n", color); + return; + } + + len = strlen(set_color) + strlen(user) + strlen(color) + 1; + cmd = (char *) malloc(len); + sprintf(cmd, set_color, user, color); + dt_cmd(cmd); + free(cmd); + + len = strlen(bg_off) + strlen(user) + 1; + cmd = (char *) malloc(len); + sprintf(cmd, bg_off, user); + dt_cmd(cmd); + free(cmd); + free(user); +} + +char *guess_desktop(void) { + Atom prop; + + if (wmdt_str && *wmdt_str != '\0') { + char *s = wmdt_str; + lowercase(s); + if (strstr(s, "xfce")) { + return "xfce"; + } + if (strstr(s, "gnome") || strstr(s, "metacity")) { + return "gnome"; + } + if (strstr(s, "kde") || strstr(s, "kwin")) { + return "kde"; + } + if (strstr(s, "cde")) { + return "cde"; + } + return "root"; + } + + if (! dpy) { + return ""; + } + + prop = XInternAtom(dpy, "XFCE_DESKTOP_WINDOW", True); + if (prop != None) return "xfce"; + + /* special case windowmaker */ + prop = XInternAtom(dpy, "_WINDOWMAKER_WM_PROTOCOLS", True); + if (prop != None) return "root"; + + prop = XInternAtom(dpy, "_WINDOWMAKER_COMMAND", True); + if (prop != None) return "root"; + + prop = XInternAtom(dpy, "NAUTILUS_DESKTOP_WINDOW_ID", True); + if (prop != None) return "gnome"; + + prop = XInternAtom(dpy, "KWIN_RUNNING", True); + if (prop != None) return "kde"; + + prop = XInternAtom(dpy, "_MOTIF_WM_INFO", True); + if (prop != None) { + prop = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True); + if (prop != None) return "cde"; + } + return "root"; +} + +void solid_bg(int restore) { + static int desktop = -1; + static int solid_on = 0; + static char *prev_str; + char *dtname, *color; + + if (started_as_root == 1 && users_list) { + /* we are still root, don't try. */ + return; + } + + if (restore) { + if (! solid_on) { + return; + } + if (desktop == 0) { + solid_root(NULL); + } else if (desktop == 1) { + solid_gnome(NULL); + } else if (desktop == 2) { + solid_kde(NULL); + } else if (desktop == 3) { + solid_cde(NULL); + } + solid_on = 0; + return; + } + if (! solid_str) { + return; + } + if (solid_on && !strcmp(prev_str, solid_str)) { + return; + } + if (strstr(solid_str, "guess:") == solid_str + || !strchr(solid_str, ':')) { + dtname = guess_desktop(); + rfbLog("guessed desktop: %s\n", dtname); + } else { + if (strstr(solid_str, "gnome:") == solid_str) { + dtname = "gnome"; + } else if (strstr(solid_str, "kde:") == solid_str) { + dtname = "kde"; + } else if (strstr(solid_str, "cde:") == solid_str) { + dtname = "cde"; + } else { + dtname = "root"; + } + } + + color = strchr(solid_str, ':'); + if (! color) { + color = solid_str; + } else { + color++; + if (*color == '\0') { + color = solid_default; + } + } + if (!strcmp(dtname, "gnome")) { + desktop = 1; + solid_gnome(color); + } else if (!strcmp(dtname, "kde")) { + desktop = 2; + solid_kde(color); + } else if (!strcmp(dtname, "cde")) { + desktop = 3; + solid_cde(color); + } else { + desktop = 0; + solid_root(color); + } + if (prev_str) { + free(prev_str); + } + prev_str = strdup(solid_str); + solid_on = 1; +} + + diff --git a/x11vnc/solid.h b/x11vnc/solid.h new file mode 100644 index 0000000..d71d8b6 --- /dev/null +++ b/x11vnc/solid.h @@ -0,0 +1,9 @@ +#ifndef _X11VNC_SOLID_H +#define _X11VNC_SOLID_H + +/* -- solid.h -- */ + +extern char *guess_desktop(void); +extern void solid_bg(int restore); + +#endif /* _X11VNC_SOLID_H */ diff --git a/x11vnc/tkx11vnc b/x11vnc/tkx11vnc index 07a12f0..d8cc569 100755 --- a/x11vnc/tkx11vnc +++ b/x11vnc/tkx11vnc @@ -90,6 +90,7 @@ Clients =DRQA disconnect: -- accept: + afteraccept: gone: vncconnect -- D @@ -5951,6 +5952,11 @@ if {$tk_version < 8.4} { if {"$argv" == "-spit"} { set fh [open $argv0 r] + puts "#ifndef _TKX11VNC_H" + puts "#define _TKX11VNC_H" + puts "#ifdef NOGUI" + puts "char gui_code[] = \"\";" + puts "#else" puts "/*" puts " * tkx11vnc.h: generated by 'tkx11vnc -spit'" puts " * Abandon all hope, ye who enter here..." @@ -5962,6 +5968,10 @@ if {"$argv" == "-spit"} { regsub -all {"} $line {\\"} line puts "\"$line\\n\"" } + puts "#endif" + puts "/* ifdef NOGUI */" + puts "#endif" + puts "/* ifndef _TKX11VNC_H */" close $fh puts ";" exit 0 diff --git a/x11vnc/tkx11vnc.h b/x11vnc/tkx11vnc.h index ee6fe5d..58c02e6 100644 --- a/x11vnc/tkx11vnc.h +++ b/x11vnc/tkx11vnc.h @@ -1,3 +1,8 @@ +#ifndef _TKX11VNC_H +#define _TKX11VNC_H +#ifdef NOGUI +char gui_code = ""; +#else /* * tkx11vnc.h: generated by 'tkx11vnc -spit' * Abandon all hope, ye who enter here... @@ -96,6 +101,7 @@ " =DRQA disconnect:\n" " --\n" " accept:\n" +" afteraccept:\n" " gone:\n" " vncconnect\n" " -- D\n" @@ -5957,6 +5963,11 @@ "\n" "if {\"$argv\" == \"-spit\"} {\n" " set fh [open $argv0 r]\n" +" puts \"#ifndef _TKX11VNC_H\"\n" +" puts \"#define _TKX11VNC_H\"\n" +" puts \"#ifdef NOGUI\"\n" +" puts \"char gui_code[] = \\\"\\\";\"\n" +" puts \"#else\"\n" " puts \"/*\"\n" " puts \" * tkx11vnc.h: generated by 'tkx11vnc -spit'\"\n" " puts \" * Abandon all hope, ye who enter here...\"\n" @@ -5968,6 +5979,10 @@ " regsub -all {\"} $line {\\\\\"} line\n" " puts \"\\\"$line\\\\n\\\"\"\n" " }\n" +" puts \"#endif\"\n" +" puts \"/* ifdef NOGUI */\"\n" +" puts \"#endif\"\n" +" puts \"/* ifndef _TKX11VNC_H */\"\n" " close $fh\n" " puts \";\"\n" " exit 0\n" @@ -6170,4 +6185,8 @@ "}\n" "\n" "# main loop.\n" +#endif +/* ifdef NOGUI */ +#endif +/* ifndef _TKX11VNC_H */ ; diff --git a/x11vnc/user.c b/x11vnc/user.c new file mode 100644 index 0000000..6d173ab --- /dev/null +++ b/x11vnc/user.c @@ -0,0 +1,930 @@ +/* -- user.c -- */ + +#include "x11vnc.h" +#include "solid.h" +#include "cleanup.h" +#include "scan.h" +#include "screen.h" + +void check_switched_user(void); +void lurk_loop(char *str); +int switch_user(char *user, int fb_mode); +int read_passwds(char *passfile); +void install_passwds(void); +void check_new_passwds(void); + + +static void switch_user_task_dummy(void); +static void switch_user_task_solid_bg(void); +static char *get_login_list(int with_display); +static char **user_list(char *user_str); +static void user2uid(char *user, uid_t *uid, char **name, char **home); +static int lurk(char **users); +static int guess_user_and_switch(char *str, int fb_mode); +static int try_user_and_display(uid_t uid, char *dpystr); +static int switch_user_env(uid_t uid, char *name, char *home, int fb_mode); +static void try_to_switch_users(void); + + +/* tasks for after we switch */ +static void switch_user_task_dummy(void) { + ; /* dummy does nothing */ +} +static void switch_user_task_solid_bg(void) { + /* we have switched users, some things to do. */ + if (use_solid_bg && client_count) { + solid_bg(0); + } +} + +void check_switched_user(void) { + static time_t sched_switched_user = 0; + static int did_solid = 0; + static int did_dummy = 0; + int delay = 15; + time_t now = time(0); + + if (started_as_root == 1 && users_list) { + try_to_switch_users(); + if (started_as_root == 2) { + /* + * schedule the switch_user_tasks() call + * 15 secs is for piggy desktops to start up. + * might not be enough for slow machines... + */ + sched_switched_user = now; + did_dummy = 0; + did_solid = 0; + /* add other activities */ + } + } + if (! sched_switched_user) { + return; + } + + if (! did_dummy) { + switch_user_task_dummy(); + did_dummy = 1; + } + if (! did_solid) { + int doit = 0; + char *ss = solid_str; + if (now >= sched_switched_user + delay) { + doit = 1; + } else if (ss && strstr(ss, "root:") == ss) { + if (now >= sched_switched_user + 3) { + doit = 1; + } + } else if (strcmp("root", guess_desktop())) { + usleep(1000 * 1000); + doit = 1; + } + if (doit) { + switch_user_task_solid_bg(); + did_solid = 1; + } + } + + if (did_dummy && did_solid) { + sched_switched_user = 0; + } +} + +/* utilities for switching users */ +static char *get_login_list(int with_display) { + char *out; +#if LIBVNCSERVER_HAVE_UTMPX_H + int i, cnt, max = 200, ut_namesize = 32; + int dpymax = 1000, sawdpy[1000]; + struct utmpx *utx; + + /* size based on "username:999," * max */ + out = (char *) malloc(max * (ut_namesize+1+3+1) + 1); + out[0] = '\0'; + + for (i=0; i<dpymax; i++) { + sawdpy[i] = 0; + } + + setutxent(); + cnt = 0; + while (1) { + char *user, *line, *host, *id; + char tmp[10]; + int d = -1; + utx = getutxent(); + if (! utx) { + break; + } + if (utx->ut_type != USER_PROCESS) { + continue; + } + user = lblanks(utx->ut_user); + if (*user == '\0') { + continue; + } + if (strchr(user, ',')) { + continue; /* unlikely, but comma is our sep. */ + } + + line = lblanks(utx->ut_line); + host = lblanks(utx->ut_host); + id = lblanks(utx->ut_id); + + if (with_display) { + if (0 && line[0] != ':' && strcmp(line, "dtlocal")) { + /* XXX useful? */ + continue; + } + + if (line[0] == ':') { + if (sscanf(line, ":%d", &d) != 1) { + d = -1; + } + } + if (d < 0 && host[0] == ':') { + if (sscanf(host, ":%d", &d) != 1) { + d = -1; + } + } + if (d < 0 && id[0] == ':') { + if (sscanf(id, ":%d", &d) != 1) { + d = -1; + } + } + + if (d < 0 || d >= dpymax || sawdpy[d]) { + continue; + } + sawdpy[d] = 1; + sprintf(tmp, ":%d", d); + } else { + /* try to eliminate repeats */ + int repeat = 0; + char *q; + + q = out; + while ((q = strstr(q, user)) != NULL) { + char *p = q + strlen(user) + strlen(":DPY"); + if (q == out || *(q-1) == ',') { + /* bounded on left. */ + if (*p == ',' || *p == '\0') { + /* bounded on right. */ + repeat = 1; + break; + } + } + q = p; + } + if (repeat) { + continue; + } + sprintf(tmp, ":DPY"); + } + + if (*out) { + strcat(out, ","); + } + strcat(out, user); + strcat(out, tmp); + + cnt++; + if (cnt >= max) { + break; + } + } + endutxent(); +#else + out = strdup(""); +#endif + return out; +} + +static char **user_list(char *user_str) { + int n, i; + char *p, **list; + + p = user_str; + n = 1; + while (*p++) { + if (*p == ',') { + n++; + } + } + list = (char **) malloc((n+1)*sizeof(char *)); + + p = strtok(user_str, ","); + i = 0; + while (p) { + list[i++] = p; + p = strtok(NULL, ","); + } + list[i] = NULL; + return list; +} + +static void user2uid(char *user, uid_t *uid, char **name, char **home) { + int numerical = 1; + char *q; + + *uid = (uid_t) -1; + *name = NULL; + *home = NULL; + + q = user; + while (*q) { + if (! isdigit(*q++)) { + numerical = 0; + break; + } + } + + if (numerical) { + int u = atoi(user); + + if (u < 0) { + return; + } + *uid = (uid_t) u; + } + +#if LIBVNCSERVER_HAVE_PWD_H + if (1) { + struct passwd *pw; + if (numerical) { + pw = getpwuid(*uid); + } else { + pw = getpwnam(user); + } + if (pw) { + *uid = pw->pw_uid; + *name = pw->pw_name; /* n.b. use immediately */ + *home = pw->pw_dir; + } + } +#endif +} + + +static int lurk(char **users) { + uid_t uid; + int success = 0, dmin = -1, dmax = -1; + char *p, *logins, **u; + + if ((u = users) != NULL && *u != NULL && *(*u) == ':') { + int len; + char *tmp; + + /* extract min and max display numbers */ + tmp = *u; + if (strchr(tmp, '-')) { + if (sscanf(tmp, ":%d-%d", &dmin, &dmax) != 2) { + dmin = -1; + dmax = -1; + } + } + if (dmin < 0) { + if (sscanf(tmp, ":%d", &dmin) != 1) { + dmin = -1; + dmax = -1; + } else { + dmax = dmin; + } + } + if ((dmin < 0 || dmax < 0) || dmin > dmax || dmax > 10000) { + dmin = -1; + dmax = -1; + } + + /* get user logins regardless of having a display: */ + logins = get_login_list(0); + + /* + * now we append the list in users (might as well try + * them) this will probably allow weird ways of starting + * xservers to work. + */ + len = strlen(logins); + u++; + while (*u != NULL) { + len += strlen(*u) + strlen(":DPY,"); + u++; + } + tmp = (char *) malloc(len+1); + strcpy(tmp, logins); + + /* now concatenate them: */ + u = users+1; + while (*u != NULL) { + char *q, chk[100]; + snprintf(chk, 100, "%s:DPY", *u); + q = strstr(tmp, chk); + if (q) { + char *p = q + strlen(chk); + + if (q == tmp || *(q-1) == ',') { + /* bounded on left. */ + if (*p == ',' || *p == '\0') { + /* bounded on right. */ + u++; + continue; + } + } + } + + if (*tmp) { + strcat(tmp, ","); + } + strcat(tmp, *u); + strcat(tmp, ":DPY"); + u++; + } + free(logins); + logins = tmp; + + } else { + logins = get_login_list(1); + } + + p = strtok(logins, ","); + while (p) { + char *user, *name, *home, dpystr[10]; + char *q, *t; + int ok = 1, dn; + + t = strdup(p); /* bob:0 */ + q = strchr(t, ':'); + if (! q) { + free(t); + break; + } + *q = '\0'; + user = t; + snprintf(dpystr, 10, ":%s", q+1); + + if (users) { + u = users; + ok = 0; + while (*u != NULL) { + if (*(*u) == ':') { + u++; + continue; + } + if (!strcmp(user, *u++)) { + ok = 1; + break; + } + } + } + + user2uid(user, &uid, &name, &home); + free(t); + + if (! uid) { + ok = 0; + } + + if (! ok) { + p = strtok(NULL, ","); + continue; + } + + for (dn = dmin; dn <= dmax; dn++) { + if (dn >= 0) { + sprintf(dpystr, ":%d", dn); + } + if (try_user_and_display(uid, dpystr)) { + if (switch_user_env(uid, name, home, 0)) { + rfbLog("lurk: now user: %s @ %s\n", + name, dpystr); + started_as_root = 2; + success = 1; + } + set_env("DISPLAY", dpystr); + break; + } + } + if (success) { + break; + } + + p = strtok(NULL, ","); + } + free(logins); + return success; +} + +void lurk_loop(char *str) { + char *tstr = NULL, **users = NULL; + + if (strstr(str, "lurk=") != str) { + exit(1); + } + rfbLog("lurking for logins using: '%s'\n", str); + if (strlen(str) > strlen("lurk=")) { + char *q = strchr(str, '='); + tstr = strdup(q+1); + users = user_list(tstr); + } + + while (1) { + if (lurk(users)) { + break; + } + sleep(3); + } + if (tstr) { + free(tstr); + } + if (users) { + free(users); + } +} + +static int guess_user_and_switch(char *str, int fb_mode) { + char *dstr, *d = DisplayString(dpy); + char *p, *tstr = NULL, *allowed = NULL, *logins, **users = NULL; + int dpy1, ret = 0; + + /* pick out ":N" */ + dstr = strchr(d, ':'); + if (! dstr) { + return 0; + } + if (sscanf(dstr, ":%d", &dpy1) != 1) { + return 0; + } + if (dpy1 < 0) { + return 0; + } + + if (strstr(str, "guess=") == str && strlen(str) > strlen("guess=")) { + allowed = strchr(str, '='); + allowed++; + + tstr = strdup(allowed); + users = user_list(tstr); + } + + /* loop over the utmpx entries looking for this display */ + logins = get_login_list(1); + p = strtok(logins, ","); + while (p) { + char *user, *q, *t; + int dpy2, ok = 1; + + t = strdup(p); + q = strchr(t, ':'); + if (! q) { + free(t); + break; + } + *q = '\0'; + user = t; + dpy2 = atoi(q+1); + + if (users) { + char **u = users; + ok = 0; + while (*u != NULL) { + if (!strcmp(user, *u++)) { + ok = 1; + break; + } + } + } + if (dpy1 != dpy2) { + ok = 0; + } + + if (! ok) { + free(t); + p = strtok(NULL, ","); + continue; + } + if (switch_user(user, fb_mode)) { + rfbLog("switched to guessed user: %s\n", user); + free(t); + ret = 1; + break; + } + + p = strtok(NULL, ","); + } + if (tstr) { + free(tstr); + } + if (users) { + free(users); + } + if (logins) { + free(logins); + } + return ret; +} + +static int try_user_and_display(uid_t uid, char *dpystr) { + /* NO strtoks */ +#if LIBVNCSERVER_HAVE_FORK && LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_PWD_H + pid_t pid, pidw; + char *home, *name; + int st; + struct passwd *pw; + + pw = getpwuid(uid); + if (pw) { + name = pw->pw_name; + home = pw->pw_dir; + } else { + return 0; + } + + /* + * We fork here and try to open the display again as the + * new user. Unreadable XAUTHORITY could be a problem... + * This is not really needed since we have DISPLAY open but: + * 1) is a good indicator this user owns the session and 2) + * some activities do spawn new X apps, e.g. xmessage(1), etc. + */ + if ((pid = fork()) > 0) { + ; + } else if (pid == -1) { + fprintf(stderr, "could not fork\n"); + rfbLogPerror("fork"); + return 0; + } else { + /* child */ + Display *dpy2 = NULL; + int rc; + + rc = switch_user_env(uid, name, home, 0); + if (! rc) { + exit(1); + } + + fclose(stderr); + dpy2 = XOpenDisplay(dpystr); + if (dpy2) { + XCloseDisplay(dpy2); + exit(0); /* success */ + } else { + exit(2); /* fail */ + } + } + + /* see what the child says: */ + pidw = waitpid(pid, &st, 0); + if (pidw == pid && WIFEXITED(st) && WEXITSTATUS(st) == 0) { + return 1; + } +#endif /* LIBVNCSERVER_HAVE_FORK ... */ + return 0; +} + +int switch_user(char *user, int fb_mode) { + /* NO strtoks */ + int doit = 0; + uid_t uid = 0; + char *name, *home; + + if (*user == '+') { + doit = 1; + user++; + } + + if (strstr(user, "guess=") == user) { + return guess_user_and_switch(user, fb_mode); + } + + user2uid(user, &uid, &name, &home); + + if (uid == (uid_t) -1 || uid == 0) { + return 0; + } + + if (! doit && dpy) { + /* see if this display works: */ + char *dstr = DisplayString(dpy); + doit = try_user_and_display(uid, dstr); + } + + if (doit) { + int rc = switch_user_env(uid, name, home, fb_mode); + if (rc) { + started_as_root = 2; + } + return rc; + } else { + return 0; + } +} + +static int switch_user_env(uid_t uid, char *name, char *home, int fb_mode) { + /* NO strtoks */ + char *xauth; + int reset_fb = 0; + +#if !LIBVNCSERVER_HAVE_SETUID + return 0; +#else + /* + * OK tricky here, we need to free the shm... otherwise + * we won't be able to delete it as the other user... + */ + if (fb_mode == 1 && using_shm) { + reset_fb = 1; + clean_shm(0); + free_tiles(); + } + if (setuid(uid) != 0) { + if (reset_fb) { + /* 2 means we did clean_shm and free_tiles */ + do_new_fb(2); + } + return 0; + } +#endif + if (reset_fb) { + do_new_fb(2); + } + + xauth = getenv("XAUTHORITY"); + if (xauth && access(xauth, R_OK) != 0) { + *(xauth-2) = '_'; /* yow */ + } + + set_env("USER", name); + set_env("LOGNAME", name); + set_env("HOME", home); + return 1; +} + +static void try_to_switch_users(void) { + static time_t last_try = 0; + time_t now = time(0); + char *users, *p; + + if (getuid() && geteuid()) { + rfbLog("try_to_switch_users: not root\n"); + started_as_root = 2; + return; + } + if (!last_try) { + last_try = now; + } else if (now <= last_try + 2) { + /* try every 3 secs or so */ + return; + } + last_try = now; + + users = strdup(users_list); + + if (strstr(users, "guess=") == users) { + if (switch_user(users, 1)) { + started_as_root = 2; + } + free(users); + return; + } + + p = strtok(users, ","); + while (p) { + if (switch_user(p, 1)) { + started_as_root = 2; + rfbLog("try_to_switch_users: now %s\n", p); + break; + } + p = strtok(NULL, ","); + } + free(users); +} + +int read_passwds(char *passfile) { + char line[1024]; + char *filename; + char **old_passwd_list = passwd_list; + int remove = 0; + int read_mode = 0; + int begin_vo = -1; + struct stat sbuf; + int linecount = 0, i, max; + FILE *in; + static time_t last_read = 0; + static int read_cnt = 0; + int db_passwd = 0; + + filename = passfile; + if (strstr(filename, "rm:") == filename) { + filename += strlen("rm:"); + remove = 1; + } else if (strstr(filename, "read:") == filename) { + filename += strlen("read:"); + read_mode = 1; + if (stat(filename, &sbuf) == 0) { + if (sbuf.st_mtime <= last_read) { + return 0; + } + last_read = sbuf.st_mtime; + } + } + + if (stat(filename, &sbuf) == 0) { + /* (poor...) upper bound to number of lines */ + max = (int) sbuf.st_size; + last_read = sbuf.st_mtime; + } else { + max = 64; + } + + /* create 1 more than max to have it be the ending NULL */ + passwd_list = (char **) malloc( (max+1) * (sizeof(char *)) ); + for (i=0; i<max+1; i++) { + passwd_list[i] = NULL; + } + + in = fopen(filename, "r"); + if (in == NULL) { + rfbLog("cannot open passwdfile: %s\n", passfile); + rfbLogPerror("fopen"); + if (remove) { + unlink(filename); + } + clean_up_exit(1); + } + + if (getenv("DEBUG_PASSWDFILE") != NULL) { + db_passwd = 1; + } + + while (fgets(line, 1024, in) != NULL) { + char *p; + int blank = 1; + int len = strlen(line); + + if (db_passwd) { + fprintf(stderr, "read_passwds: raw line: %s\n", line); + } + + if (len == 0) { + continue; + } else if (line[len-1] == '\n') { + line[len-1] = '\0'; + } + if (line[0] == '\0') { + continue; + } + if (strstr(line, "__SKIP__") != NULL) { + continue; + } + if (strstr(line, "__COMM__") == line) { + continue; + } + if (!strcmp(line, "__BEGIN_VIEWONLY__")) { + if (begin_vo < 0) { + begin_vo = linecount; + } + continue; + } + if (line[0] == '#') { + /* commented out, cannot have password beginning with # */ + continue; + } + p = line; + while (*p != '\0') { + if (! isspace(*p)) { + blank = 0; + break; + } + p++; + } + if (blank) { + continue; + } + + passwd_list[linecount++] = strdup(line); + if (db_passwd) { + fprintf(stderr, "read_passwds: keepline: %s\n", line); + fprintf(stderr, "read_passwds: begin_vo: %d\n", begin_vo); + } + + if (linecount >= max) { + break; + } + } + fclose(in); + + for (i=0; i<1024; i++) { + line[i] = '\0'; + } + + if (remove) { + unlink(filename); + } + + if (! linecount) { + rfbLog("cannot read a valid line from passwdfile: %s\n", + passfile); + if (read_cnt == 0) { + clean_up_exit(1); + } else { + return 0; + } + } + read_cnt++; + + for (i=0; i<linecount; i++) { + char *q, *p = passwd_list[i]; + if (!strcmp(p, "__EMPTY__")) { + *p = '\0'; + } else if ((q = strstr(p, "__COMM__")) != NULL) { + *q = '\0'; + } + passwd_list[i] = strdup(p); + if (db_passwd) { + fprintf(stderr, "read_passwds: trimline: %s\n", p); + } + strzero(p); + } + + begin_viewonly = begin_vo; + if (read_mode && read_cnt > 1) { + if (viewonly_passwd) { + free(viewonly_passwd); + viewonly_passwd = NULL; + } + } + + if (begin_viewonly < 0 && linecount == 2) { + /* for compatibility with previous 2-line usage: */ + viewonly_passwd = strdup(passwd_list[1]); + if (db_passwd) { + fprintf(stderr, "read_passwds: linecount is 2.\n"); + } + if (screen) { + char **apd = (char **) screen->authPasswdData; + if (apd) { + if (apd[0] != NULL) { + strzero(apd[0]); + } + apd[0] = strdup(passwd_list[0]); + } + } + begin_viewonly = 1; + } + + if (old_passwd_list != NULL) { + char *p; + i = 0; + while (old_passwd_list[i] != NULL) { + p = old_passwd_list[i]; + strzero(p); + free(old_passwd_list[i]); + i++; + } + free(old_passwd_list); + } + return 1; +} + +void install_passwds(void) { + if (viewonly_passwd) { + /* append the view only passwd after the normal passwd */ + char **passwds_new = (char **) malloc(3*sizeof(char *)); + char **passwds_old = (char **) screen->authPasswdData; + passwds_new[0] = passwds_old[0]; + passwds_new[1] = viewonly_passwd; + passwds_new[2] = NULL; + screen->authPasswdData = (void*) passwds_new; + } else if (passwd_list) { + int i = 0; + while(passwd_list[i] != NULL) { + i++; + } + if (begin_viewonly < 0) { + begin_viewonly = i+1; + } + screen->authPasswdData = (void*) passwd_list; + screen->authPasswdFirstViewOnly = begin_viewonly; + } +} + +void check_new_passwds(void) { + static time_t last_check = 0; + time_t now; + + if (! passwdfile) { + return; + } + if (strstr(passwdfile, "read:") != passwdfile) { + return; + } + now = time(0); + if (now > last_check + 1) { + if (read_passwds(passwdfile)) { + install_passwds(); + } + last_check = now; + } +} + + diff --git a/x11vnc/user.h b/x11vnc/user.h new file mode 100644 index 0000000..1469c0f --- /dev/null +++ b/x11vnc/user.h @@ -0,0 +1,13 @@ +#ifndef _X11VNC_USER_H +#define _X11VNC_USER_H + +/* -- user.h -- */ + +extern void check_switched_user(void); +extern void lurk_loop(char *str); +extern int switch_user(char *, int); +extern int read_passwds(char *passfile); +extern void install_passwds(void); +extern void check_new_passwds(void); + +#endif /* _X11VNC_USER_H */ diff --git a/x11vnc/userinput.c b/x11vnc/userinput.c new file mode 100644 index 0000000..75f0fa4 --- /dev/null +++ b/x11vnc/userinput.c @@ -0,0 +1,4293 @@ +/* -- userinput.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "xdamage.h" +#include "xrecord.h" +#include "xinerama.h" +#include "win_utils.h" +#include "user.h" +#include "scan.h" +#include "cleanup.h" +#include "pointer.h" +#include "rates.h" +#include "keyboard.h" +#include "solid.h" +#include "xrandr.h" + +/* + * user input handling heuristics + */ +int defer_update_nofb = 6; /* defer a shorter time under -nofb */ +int last_scroll_type = SCR_NONE; + + +int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, + Window *frame, Window *win); +void parse_scroll_copyrect(void); +void parse_fixscreen(void); +void parse_wireframe(void); + +void set_wirecopyrect_mode(char *str); +void set_scrollcopyrect_mode(char *str); +void initialize_scroll_keys(void); +void initialize_scroll_matches(void); +void initialize_scroll_term(void); +void initialize_max_keyrepeat(void); + +int direct_fb_copy(int x1, int y1, int x2, int y2, int mark); +void fb_push(void); +void fb_push_wait(double max_wait, int flags); +void eat_viewonly_input(int max_eat, int keep); + +void mark_for_xdamage(int x, int y, int w, int h); +void mark_region_for_xdamage(sraRegionPtr region); +void set_xdamage_mark(int x, int y, int w, int h); +int near_wm_edge(int x, int y, int w, int h, int px, int py); +int near_scrollbar_edge(int x, int y, int w, int h, int px, int py); + +void check_fixscreen(void); +int check_xrecord(void); +int check_wireframe(void); +int fb_update_sent(int *count); +int check_user_input(double dt, double dtr, int tile_diffs, int *cnt); + + +static void get_client_regions(int *req, int *mod, int *cpy, int *num) ; +static void do_copyregion(sraRegionPtr region, int dx, int dy) ; +static void parse_scroll_copyrect_str(char *scr); +static void parse_wireframe_str(char *wf); +static void destroy_str_list(char **list); +static void draw_box(int x, int y, int w, int h, int restore); +static int do_bdpush(Window wm_win, int x0, int y0, int w0, int h0, int bdx, + int bdy, int bdskinny); +static int set_ypad(void); +static void scale_mark(int x1, int y1, int x2, int y2); +static int push_scr_ev(double *age, int type, int bdpush, int bdx, int bdy, + int bdskinny); +static int crfix(int x, int dx, int Lx); +static int scrollability(Window win, int set); +static int eat_pointer(int max_ptr_eat, int keep); +static void set_bdpush(int type, double *last_bdpush, int *pushit); +static int repeat_check(double last_key_scroll); +static int check_xrecord_keys(void); +static int check_xrecord_mouse(void); +static int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, + int *obscured, sraRegionPtr extra_clip, double max_wait); +static int wireframe_mod_state(); +static void check_user_input2(double dt); +static void check_user_input3(double dt, double dtr, int tile_diffs); +static void check_user_input4(double dt, double dtr, int tile_diffs); + + +/* + * 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 *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); + + *frame = c; + + /* current pointer position is returned too */ + *px = rootx; + *py = rooty; + + if (!ret || ! c || c == rootwin) { + /* no immediate child */ + return 0; + } + + /* child window position and size */ + if (! valid_window(c, &attr, 1)) { + return 0; + } + + *x = attr.x; + *y = attr.y; + *w = attr.width; + *h = attr.height; + + if (win != NULL) { + *win = descend_pointer(5, c, NULL, 0); + } + + return 1; +} + +static int scrollcopyrect_top, scrollcopyrect_bot; +static int scrollcopyrect_left, scrollcopyrect_right; +static double scr_key_time, scr_key_persist; +static double scr_mouse_time, scr_mouse_persist, scr_mouse_maxtime; +static double scr_mouse_pointer_delay; +static double scr_key_bdpush_time, scr_mouse_bdpush_time; + +static void parse_scroll_copyrect_str(char *scr) { + char *p, *str; + int i; + char *part[10]; + + for (i=0; i<10; i++) { + part[i] = NULL; + } + + if (scr == NULL || *scr == '\0') { + return; + } + + str = strdup(scr); + + p = strtok(str, ","); + i = 0; + while (p) { + part[i++] = strdup(p); + p = strtok(NULL, ","); + } + free(str); + + + /* + * Top, Bottom, Left, Right tolerances for scrollbar locations. + */ + if ((str = part[0]) != NULL) { + int t, b, l, r; + if (sscanf(str, "%d+%d+%d+%d", &t, &b, &l, &r) == 4) { + scrollcopyrect_top = t; + scrollcopyrect_bot = b; + scrollcopyrect_left = l; + scrollcopyrect_right = r; + } + free(str); + } + + /* key scrolling timing heuristics. */ + if ((str = part[1]) != NULL) { + 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, 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); + } +} + +void parse_scroll_copyrect(void) { + parse_scroll_copyrect_str(SCROLL_COPYRECT_PARMS); + if (! scroll_copyrect_str) { + scroll_copyrect_str = strdup(SCROLL_COPYRECT_PARMS); + } + parse_scroll_copyrect_str(scroll_copyrect_str); +} + +void parse_fixscreen(void) { + char *str, *p; + + screen_fixup_V = 0.0; + screen_fixup_C = 0.0; + screen_fixup_X = 0.0; + + if (! screen_fixup_str) { + return; + } + + str = strdup(screen_fixup_str); + + p = strtok(str, ","); + while (p) { + double t; + if (*p == 'V' && sscanf(p, "V=%lf", &t) == 1) { + screen_fixup_V = t; + } else if (*p == 'C' && sscanf(p, "C=%lf", &t) == 1) { + screen_fixup_C = t; + } else if (*p == 'X' && sscanf(p, "X=%lf", &t) == 1) { + screen_fixup_X = t; + } + p = strtok(NULL, ","); + } + free(str); + + if (screen_fixup_V < 0.0) screen_fixup_V = 0.0; + if (screen_fixup_C < 0.0) screen_fixup_C = 0.0; + if (screen_fixup_X < 0.0) screen_fixup_X = 0.0; +} + +/* +WIREFRAME_PARMS "0xff,2,0,30+6+6+6,Alt,0.05+0.3+2.0,8" +shade,linewidth,percent,T+B+L+R,mods,t1+t2+t3+t4 + */ +#define LW_MAX 8 +static unsigned long wireframe_shade; +static int wireframe_lw; +static double wireframe_frac; +static int wireframe_top, wireframe_bot, wireframe_left, wireframe_right; +static double wireframe_t1, wireframe_t2, wireframe_t3, wireframe_t4; +static char *wireframe_mods = NULL; + +/* + * Parse the gory -wireframe string for parameters. + */ +static void parse_wireframe_str(char *wf) { + char *p, *str; + int i; + char *part[10]; + + for (i=0; i<10; i++) { + part[i] = NULL; + } + + if (wf == NULL || *wf == '\0') { + return; + } + + str = strdup(wf); + + /* leading ",", make it start with ignorable string "z" */ + if (*str == ',') { + char *tmp = (char *) malloc(strlen(str)+2); + strcpy(tmp, "z"); + strcat(tmp, str); + free(str); + str = tmp; + } + + p = strtok(str, ","); + i = 0; + while (p) { + part[i++] = strdup(p); + p = strtok(NULL, ","); + } + free(str); + + + /* Wireframe shade, color, RGB: */ + if ((str = part[0]) != NULL) { + unsigned long n; + int r, g, b, ok = 0; + XColor cdef; + Colormap cmap; + if (dpy && (bpp == 32 || bpp == 16)) { + cmap = DefaultColormap (dpy, scr); + if (XParseColor(dpy, cmap, str, &cdef) && + XAllocColor(dpy, cmap, &cdef)) { + r = cdef.red >> 8; + g = cdef.green >> 8; + b = cdef.blue >> 8; + if (r == 0 && g == 0) { + g = 1; /* need to be > 255 */ + } + n = 0; + n |= (r << main_red_shift); + n |= (g << main_green_shift); + n |= (b << main_blue_shift); + wireframe_shade = n; + ok = 1; + } + } + if (ok) { + ; + } else if (sscanf(str, "0x%lx", &n) == 1) { + wireframe_shade = n; + } else if (sscanf(str, "%lu", &n) == 1) { + wireframe_shade = n; + } else if (sscanf(str, "%lx", &n) == 1) { + wireframe_shade = n; + } + free(str); + } + + /* linewidth: # of pixels wide for the wireframe lines */ + if ((str = part[1]) != NULL) { + int n; + if (sscanf(str, "%d", &n) == 1) { + wireframe_lw = n; + if (wireframe_lw < 1) { + wireframe_lw = 1; + } + if (wireframe_lw > LW_MAX) { + wireframe_lw = LW_MAX; + } + } + free(str); + } + + /* percentage cutoff for opaque move/resize (like WM's) */ + if ((str = part[2]) != NULL) { + if (*str == '\0') { + ; + } else if (strchr(str, '.')) { + wireframe_frac = atof(str); + } else { + wireframe_frac = ((double) atoi(str))/100.0; + } + free(str); + } + + /* + * Top, Bottom, Left, Right tolerances to guess the wm frame is + * being grabbed (Top is traditionally bigger, i.e. titlebar): + */ + if ((str = part[3]) != NULL) { + int t, b, l, r; + if (sscanf(str, "%d+%d+%d+%d", &t, &b, &l, &r) == 4) { + wireframe_top = t; + wireframe_bot = b; + wireframe_left = l; + wireframe_right = r; + } + free(str); + } + + /* + * wireframe in interior with Modifier down. + * 0 => no mods + * 1 => all mods + * Shift,Alt,Control,Meta,Super,Hyper + */ + if (wireframe_mods) { + free(wireframe_mods); + } + wireframe_mods = NULL; + if ((str = part[4]) != NULL) { + if (*str == '0' || !strcmp(str, "none")) { + ; + } else if (*str == '1' || !strcmp(str, "all")) { + wireframe_mods = strdup("all"); + } else if (!strcmp(str, "Alt") || !strcmp(str, "Shift") + || !strcmp(str, "Control") || !strcmp(str, "Meta") + || !strcmp(str, "Super") || !strcmp(str, "Hyper")) { + wireframe_mods = strdup(str); + } + } + + /* check_wireframe() timing heuristics. */ + if ((str = part[5]) != NULL) { + double t1, t2, t3, t4; + if (sscanf(str, "%lf+%lf+%lf+%lf", &t1, &t2, &t3, &t4) == 4) { + wireframe_t1 = t1; + wireframe_t2 = t2; + wireframe_t3 = t3; + wireframe_t4 = t4; + } + free(str); + } +} + +/* + * First parse the defaults and apply any user supplied ones (may be a subset) + */ +void parse_wireframe(void) { + parse_wireframe_str(WIREFRAME_PARMS); + if (! wireframe_str) { + wireframe_str = strdup(WIREFRAME_PARMS); + } + parse_wireframe_str(wireframe_str); +} + +/* + * Set wireframe_copyrect based on desired mode. + */ +void set_wirecopyrect_mode(char *str) { + char *orig = wireframe_copyrect; + if (str == NULL || *str == '\0') { + wireframe_copyrect = strdup(wireframe_copyrect_default); + } else if (!strcmp(str, "always") || !strcmp(str, "all")) { + wireframe_copyrect = strdup("always"); + } else if (!strcmp(str, "top")) { + wireframe_copyrect = strdup("top"); + } else if (!strcmp(str, "never") || !strcmp(str, "none")) { + wireframe_copyrect = strdup("never"); + } else { + if (! wireframe_copyrect) { + wireframe_copyrect = strdup(wireframe_copyrect_default); + } else { + orig = NULL; + } + rfbLog("unknown -wirecopyrect mode: %s, using: %s\n", str, + wireframe_copyrect); + } + if (orig) { + free(orig); + } +} + +/* + * Set scroll_copyrect based on desired mode. + */ +void set_scrollcopyrect_mode(char *str) { + char *orig = scroll_copyrect; + if (str == NULL || *str == '\0') { + scroll_copyrect = strdup(scroll_copyrect_default); + } else if (!strcmp(str, "always") || !strcmp(str, "all") || + !strcmp(str, "both")) { + scroll_copyrect = strdup("always"); + } else if (!strcmp(str, "keys") || !strcmp(str, "keyboard")) { + scroll_copyrect = strdup("keys"); + } else if (!strcmp(str, "mouse") || !strcmp(str, "pointer")) { + scroll_copyrect = strdup("mouse"); + } else if (!strcmp(str, "never") || !strcmp(str, "none")) { + scroll_copyrect = strdup("never"); + } else { + if (! scroll_copyrect) { + scroll_copyrect = strdup(scroll_copyrect_default); + } else { + orig = NULL; + } + rfbLog("unknown -scrollcopyrect mode: %s, using: %s\n", str, + scroll_copyrect); + } + if (orig) { + free(orig); + } +} + +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 { + unsigned int in; + if (sscanf(p, "%u", &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); +} + +static 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); + } + } +} + +void initialize_scroll_term(void) { + char *str; + int n; + + destroy_str_list(scroll_term); + scroll_term = NULL; + + if (scroll_term_str != NULL && *scroll_term_str != '\0') { + str = scroll_term_str; + } else { + str = scroll_term_str0; + } + if (!strcmp(str, "none")) { + return; + } + scroll_term = create_str_list(str); + + n = 0; + while (scroll_term[n] != NULL) { + char *s = scroll_good_all[n++]; + /* pull parameters out at some point */ + s = NULL; + } +} + +void initialize_max_keyrepeat(void) { + char *str; + int lo, hi; + + if (max_keyrepeat_str != NULL && *max_keyrepeat_str != '\0') { + str = max_keyrepeat_str; + } else { + str = max_keyrepeat_str0; + } + + if (sscanf(str, "%d-%d", &lo, &hi) != 2) { + rfbLog("skipping invalid -scr_keyrepeat string: %s\n", str); + sscanf(max_keyrepeat_str0, "%d-%d", &lo, &hi); + } + max_keyrepeat_lo = lo; + max_keyrepeat_hi = hi; + if (max_keyrepeat_lo < 1) { + max_keyrepeat_lo = 1; + } + if (max_keyrepeat_hi > 40) { + max_keyrepeat_hi = 40; + } +} + +typedef struct saveline { + int x0, y0, x1, y1; + int shift; + int vert; + int saved; + char *data; +} saveline_t; + +/* + * Draw the wireframe box onto the framebuffer. Saves the real + * framebuffer data to some storage lines. Restores previous lines. + * use restore = 1 to clean up (done with animation). + * This works with -scale. + */ +static void draw_box(int x, int y, int w, int h, int restore) { + int x0, y0, x1, y1, i, pixelsize = bpp/8; + char *dst, *src; + static saveline_t *save[4]; + static int first = 1, len = 0; + int max = dpy_x > dpy_y ? dpy_x : dpy_y; + int sz, lw = wireframe_lw; + unsigned long shade = wireframe_shade; + int color = 0; + unsigned short us; + unsigned long ul; + + if (clipshift) { + x -= coff_x; + y -= coff_y; + } + /* no subwin for wireframe */ + + if (max > len) { + /* create/resize storage lines: */ + for (i=0; i<4; i++) { + len = max; + if (! first && save[i]) { + if (save[i]->data) { + free(save[i]->data); + save[i]->data = NULL; + } + free(save[i]); + } + save[i] = (saveline_t *) malloc(sizeof(saveline_t)); + save[i]->saved = 0; + sz = (LW_MAX+1)*len*pixelsize; + save[i]->data = (char *) malloc(sz); + + /* + * Four types of lines: + * 0) top horizontal + * 1) bottom horizontal + * 2) left vertical + * 3) right vertical + * + * shift means shifted by width or height. + */ + if (i == 0) { + save[i]->vert = 0; + save[i]->shift = 0; + } else if (i == 1) { + save[i]->vert = 0; + save[i]->shift = 1; + } else if (i == 2) { + save[i]->vert = 1; + save[i]->shift = 0; + } else if (i == 3) { + save[i]->vert = 1; + save[i]->shift = 1; + } + } + } + first = 0; + + /* + * restore any saved lines. see below for algorithm and + * how x0, etc. are used. we just reverse those steps. + */ + for (i=0; i<4; i++) { + int s = save[i]->shift; + int yu, y_min = -1, y_max = -1; + int y_start, y_stop, y_step; + + if (! save[i]->saved) { + continue; + } + x0 = save[i]->x0; + y0 = save[i]->y0; + x1 = save[i]->x1; + y1 = save[i]->y1; + if (save[i]->vert) { + y_start = y0+lw; + y_stop = y1-lw; + y_step = lw*pixelsize; + } else { + y_start = y0 - s*lw; + y_stop = y_start + lw; + y_step = max*pixelsize; + } + for (yu = y_start; yu < y_stop; yu++) { + if (x0 == x1) { + continue; + } + if (yu < 0 || yu >= dpy_y) { + continue; + } + if (y_min < 0 || yu < y_min) { + y_min = yu; + } + if (y_max < 0 || yu > y_max) { + y_max = yu; + } + src = save[i]->data + (yu-y_start)*y_step; + dst = main_fb + yu*main_bytes_per_line + + x0*pixelsize; + memcpy(dst, src, (x1-x0)*pixelsize); + } + if (y_min >= 0) { + mark_rect_as_modified(x0, y_min, x1, y_max+1, 0); + } + save[i]->saved = 0; + } + + if (restore) { + return; + } + +if (0) fprintf(stderr, " DrawBox: %dx%d+%d+%d\n", w, h, x, y); + + /* + * work out shade/color for the wireframe line, could be a color + * for 16bpp or 24bpp. + */ + if (shade > 255) { + if (pixelsize == 2) { + us = (unsigned short) (shade & 0xffff); + color = 1; + } else if (pixelsize == 4) { + ul = (unsigned long) shade; + color = 1; + } else { + shade = shade % 256; + } + } + + for (i=0; i<4; i++) { + int s = save[i]->shift; + int yu, y_min = -1, y_max = -1; + int yblack = -1, xblack1 = -1, xblack2 = -1; + int y_start, y_stop, y_step; + + if (save[i]->vert) { + /* + * make the narrow x's be on the screen, let + * the y's hang off (not drawn). + */ + save[i]->x0 = x0 = nfix(x + s*w - s*lw, dpy_x); + save[i]->y0 = y0 = y; + save[i]->x1 = x1 = nfix(x + s*w - s*lw + lw, dpy_x); + save[i]->y1 = y1 = y + h; + + /* + * start and stop a linewidth away from true edge, + * to avoid interfering with horizontal lines. + */ + y_start = y0+lw; + y_stop = y1-lw; + y_step = lw*pixelsize; + + /* draw a black pixel for the border if lw > 1 */ + if (s) { + xblack1 = x1-1; + } else { + xblack1 = x0; + } + } else { + /* + * make the wide x's be on the screen, let the y's + * hang off (not drawn). + */ + save[i]->x0 = x0 = nfix(x, dpy_x); + save[i]->y0 = y0 = y + s*h; + save[i]->x1 = x1 = nfix(x + w, dpy_x); + save[i]->y1 = y1 = y0 + lw; + y_start = y0 - s*lw; + y_stop = y_start + lw; + y_step = max*pixelsize; + + /* draw a black pixels for the border if lw > 1 */ + if (s) { + yblack = y_stop - 1; + } else { + yblack = y_start; + } + xblack1 = x0; + xblack2 = x1-1; + } + + /* now loop over the allowed y's for either case */ + for (yu = y_start; yu < y_stop; yu++) { + if (x0 == x1) { + continue; + } + if (yu < 0 || yu >= dpy_y) { + continue; + } + + /* record min and max y's for marking rectangle: */ + if (y_min < 0 || yu < y_min) { + y_min = yu; + } + if (y_max < 0 || yu > y_max) { + y_max = yu; + } + + /* save fb data for this line: */ + save[i]->saved = 1; + src = main_fb + yu*main_bytes_per_line + + x0*pixelsize; + dst = save[i]->data + (yu-y_start)*y_step; + memcpy(dst, src, (x1-x0)*pixelsize); + + /* apply the shade/color to make the wireframe line: */ + if (! color) { + memset(src, shade, (x1-x0)*pixelsize); + } else { + char *csrc = src; + unsigned short *usp; + unsigned long *ulp; + int k; + for (k=0; k < x1 - x0; k++) { + if (pixelsize == 4) { + ulp = (unsigned long *)csrc; + *ulp = ul; + } else if (pixelsize == 2) { + usp = (unsigned short *)csrc; + *usp = us; + } + csrc += pixelsize; + } + } + + /* apply black border for lw >= 2 */ + if (lw > 1) { + if (yu == yblack) { + memset(src, 0, (x1-x0)*pixelsize); + } + if (xblack1 >= 0) { + src = src + (xblack1 - x0)*pixelsize; + memset(src, 0, pixelsize); + } + if (xblack2 >= 0) { + src = src + (xblack2 - x0)*pixelsize; + memset(src, 0, pixelsize); + } + } + } + /* mark it for sending: */ + if (save[i]->saved) { + mark_rect_as_modified(x0, y_min, x1, y_max+1, 0); + } + } +} + +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 = 2; + double tm; + int db = 0; + +if (db) dtime0(&tm); + + x1 = nfix(x1, dpy_x); + y1 = nfix(y1, dpy_y); + x2 = nfix(x2, dpy_x+1); + y2 = nfix(y2, dpy_y+1); + + if (x1 == x2) { + return 1; + } + if (y1 == y2) { + return 1; + } + + X_LOCK; + for (y = y1; y < y2; y++) { + XRANDR_SET_TRAP_RET(0, "direct_fb_copy-set"); + copy_image(scanline, x1, y, x2 - x1, 1); + XRANDR_CHK_TRAP_RET(0, "direct_fb_copy-chk"); + + src = scanline->data; + dst = main_fb + y * main_bytes_per_line + x1 * pixelsize; + + if (do_cmp == 0 || !mark) { + memcpy(dst, src, (x2 - x1)*pixelsize); + + } else if (do_cmp == 1) { + if (memcmp(dst, src, (x2 - x1)*pixelsize)) { + if (ymin == -1 || y < ymin) { + ymin = y; + } + if (ymax == -1 || y > ymax) { + ymax = y; + } + memcpy(dst, src, (x2 - x1)*pixelsize); + } + + } else if (do_cmp == 2) { + int n, shift, xlo, xhi, k, block = 32; + char *dst2, *src2; + + for (k=0; k*block < (x2 - x1); k++) { + shift = k*block; + xlo = x1 + shift; + xhi = xlo + block; + if (xhi > x2) { + xhi = x2; + } + n = xhi - xlo; + if (n < 1) { + continue; + } + src2 = src + shift*pixelsize; + dst2 = dst + shift*pixelsize; + if (memcmp(dst2, src2, n*pixelsize)) { + if (ymin == -1 || y < ymin) { + ymin = y; + } + if (ymax == -1 || y > ymax) { + ymax = y; + } + if (xmin == -1 || xlo < xmin) { + xmin = xlo; + } + if (xmax == -1 || xhi > xmax) { + xmax = xhi; + } + memcpy(dst2, src2, n*pixelsize); + } + } + } + } + X_UNLOCK; + + if (do_cmp == 0) { + xmin = x1; + ymin = y1; + xmax = x2; + ymax = y2; + } else if (do_cmp == 1) { + xmin = x1; + xmax = x2; + } + + if (xmin < 0 || ymin < 0 || xmax < 0 || xmin < 0) { + /* no diffs */ + return 1; + } + + if (xmax < x2) { + xmax++; + } + if (ymax < y2) { + ymax++; + } + + if (mark) { + mark_rect_as_modified(xmin, ymin, xmax, ymax, 0); + } + + 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; +} + +static 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); + if (clipshift) { + sraRgnOffset(whole, coff_x, coff_y); + } + if (subwin) { + sraRgnOffset(whole, off_x, off_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; + } + } + if (clipshift) { + tx1 -= coff_x; + ty1 -= coff_y; + tx2 -= coff_x; + ty2 -= coff_y; + } + if (subwin) { + tx1 -= off_x; + ty1 -= off_y; + tx2 -= off_x; + ty2 -= off_y; + } + + 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; +} + +static int set_ypad(void) { + int ev, ev_tot = scr_ev_cnt; + static Window last_win = None; + static double last_time = 0.0; + static int y_accum = 0, last_sign = 0; + double now, cut = 0.1; + int dy_sum = 0, ys = 0, sign; + int font_size = 15; + int win_y, scr_y, loc_cut = 4*font_size, y_cut = 10*font_size; + + if (!xrecord_set_by_keys || !xrecord_name_info) { + return 0; + } + if (xrecord_name_info[0] == '\0') { + return 0; + } + if (! ev_tot) { + return 0; + } + if (xrecord_keysym == NoSymbol) { + return 0; + } + if (!xrecord_scroll_keysym(xrecord_keysym)) { + return 0; + } + if (!scroll_term) { + return 0; + } + if (!match_str_list(xrecord_name_info, scroll_term)) { + return 0; + } + + for (ev=0; ev < ev_tot; ev++) { + dy_sum += nabs(scr_ev[ev].dy); + if (scr_ev[ev].dy < 0) { + ys--; + } else if (scr_ev[ev].dy > 0) { + ys++; + } else { + ys = 0; + break; + } + if (scr_ev[ev].win != scr_ev[0].win) { + ys = 0; + break; + } + if (scr_ev[ev].dx != 0) { + ys = 0; + break; + } + } + if (ys != ev_tot && ys != -ev_tot) { + return 0; + } + if (ys < 0) { + sign = -1; + } else { + sign = 1; + } + + if (sign > 0) { + /* + * this case is not as useful as scrolling near the + * bottom of a terminal. But there are problems for it too. + */ + return 0; + } + + win_y = scr_ev[0].win_y + scr_ev[0].win_h; + scr_y = scr_ev[0].y + scr_ev[0].h; + if (nabs(scr_y - win_y) > loc_cut) { + /* require it to be near the bottom. */ + return 0; + } + + now = dnow(); + + if (now < last_time + cut) { + int ok = 1; + if (last_win && scr_ev[0].win != last_win) { + ok = 0; + } + if (last_sign && sign != last_sign) { + ok = 0; + } + if (! ok) { + last_win = None; + last_sign = 0; + y_accum = 0; + last_time = 0.0; + return 0; + } + } else { + last_win = None; + last_sign = 0; + last_time = 0.0; + y_accum = 0; + } + + y_accum += sign * dy_sum; + + if (4 * nabs(y_accum) > scr_ev[0].h && y_cut) { + ; /* TBD */ + } + + last_sign = sign; + last_win = scr_ev[0].win; + last_time = now; + + return y_accum; +} + +static void scale_mark(int x1, int y1, int x2, int y2) { + int s = 2; + x1 = nfix(x1 - s, dpy_x); + y1 = nfix(y1 - s, dpy_y); + x2 = nfix(x2 + s, dpy_x+1); + y2 = nfix(y2 + s, dpy_y+1); + scale_and_mark_rect(x1, y1, x2, y2); +} + +#define PUSH_TEST(n) \ +if (n) { \ + double dt = 0.0, tm; dtime0(&tm); \ + fprintf(stderr, "PUSH---\n"); \ + while (dt < 2.0) { rfbPE(50000); dt += dtime(&tm); } \ + fprintf(stderr, "---PUSH\n"); \ +} + +static int push_scr_ev(double *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, do_fb_push = 0, obscured; + int ev, ev_tot = scr_ev_cnt; + double tm, dt, st, waittime = 0.125; + double max_age = *age; + int db = debug_scroll, rrate = get_read_rate(); + sraRegionPtr backfill, whole, tmpregion, tmpregion2; + int link, latency, netrate; + int ypad = 0; + double last_scroll_event_save = last_scroll_event; + + /* we return the oldest one. */ + *age = 0.0; + + 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; + } + + backfill = sraRgnCreate(); + whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + if (clipshift) { + sraRgnOffset(whole, coff_x, coff_y); + } + if (subwin) { + sraRgnOffset(whole, off_x, off_y); + } + + win0 = scr_ev[0].win; + x0 = scr_ev[0].win_x; + y0 = scr_ev[0].win_y; + w0 = scr_ev[0].win_w; + h0 = scr_ev[0].win_h; + + ypad = set_ypad(); + +if (db) fprintf(stderr, "ypad: %d dy[0]: %d\n", ypad, scr_ev[0].dy); + + for (ev=0; ev < ev_tot; ev++) { + double ag; + + x = scr_ev[ev].x; + y = scr_ev[ev].y; + w = scr_ev[ev].w; + h = scr_ev[ev].h; + dx = scr_ev[ev].dx; + dy = scr_ev[ev].dy; + win = scr_ev[ev].win; + wx = scr_ev[ev].win_x; + wy = scr_ev[ev].win_y; + ww = scr_ev[ev].win_w; + wh = scr_ev[ev].win_h; + nx = scr_ev[ev].new_x; + ny = scr_ev[ev].new_y; + nw = scr_ev[ev].new_w; + nh = scr_ev[ev].new_h; + st = scr_ev[ev].t; + + ag = (dnow() - servertime_diff) - st; + if (ag > *age) { + *age = ag; + } + + if (dabs(ag) > max_age) { +if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f :: (%.4f - %.4f) " + "- %.4f \n", ag, dnow(), servertime_diff, st); + dret = 0; + break; + } else { +if (db) fprintf(stderr, "push_scr_ev: AGE: %.4f\n", ag); + } + if (win != win0) { +if (db) fprintf(stderr, "push_scr_ev: DIFF WIN: 0x%lx != 0x%lx\n", win, win0); + dret = 0; + break; + } + if (wx != x0 || wy != y0) { +if (db) fprintf(stderr, "push_scr_ev: WIN SHIFT: %d %d, %d %d", wx, x0, wy, y0); + dret = 0; + break; + } + if (ww != w0 || wh != h0) { +if (db) fprintf(stderr, "push_scr_ev: WIN RESIZE: %d %d, %d %d", ww, w0, wh, h0); + dret = 0; + break; + } + if (w < 1 || h < 1 || ww < 1 || wh < 1) { +if (db) fprintf(stderr, "push_scr_ev: NEGATIVE h/w: %d %d %d %d\n", w, h, ww, wh); + dret = 0; + break; + } + +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 > 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 > 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) { + frame = xrecord_wm_window; + } + if (! frame) { + X_LOCK; + frame = query_pointer(rootwin); + X_UNLOCK; + } + if (! frame) { + frame = win; + } + + dtime0(&tm); + + tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + if (clipshift) { + sraRgnOffset(tmpregion, coff_x, coff_y); + } + if (subwin) { + sraRgnOffset(tmpregion, off_x, off_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 (ypad) { + if (ypad < 0) { + if (h > -ypad) { + h += ypad; + } else { + ypad = 0; + } + } else { + if (h > ypad) { + y += ypad; + } else { + ypad = 0; + } + } + } + + 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); + sraRgnAnd(backfill, whole); + } + + if (ypad) { + if (ypad < 0) { + ny += ypad; + nh -= ypad; + } else { + ; + } + } + + tmpregion = sraRgnCreateRect(nx, ny, nx + nw, ny + nh); + sraRgnAnd(tmpregion, whole); + sraRgnOr(backfill, tmpregion); + 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; + + tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0); + sraRgnAnd(tmpregion, whole); + + sraRgnAnd(backfill, tmpregion); + + 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); + sraRgnDestroy(tmpregion); + + + iter = sraRgnGetIterator(backfill); + while (sraRgnIteratorNext(iter, &rect)) { + tx1 = rect.x1; + ty1 = rect.y1; + tx2 = rect.x2; + ty2 = rect.y2; + + area += (tx2 - tx1)*(ty2 - ty1); + } + sraRgnReleaseIterator(iter); + + 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.90 * win_area) { +if (db) fprintf(stderr, " AREA_TOO_MUCH"); + dret = 0; + } else if (est > 0.6) { +if (db) fprintf(stderr, " EST_TOO_LARGE"); + dret = 0; + } else if (area <= 0.0) { + ; + } else { + dtime0(&tm); + iter = sraRgnGetIterator(backfill); + while (sraRgnIteratorNext(iter, &rect)) { + tx1 = rect.x1; + ty1 = rect.y1; + tx2 = rect.x2; + ty2 = rect.y2; + + if (clipshift) { + tx1 -= coff_x; + ty1 -= coff_y; + tx2 -= coff_x; + ty2 -= coff_y; + } + if (subwin) { + tx1 -= off_x; + ty1 -= off_y; + tx2 -= off_x; + ty2 -= off_y; + } + tx1 = nfix(tx1, dpy_x); + ty1 = nfix(ty1, dpy_y); + tx2 = nfix(tx2, dpy_x+1); + ty2 = nfix(ty2, dpy_y+1); + + dtime(&tm); +if (db) fprintf(stderr, " DFC(%d,%d-%d,%d)", tx1, ty1, tx2, ty2); + direct_fb_copy(tx1, ty1, tx2, ty2, 1); + do_fb_push++; +PUSH_TEST(0); + } + sraRgnReleaseIterator(iter); + dt = dtime(&tm); +if (db) fprintf(stderr, " dfc---- dt: %.4f", dt); + + } +if (db && dret) fprintf(stderr, " **** dret=%d", dret); +if (db && !dret) fprintf(stderr, " ---- dret=%d", dret); +if (db) fprintf(stderr, "\n"); + } + +if (db && bdpush) fprintf(stderr, "BDPUSH-TIME: 0x%lx\n", xrecord_wm_window); + + 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 (do_fb_push) { + dtime0(&tm); + fb_push(); + dt = dtime(&tm); +if (0) fprintf(stderr, " fb_push dt: %.4f", dt); + if (scaling) { + static double last_time = 0.0; + double now = dnow(), delay = 0.4, first_wait = 3.0; + double trate; + int repeating, lat, rate; + int link = link_rate(&lat, &rate); + int skip_first = 0; + + if (link == LR_DIALUP || rate < 35) { + delay *= 4; + } else if (link != LR_LAN || rate < 100) { + delay *= 2; + } + + trate = typing_rate(0.0, &repeating); + + if (xrecord_set_by_mouse || repeating >= 3) { + if (now > last_scroll_event_save + first_wait) { + skip_first = 1; + } + } + + if (skip_first) { + /* + * try not to send the first one, but a + * single keystroke scroll would be OK. + */ + } else if (now > last_time + delay) { + + scale_mark(x0, y0, x0 + w0, y0 + h0); + last_copyrect_fix = now; + } + last_time = now; + } + } + + sraRgnDestroy(backfill); + sraRgnDestroy(whole); + return dret; +} + +static 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 + * only approximate. + */ +static void do_copyregion(sraRegionPtr region, int dx, int dy) { + sraRectangleIterator *iter; + sraRect rect; + 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; + + last_copyrect = dnow(); + + 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 = sraRgnGetReverseIterator(region, dx < 0, dy < 0); + while(sraRgnIteratorNext(iter, &rect)) { + int j; + + x1 = rect.x1; + y1 = rect.y1; + x2 = rect.x2; + y2 = rect.y2; + + w = (x2 - x1)*Bpp; + dst = main_fb + y1*stride + x1*Bpp; + src = main_fb + (y1-dy)*stride + (x1-dx)*Bpp; + + if (dy < 0) { + for (j=y1; j<y2; j++) { + memmove(dst, src, w); + dst += stride; + src += stride; + } + } else { + dst += (y2 - y1 - 1)*stride; + src += (y2 - y1 - 1)*stride; + for (j=y2-1; j>=y1; j--) { + memmove(dst, src, w); + dst -= stride; + src -= stride; + } + } + + sx1 = ((double) x1 / dpy_x) * scaled_x; + sy1 = ((double) y1 / dpy_y) * scaled_y; + sx2 = ((double) x2 / dpy_x) * scaled_x; + sy2 = ((double) y2 / dpy_y) * scaled_y; + sdx = ((double) dx / dpy_x) * scaled_x; + sdy = ((double) dy / dpy_y) * scaled_y; + + rfbDoCopyRect(screen, sx1, sy1, sx2, sy2, sdx, sdy); + } + sraRgnReleaseIterator(iter); +} + +void fb_push(void) { + char *httpdir = screen->httpDir; + int defer = screen->deferUpdateTime; + int req0, mod0, cpy0, req1, mod1, cpy1, ncli; + int db = (debug_scroll || debug_wireframe); + + screen->httpDir = NULL; + screen->deferUpdateTime = 0; + +if (db) get_client_regions(&req0, &mod0, &cpy0, &ncli); + + rfbPE(0); + + screen->httpDir = httpdir; + screen->deferUpdateTime = defer; + + 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); + } + +} + +void fb_push_wait(double max_wait, int flags) { + double tm, dt = 0.0; + int req, mod, cpy, ncli; + + dtime0(&tm); + while (dt < max_wait) { + int done = 1; + rfbCFD(0); + get_client_regions(&req, &mod, &cpy, &ncli); + if (flags & FB_COPY && cpy) { + done = 0; + } + if (flags & FB_MOD && mod) { + done = 0; + } + if (flags & FB_REQ && req) { + done = 0; + } + if (done) { + break; + } + + usleep(1000); + fb_push(); + dt += dtime(&tm); + } +} + +/* + * utility routine for CopyRect of the window (but not CopyRegion) + */ +static int crfix(int x, int dx, int Lx) { + /* adjust x so that copy source is on screen */ + if (dx > 0) { + if (x-dx < 0) { + /* off on the left */ + x = dx; + } + } else { + if (x-dx >= Lx) { + /* off on the right */ + x = Lx + dx - 1; + } + } + return x; +} + +typedef struct scroll_result { + Window win; + double time; + int result; +} scroll_result_t; + +#define SCR_RESULTS_MAX 256 +static scroll_result_t scroll_results[SCR_RESULTS_MAX]; + +static int scrollability(Window win, int set) { + double oldest = -1.0; + int i, index = -1, next_index = -1; + static int first = 1; + + if (first) { + 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 (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; + } + if (scroll_results[i].win == None) { + break; + } + } + 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 (next_index >= 0 && scroll_results[i].win == None) { + 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].result = set; + } + scroll_results[index].time = dnow(); + } + + return set; +} + +void eat_viewonly_input(int max_eat, int keep) { + int i, gp, gk; + + for (i=0; i<max_eat; i++) { + int cont = 0; + gp = got_pointer_calls; + gk = got_keyboard_calls; + rfbCFD(0); + if (got_pointer_calls > gp) { + if (debug_pointer) { + rfbLog("eat_viewonly_input: pointer: %d\n", i); + } + cont++; + } + if (got_keyboard_calls > gk) { + if (debug_keyboard) { + rfbLog("eat_viewonly_input: keyboard: %d\n", i); + } + cont++; + } + if (i >= keep - 1 && ! cont) { + break; + } + } +} + +static 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; +} + +static 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+1); + ty2 = nfix(y + h, dpy_y+1); + + tmpregion = sraRgnCreateRect(tx1, ty1, tx2, ty2); + add_region_xdamage(tmpregion); + sraRgnDestroy(tmpregion); +} + +void mark_region_for_xdamage(sraRegionPtr region) { + sraRectangleIterator *iter; + sraRect rect; + iter = sraRgnGetIterator(region); + while (sraRgnIteratorNext(iter, &rect)) { + int x1 = rect.x1; + int y1 = rect.y1; + int x2 = rect.x2; + int y2 = rect.y2; + mark_for_xdamage(x1, y1, x2 - x1, y2 - y1); + } + sraRgnReleaseIterator(iter); +} + +void set_xdamage_mark(int x, int y, int w, int h) { + sraRegionPtr region; + + if (! use_xdamage) { + return; + } + mark_for_xdamage(x, y, w, h); + + if (xdamage_scheduled_mark == 0.0) { + xdamage_scheduled_mark = dnow() + 2.0; + } + + if (xdamage_scheduled_mark_region == NULL) { + xdamage_scheduled_mark_region = sraRgnCreate(); + } + region = sraRgnCreateRect(x, y, x + w, y + w); + sraRgnOr(xdamage_scheduled_mark_region, region); + sraRgnDestroy(region); +} + +static int repeat_check(double last_key_scroll) { + int repeating; + double rate = typing_rate(0.0, &repeating); + double now = dnow(), delay = 0.5; + if (rate > 2.0 && repeating && now > last_key_scroll + delay) { + return 0; + } else { + return 1; + } +} + +static int check_xrecord_keys(void) { + 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; + static double last_bdpush = 0.0; + static int persist_count = 0; + int scroll_keysym = 0; + double last_scroll, scroll_persist = scr_key_persist; + double spin_fac = 1.0, scroll_fac = 2.0, noscroll_fac = 0.75; + double max_spin, max_long_spin = 0.3; + double set_repeat_in; + static double set_repeat = 0.0; + + set_repeat_in = set_repeat; + set_repeat = 0.0; + + get_out = 1; + if (got_keyboard_input) { + get_out = 0; + } + + dtime0(&tnow); + if (tnow < last_key_scroll + scroll_persist) { + get_out = 0; + } + + if (set_repeat_in > 0.0 && tnow < last_key_scroll + set_repeat_in) { + get_out = 0; + } + + if (get_out) { + persist_start = 0.0; + persist_count = 0; + last_bdpush = 0.0; + if (xrecording) { + xrecord_watch(0, SCR_KEY); + } + return 0; + } + +#if 0 + /* not used for keyboard yet */ + scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1; + if (scroll_rep == 1) { + scroll_rep = 2; /* if no info, assume the best. */ + } +#endif + + scroll_keysym = xrecord_scroll_keysym(last_rfb_keysym); + + max_spin = scr_key_time; + + if (set_repeat_in > 0.0 && tnow < last_key_scroll + 2*set_repeat_in) { + max_spin = 2 * set_repeat_in; + } else if (tnow < last_key_scroll + scroll_persist) { + max_spin = 1.25*(tnow - last_key_scroll); + } else if (tnow < last_key_to_button_remap_time + 1.5*scroll_persist) { + /* mostly a hack I use for testing -remap key -> btn4/btn5 */ + max_spin = scroll_persist; + } else if (scroll_keysym) { + if (repeat_check(last_key_scroll)) { + spin_fac = scroll_fac; + } else { + spin_fac = noscroll_fac; + } + } + if (max_spin > max_long_spin) { + max_spin = max_long_spin; + } + + /* XXX use this somehow */ +if (0) link = link_rate(&latency, &netrate); + + gk = gk0 = got_keyboard_input; + dtime0(&tm); + +if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: " + "%d max: %.3f %.4f\n", scr_ev_cnt, max_spin, tm - x11vnc_start); + + while (1) { + + if (scr_ev_cnt) { + got_one = 1; + + scrollability(xrecord_ptr_window, SCR_SUCCESS); + scroll_rep = 2; + + dtime0(&last_scroll); + last_key_scroll = last_scroll; + scr_cnt++; + break; + } + + X_LOCK; + flush1 = 1; + XFlush(dpy); + X_UNLOCK; + + if (set_repeat_in > 0.0) { + max_keyrepeat_time = set_repeat_in; + } + + if (use_threads) { + usleep(1000); + } else { + rfbCFD(1000); + } + spin += dtime(&tm); + + X_LOCK; + if (got_keyboard_input > gk) { + gk = got_keyboard_input; + input++; + if (set_repeat_in) { + ; + } else if (xrecord_scroll_keysym(last_rfb_keysym)) { + if (repeat_check(last_key_scroll)) { + spin_fac = scroll_fac; + } else { + spin_fac = noscroll_fac; + } + } +if (0 || db) fprintf(stderr, "check_xrecord: more keys: %.3f 0x%x " + " %.4f %s %s\n", spin, last_rfb_keysym, last_rfb_keytime - x11vnc_start, + last_rfb_down ? "down":"up ", last_rfb_key_accepted ? "accept":"skip"); + flush2 = 1; + XFlush(dpy); + } +#if LIBVNCSERVER_HAVE_RECORD + SCR_LOCK; + XRecordProcessReplies(rdpy_data); + SCR_UNLOCK; +#endif + X_UNLOCK; + + if (spin >= max_spin * spin_fac) { +if (0 || db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin, + max_spin * spin_fac); + fail = 1; + break; + } + } + + max_keyrepeat_time = 0.0; + + if (scr_ev_cnt) { + int dret, ev = scr_ev_cnt - 1; + int bdx, bdy, bdskinny, bdpush = 0; + double max_age = 0.25, age, tm, dt; + static double last_scr_ev = 0.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); + } + + dtime0(&tm); + age = max_age; + dret = push_scr_ev(&age, SCR_KEY, bdpush, bdx, bdy, bdskinny); + dt = dtime(&tm); + + ret = 1 + dret; + scr_ev_cnt = 0; + + if (ret == 2 && xrecord_scroll_keysym(last_rfb_keysym)) { + int repeating; + double time_lo = 1.0/max_keyrepeat_lo; + double time_hi = 1.0/max_keyrepeat_hi; + double rate = typing_rate(0.0, &repeating); +if (0 || db) fprintf(stderr, "Typing: dt: %.4f rate: %.1f\n", dt, rate); + if (repeating) { + /* n.b. the "quantum" is about 1/30 sec. */ + max_keyrepeat_time = 1.0*dt; + if (max_keyrepeat_time > time_lo || + max_keyrepeat_time < time_hi) { + max_keyrepeat_time = 0.0; + } else { + set_repeat = max_keyrepeat_time; +if (0 || db) fprintf(stderr, "set max_keyrepeat_time: %.2f\n", max_keyrepeat_time); + } + } + } + + last_scr_ev = dnow(); + } + + if ((got_one && ret < 2) || persist_count) { + set_xdamage_mark(last_wx, last_wy, last_ww, last_wh); + } + + if (fail) { + scrollability(xrecord_ptr_window, SCR_FAIL); + } + + if (xrecording) { + if (ret < 2) { + xrecord_watch(0, SCR_KEY); + } + } + + 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; +} + +static int check_xrecord_mouse(void) { + 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; + 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; + + int scroll_wheel = 0; + int btn4 = (1<<3); + int btn5 = (1<<4); + + get_out = 1; + if (button_mask) { + get_out = 0; + } + if (want_back_in) { + get_out = 0; + } + dtime0(&tnow); +if (0) fprintf(stderr, "check_xrecord_mouse: IN xrecording: %d\n", xrecording); + + if (get_out) { + if (xrecording) { + xrecord_watch(0, SCR_MOUSE); + } + return 0; + } + + scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1; + if (scroll_rep == 1) { + scroll_rep = 2; /* if no info, assume the best. */ + } + + 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 (button_mask & (btn4|btn5)) { + scroll_wheel = 1; + } + + /* + * 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; + gk = gk0 = got_keyboard_input; + dtime0(&tm); + + /* + * 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; + +if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: " + "%d max: %.3f %.4f\n", scr_ev_cnt, max_spin[scroll_rep], tm - x11vnc_start); + + 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, age = 0.35; + + got_one = 1; + 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; + if (clipshift) { + bdx += coff_x; + bdy += coff_y; + } + if (subwin) { + bdx += off_x; + bdy += off_y; + } + bdskinny = 32; + + set_bdpush(SCR_MOUSE, &last_bdpush, &bdpush); + + dtime0(&tm); + + dret = push_scr_ev(&age, 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; + } + } + + + 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 (got_pointer_input > gp) { + flush2 = 1; + input += eat_pointer(max_ptr_eat, 1); + gp = got_pointer_input; + } + if (got_keyboard_input > gk) { + gk = got_keyboard_input; + input++; + } + X_LOCK; +#if LIBVNCSERVER_HAVE_RECORD + SCR_LOCK; + XRecordProcessReplies(rdpy_data); + SCR_UNLOCK; +#endif + X_UNLOCK; + + if (! input) { + spin_check = 1.5 * max_spin[scroll_rep]; + } else { + spin_check = max_spin[scroll_rep]; + } + + 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 (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 (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; + } + + if (! button_mask) { + int doflush = 0; + if (button_up_time > 0.0) { + ; + } else if (came_back_in) { + dtime0(&button_up_time); + doflush = 1; + } else if (scroll_wheel) { +if (db) fprintf(stderr, "check_xrecord: SCROLL-WHEEL-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); + } 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); + } + } + + last_x = cursor_x; + last_y = cursor_y; + } + + if (got_one) { + set_xdamage_mark(last_wx, last_wy, last_ww, last_wh); + } + + if (fail) { + scrollability(xrecord_ptr_window, SCR_FAIL); + } + + /* 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 { + if (ret) { + ret = 1; + } else { + ret = 0; + } + if (xrecording) { + xrecord_watch(0, SCR_MOUSE); + } + } + + 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, consider_mouse; + static int mouse_wants_back_in = 0; + + if (! use_xrecord) { + return 0; + } + if (skip_cr_when_scaling("scroll")) { + return 0; + } + +if (0) fprintf(stderr, "check_xrecord: IN xrecording: %d\n", xrecording); + + if (! xrecording) { + return 0; + } + + if (!strcmp(scroll_copyrect, "always")) { + watch_keys = 1; + watch_mouse = 1; + } else if (!strcmp(scroll_copyrect, "keys")) { + watch_keys = 1; + } else if (!strcmp(scroll_copyrect, "mouse")) { + watch_mouse = 1; + } + + 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; + } +} + +#define DB_SET \ + int db = 0; \ + int db2 = 0; \ + if (debug_wireframe == 1) { \ + db = 1; \ + } \ + if (debug_wireframe == 2) { \ + db2 = 1; \ + } \ + if (debug_wireframe == 3) { \ + db = 1; \ + db2 = 1; \ + } + +static int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, + 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, FB_COPY); + + get_client_regions(&req, &mod, &cpy, &ncli); + } + if (cpy) { + return 0; + } + + *obscured = 0; + /* + * XXX KDE and xfce do some weird things with the + * stacking, it does not match XQueryTree. Work around + * it for now by CopyRect-ing the *whole* on-screen + * rectangle (whether obscured or not!) + */ + if (time(0) > dt_bad_check + 5) { + char *dt = guess_desktop(); + if (!strcmp(dt, "kde")) { + dt_bad = 1; + } else if (!strcmp(dt, "xfce")) { + dt_bad = 1; + } else { + dt_bad = 0; + } + dt_bad_check = time(0); + } + + if (clipshift) { + x -= coff_x; + y -= coff_y; + } + if (subwin) { + x -= off_x; + y -= off_y; + } + + if (dt_bad && wireframe_in_progress) { + sraRegionPtr rect; + /* send the whole thing... */ + x1 = crfix(nfix(x, dpy_x), dx, dpy_x); + y1 = crfix(nfix(y, dpy_y), dy, dpy_y); + x2 = crfix(nfix(x+w, dpy_x+1), dx, dpy_x+1); + y2 = crfix(nfix(y+h, dpy_y+1), dy, dpy_y+1); + + rect = sraRgnCreateRect(x1, y1, x2, y2); + + if (blackouts) { + int i; + sraRegionPtr bo_rect; + for (i=0; i<blackouts; i++) { + bo_rect = sraRgnCreateRect(blackr[i].x1, + blackr[i].y1, blackr[i].x2, blackr[i].y2); + sraRgnSubtract(rect, bo_rect); + sraRgnDestroy(bo_rect); + } + } + do_copyregion(rect, dx, dy); + sraRgnDestroy(rect); + + sent_copyrect = 1; + *obscured = 1; /* set to avoid an aggressive push */ + + } else if (stack_list_num || dt_bad) { + int k, tx1, tx2, ty1, ty2; + sraRegionPtr moved_win, tmp_win, whole; + sraRectangleIterator *iter; + sraRect rect; + int saw_me = 0; + int orig_x, orig_y; + XWindowAttributes attr; + + orig_x = x - dx; + orig_y = y - dy; + + tx1 = nfix(orig_x, dpy_x); + ty1 = nfix(orig_y, dpy_y); + tx2 = nfix(orig_x+w, dpy_x+1); + ty2 = nfix(orig_y+h, dpy_y+1); + +if (db2) fprintf(stderr, "moved_win: %4d %3d, %4d %3d 0x%lx ---\n", + tx1, ty1, tx2, ty2, frame); + + 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_list_num - 1; k >= 0; k--) { + Window swin; + + if (0 && dt_bad) { + break; + } + + swin = stack_list[k].win; + if (swin == frame) { + if (db2) { + saw_me = 1; fprintf(stderr, " ----------\n"); + } else { + break; + } + } +#if 0 +fprintf(stderr, "bo: %d/%lx\n", k, swin); +#endif + + /* skip some unwanted cases: */ + if (swin == None) { + continue; + } + if (swin < 10) { + ; /* blackouts */ + } else 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; + } + + if (clipshift) { + attr.x -= coff_x; + attr.y -= coff_y; + } + if (subwin) { + attr.x -= off_x; + attr.y -= off_y; + } + + /* + * first subtract any overlap from the initial + * window rectangle + */ + + /* clip the window to the visible screen: */ + tx1 = nfix(attr.x, dpy_x); + ty1 = nfix(attr.y, dpy_y); + tx2 = nfix(attr.x + attr.width, dpy_x+1); + ty2 = nfix(attr.y + attr.height, dpy_y+1); + +if (db2) fprintf(stderr, " tmp_win-1: %4d %3d, %4d %3d 0x%lx\n", + tx1, ty1, tx2, ty2, swin); +if (db2 && saw_me) continue; + + /* see if window clips us: */ + tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); + if (sraRgnAnd(tmp_win, moved_win)) { + *obscured = 1; +if (db2) fprintf(stderr, " : clips it.\n"); + } + 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. + */ + + /* clip the window to the visible screen: */ + tx1 = nfix(attr.x - dx, dpy_x); + ty1 = nfix(attr.y - dy, dpy_y); + tx2 = nfix(attr.x - dx + attr.width, dpy_x+1); + ty2 = nfix(attr.y - dy + attr.height, dpy_y+1); + +if (db2) fprintf(stderr, " tmp_win-2: %4d %3d, %4d %3d 0x%lx\n", + tx1, ty1, tx2, ty2, swin); +if (db2 && saw_me) continue; + + /* subtract it from our region: */ + tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); + sraRgnSubtract(moved_win, tmp_win); + sraRgnDestroy(tmp_win); + } + X_UNLOCK; + + if (extra_clip && ! sraRgnEmpty(extra_clip)) { + whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + + if (clipshift) { + sraRgnOffset(extra_clip, -coff_x, -coff_y); + } + if (subwin) { + sraRgnOffset(extra_clip, -off_x, -off_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)) { + sraRegionPtr whole, shifted_region; + + whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + shifted_region = sraRgnCreateRgn(moved_win); + sraRgnOffset(shifted_region, dx, dy); + sraRgnAnd(shifted_region, whole); + + sraRgnDestroy(whole); + + /* now send the CopyRegion: */ + if (! sraRgnEmpty(shifted_region)) { + dtime0(&tm); + do_copyregion(shifted_region, dx, dy); + dt = dtime(&tm); +if (0 || 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); + } + sraRgnDestroy(moved_win); + } + return sent_copyrect; +} + +int near_wm_edge(int x, int y, int w, int h, int px, int py) { + /* heuristics: */ + int wf_t = wireframe_top; + int wf_b = wireframe_bot; + int wf_l = wireframe_left; + int wf_r = wireframe_right; + + int near_edge = 0; + + if (wf_t || wf_b || wf_l || wf_r) { + if (nabs(y - py) < wf_t) { + near_edge = 1; + } + if (nabs(y + h - py) < wf_b) { + near_edge = 1; + } + if (nabs(x - px) < wf_l) { + near_edge = 1; + } + if (nabs(x + w - px) < wf_r) { + near_edge = 1; + } + } else { + /* all zero; always "near" edge: */ + near_edge = 1; + } + return near_edge; +} + +int near_scrollbar_edge(int x, int y, int w, int h, int px, int py) { + /* heuristics: */ + int sb_t = scrollcopyrect_top; + int sb_b = scrollcopyrect_bot; + int sb_l = scrollcopyrect_left; + int sb_r = scrollcopyrect_right; + + int near_edge = 0; + + if (sb_t || sb_b || sb_l || sb_r) { + if (nabs(y - py) < sb_t) { + near_edge = 1; + } + if (nabs(y + h - py) < sb_b) { + near_edge = 1; + } + if (nabs(x - px) < sb_l) { + near_edge = 1; + } + if (nabs(x + w - px) < sb_r) { + near_edge = 1; + } + } else { + /* all zero; always "near" edge: */ + near_edge = 1; + } + return near_edge; +} + +void check_fixscreen(void) { + double now = dnow(); + int didfull = 0, db = 0; + + if (!client_count) { + return; + } + + if (screen_fixup_X > 0.0) { + static double last = 0.0; + if (now > last + screen_fixup_X) { + if (db) rfbLog("doing screen_fixup_X\n"); + do_copy_screen = 1; + last = now; + didfull = 1; + } + + } + if (screen_fixup_V > 0.0) { + static double last = 0.0; + if (now > last + screen_fixup_V) { + if (! didfull) { + refresh_screen(0); + if (db) rfbLog("doing screen_fixup_V\n"); + } + last = now; + didfull = 1; + } + } + if (screen_fixup_C > 0.0) { + static double last = 0.0; + if (last_copyrect_fix < last_copyrect && + now > last_copyrect + screen_fixup_C) { + if (! didfull) { + refresh_screen(0); + if (db) rfbLog("doing screen_fixup_C\n"); + } + last_copyrect_fix = now; + last = now; + didfull = 1; + } + } + if (scaling && last_copyrect_fix < last_copyrect) { + static double last = 0.0; + double delay = 3.0; + if (now > last + delay) { + if (! didfull) { + scale_and_mark_rect(0, 0, dpy_x, dpy_y); + if (db) rfbLog("doing scale screen_fixup\n"); + } + last_copyrect_fix = now; + last = now; + didfull = 1; + } + } +} + +static int wireframe_mod_state() { + if (! wireframe_mods) { + return 0; + } + if (!strcmp(wireframe_mods, "all")) { + if (track_mod_state(NoSymbol, FALSE, FALSE)) { + return 1; + } else { + return 0; + } + + } else if (!strcmp(wireframe_mods, "Alt")) { + if (track_mod_state(XK_Alt_L, FALSE, FALSE) == 1) { + return 1; + } else if (track_mod_state(XK_Alt_R, FALSE, FALSE) == 1) { + return 1; + } + } else if (!strcmp(wireframe_mods, "Shift")) { + if (track_mod_state(XK_Shift_L, FALSE, FALSE) == 1) { + return 1; + } else if (track_mod_state(XK_Shift_R, FALSE, FALSE) == 1) { + return 1; + } + } else if (!strcmp(wireframe_mods, "Control")) { + if (track_mod_state(XK_Control_L, FALSE, FALSE) == 1) { + return 1; + } else if (track_mod_state(XK_Control_R, FALSE, FALSE) == 1) { + return 1; + } + } else if (!strcmp(wireframe_mods, "Meta")) { + if (track_mod_state(XK_Meta_L, FALSE, FALSE) == 1) { + return 1; + } else if (track_mod_state(XK_Meta_R, FALSE, FALSE) == 1) { + return 1; + } + } else if (!strcmp(wireframe_mods, "Super")) { + if (track_mod_state(XK_Super_L, FALSE, FALSE) == 1) { + return 1; + } else if (track_mod_state(XK_Super_R, FALSE, FALSE) == 1) { + return 1; + } + } else if (!strcmp(wireframe_mods, "Hyper")) { + if (track_mod_state(XK_Hyper_L, FALSE, FALSE) == 1) { + return 1; + } else if (track_mod_state(XK_Hyper_R, FALSE, FALSE) == 1) { + return 1; + } + } + return 0; +} + +/* + * Applied just before any check_user_input() modes. Look for a + * ButtonPress; find window it happened in; find the wm frame window + * for it; watch for that window moving or resizing. If it does, do the + * wireframe animation. Do this until ButtonRelease or timeouts occur. + * Remove wireframe. + * + * Under -nowirecopyrect, return control to base scheme + * (check_user_input() ...) that will repaint the screen with the window + * in the new postion or size. Under -wirecopyrect, apply rfbDoCopyRect + * or rfbDoCopyRegion: this "pollutes" our framebuffer, but the normal + * polling will quickly repair it. Under happy circumstances, this + * reduces actual XShmGetImage work (i.e. if we correctly predicted how + * the X fb has changed. + * + * -scale doesn't always work under -wirecopyrect, but the wireframe does. + * + * testing of this mode under -threads is incomplete. + * + * returns 1 if it did an animation, 0 if no move/resize triggers + * went off. + * + * TBD: see if we can select StructureNotify ConfigureNotify events for + * the toplevel windows to get better info on moves and resizes. + */ +int check_wireframe(void) { + Window frame, orig_frame; + XWindowAttributes attr; + int dx, dy; + + int orig_px, orig_py, orig_x, orig_y, orig_w, orig_h; + int px, py, x, y, w, h; + 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, 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; + static int first_dt_cnt = 0; + static time_t last_save_stacklist = 0; + + /* heuristics: */ + double first_event_spin = wireframe_t1; + double frame_changed_spin = wireframe_t2; + double max_spin = wireframe_t3; + double min_draw = wireframe_t4; + int try_it = 0; + DB_SET + + if (nofb) { + return 0; + } + if (subwin) { + return 0; /* don't even bother for -id case */ + } + if (! button_mask) { + return 0; /* no button pressed down */ + } + if (!use_threads && !got_pointer_input) { + return 0; /* need ptr input, e.g. button down, motion */ + } + +if (db) fprintf(stderr, "\n*** button down!! x: %d y: %d\n", cursor_x, cursor_y); + + /* + * Query where the pointer is and which child of the root + * window. We will assume this is the frame the window manager + * makes when it reparents the toplevel window. + */ + X_LOCK; + 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; + } + X_UNLOCK; +if (db) fprintf(stderr, "a: %d wf: %.3f A: %d\n", w*h, wireframe_frac, (dpy_x*dpy_y)); + + /* + * apply the percentage size criterion (allow opaque moves for + * small windows) + */ + if ((double) w*h < wireframe_frac * (dpy_x * dpy_y)) { +if (db) fprintf(stderr, "small window %.3f\n", ((double) w*h)/(dpy_x * dpy_y)); + return 0; + } +if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr: 0x%lx\n", x, y, w, h, px, py, orig_frame); + + /* + * see if the pointer is within range of the assumed wm frame + * decorations on the edge of the window. + */ + + try_it = near_wm_edge(x, y, w, h, px, py); + + /* Often Alt+ButtonDown starts a window move: */ + if (! try_it && wireframe_mod_state()) { + try_it = 1; + } + if (! try_it) { +if (db) fprintf(stderr, "INTERIOR\n"); + return 0; + } + + wireframe_in_progress = 1; + + if (button_mask_prev) { + already_down = 1; + } + + if (! wireframe_str || !strcmp(wireframe_str, WIREFRAME_PARMS)) { + int link, latency, netrate; + static int didmsg = 0; + + 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; + max_spin *= 2.0; + min_draw *= 1.5; + if (! didmsg) { + rfbLog("increased wireframe timeouts for " + "slow network connection.\n"); + rfbLog("netrate: %d KB/sec, latency: %d ms\n", + netrate, latency); + didmsg = 1; + } + } + } + + /* + * pointer() should have snapped the stacking list for us, if + * not, do it now (if the XFakeButtonEvent has been flushed by + * now the stacking order may be incorrect). + */ + if (strcmp(wireframe_copyrect, "never")) { + if (already_down) { + double age = 0.0; + /* + * see if we can reuse the stack list (pause + * with button down) + */ + if (stack_list_num) { + int k, got_me = 0; + for (k = stack_list_num -1; k >=0; k--) { + if (frame == stack_list[k].win) { + got_me = 1; + break; + } + } + if (got_me) { + age = 1.0; + } + snapshot_stack_list(0, age); + } + } + if (! stack_list_num) { + snapshot_stack_list(0, 0.0); + } + } + + + /* store initial parameters, we look for changes in them */ + orig_frame = frame; + orig_px = px; /* pointer position */ + orig_py = py; + orig_x = x; /* frame position */ + orig_y = y; + orig_w = w; /* frame size */ + orig_h = h; + + orig_cursor_x = cursor_x; + orig_cursor_y = cursor_y; + + /* this is the box frame we would draw */ + box_x = x; + box_y = y; + box_w = w; + box_h = h; + + dtime0(&tm); + + last_draw = spin; + + /* -threads support for check_wireframe() is rough... crash? */ + if (use_threads) { + /* purge any stored up pointer events: */ + pointer(-1, 0, 0, NULL); + } + + g = got_pointer_input; + + while (1) { + + X_LOCK; + XFlush(dpy); + X_UNLOCK; + + /* try do induce/waitfor some more user input */ + if (use_threads) { + usleep(1000); + } else if (drew_box) { + rfbPE(1000); + } else { + rfbCFD(1000); + } + + spin += dtime(&tm); + +if (0) fprintf(stderr, "wf-spin: %.3f\n", spin); + + /* check for any timeouts: */ + if (frame_changed) { + double delay; + /* max time we play this game: */ + if (spin > max_spin) { +if (db || db2) fprintf(stderr, " SPIN-OUT-MAX: %.3f\n", spin); + break_reason = 1; + break; + } + /* watch for pointer events slowing down: */ + if (special_t1) { + delay = max_spin; + } else { + delay = 2.0* frame_changed_spin; + if (spin > 3.0 * frame_changed_spin) { + delay = 1.5 * delay; + } + } + if (spin > last_ptr + delay) { +if (db || db2) fprintf(stderr, " SPIN-OUT-NOT-FAST: %.3f\n", spin); + break_reason = 2; + break; + } + } else if (got_2nd_pointer) { + /* + * pointer is moving, max time we wait for wm + * move or resize to be detected + */ + if (spin > frame_changed_spin) { +if (db || db2) fprintf(stderr, " SPIN-OUT-NOFRAME-SPIN: %.3f\n", spin); + break_reason = 3; + break; + } + } else { + /* max time we wait for any pointer input */ + if (spin > first_event_spin) { +if (db || db2) fprintf(stderr, " SPIN-OUT-NO2ND_PTR: %.3f\n", spin); + break_reason = 4; + break; + } + } + + /* see if some pointer input occurred: */ + if (got_pointer_input > g) { +if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mask: %d\n", got_2nd_pointer+1, spin, cursor_x, cursor_y, button_mask); + + g = got_pointer_input; + + X_LOCK; + XFlush(dpy); + X_UNLOCK; + + /* periodically try to let the wm get moving: */ + if (!frame_changed && got_2nd_pointer % 4 == 0) { + if (got_2nd_pointer == 0) { + usleep(50 * 1000); + } else { + usleep(25 * 1000); + } + } + got_2nd_pointer++; + last_ptr = spin; + + /* + * see where the pointer currently is. It may + * not be our starting frame (i.e. mouse now + * outside of the moving window). + */ + frame = 0x0; + X_LOCK; + + if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, + &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, 1)) { + X_UNLOCK; + /* our window frame went away! */ + win_gone = 1; +if (db) fprintf(stderr, "FRAME-GONE: 0x%lx\n", orig_frame); + break_reason = 5; + break; + } + if (attr.map_state == IsUnmapped) { + X_UNLOCK; + /* our window frame is now unmapped! */ + win_unmapped = 1; +if (db) fprintf(stderr, "FRAME-UNMAPPED: 0x%lx\n", orig_frame); + break_reason = 5; + break; + } + +if (db) fprintf(stderr, "OUT-OF-FRAME: old: x: %d y: %d px: %d py: %d 0x%lx\n", x, y, px, py, frame); + + /* new parameters for our frame */ + x = attr.x; /* n.b. rootwin is parent */ + y = attr.y; + w = attr.width; + h = attr.height; + } + X_UNLOCK; + +if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr: 0x%lx\n", x, y, w, h, px, py, frame); +if (db) fprintf(stderr, " MO,PT,FR: %d/%d %d/%d %d/%d\n", cursor_x - orig_cursor_x, cursor_y - orig_cursor_y, px - orig_px, py - orig_py, x - orig_x, y - orig_y); + + if (frame_changed && frame != orig_frame) { +if (db) fprintf(stderr, "CHANGED and window switch: 0x%lx\n", frame); + } + if (frame_changed && px - orig_px != x - orig_x) { +if (db) fprintf(stderr, "MOVED and diff DX\n"); + } + if (frame_changed && py - orig_py != y - orig_y) { +if (db) fprintf(stderr, "MOVED and diff DY\n"); + } + + /* check and see if our frame has been resized: */ + if (!frame_changed && (w != orig_w || h != orig_h)) { + int n; + if (!already_down) { + first_dt_ave += spin; + first_dt_cnt++; + } + n = first_dt_cnt ? first_dt_cnt : 1; + frame_changed = 2; + +if (db) fprintf(stderr, "WIN RESIZE 1st-dt: %.3f\n", first_dt_ave/n); + } + + /* check and see if our frame has been moved: */ + if (!frame_changed && (x != orig_x || y != orig_y)) { + int n; + if (!already_down) { + first_dt_ave += spin; + first_dt_cnt++; + } + n = first_dt_cnt ? first_dt_cnt : 1; + frame_changed = 1; +if (db) fprintf(stderr, "FRAME MOVE 1st-dt: %.3f\n", first_dt_ave/n); + } + + /* + * see if it is time to draw any or a new wireframe box + */ + if (frame_changed) { + int drawit = 0; + if (x != box_x || y != box_y) { + /* moved since last */ + drawit = 1; + } else if (w != box_w || h != box_h) { + /* resize since last */ + drawit = 1; + } + if (drawit) { + /* + * check time (to avoid too much + * animations on slow machines + * or links). + */ + if (spin > last_draw + min_draw || + ! drew_box) { + draw_box(x, y, w, h, 0); + drew_box = 1; + rfbPE(1000); + last_draw = spin; + } + } + box_x = x; + box_y = y; + box_w = w; + box_h = h; + } + } + + /* + * Now (not earlier) check if the button has come back up. + * we check here to get a better location and size of + * the final window. + */ + if (! button_mask) { +if (db || db2) fprintf(stderr, "NO button_mask\n"); + break_reason = 6; + break; + } + } + + if (! drew_box) { + /* nice try, but no move or resize detected. cleanup. */ + if (stack_list_num) { + stack_list_num = 0; + } + wireframe_in_progress = 0; + return 0; + } + + /* remove the wireframe */ + draw_box(0, 0, 0, 0, 1); + + dx = x - orig_x; + dy = y - orig_y; + + /* + * see if we can apply CopyRect or CopyRegion to the change: + */ + if (!strcmp(wireframe_copyrect, "never")) { + ; + } else if (win_gone || win_unmapped) { + ; + } else if (skip_cr_when_scaling("wireframe")) { + ; + } else if (w != orig_w || h != orig_h) { + ; + } else if (dx == 0 && dy == 0) { + ; + } else { + int spin_ms = (int) (spin * 1000 * 1000); + int obscured, sent_copyrect = 0; + + /* + * set a timescale comparable to the spin time, + * but not too short or too long. + */ + if (spin_ms < 30) { + spin_ms = 30; + } else if (spin_ms > 400) { + spin_ms = 400; + } + + /* try to flush the wireframe removal: */ + fb_push_wait(0.1, FB_COPY|FB_MOD); + + /* try to send a clipped copyrect of translation: */ + sent_copyrect = try_copyrect(frame, x, y, w, h, dx, dy, + &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_wait(0.1, FB_COPY); + } else { + /* no diff for now... */ + fb_push_wait(0.1, FB_COPY); + } + if (scaling) { + static double last_time = 0.0; + double now = dnow(), delay = 0.35; + + fb_push_wait(0.1, FB_COPY); + + if (now > last_time + delay) { + int xt = x, yt = y; + + if (clipshift) { + xt -= coff_x; + yt -= coff_y; + } + if (subwin) { + xt -= off_x; + yt -= off_y; + } + + scale_mark(xt, yt, xt+w, yt+h); + last_time = now; + last_copyrect_fix = now; + } + } + } + } + + if (stack_list_num) { + /* clean up stack_list for next time: */ + if (break_reason == 1 || break_reason == 2) { + /* + * save the stack list, perhaps the user has + * paused with button down. + */ + last_save_stacklist = time(0); + } else { + 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); + xdamage_scheduled_mark = dnow() + 2.0; + } + + return 1; +} + +/* + * We need to handle user input, particularly pointer input, carefully. + * This function is only called when non-threaded. Note that + * rfbProcessEvents() only processes *one* pointer event per call, + * so if we interlace it with scan_for_updates(), we can get swamped + * with queued up pointer inputs. And if the pointer inputs are inducing + * large changes on the screen (e.g. window drags), the whole thing + * bogs down miserably and only comes back to life at some time after + * one stops moving the mouse. So, to first approximation, we are trying + * to eat as much user input here as we can using some hints from the + * duration of the previous scan_for_updates() call (in dt). + * + * note: we do this even under -nofb + * + * return of 1 means watch_loop should short-circuit and reloop, + * return of 0 means watch_loop should proceed to scan_for_updates(). + * (this is for pointer_mode == 1 mode, the others do it all internally, + * cnt is also only for that mode). + */ + +static void check_user_input2(double dt) { + + int eaten = 0, miss = 0, max_eat = 50, do_flush = 1; + int g, g_in; + double spin = 0.0, tm; + double quick_spin_fac = 0.40; + double grind_spin_time = 0.175; + + dtime0(&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) { + if (show_multiple_cursors) { + rfbPE(1000); + } else { + rfbCFD(1000); + } + rfbCFD(0); + + spin += dtime(&tm); + + if (spin > quick_spin_fac * dt) { + /* get out if spin time comparable to last scan time */ + break; + } + if (got_pointer_input > g) { + int i, max_extra = max_eat / 2; + g = got_pointer_input; + 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 { + miss++; + } + if (miss > 1) { /* 1 means out on 2nd miss */ + break; + } + } + if (do_flush) { + X_LOCK; +if (0) fprintf(stderr, "check_user_input2-B: XFlush %.4f\n", tm); + XFlush(dpy); + X_UNLOCK; + } + + + /* + * 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; + dtime0(&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) { + 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; + } else { + miss++; + } + g = got_pointer_input; + if (miss > 2) { + break; + } + if (1000 * spin > ms * split) { + break; + } + } + } +} + +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; + double spin, spin_max, tm, to, dtm; + int rfb_wait_ms = 2; + static double dt_cut = 0.075; + int gcnt, ginput; + static int first = 1; + + if (dtr || tile_diffs) {} /* unused vars warning: */ + + if (first) { + char *p = getenv("SPIN"); + if (p) { + double junk; + sscanf(p, "%lf,%lf", &dt_cut, &junk); + } + first = 0; + } + + if (!got_pointer_input) { + return; + } + + + if (dt < dt_cut) { + dt = dt_cut; /* this is to try to avoid early exit */ + } + spin_max = 0.5; + + spin = 0.0; /* amount of time spinning */ + allowed_misses = 10; /* number of ptr inputs we can miss */ + miss_tweak = 8; + last_was_miss = 0; + consecutive_misses = 1; + gcnt = 0; + ginput = 0; + + dtime0(&tm); + to = tm; /* last time we did rfbPE() */ + + g = g_in = got_pointer_input; + + while (1) { + int got_input = 0; + + gcnt++; + + if (button_mask) { + drag_in_progress = 1; + } + + rfbCFD(rfb_wait_ms * 1000); + + dtm = dtime(&tm); + spin += dtm; + + if (got_pointer_input == g) { + if (last_was_miss) { + consecutive_misses++; + } + last_was_miss = 1; + } else { + ginput++; + if (ginput % miss_tweak == 0) { + allowed_misses++; + } + consecutive_misses = 1; + last_was_miss = 0; + } + + if (spin > spin_max) { + /* get out if spin time over limit */ + break; + + } else if (got_pointer_input > g) { + /* received some input, flush to display. */ + got_input = 1; + g = got_pointer_input; + X_LOCK; + XFlush(dpy); + X_UNLOCK; + } else if (--allowed_misses <= 0) { + /* too many misses */ + break; + } else if (consecutive_misses >=3) { + /* too many misses */ + break; + } else { + /* these are misses */ + int wms = 0; + if (gcnt == 1 && button_mask) { + /* + * missed our first input, wait + * for a defer time. (e.g. on + * slow link) hopefully client + * will batch them. + */ + wms = 50; + } else if (button_mask) { + wms = 10; + } else { + } + if (wms) { + usleep(wms * 1000); + } + } + } + + if (ginput >= 2) { + /* try for a couple more quick ones */ + for (i=0; i<2; i++) { + rfbCFD(rfb_wait_ms * 1000); + } + } + + drag_in_progress = 0; +} + +int fb_update_sent(int *count) { + static int last_count = 0; + int sent = 0, rc = 0; + rfbClientIteratorPtr i; + rfbClientPtr cl; + + if (nofb) { + return 0; + } + + i = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(i)) ) { + sent += cl->framebufferUpdateMessagesSent; + } + rfbReleaseClientIterator(i); + if (sent != last_count) { + rc = 1; + } + if (count != NULL) { + *count = sent; + } + last_count = sent; + return rc; +} + +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; + int min_frame_size = 10; /* 10 tiles */ + double spin, tm, to, tc, dtm, rpe_last; + int rfb_wait_ms = 2; + static double dt_cut = 0.050; + static int first = 1; + + int Btile = tile_x * tile_y * bpp/8; /* Bytes per tile */ + double Ttile, dt_use; + double screen_rate = 6000000.; /* 5 MB/sec */ + double vnccpu_rate = 80 * 100000.; /* 20 KB/sec @ 80X compression */ + double net_rate = 50000.; + static double Tfac_r = 1.0, Tfac_v = 1.0, Tfac_n = 1.0, Tdelay = 0.001; + static double dt_min = -1.0, dt_max = -1.0; + double dt_min_fallback = 0.050; + static int ssec = 0, total_calls = 0; + static int push_frame = 0, update_count = 0; + + if (first) { + char *p = getenv("SPIN"); + if (p) { + sscanf(p, "%lf,%lf,%lf,%lf", &dt_cut, &Tfac_r, &Tfac_v, &Tfac_n); + } + first = 0; + ssec = time(0); + + if (dtr) {} /* unused vars warning: */ + } + + total_calls++; + + if (dt_min < 0.0 || dt < dt_min) { + if (dt > 0.0) { + dt_min = dt; + } + } + if (dt_min < 0.0) { + /* sensible value for the very 1st call if dt = 0.0 */ + dt_min = dt_min_fallback; + } + if (dt_max < 0.0 || dt > dt_max) { + dt_max = dt; + } + + if (total_calls > 30 && dt_min > 0.0) { + static int first = 1; + /* + * dt_min will soon be the quickest time to do + * one scan_for_updates with no tiles copied. + * use this (instead of copy_tiles) to estimate + * screen read rate. + */ + screen_rate = (main_bytes_per_line * ntiles_y) / dt_min; + if (first) { + rfbLog("measured screen read rate: %.2f Bytes/sec\n", + screen_rate); + } + first = 0; + } + + dtime0(&tm); + + if (dt < dt_cut) { + dt_use = dt_cut; + } else { + dt_use = dt; + } + + if (push_frame) { + int cnt, iter = 0; + double tp, push_spin = 0.0; + dtime0(&tp); + while (push_spin < dt_use * 0.5) { + fb_update_sent(&cnt); + if (cnt != update_count) { + break; + } + /* damn, they didn't push our frame! */ + iter++; + rfbPE(rfb_wait_ms * 1000); + + push_spin += dtime(&tp); + } + if (iter) { + X_LOCK; + XFlush(dpy); + X_UNLOCK; + } + push_frame = 0; + update_count = 0; + } + + /* + * when we first enter we require some pointer input + */ + if (!got_pointer_input) { + return; + } + + vnccpu_rate = get_raw_rate(); + + if ((tmp = get_read_rate()) != 0) { + screen_rate = (double) tmp; + } + if ((tmp = get_net_rate()) != 0) { + net_rate = (double) tmp; + } + net_rate = (vnccpu_rate/get_cmp_rate()) * net_rate; + + if ((tmp = get_net_latency()) != 0) { + Tdelay = 0.5 * ((double) tmp)/1000.; + } + + Ttile = Btile * (Tfac_r/screen_rate + Tfac_v/vnccpu_rate + Tfac_n/net_rate); + + spin = 0.0; /* amount of time spinning */ + last_was_miss = 0; + consecutive_misses = 1; + gcnt = 0; + ginput = 0; + + rpe_last = to = tc = tm; /* last time we did rfbPE() */ + g = g_in = got_pointer_input; + + tile_diffs = 0; /* reset our knowlegde of tile_diffs to zero */ + + while (1) { + int got_input = 0; + + gcnt++; + + if (button_mask) { + /* this varible is used by our pointer handler */ + drag_in_progress = 1; + } + + /* turn libvncserver crank to process events: */ + rfbCFD(rfb_wait_ms * 1000); + + dtm = dtime(&tm); + spin += dtm; + + if ( (gcnt == 1 && got_pointer_input > g) || tm-tc > 2*dt_min) { + tile_diffs = scan_for_updates(1); + tc = tm; + } + + if (got_pointer_input == g) { + if (last_was_miss) { + consecutive_misses++; + } + last_was_miss = 1; + } else { + ginput++; + consecutive_misses = 1; + last_was_miss = 0; + } + + if (tile_diffs > min_frame_size && spin > Ttile * tile_diffs + Tdelay) { + /* we think we can push the frame */ + push_frame = 1; + fb_update_sent(&update_count); + break; + + } else if (got_pointer_input > g) { + /* received some input, flush it to display. */ + got_input = 1; + g = got_pointer_input; + X_LOCK; + XFlush(dpy); + X_UNLOCK; + + } else if (consecutive_misses >= 2) { + /* too many misses in a row */ + break; + + } else { + /* these are pointer input misses */ + int wms; + if (gcnt == 1 && button_mask) { + /* + * missed our first input, wait for + * a defer time. (e.g. on slow link) + * hopefully client will batch many + * of them for the next read. + */ + wms = 50; + + } else if (button_mask) { + wms = 10; + } else { + wms = 0; + } + if (wms) { + usleep(wms * 1000); + } + } + } + if (ginput >= 2) { + /* try for a couple more quick ones */ + for (i=0; i<2; i++) { + rfbCFD(rfb_wait_ms * 1000); + } + } + drag_in_progress = 0; +} + +int check_user_input(double dt, double dtr, int tile_diffs, int *cnt) { + + if (raw_fb && ! dpy) return 0; /* raw_fb hack */ + + if (use_xrecord) { + int rc = check_xrecord(); + /* + * 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; + } + } + + if (wireframe) { + if (check_wireframe()) { + return 0; + } + } + + if (pointer_mode == 1) { + if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) { + /* every ui_skip-th drops thru to scan */ + *cnt++; + X_LOCK; + XFlush(dpy); + X_UNLOCK; + return 1; /* short circuit watch_loop */ + } else { + return 0; + } + } + if (pointer_mode >= 2 && pointer_mode <= 4) { + if (got_keyboard_input) { + /* + * for these modes, short circuit watch_loop on + * *keyboard* input. + */ + if (*cnt % ui_skip != 0) { + *cnt++; + return 1; + } + } + /* otherwise continue below with pointer input method */ + } + + if (pointer_mode == 2) { + check_user_input2(dt); + } else if (pointer_mode == 3) { + check_user_input3(dt, dtr, tile_diffs); + } else if (pointer_mode == 4) { + check_user_input4(dt, dtr, tile_diffs); + } + return 0; +} + + diff --git a/x11vnc/userinput.h b/x11vnc/userinput.h new file mode 100644 index 0000000..fa8a3c4 --- /dev/null +++ b/x11vnc/userinput.h @@ -0,0 +1,39 @@ +#ifndef _X11VNC_USERINPUT_H +#define _X11VNC_USERINPUT_H + +/* -- userinput.h -- */ + +extern int defer_update_nofb; +extern int last_scroll_type; + +extern int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, + Window *frame, Window *win); +extern void parse_scroll_copyrect(void); +extern void parse_fixscreen(void); +extern void parse_wireframe(void); + +extern void set_wirecopyrect_mode(char *str); +extern void set_scrollcopyrect_mode(char *str); +extern void initialize_scroll_keys(void); +extern void initialize_scroll_matches(void); +extern void initialize_scroll_term(void); +extern void initialize_max_keyrepeat(void); + +extern int direct_fb_copy(int x1, int y1, int x2, int y2, int mark); +extern void fb_push(void); +extern void fb_push_wait(double max_wait, int flags); +extern void eat_viewonly_input(int max_eat, int keep); + +extern void mark_for_xdamage(int x, int y, int w, int h); +extern void mark_region_for_xdamage(sraRegionPtr region); +extern void set_xdamage_mark(int x, int y, int w, int h); +extern int near_wm_edge(int x, int y, int w, int h, int px, int py); +extern int near_scrollbar_edge(int x, int y, int w, int h, int px, int py); + +extern void check_fixscreen(void); +extern int check_xrecord(void); +extern int check_wireframe(void); +extern int fb_update_sent(int *count); +extern int check_user_input(double dt, double dtr, int tile_diffs, int *cnt); + +#endif /* _X11VNC_USERINPUT_H */ diff --git a/x11vnc/util.c b/x11vnc/util.c new file mode 100644 index 0000000..4d5dfe2 --- /dev/null +++ b/x11vnc/util.c @@ -0,0 +1,397 @@ +/* -- util.c -- */ + +#include "x11vnc.h" +#include "cleanup.h" + +struct timeval _mysleep; + +/* this is only for debugging mutexes. see util.h */ +int hxl = 0; + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +MUTEX(x11Mutex); +MUTEX(scrollMutex); +#endif + +int nfix(int i, int n); +int nabs(int n); +double dabs(double x); +void lowercase(char *str); +void uppercase(char *str); +char *lblanks(char *str); +void strzero(char *str); +int scan_hexdec(char *str, unsigned long *num); +int parse_geom(char *str, int *wp, int *hp, int *xp, int *yp, int W, int H); +void set_env(char *name, char *value); +char *bitprint(unsigned int st, int nbits); +char *get_user_name(void); +char *get_home_dir(void); +char *get_shell(void); +char *this_host(void); + +int match_str_list(char *str, char **list); +char **create_str_list(char *cslist); + +double dtime(double *); +double dtime0(double *); +double dnow(void); +double dnowx(void); +double rnow(void); +double rfac(void); + +void rfbPE(long usec); +void rfbCFD(long usec); + + +/* + * routine to keep 0 <= i < n, should use in more places... + */ +int nfix(int i, int n) { + if (i < 0) { + i = 0; + } else if (i >= n) { + i = n - 1; + } + return i; +} + +int nabs(int n) { + if (n < 0) { + return -n; + } else { + return n; + } +} + +double dabs(double x) { + if (x < 0.0) { + return -x; + } else { + return x; + } +} + +void lowercase(char *str) { + char *p; + if (str == NULL) { + return; + } + p = str; + while (*p != '\0') { + *p = tolower(*p); + p++; + } +} + +void uppercase(char *str) { + char *p; + if (str == NULL) { + return; + } + p = str; + while (*p != '\0') { + *p = toupper(*p); + p++; + } +} + +char *lblanks(char *str) { + char *p = str; + while (*p) { + if (! isspace(*p)) { + break; + } + p++; + } + return p; +} + +void strzero(char *str) { + char *p = str; + if (p != NULL) { + while (*p != '\0') { + *p = '\0'; + p++; + } + } +} + +int scan_hexdec(char *str, unsigned long *num) { + if (sscanf(str, "0x%lx", num) != 1) { + if (sscanf(str, "%lu", num) != 1) { + return 0; + } + } + return 1; +} + +int parse_geom(char *str, int *wp, int *hp, int *xp, int *yp, int W, int H) { + int w, h, x, y; + /* handle +/-x and +/-y */ + if (sscanf(str, "%dx%d+%d+%d", &w, &h, &x, &y) == 4) { + ; + } else if (sscanf(str, "%dx%d-%d+%d", &w, &h, &x, &y) == 4) { + w = nabs(w); + x = W - x - w; + } else if (sscanf(str, "%dx%d+%d-%d", &w, &h, &x, &y) == 4) { + h = nabs(h); + y = H - y - h; + } else if (sscanf(str, "%dx%d-%d-%d", &w, &h, &x, &y) == 4) { + w = nabs(w); + h = nabs(h); + x = W - x - w; + y = H - y - h; + } else { + return 0; + } + *wp = w; + *hp = h; + *xp = x; + *yp = y; + return 1; +} + +void set_env(char *name, char *value) { + char *str; + if (!value) { + value = ""; + } + str = (char *) malloc(strlen(name)+strlen(value)+2); + sprintf(str, "%s=%s", name, value); + putenv(str); +} + +char *bitprint(unsigned int st, int nbits) { + static char str[33]; + int i, mask; + if (nbits > 32) { + nbits = 32; + } + for (i=0; i<nbits; i++) { + str[i] = '0'; + } + str[nbits] = '\0'; + mask = 1; + for (i=nbits-1; i>=0; i--) { + if (st & mask) { + str[i] = '1'; + } + mask = mask << 1; + } + return str; /* take care to use or copy immediately */ +} + +char *get_user_name(void) { + char *user = NULL; + + user = getenv("USER"); + if (user == NULL) { + user = getenv("LOGNAME"); + } + +#if LIBVNCSERVER_HAVE_PWD_H + if (user == NULL) { + struct passwd *pw = getpwuid(getuid()); + if (pw) { + user = pw->pw_name; + } + } +#endif + + if (user) { + return(strdup(user)); + } else { + return(strdup("unknown-user")); + } +} + +char *get_home_dir(void) { + char *home = NULL; + + home = getenv("HOME"); + +#if LIBVNCSERVER_HAVE_PWD_H + if (home == NULL) { + struct passwd *pw = getpwuid(getuid()); + if (pw) { + home = pw->pw_dir; + } + } +#endif + + if (home) { + return(strdup(home)); + } else { + return(strdup("/")); + } +} + +char *get_shell(void) { + char *shell = NULL; + + shell = getenv("SHELL"); + +#if LIBVNCSERVER_HAVE_PWD_H + if (shell == NULL) { + struct passwd *pw = getpwuid(getuid()); + if (pw) { + shell = pw->pw_shell; + } + } +#endif + + if (shell) { + return(strdup(shell)); + } else { + return(strdup("/bin/sh")); + } +} + +/* + * utility to get the current host name + */ +char *this_host(void) { + char host[MAXN]; +#if LIBVNCSERVER_HAVE_GETHOSTNAME + if (gethostname(host, MAXN) == 0) { + return strdup(host); + } +#endif + return NULL; +} + +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; +} + +/* + * simple function for measuring sub-second time differences, using + * a double to hold the value. + */ +double dtime(double *t_old) { + /* + * usage: call with 0.0 to initialize, subsequent calls give + * the time difference since last call. + */ + double t_now, dt; + struct timeval now; + + gettimeofday(&now, NULL); + t_now = now.tv_sec + ( (double) now.tv_usec/1000000. ); + if (*t_old == 0.0) { + *t_old = t_now; + return t_now; + } + dt = t_now - *t_old; + *t_old = t_now; + 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); +} + +double dnowx(void) { + return dnow() - x11vnc_start; +} + +double rnow(void) { + double t = dnowx(); + t = t - ((int) t); + if (t > 1.0) { + t = 1.0; + } else if (t < 0.0) { + t = 0.0; + } + return t; +} + +double rfac(void) { + double f = (double) rand(); + f = f / ((double) RAND_MAX); + return f; +} + +/* + * 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); + } +} + +void rfbCFD(long usec) { + if (! screen) { + return; + } + if (usec > USEC_MAX) { + usec = USEC_MAX; + } + if (! use_threads) { + rfbCheckFds(screen, usec); + } +} + + diff --git a/x11vnc/util.h b/x11vnc/util.h new file mode 100644 index 0000000..e919cf3 --- /dev/null +++ b/x11vnc/util.h @@ -0,0 +1,84 @@ +#ifndef _X11VNC_UTIL_H +#define _X11VNC_UTIL_H + +/* -- util.h -- */ + +extern int nfix(int i, int n); +extern int nabs(int n); +extern double dabs(double x); +extern void lowercase(char *str); +extern void uppercase(char *str); +extern char *lblanks(char *str); +extern void strzero(char *str); +extern int scan_hexdec(char *str, unsigned long *num); +extern int parse_geom(char *str, int *wp, int *hp, int *xp, int *yp, int W, int H); +extern void set_env(char *name, char *value); +extern char *bitprint(unsigned int st, int nbits); +extern char *get_user_name(void); +extern char *get_home_dir(void); +extern char *get_shell(void); +extern char *this_host(void); + +extern int match_str_list(char *str, char **list); +extern char **create_str_list(char *cslist); + +extern double dtime(double *); +extern double dtime0(double *); +extern double dnow(void); +extern double dnowx(void); +extern double rnow(void); +extern double rfac(void); + +extern void rfbPE(long usec); +extern void rfbCFD(long usec); + + +#define NONUL(x) ((x) ? (x) : "") + +/* XXX usleep(3) is not thread safe on some older systems... */ +extern struct timeval _mysleep; +#define usleep2(x) \ + _mysleep.tv_sec = (x) / 1000000; \ + _mysleep.tv_usec = (x) % 1000000; \ + select(0, NULL, NULL, NULL, &_mysleep); +#if !defined(X11VNC_USLEEP) +#undef usleep +#define usleep usleep2 +#endif + +/* + * following is based on IsModifierKey in Xutil.h +*/ +#define ismodkey(keysym) \ + ((((KeySym)(keysym) >= XK_Shift_L) && ((KeySym)(keysym) <= XK_Hyper_R) && \ + ((KeySym)(keysym) != XK_Caps_Lock) && ((KeySym)(keysym) != XK_Shift_Lock))) + +/* + * Not sure why... but when threaded we have to mutex our X11 calls to + * avoid XIO crashes. + */ +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +extern MUTEX(x11Mutex); +#endif + +#define X_INIT INIT_MUTEX(x11Mutex) + +#if 1 +#define X_LOCK LOCK(x11Mutex) +#define X_UNLOCK UNLOCK(x11Mutex) +#else +extern int hxl; +#define X_LOCK fprintf(stderr, "*** X_LOCK**[%05d] %d%s\n", \ + __LINE__, hxl, hxl ? " BAD-PRE-LOCK":""); LOCK(x11Mutex); hxl = 1; +#define X_UNLOCK fprintf(stderr, " x_unlock[%05d] %d%s\n", \ + __LINE__, hxl, !hxl ? " BAD-PRE-UNLOCK":""); UNLOCK(x11Mutex); hxl = 0; +#endif + +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD +extern MUTEX(scrollMutex); +#endif +#define SCR_LOCK if (use_threads) {LOCK(scrollMutex);} +#define SCR_UNLOCK if (use_threads) {UNLOCK(scrollMutex);} +#define SCR_INIT INIT_MUTEX(scrollMutex) + +#endif /* _X11VNC_UTIL_H */ diff --git a/x11vnc/win_utils.c b/x11vnc/win_utils.c new file mode 100644 index 0000000..11efaca --- /dev/null +++ b/x11vnc/win_utils.c @@ -0,0 +1,444 @@ +/* -- win_utils.c -- */ + +#include "x11vnc.h" +#include "xinerama.h" +#include "winattr_t.h" +#include "cleanup.h" + +winattr_t *stack_list = NULL; +int stack_list_len = 0; +int stack_list_num = 0; + + +Window parent_window(Window win, char **name); +int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet); +Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x, + int *dst_y, Window *child, int bequiet); +int get_window_size(Window win, int *x, int *y); +void snapshot_stack_list(int free_only, double allowed_age); +void update_stack_list(void); +Window query_pointer(Window start); +int pick_windowid(unsigned long *num); +Window descend_pointer(int depth, Window start, char *name_info, int len); + + +Window parent_window(Window win, char **name) { + Window r, parent; + Window *list; + unsigned int nchild; + + if (name != NULL) { + *name = NULL; + } + + if (! XQueryTree(dpy, win, &r, &parent, &list, &nchild)) { + return None; + } + if (list) { + XFree(list); + } + if (parent && name) { + XFetchName(dpy, parent, name); + } + return parent; +} + +/* trapping utility to check for a valid window: */ +int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet) { + XErrorHandler old_handler; + XWindowAttributes attr, *pattr; + int ok = 0; + + if (attr_ret == NULL) { + pattr = &attr; + } else { + pattr = attr_ret; + } + + if (win == None) { + return 0; + } + + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + if (XGetWindowAttributes(dpy, win, pattr)) { + ok = 1; + } + 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; + + return ok; +} + +Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x, + int *dst_y, Window *child, int bequiet) { + XErrorHandler old_handler; + Bool ok = False; + + trapped_xerror = 0; + old_handler = XSetErrorHandler(trap_xerror); + if (XTranslateCoordinates(dpy, src, dst, src_x, src_y, dst_x, + dst_y, child)) { + ok = True; + } + if (trapped_xerror && trapped_xerror_event) { + if (! quiet && ! bequiet) { + rfbLog("xtranslate: trapped XError: %s (0x%lx)\n", + xerror_string(trapped_xerror_event), src); + } + ok = False; + } + XSetErrorHandler(old_handler); + trapped_xerror = 0; + + return ok; +} + +int get_window_size(Window win, int *x, int *y) { + XWindowAttributes attr; + /* valid_window? */ + if (valid_window(win, &attr, 1)) { + *x = attr.width; + *y = attr.height; + return 1; + } else { + return 0; + } +} + +/* + * For use in the -wireframe stuff, save the stacking order of the direct + * children of the root window. Ideally done before we send ButtonPress + * to the X server. + */ +void snapshot_stack_list(int free_only, double allowed_age) { + static double last_snap = 0.0, last_free = 0.0; + double now; + int num, rc, i, j; + unsigned int ui; + Window r, w; + Window *list; + + 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) { + /* we really don't free it, just reset to zero windows */ + stack_list_num = 0; + last_free = now; + return; + } + + if (stack_list_num && now < last_snap + allowed_age) { + return; + } + + stack_list_num = 0; + last_free = now; + + X_LOCK; + rc = XQueryTree(dpy, rootwin, &r, &w, &list, &ui); + num = (int) ui; + + if (! rc) { + stack_list_num = 0; + last_free = now; + last_snap = 0.0; + X_UNLOCK; + return; + } + + last_snap = now; + if (num > stack_list_len + blackouts) { + int n = 2*num; + free(stack_list); + stack_list = (winattr_t *) malloc(n*sizeof(winattr_t)); + stack_list_len = n; + } + j = 0; + for (i=0; i<num; i++) { + stack_list[j].win = list[i]; + stack_list[j].fetched = 0; + stack_list[j].valid = 0; + stack_list[j].time = now; + j++; + } + for (i=0; i<blackouts; i++) { + stack_list[j].win = 0x1; + stack_list[j].fetched = 1; + stack_list[j].valid = 1; + stack_list[j].x = blackr[i].x1; + stack_list[j].y = blackr[i].y1; + stack_list[j].width = blackr[i].x2 - blackr[i].x1; + stack_list[j].height = blackr[i].y2 - blackr[i].y1; + stack_list[j].time = now; + stack_list[j].map_state = IsViewable; + stack_list[j].rx = -1; + stack_list[j].ry = -1; + j++; + +if (0) fprintf(stderr, "blackr: %d %dx%d+%d+%d\n", i, + stack_list[j-1].width, stack_list[j-1].height, + stack_list[j-1].x, stack_list[j-1].y); + + } + stack_list_num = num + blackouts; + if (debug_wireframe > 1) { + fprintf(stderr, "snapshot_stack_list: num=%d len=%d\n", + stack_list_num, stack_list_len); + } + + 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 (win != None && win < 10) { + ; /* special, blackout */ + } else 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)); +} + +Window query_pointer(Window start) { + Window r, c; + int rx, ry, wx, wy; + unsigned int mask; + if (start == None) { + start = rootwin; + } + if (XQueryPointer(dpy, start, &r, &c, &rx, &ry, &wx, &wy, &mask)) { + return c; + } else { + return None; + } +} + +int pick_windowid(unsigned long *num) { + char line[512]; + int ok = 0, n = 0, msec = 10, secmax = 15; + FILE *p; + + if (use_dpy) { + set_env("DISPLAY", use_dpy); + } + if (no_external_cmds) { + rfbLogEnable(1); + rfbLog("cannot run external commands in -nocmds mode:\n"); + rfbLog(" \"%s\"\n", "xwininfo"); + rfbLog(" exiting.\n"); + clean_up_exit(1); + } + p = popen("xwininfo", "r"); + + if (! p) { + return 0; + } + + fprintf(stderr, "\n"); + fprintf(stderr, " Please select the window for x11vnc to poll\n"); + fprintf(stderr, " by clicking the mouse in that window.\n"); + fprintf(stderr, "\n"); + + while (msec * n++ < 1000 * secmax) { + unsigned long tmp; + char *q; + fd_set set; + struct timeval tv; + + if (screen && screen->clientHead) { + /* they may be doing the pointer-pick thru vnc: */ + int nfds; + tv.tv_sec = 0; + tv.tv_usec = msec * 1000; + FD_ZERO(&set); + FD_SET(fileno(p), &set); + + nfds = select(fileno(p)+1, &set, NULL, NULL, &tv); + + if (nfds == 0 || nfds < 0) { + /* + * select timedout or error. + * note this rfbPE takes about 30ms too: + */ + rfbPE(-1); + XFlush(dpy); + continue; + } + } + + if (fgets(line, 512, p) == NULL) { + break; + } + q = strstr(line, " id: 0x"); + if (q) { + q += 5; + if (sscanf(q, "0x%lx ", &tmp) == 1) { + ok = 1; + *num = tmp; + fprintf(stderr, " Picked: 0x%lx\n\n", tmp); + break; + } + } + } + pclose(p); + return ok; +} + +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) && name != NULL) { + int l = strlen(name); + if (written + l+2 < len) { + strcat(store, "^^"); + written += 2; + strcat(store, name); + written += l; + } else { + filled = 1; + } + XFree(name); + } + } + if (store && classhint && ! filled) { + classhint->res_name = NULL; + classhint->res_class = NULL; + if (XGetClassHint(dpy, clast, classhint)) { + 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, "##"); + if (classhint->res_class) { + strcat(store, + classhint->res_class); + } + strcat(store, "++"); + if (classhint->res_name) { + strcat(store, + classhint->res_name); + } + written += l+4; + } else { + filled = 1; + } + if (classhint->res_class) { + XFree(classhint->res_class); + } + if (classhint->res_name) { + XFree(classhint->res_name); + } + } + } + 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; +} + + diff --git a/x11vnc/win_utils.h b/x11vnc/win_utils.h new file mode 100644 index 0000000..386666e --- /dev/null +++ b/x11vnc/win_utils.h @@ -0,0 +1,23 @@ +#ifndef _X11VNC_WIN_UTILS_H +#define _X11VNC_WIN_UTILS_H + +/* -- win_utils.h -- */ +#include "xinerama.h" +#include "winattr_t.h" + +extern winattr_t *stack_list; +extern int stack_list_len; +extern int stack_list_num; + +extern Window parent_window(Window win, char **name); +extern int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet); +extern Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x, + int *dst_y, Window *child, int bequiet); +extern int get_window_size(Window win, int *x, int *y); +extern void snapshot_stack_list(int free_only, double allowed_age); +extern void update_stack_list(void); +extern Window query_pointer(Window start); +extern int pick_windowid(unsigned long *num); +extern Window descend_pointer(int depth, Window start, char *name_info, int len); + +#endif /* _X11VNC_WIN_UTILS_H */ diff --git a/x11vnc/winattr_t.h b/x11vnc/winattr_t.h new file mode 100644 index 0000000..0956a50 --- /dev/null +++ b/x11vnc/winattr_t.h @@ -0,0 +1,20 @@ +#ifndef _X11VNC_WINATTR_T_H +#define _X11VNC_WINATTR_T_H + +/* -- winattr_t.h -- */ + +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; + +#endif /* _X11VNC_WINATTR_T_H */ diff --git a/x11vnc/x11vnc.1 b/x11vnc/x11vnc.1 index 05f5cad..28f8c05 100644 --- a/x11vnc/x11vnc.1 +++ b/x11vnc/x11vnc.1 @@ -1,8 +1,8 @@ .\" This file was automatically generated from x11vnc -help output. -.TH X11VNC "1" "December 2005" "x11vnc " "User Commands" +.TH X11VNC "1" "January 2006" "x11vnc " "User Commands" .SH NAME x11vnc - allow VNC connections to real X11 displays - version: 0.7.3, lastmod: 2005-12-24 + version: 0.7.3, lastmod: 2006-01-08 .SH SYNOPSIS .B x11vnc [OPTION]... @@ -372,11 +372,17 @@ of the file \fIfilename\fR (instead of via \fB-passwd\fR on the command line where others might see it via .IR ps (1) ). +See below for how to supply multiple passwords. .IP If the filename is prefixed with "rm:" it will be -removed after being read. In general, the password file -should not be readable by untrusted users (BTW: neither -should the VNC \fB-rfbauth\fR file: it is NOT encrypted). +removed after being read. Perhaps this is useful in +limiting the readability of the file. In general, +the password file should not be readable by untrusted +users (BTW: neither should the VNC \fB-rfbauth\fR file: +it is NOT encrypted). +.IP +If the filename is prefixed with "read:" it will +periodically be checked for changes and reread. .IP Note that only the first 8 characters of a password are used. @@ -396,9 +402,10 @@ line by itself, the remaining passwords are used for viewonly access. For compatibility, as a special case if the file contains only two password lines the 2nd one is automatically taken as the viewonly password. -Otherwise the "__BEGIN_VIEWONLY__" token must be used -to have viewonly passwords. (tip: make it the 3rd and -last line to have 2 full-access passwords) +Otherwise the "__BEGIN_VIEWONLY__" token must be +used to have viewonly passwords. (tip: make the 3rd +and last line be "__BEGIN_VIEWONLY__" to have 2 +full-access passwords) .PP \fB-nopw\fR .IP @@ -444,7 +451,13 @@ of the connection), are set to allow identification of the tcp virtual circuit. The x11vnc process id will be in RFB_X11VNC_PID, a client id number in RFB_CLIENT_ID, and the number of other connected clients -in RFB_CLIENT_COUNT. RFB_MODE will be "accept" +in RFB_CLIENT_COUNT. RFB_MODE will be "accept". +RFB_STATE will be PROTOCOL_VERSION, SECURITY_TYPE, +AUTHENTICATION, INITIALISATION, NORMAL, or UNKNOWN +indicating up to which state the client has acheived. +RFB_LOGIN_VIEWONLY will be 0, 1, or -1 (unknown). +RFB_USERNAME, RFB_LOGIN_TIME, and RFB_CURRENT_TIME may +also be set. .IP If \fIstring\fR is "popup" then a builtin popup window is used. The popup will time out after 120 seconds, @@ -485,6 +498,15 @@ clicking. All 3 of the popup keywords can be followed by +N+M to supply a position for the popup window. The default is to center the popup window. .PP +\fB-afteraccept\fR \fIstring\fR +.IP +As \fB-accept,\fR except to run a user supplied command after +a client has been accepted and authenticated. RFB_MODE +will be set to "afteraccept" and the other RFB_* +variables are as in \fB-accept.\fR Unlike \fB-accept,\fR the +command return code is not interpreted by x11vnc. +Example: \fB-afteraccept\fR 'killall xlock &' +.PP \fB-gone\fR \fIstring\fR .IP As \fB-accept,\fR except to run a user supplied command when @@ -2026,6 +2048,8 @@ or client_input:0x2:K .IP accept:cmd set \fB-accept\fR "cmd" (empty to disable). .IP +afteraccept:cmd set \fB-afteraccept\fR (empty to disable). +.IP gone:cmd set \fB-gone\fR "cmd" (empty to disable). .IP noshm enable \fB-noshm\fR mode. @@ -2395,20 +2419,21 @@ truecolor notruecolor overlay nooverlay overlay_cursor overlay_yescursor nooverlay_nocursor nooverlay_cursor nooverlay_yescursor overlay_nocursor visual scale scale_cursor viewonly noviewonly shared noshared -forever noforever once timeout deny lock nodeny unlock -connect allowonce allow localhost nolocalhost listen -lookup nolookup accept gone shm noshm flipbyteorder -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 sloppy_keys -nosloppy_keys 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 seldir cursorshape -nocursorshape cursorpos nocursorpos cursor show_cursor -noshow_cursor nocursor arrow xfixes noxfixes xdamage -noxdamage xd_area xd_mem alphacut alphafrac alpharemove +forever noforever once timeout filexfer deny lock +nodeny unlock connect allowonce allow localhost +nolocalhost listen lookup nolookup accept afteraccept +gone shm noshm flipbyteorder 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 sloppy_keys nosloppy_keys +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 seldir 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 diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c index c25d364..70026fc 100644 --- a/x11vnc/x11vnc.c +++ b/x11vnc/x11vnc.c @@ -1,7 +1,7 @@ /* - * x11vnc.c: a VNC server for X displays. + * x11vnc: a VNC server for X displays. * - * Copyright (c) 2002-2005 Karl J. Runge <[email protected]> + * Copyright (c) 2002-2006 Karl J. Runge <[email protected]> * All rights reserved. * * This is free software; you can redistribute it and/or modify @@ -112,29614 +112,51 @@ * */ -/* - * These ' -- filename.[ch] -- ' comments represent a partial cleanup: - * they are an odd way to indicate how this huge file would be split up - * someday into multiple files. Not finished, externs and other things - * would need to be done, but it indicates a breakup, including static - * keyword for some items. - * - * The primary reason we do not break up this file is for user - * convenience: those wanting to use the latest version download a single - * file, x11vnc.c, and off they go... - */ - -/* -- x11vnc.h -- */ - -/****************************************************************************/ - -/* Standard includes and libvncserver */ - -#include <unistd.h> -#include <signal.h> -#include <sys/utsname.h> -#include <errno.h> - -#include <X11/Xlib.h> -#include <X11/Xutil.h> - -#include <X11/keysym.h> -#include <X11/Xatom.h> - -#include <sys/stat.h> -#include <fcntl.h> -#include <ctype.h> - -#include <rfb/rfb.h> -#include <rfb/rfbregion.h> - -/****************************************************************************/ - -/* Build-time customization via CPPFLAGS. */ - -/* - * Summary of options to include in CPPFLAGS for custom builds: - * - * -DVNCSHARED 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. - * -DEXTERNAL_COMMANDS=0 to disable the running of all external commands. - * -DFILEXFER=0 disable filexfer. - * - * -DHARDWIRE_PASSWD=... hardwired passwords, quoting necessary. - * -DHARDWIRE_VIEWPASSWD=... - * -DNOPW=1 make -nopw the default (skip warning) - * -DPASSWD_REQUIRED=1 exit unless a password is supplied. - * -DPASSWD_UNLESS_NOPW=1 exit unless a password is supplied and no -nopw. - * - * -DWIREFRAME=0 to have -nowireframe as the default. - * -DWIREFRAME_COPYRECT=0 to have -nowirecopyrect as the default. - * -DWIREFRAME_PARMS=... set default -wirecopyrect parameters. - * -DSCROLL_COPYRECT=0 to have -noscrollcopyrect as the default. - * -DSCROLL_COPYRECT_PARMS=... set default -scrollcopyrect parameters. - * -DXDAMAGE=0 to have -noxdamage as the default. - * -DSKIPDUPS=0 to have -noskip_dups as the default or vice versa. - * - * -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. - * -DNOGUI do not include the gui tkx11vnc. - * - * Set these in CPPFLAGS before running configure. E.g.: - * - * % env CPPFLAGS="-DFOREVER -DREMOTE_CONTROL=0" ./configure - * % make - */ - -/* - * This can be used to disable the remote control mechanism. - */ -#ifndef REMOTE_CONTROL -#define REMOTE_CONTROL 1 -#endif - -#ifndef NOPW -#define NOPW 0 -#endif - -#ifndef PASSWD_REQUIRED -#define PASSWD_REQUIRED 0 -#endif - -#ifndef PASSWD_UNLESS_NOPW -#define PASSWD_UNLESS_NOPW 0 -#endif - -/* - * Beginning of support for small binary footprint build for embedded - * systems, PDA's etc. It currently just cuts out the low-hanging - * fruit (large text passages). Set to 2, 3 to cut out some of the - * more esoteric extensions. More tedious is to modify LDFLAGS in the - * Makefile to not link against the extension libraries... but that - * should be done too (manually for now). - * - * If there is interest more of the bloat can be removed... Currently - * these shrink the binary from 500K to about 270K. - */ -#ifndef SMALL_FOOTPRINT -#define SMALL_FOOTPRINT 0 -#endif - -#if SMALL_FOOTPRINT -#define NOGUI -#endif - -#if (SMALL_FOOTPRINT > 1) -#define LIBVNCSERVER_HAVE_XKEYBOARD 0 -#define LIBVNCSERVER_HAVE_LIBXINERAMA 0 -#define LIBVNCSERVER_HAVE_LIBXRANDR 0 -#define LIBVNCSERVER_HAVE_LIBXFIXES 0 -#define LIBVNCSERVER_HAVE_LIBXDAMAGE 0 -#endif - -#if (SMALL_FOOTPRINT > 2) -#define LIBVNCSERVER_HAVE_UTMPX_H 0 -#define LIBVNCSERVER_HAVE_PWD_H 0 -#define REMOTE_CONTROL 0 -#endif - -/* - * Not recommended unless you know what you are getting into, but if you - * define the HARDWIRE_PASSWD or HARDWIRE_VIEWPASSWD variables here or in - * CPPFLAGS you can set a default -passwd and -viewpasswd string values, - * perhaps this would be better than nothing on an embedded system, etc. - * These default values will be overridden by the command line. - * We don't even give an example ;-) - */ - -/****************************************************************************/ - - -/* Extensions and related includes: */ - -#if LIBVNCSERVER_HAVE_XSHM -# if defined(__hpux) && defined(__ia64) /* something weird on hp/itanic */ -# undef _INCLUDE_HPUX_SOURCE -# endif -#include <sys/ipc.h> -#include <sys/shm.h> -#include <X11/extensions/XShm.h> -#endif - -#if LIBVNCSERVER_HAVE_XTEST -#include <X11/extensions/XTest.h> -#endif -int xtest_base_event_type = 0; - -#if LIBVNCSERVER_HAVE_LIBXTRAP -#define NEED_EVENTS -#define NEED_REPLIES -#include <X11/extensions/xtraplib.h> -#include <X11/extensions/xtraplibp.h> -XETC *trap_ctx = NULL; -#endif -int xtrap_base_event_type = 0; - -#if LIBVNCSERVER_HAVE_RECORD -#include <X11/Xproto.h> -#include <X11/extensions/record.h> -#endif - -#if LIBVNCSERVER_HAVE_XKEYBOARD -#include <X11/XKBlib.h> -#endif - -#if LIBVNCSERVER_HAVE_LIBXINERAMA -#include <X11/extensions/Xinerama.h> -#endif - -#if LIBVNCSERVER_HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#endif - -#include <netdb.h> -extern int h_errno; - -#if LIBVNCSERVER_HAVE_NETINET_IN_H -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <arpa/inet.h> -#endif - -/* XXX autoconf */ -#if LIBVNCSERVER_HAVE_PWD_H -#include <pwd.h> -#endif -#if LIBVNCSERVER_HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif -#if LIBVNCSERVER_HAVE_UTMPX_H -#include <utmpx.h> -#endif - -#if LIBVNCSERVER_HAVE_MMAP -#include <sys/mman.h> -#endif - -/* - * overlay/multi-depth screen reading support - * undef SOLARIS_OVERLAY or IRIX_OVERLAY if there are problems building. - */ - -/* solaris/sun */ -#if defined (__SVR4) && defined (__sun) -# define SOLARIS -# ifdef LIBVNCSERVER_HAVE_SOLARIS_XREADSCREEN -# define SOLARIS_OVERLAY -# define OVERLAY_OS -# endif -#endif - -#ifdef SOLARIS_OVERLAY -#include <X11/extensions/transovl.h> -#endif - -/* irix/sgi */ -#if defined(__sgi) -# define IRIX -# ifdef LIBVNCSERVER_HAVE_IRIX_XREADDISPLAY -# define IRIX_OVERLAY -# define OVERLAY_OS -# endif -#endif - -#ifdef IRIX_OVERLAY -#include <X11/extensions/readdisplay.h> -#endif - -int overlay_present = 0; - -/* - * Ditto for librandr. - * (e.g. LDFLAGS=-lXrandr before configure). -#define LIBVNCSERVER_HAVE_LIBXRANDR 1 - */ -#if LIBVNCSERVER_HAVE_LIBXRANDR -#include <X11/extensions/Xrandr.h> -#endif -int xrandr_base_event_type = 0; - - -int xfixes_present = 0; -int use_xfixes = 1; -int got_xfixes_cursor_notify = 0; -int cursor_changes = 0; -int alpha_threshold = 240; -double alpha_frac = 0.33; -int alpha_remove = 0; -int alpha_blend = 1; -int alt_arrow = 1; - -#if LIBVNCSERVER_HAVE_LIBXFIXES -#include <X11/extensions/Xfixes.h> -#endif -int xfixes_base_event_type = 0; - - -#ifndef XDAMAGE -#define XDAMAGE 1 -#endif -int use_xdamage = XDAMAGE; /* use the xdamage rects for scanline hints */ -int xdamage_present = 0; -#if LIBVNCSERVER_HAVE_LIBXDAMAGE -#include <X11/extensions/Xdamage.h> -Damage xdamage = 0; -#endif -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, xdamage_direct_count; -double xdamage_scheduled_mark = 0.0; -sraRegionPtr xdamage_scheduled_mark_region = NULL; - -/* date +'lastmod: %Y-%m-%d' */ -char lastmod[] = "0.7.3 lastmod: 2005-12-24"; -int hack_val = 0; - -/* X display info */ - -Display *dpy = NULL; /* the single display screen we connect to */ -int scr; -Window window, rootwin; /* polled window, root window (usu. same) */ -Visual *default_visual; /* the default visual (unless -visual) */ -int bpp, depth; -int indexed_color = 0; -int dpy_x, dpy_y; /* size of display */ -int off_x, off_y; /* offsets for -sid */ -int wdpy_x, wdpy_y; /* for actual sizes in case of -clip */ -int cdpy_x, cdpy_y, coff_x, coff_y; /* the -clip params */ -int button_mask = 0; /* button state and info */ -int button_mask_prev = 0; -int num_buttons = -1; - -/* image structures */ -XImage *scanline; -XImage *fullscreen; -XImage **tile_row; /* for all possible row runs */ -XImage *fb0; -XImage *snaprect = NULL; /* for XShmGetImage (fs_factor) */ -XImage *snap = NULL; /* the full snap fb */ -XImage *raw_fb_image = NULL; /* the raw fb */ - -#if !LIBVNCSERVER_HAVE_XSHM -/* - * for simplicity, define this struct since we'll never use them - * under using_shm = 0. - */ -typedef struct { - int shmid; char *shmaddr; Bool readOnly; -} XShmSegmentInfo; -#endif - -/* corresponding shm structures */ -XShmSegmentInfo scanline_shm; -XShmSegmentInfo fullscreen_shm; -XShmSegmentInfo *tile_row_shm; /* for all possible row runs */ -XShmSegmentInfo snaprect_shm; - -/* rfb screen info */ -rfbScreenInfoPtr screen = NULL; -char *rfb_desktop_name = NULL; -char *http_dir = NULL; -char vnc_desktop_name[256]; -char *main_fb; /* our copy of the X11 fb */ -char *rfb_fb; /* same as main_fb unless transformation */ -char *fake_fb = NULL; /* used under -padgeom */ -char *snap_fb = NULL; /* used under -snapfb */ -char *raw_fb = NULL; -char *raw_fb_addr = NULL; -int raw_fb_offset = 0; -int raw_fb_shm = 0; -int raw_fb_mmap = 0; -int raw_fb_seek = 0; -int raw_fb_fd = -1; - -int rfb_bytes_per_line; -int main_bytes_per_line; -unsigned long main_red_mask, main_green_mask, main_blue_mask; -unsigned short main_red_max, main_green_max, main_blue_max; -unsigned short main_red_shift, main_green_shift, main_blue_shift; - -/* struct with client specific data: */ -#define CILEN 10 -typedef struct _ClientData { - int uid; - char *hostname; - char *username; - int client_port; - int server_port; - char *server_ip; - char input[CILEN]; - int login_viewonly; - - int had_cursor_shape_updates; - int had_cursor_pos_updates; - - double timer; - double send_cmp_rate; - double send_raw_rate; - double latency; - int cmp_bytes_sent; - int raw_bytes_sent; -} ClientData; - -/* scaling parameters */ -char *scale_str = NULL; -double scale_fac = 1.0; -int scaling = 0; -int scaling_blend = 1; /* for no blending option (very course) */ -int scaling_nomult4 = 0; /* do not require width = n * 4 */ -int scaling_pad = 0; /* pad out scaled sizes to fit denominator */ -int scaling_interpolate = 0; /* use interpolation scheme when shrinking */ -int scaled_x = 0, scaled_y = 0; /* dimensions of scaled display */ -int scale_numer = 0, scale_denom = 0; /* n/m */ - - -/* scale cursor */ -char *scale_cursor_str = NULL; -double scale_cursor_fac = 1.0; -int scaling_cursor = 0; -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; -int ntiles, ntiles_x, ntiles_y; - -/* arrays that indicate changed or checked tiles. */ -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 = 0, last_client = 0; -time_t last_keyboard_input = 0, last_pointer_input = 0; -time_t last_fb_bytes_sent = 0; -double last_keyboard_time = 0.0; -double last_pointer_time = 0.0; -double last_pointer_click_time = 0.0; -double last_pointer_motion_time = 0.0; -double last_key_to_button_remap_time = 0.0; -double last_copyrect = 0.0; -double last_copyrect_fix = 0.0; -double servertime_diff = 0.0; -double x11vnc_start = 0.0; - -/* last client to move pointer */ -rfbClientPtr last_pointer_client = NULL; - -int accepted_client = 0; -int client_count = 0; -int clients_served = 0; - -/* more transient kludge variables: */ -int cursor_x, cursor_y; /* x and y from the viewer(s) */ -int button_change_x, button_change_y; -int got_user_input = 0; -int got_pointer_input = 0; -int got_pointer_calls = 0; -int got_keyboard_input = 0; -int got_keyboard_calls = 0; -int urgent_update = 0; -int last_keyboard_keycode = 0; -rfbBool last_rfb_down = FALSE; -rfbBool last_rfb_key_accepted = FALSE; -rfbKeySym last_rfb_keysym = 0; -double last_rfb_keytime = 0.0; -int fb_copy_in_progress = 0; -int drag_in_progress = 0; -int shut_down = 0; -int do_copy_screen = 0; -time_t damage_time = 0; -int damage_delay = 0; - -int program_pid = 0; -char *program_name = NULL; -char *program_cmdline = NULL; - -/* string for the VNC_CONNECT property */ -#define VNC_CONNECT_MAX 16384 -char vnc_connect_str[VNC_CONNECT_MAX+1]; -Atom vnc_connect_prop = None; - -struct utsname UT; - -/* scan pattern jitter from x0rfbserver */ -#define NSCAN 32 -int scanlines[NSCAN] = { - 0, 16, 8, 24, 4, 20, 12, 28, - 10, 26, 18, 2, 22, 6, 30, 14, - 1, 17, 9, 25, 7, 23, 15, 31, - 19, 3, 27, 11, 29, 13, 5, 21 -}; - - -/* function prototypes (see filename comment above) */ -int all_clients_initialized(void); -void close_all_clients(void); -void close_clients(char *); -int get_autorepeat_state(void); -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); -void check_connect_inputs(void); -void check_gui_inputs(void); -void record_last_fb_update(void); -void check_padded_fb(void); -void clean_up_exit(int); -void clear_modifiers(int init); -void clear_keys(void); -void init_track_keycode_state(void); -void get_keystate(int *); -int copy_screen(void); -void check_black_fb(void); -void do_new_fb(int); -void install_padded_fb(char *); -void install_fake_fb(int, int, int); -void remove_fake_fb(void); - -int add_keysym(KeySym); -void delete_keycode(KeyCode, int); -void delete_added_keycodes(int); -int count_added_keycodes(void); - -double dtime(double *); -double dtime0(double *); -double dnow(void); -double dnowx(void); -double rnow(void); -double rfac(void); - -void initialize_blackouts(char *); -void initialize_blackouts_and_xinerama(void); -void initialize_clipshift(void); -void initialize_keyboard_and_pointer(void); -void initialize_allowed_input(void); -void initialize_modtweak(void); -void initialize_pointer_map(char *); -void initialize_cursors_mode(void); -void initialize_remap(char *); -void initialize_screen(int *argc, char **argv, XImage *fb); -void initialize_polling_images(void); -void initialize_signals(void); -void initialize_tiles(void); -void initialize_speeds(void); -void clean_shm(int); -void free_tiles(void); -void initialize_watch_bell(void); -void initialize_xinerama(void); -void initialize_xfixes(void); -void initialize_xdamage(void); -int valid_window(Window, XWindowAttributes *, int); -Window parent_window(Window, char**); -int xtranslate(Window, Window, int, int, int*, int*, Window*, int); -void create_xdamage_if_needed(void); -void destroy_xdamage_if_needed(void); -void mark_for_xdamage(int, int, int, int); -void mark_region_for_xdamage(sraRegionPtr); -void set_xdamage_mark(int, int, int, int); -void initialize_xrandr(void); -XImage *initialize_xdisplay_fb(void); - -void initialize_pipeinput(void); -void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); -void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client); - -void XTestFakeKeyEvent_wr(Display*, KeyCode, Bool, unsigned long); -void XTestFakeButtonEvent_wr(Display*, unsigned int, Bool, unsigned long); -void XTestFakeMotionEvent_wr(Display*, int, int, int, unsigned long); -int XTestGrabControl_wr(Display*, Bool); -Bool XTestCompareCurrentCursorWithWindow_wr(Display*, Window); -Bool XTestCompareCursorWithWindow_wr(Display*, Window, Cursor); -Bool XTestQueryExtension_wr(Display*, int*, int*, int*, int*); -Bool XETrapQueryExtension_wr(Display*, int*, int*, int*); -void XTestDiscard_wr(Display*); - -typedef struct hint { - /* location x, y, height, and width of a change-rectangle */ - /* (grows as adjacent horizontal tiles are glued together) */ - int x, y, w, h; -} hint_t; -void mark_hint(hint_t); -void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force); - -enum rfbNewClientAction new_client(rfbClientPtr client); -void set_nofb_params(int); -void set_raw_fb_params(int); -void nofb_hook(rfbClientPtr client); -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 parse_fixscreen(void); -void set_wirecopyrect_mode(char *); -void set_scrollcopyrect_mode(char *); -void initialize_scroll_matches(void); -void initialize_scroll_term(void); -void initialize_max_keyrepeat(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 *, 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); -#define FB_COPY 0x1 -#define FB_MOD 0x2 -#define FB_REQ 0x4 -void fb_push_wait(double, int); -char *process_remote_cmd(char *, int); -void rfbPE(long); -void rfbCFD(long); -int scan_for_updates(int); -void set_colormap(int); -void set_offset(void); -void set_rfb_cursor(int); -void set_visual(char *vstring); -int set_cursor(int, int, int); -void setup_cursors(void); -void setup_cursors_and_push(void); -void first_cursor(void); -rfbCursorPtr pixels2curs(unsigned long *, int, int, int, int, int); -void set_no_cursor(void); -void set_cursor_was_changed(rfbScreenInfoPtr); -void set_cursor_was_moved(rfbScreenInfoPtr); -int get_which_cursor(void); -int get_xfixes_cursor(int); - -void disable_cursor_shape_updates(rfbScreenInfoPtr); -void restore_cursor_shape_updates(rfbScreenInfoPtr); -int new_fb_size_clients(rfbScreenInfoPtr); -void get_client_regions(int *, int *, int *, int *); - -void shm_clean(XShmSegmentInfo *, XImage *); -void shm_delete(XShmSegmentInfo *); - -int check_x11_pointer(void); -void check_bell_event(void); -void check_xevents(void); -char *this_host(void); -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); -int get_net_rate(void); -int get_net_latency(void); -int get_latency(void); -void measure_send_rates(int); -int fb_update_sent(int *); -void snapshot_stack_list(int, double); - -int get_remote_port(int sock); -int get_local_port(int sock); -char *get_remote_host(int sock); -char *get_local_host(int sock); - -void xcut_receive(char *text, int len, rfbClientPtr client); - -void parse_scale_string(char *, double *, int *, int *, - int *, int *, int *, int *, int *); -void scale_rect(double, int, int, int, - char *, int, char *, int, - int, int, int, int, int, int, int, int, int); -int scale_round(int, double); - -void zero_fb(int, int, int, int); -void push_black_screen(int); -void push_sleep(int); -void refresh_screen(int); - -int tray_embed(Window, int); -int tray_manager_running(Display *, Window *); - -/* -- options.h -- */ -/* - * variables for the command line options - */ -char *use_dpy = NULL; /* -display */ -char *auth_file = NULL; /* -auth/-xauth */ -char *visual_str = NULL; /* -visual */ -char *logfile = NULL; /* -o, -logfile */ -int logfile_append = 0; -char *flagfile = NULL; /* -flag */ -char *passwdfile = NULL; /* -passwdfile */ -char *blackout_str = NULL; /* -blackout */ -int blackout_ptr = 0; -char *clip_str = NULL; /* -clip */ -int use_solid_bg = 0; /* -solid */ -char *solid_str = NULL; -char *solid_default = "cyan4"; - -char *wmdt_str = NULL; /* -wmdt */ - -#define LATENCY0 20 /* 20ms */ -#define NETRATE0 20 /* 20KB/sec */ -char *speeds_str = NULL; /* -speeds TBD */ -int measure_speeds = 1; -int speeds_net_rate = 0; -int speeds_net_rate_measured = 0; -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_rcfile_default = 0; -int rc_norc = 0; -int got_norc = 0; -int opts_bg = 0; - -#ifndef VNCSHARED -int shared = 0; /* share vnc display. */ -#else -int shared = 1; -#endif -#ifndef FOREVER -int connect_once = 1; /* disconnect after first connection session. */ -#else -int connect_once = 0; -#endif -int got_connect_once = 0; -int deny_all = 0; /* global locking of new clients */ -#ifndef REMOTE_DEFAULT -#define REMOTE_DEFAULT 1 -#endif -int accept_remote_cmds = REMOTE_DEFAULT; /* -noremote */ -int query_default = 0; -int safe_remote_only = 1; /* -unsafe */ -int priv_remote = 0; /* -privremote */ -int more_safe = 0; /* -safer */ -#ifndef EXTERNAL_COMMANDS -#define EXTERNAL_COMMANDS 1 -#endif -#if EXTERNAL_COMMANDS -int no_external_cmds = 0; /* -nocmds */ -#else -int no_external_cmds = 1; /* cannot be turned back on. */ -#endif -int started_as_root = 0; -int host_lookup = 1; -char *users_list = NULL; /* -users */ -char *allow_list = NULL; /* for -allow and -localhost */ -char *listen_str = NULL; -char *allow_once = NULL; /* one time -allow */ -char *accept_cmd = NULL; /* for -accept */ -char *gone_cmd = NULL; /* for -gone */ -#ifndef VIEWONLY -#define VIEWONLY 0 -#endif -int view_only = VIEWONLY; /* clients can only watch. */ -char *allowed_input_view_only = NULL; -char *allowed_input_normal = NULL; -char *allowed_input_str = NULL; -char *viewonly_passwd = NULL; /* view only passwd. */ -char **passwd_list = NULL; /* for -passwdfile */ -int begin_viewonly = -1; -int inetd = 0; /* spawned from inetd(1) */ -#ifndef FILEXFER -#define FILEXFER 1 -#endif -int filexfer = FILEXFER; -int first_conn_timeout = 0; /* -timeout */ -int flash_cmap = 0; /* follow installed colormaps */ -int shift_cmap = 0; /* ncells < 256 and needs shift of pixel values */ -int force_indexed_color = 0; /* whether to force indexed color for 8bpp */ -int launch_gui = 0; /* -gui */ -char *gui_geometry = NULL; - -int icon_mode = 0; /* hack for -gui tray */ -int icon_in_tray = 0; -char *icon_mode_file = NULL; -char *icon_mode_params = NULL; -char *icon_mode_embed_id = NULL; -char *icon_mode_font = NULL; -FILE *icon_mode_fh = NULL; -#define ICON_MODE_SOCKS 16 -int icon_mode_socks[ICON_MODE_SOCKS]; - -int tray_manager_ok = 0; -Window tray_request = None; -Window tray_window = None; -int tray_unembed = 0; - -int use_modifier_tweak = 1; /* use the shift/altgr modifier tweak */ -int use_iso_level3 = 0; /* ISO_Level3_Shift instead of Mode_switch */ -int clear_mods = 0; /* -clear_mods (1) and -clear_keys (2) */ -int nofb = 0; /* do not send any fb updates */ -char *raw_fb_str = NULL; /* used under -rawfb */ -char *pipeinput_str = NULL; /* -pipeinput [tee,reopen,keycodes:]cmd */ -char *pipeinput_opts = NULL; -FILE *pipeinput_fh = NULL; -int pipeinput_tee = 0; - -unsigned long subwin = 0x0; /* -id, -sid */ -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_tiles = 0; -int debug_grabs = 0; - -int xtrap_input = 0; /* -xtrap for user input insertion */ -int xinerama = 0; /* -xinerama */ -int xrandr = 0; /* -xrandr */ -int xrandr_present = 0; -int xrandr_width = -1; -int xrandr_height = -1; -int xrandr_rotation = -1; -Time xrandr_timestamp = 0; -Time xrandr_cfg_time = 0; -char *xrandr_mode = NULL; -char *pad_geometry = NULL; -time_t pad_geometry_time; -int use_snapfb = 0; - -Display *rdpy_data = NULL; /* Data connection for RECORD */ -Display *rdpy_ctrl = NULL; /* Control connection for RECORD */ -int use_xrecord = 0; -int noxrecord = 0; - -Display *gdpy_data = NULL; /* Ditto for GrabServer watcher */ -Display *gdpy_ctrl = NULL; -int xserver_grabbed = 0; - -/* XXX CHECK BEFORE RELEASE */ -int grab_buster = 0; -int grab_buster_delay = 20; -int sync_tod_delay = 3; -pid_t grab_buster_pid = 0; - -char *client_connect = NULL; /* strings for -connect option */ -char *client_connect_file = NULL; -int vnc_connect = 1; /* -vncconnect option */ - -int show_cursor = 1; /* show cursor shapes */ -int show_multiple_cursors = 0; /* show X when on root background, etc */ -char *multiple_cursors_mode = NULL; -int cursor_pos_updates = 1; /* cursor position updates -cursorpos */ -int cursor_shape_updates = 1; /* cursor shape updates -nocursorshape */ -int use_xwarppointer = 0; /* use XWarpPointer instead of XTestFake... */ -int show_dragging = 1; /* process mouse movement events */ -#ifndef WIREFRAME -#define WIREFRAME 1 -#endif -int wireframe = WIREFRAME; /* try to emulate wireframe wm moves */ -/* shade,linewidth,percent,T+B+L+R,t1+t2+t3+t4 */ -#ifndef WIREFRAME_PARMS -#define WIREFRAME_PARMS "0xff,3,0,32+8+8+8,all,0.15+0.30+5.0+0.125" -#endif -char *wireframe_str = NULL; -char *wireframe_copyrect = NULL; -#ifndef WIREFRAME_COPYRECT -#define WIREFRAME_COPYRECT 1 -#endif -#if WIREFRAME_COPYRECT -char *wireframe_copyrect_default = "always"; -#else -char *wireframe_copyrect_default = "never"; -#endif -int wireframe_in_progress = 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; -#if 0 -winattr_t *stack_clip = NULL; -int stack_clip_len = 0; -int stack_clip_num = 0; -#endif - -/* 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.10+0.9,0.03+0.06+0.5+0.1+5.0" -#endif -char *scroll_copyrect_str = NULL; -#ifndef SCROLL_COPYRECT -#define SCROLL_COPYRECT 1 -#endif -char *scroll_copyrect = NULL; -#if SCROLL_COPYRECT -#if 1 -char *scroll_copyrect_default = "always"; /* -scrollcopyrect */ -#else -char *scroll_copyrect_default = "keys"; -#endif -#else -char *scroll_copyrect_default = "never"; -#endif -char *scroll_key_list_str = NULL; -KeySym *scroll_key_list = NULL; -int pointer_queued_sent = 0; - -#ifndef SCALING_COPYRECT -#define SCALING_COPYRECT 1 -#endif -int scaling_copyrect0 = SCALING_COPYRECT; -int scaling_copyrect = SCALING_COPYRECT; - -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; -double max_keyrepeat_time = 0.0; -char *max_keyrepeat_str = NULL; -char *max_keyrepeat_str0 = "4-20"; -int max_keyrepeat_lo = 1, max_keyrepeat_hi = 40; -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 */ - "##StarOffice" -; - -char **scroll_term = NULL; -char *scroll_term_str = NULL; -char *scroll_term_str0 = - "term" -; - -char* screen_fixup_str = NULL; -double screen_fixup_V = 0.0; -double screen_fixup_C = 0.0; -double screen_fixup_X = 0.0; - -#ifndef NOREPEAT -#define NOREPEAT 1 -#endif -int no_autorepeat = NOREPEAT; /* turn off autorepeat with clients */ -int no_repeat_countdown = 2; -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 0 -#endif -int skip_duplicate_key_events = SKIPDUPS; -char *skip_keycodes = NULL; -int sloppy_keys = 0; -#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; -/* use the various ways of updating pointer */ -#ifndef POINTER_MODE_DEFAULT -#define POINTER_MODE_DEFAULT 2 -#endif -#define POINTER_MODE_NOFB 2 -int pointer_mode = POINTER_MODE_DEFAULT; -int pointer_mode_max = 4; -int single_copytile = 0; /* use the old way copy_tiles() */ -int single_copytile_orig = 0; -int single_copytile_count = 0; -int tile_shm_count = 0; - -int using_shm = 1; /* whether mit-shm is used */ -int flip_byte_order = 0; /* sometimes needed when using_shm = 0 */ -/* - * waitms is the msec to wait between screen polls. Not too old h/w shows - * poll times of 10-35ms, so maybe this value cuts the idle load by 2 or so. - */ -int waitms = 30; -double wait_ui = 2.0; -double slow_fb = 0.0; -int wait_bog = 1; -int defer_update = 30; /* deferUpdateTime ms to wait before sends. */ -int got_defer = 0; -int got_deferupdate = 0; - -int screen_blank = 60; /* number of seconds of no activity to throttle */ - /* down the screen polls. zero to disable. */ -int no_fbu_blank = 30; /* nap if no client updates in this many secs. */ -int take_naps = 1; /* -nap/-nonap */ -int naptile = 4; /* tile change threshold per poll to take a nap */ -int napfac = 4; /* time = napfac*waitms, cut load with extra waits */ -int napmax = 1500; /* longest nap in ms. */ -int ui_skip = 10; /* see watchloop. negative means ignore input */ - -int watch_selection = 1; /* normal selection/cutbuffer maintenance */ -int watch_primary = 1; /* more dicey, poll for changes in PRIMARY */ -char *sel_direction = NULL; /* "send" or "recv" for one-way */ - -char *sigpipe = NULL; /* skip, ignore, exit */ - -/* visual stuff for -visual override or -overlay */ -VisualID visual_id = (VisualID) 0; -int visual_depth = 0; - -/* for -overlay mode on Solaris/IRIX. X server draws cursor correctly. */ -int overlay = 0; -int overlay_cursor = 1; - -int xshm_present = 0; -int xtest_present = 0; -int xtrap_present = 0; -int xrecord_present = 0; -int xkb_present = 0; -int xinerama_present = 0; - -/* tile heuristics: */ -double fs_frac = 0.75; /* threshold tile fraction to do fullscreen updates. */ -int tile_fuzz = 2; /* tolerance for suspecting changed tiles touching */ - /* a known changed tile. */ -int grow_fill = 3; /* do the grow islands heuristic with this width. */ -int gaps_fill = 4; /* do a final pass to try to fill gaps between tiles. */ - -int debug_pointer = 0; -int debug_keyboard = 0; - -int quiet = 0; - -/* info about command line opts */ -int got_rfbport = 0; -int got_alwaysshared = 0; -int got_nevershared = 0; -int got_cursorpos = 0; -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) -#define X11VNC_THREADED -#endif - -#if LIBVNCSERVER_HAVE_LIBPTHREAD && defined(X11VNC_THREADED) - int use_threads = 1; -#else - int use_threads = 0; -#endif - - -/* -- util.h -- */ - -#define NONUL(x) ((x) ? (x) : "") - -/* XXX usleep(3) is not thread safe on some older systems... */ -struct timeval _mysleep; -#define usleep2(x) \ - _mysleep.tv_sec = (x) / 1000000; \ - _mysleep.tv_usec = (x) % 1000000; \ - select(0, NULL, NULL, NULL, &_mysleep); -#if !defined(X11VNC_USLEEP) -#undef usleep -#define usleep usleep2 -#endif - -/* - * following is based on IsModifierKey in Xutil.h -*/ -#define ismodkey(keysym) \ - ((((KeySym)(keysym) >= XK_Shift_L) && ((KeySym)(keysym) <= XK_Hyper_R) && \ - ((KeySym)(keysym) != XK_Caps_Lock) && ((KeySym)(keysym) != XK_Shift_Lock))) - -/* - * Not sure why... but when threaded we have to mutex our X11 calls to - * avoid XIO crashes. - */ -MUTEX(x11Mutex); -#define X_INIT INIT_MUTEX(x11Mutex) -#if 1 -#define X_LOCK LOCK(x11Mutex) -#define X_UNLOCK UNLOCK(x11Mutex) -#else -int hxl = 0; -#define X_LOCK fprintf(stderr, "*** X_LOCK**[%05d] %d%s\n", \ - __LINE__, hxl, hxl ? " BAD-PRE-LOCK":""); LOCK(x11Mutex); hxl = 1; -#define X_UNLOCK fprintf(stderr, " x_unlock[%05d] %d%s\n", \ - __LINE__, hxl, !hxl ? " BAD-PRE-UNLOCK":""); UNLOCK(x11Mutex); hxl = 0; -#endif - -MUTEX(scrollMutex); -#define SCR_LOCK if (use_threads) {LOCK(scrollMutex);} -#define SCR_UNLOCK if (use_threads) {UNLOCK(scrollMutex);} -#define SCR_INIT INIT_MUTEX(scrollMutex) - -/* -- util.c -- */ - -/* - * routine to keep 0 <= i < n, should use in more places... - */ -int nfix(int i, int n) { - if (i < 0) { - i = 0; - } else if (i >= n) { - i = n - 1; - } - return i; -} - -int nabs(int n) { - if (n < 0) { - return -n; - } else { - return n; - } -} - -double dabs(double x) { - if (x < 0.0) { - return -x; - } else { - return x; - } -} - -void lowercase(char *str) { - char *p; - if (str == NULL) { - return; - } - p = str; - while (*p != '\0') { - *p = tolower(*p); - p++; - } -} - -void uppercase(char *str) { - char *p; - if (str == NULL) { - return; - } - p = str; - while (*p != '\0') { - *p = toupper(*p); - p++; - } -} - -char *lblanks(char *str) { - char *p = str; - while (*p) { - if (! isspace(*p)) { - break; - } - p++; - } - return p; -} - -int scan_hexdec(char *str, unsigned long *num) { - if (sscanf(str, "0x%lx", num) != 1) { - if (sscanf(str, "%lu", num) != 1) { - return 0; - } - } - return 1; -} - -int parse_geom(char *str, int *wp, int *hp, int *xp, int *yp, int W, int H) { - int w, h, x, y; - /* handle +/-x and +/-y */ - if (sscanf(str, "%dx%d+%d+%d", &w, &h, &x, &y) == 4) { - ; - } else if (sscanf(str, "%dx%d-%d+%d", &w, &h, &x, &y) == 4) { - w = nabs(w); - x = W - x - w; - } else if (sscanf(str, "%dx%d+%d-%d", &w, &h, &x, &y) == 4) { - h = nabs(h); - y = H - y - h; - } else if (sscanf(str, "%dx%d-%d-%d", &w, &h, &x, &y) == 4) { - w = nabs(w); - h = nabs(h); - x = W - x - w; - y = H - y - h; - } else { - return 0; - } - *wp = w; - *hp = h; - *xp = x; - *yp = y; - return 1; -} - -void set_env(char *name, char *value) { - char *str; - if (!value) { - value = ""; - } - str = (char *) malloc(strlen(name)+strlen(value)+2); - sprintf(str, "%s=%s", name, value); - putenv(str); -} - -int pick_windowid(unsigned long *num) { - char line[512]; - int ok = 0, n = 0, msec = 10, secmax = 15; - FILE *p; - - if (use_dpy) { - set_env("DISPLAY", use_dpy); - } - if (no_external_cmds) { - rfbLogEnable(1); - rfbLog("cannot run external commands in -nocmds mode:\n"); - rfbLog(" \"%s\"\n", "xwininfo"); - rfbLog(" exiting.\n"); - clean_up_exit(1); - } - p = popen("xwininfo", "r"); - - if (! p) { - return 0; - } - - fprintf(stderr, "\n"); - fprintf(stderr, " Please select the window for x11vnc to poll\n"); - fprintf(stderr, " by clicking the mouse in that window.\n"); - fprintf(stderr, "\n"); - - while (msec * n++ < 1000 * secmax) { - unsigned long tmp; - char *q; - fd_set set; - struct timeval tv; - - if (screen && screen->clientHead) { - /* they may be doing the pointer-pick thru vnc: */ - int nfds; - tv.tv_sec = 0; - tv.tv_usec = msec * 1000; - FD_ZERO(&set); - FD_SET(fileno(p), &set); - - nfds = select(fileno(p)+1, &set, NULL, NULL, &tv); - - if (nfds == 0 || nfds < 0) { - /* - * select timedout or error. - * note this rfbPE takes about 30ms too: - */ - rfbPE(-1); - XFlush(dpy); - continue; - } - } - - if (fgets(line, 512, p) == NULL) { - break; - } - q = strstr(line, " id: 0x"); - if (q) { - q += 5; - if (sscanf(q, "0x%lx ", &tmp) == 1) { - ok = 1; - *num = tmp; - fprintf(stderr, " Picked: 0x%lx\n\n", tmp); - break; - } - } - } - pclose(p); - return ok; -} - -Window query_pointer(Window start) { - Window r, c; - int rx, ry, wx, wy; - unsigned int mask; - if (start == None) { - start = rootwin; - } - if (XQueryPointer(dpy, start, &r, &c, &rx, &ry, &wx, &wy, &mask)) { - return c; - } else { - return None; - } -} - -char *bitprint(unsigned int st, int nbits) { - static char str[33]; - int i, mask; - if (nbits > 32) { - nbits = 32; - } - for (i=0; i<nbits; i++) { - str[i] = '0'; - } - str[nbits] = '\0'; - mask = 1; - for (i=nbits-1; i>=0; i--) { - if (st & mask) { - str[i] = '1'; - } - mask = mask << 1; - } - return str; /* take care to use or copy immediately */ -} - -char *get_user_name(void) { - char *user = NULL; - - user = getenv("USER"); - if (user == NULL) { - user = getenv("LOGNAME"); - } - -#if LIBVNCSERVER_HAVE_PWD_H - if (user == NULL) { - struct passwd *pw = getpwuid(getuid()); - if (pw) { - user = pw->pw_name; - } - } -#endif - - if (user) { - return(strdup(user)); - } else { - return(strdup("unknown-user")); - } -} - -char *get_home_dir(void) { - char *home = NULL; - - home = getenv("HOME"); - -#if LIBVNCSERVER_HAVE_PWD_H - if (home == NULL) { - struct passwd *pw = getpwuid(getuid()); - if (pw) { - home = pw->pw_dir; - } - } -#endif - - if (home) { - return(strdup(home)); - } else { - return(strdup("/")); - } -} - -char *get_shell(void) { - char *shell = NULL; - - shell = getenv("SHELL"); - -#if LIBVNCSERVER_HAVE_PWD_H - if (shell == NULL) { - struct passwd *pw = getpwuid(getuid()); - if (pw) { - shell = pw->pw_shell; - } - } -#endif - - if (shell) { - return(strdup(shell)); - } else { - return(strdup("/bin/sh")); - } -} - -/* -- user.c -- */ - -int switch_user(char *, int); -int switch_user_env(uid_t, char*, char *, int); -void try_to_switch_users(void); -char *guess_desktop(void); - -/* tasks for after we switch */ -void switch_user_task_dummy(void) { - ; /* dummy does nothing */ -} -void switch_user_task_solid_bg(void) { - /* we have switched users, some things to do. */ - if (use_solid_bg && client_count) { - solid_bg(0); - } -} - -void check_switched_user(void) { - static time_t sched_switched_user = 0; - static int did_solid = 0; - static int did_dummy = 0; - int delay = 15; - time_t now = time(0); - - if (started_as_root == 1 && users_list) { - try_to_switch_users(); - if (started_as_root == 2) { - /* - * schedule the switch_user_tasks() call - * 15 secs is for piggy desktops to start up. - * might not be enough for slow machines... - */ - sched_switched_user = now; - did_dummy = 0; - did_solid = 0; - /* add other activities */ - } - } - if (! sched_switched_user) { - return; - } - - if (! did_dummy) { - switch_user_task_dummy(); - did_dummy = 1; - } - if (! did_solid) { - int doit = 0; - char *ss = solid_str; - if (now >= sched_switched_user + delay) { - doit = 1; - } else if (ss && strstr(ss, "root:") == ss) { - if (now >= sched_switched_user + 3) { - doit = 1; - } - } else if (strcmp("root", guess_desktop())) { - usleep(1000 * 1000); - doit = 1; - } - if (doit) { - switch_user_task_solid_bg(); - did_solid = 1; - } - } - - if (did_dummy && did_solid) { - sched_switched_user = 0; - } -} - -/* utilities for switching users */ -char *get_login_list(int with_display) { - char *out; -#if LIBVNCSERVER_HAVE_UTMPX_H - int i, cnt, max = 200, ut_namesize = 32; - int dpymax = 1000, sawdpy[1000]; - struct utmpx *utx; - - /* size based on "username:999," * max */ - out = (char *) malloc(max * (ut_namesize+1+3+1) + 1); - out[0] = '\0'; - - for (i=0; i<dpymax; i++) { - sawdpy[i] = 0; - } - - setutxent(); - cnt = 0; - while (1) { - char *user, *line, *host, *id; - char tmp[10]; - int d = -1; - utx = getutxent(); - if (! utx) { - break; - } - if (utx->ut_type != USER_PROCESS) { - continue; - } - user = lblanks(utx->ut_user); - if (*user == '\0') { - continue; - } - if (strchr(user, ',')) { - continue; /* unlikely, but comma is our sep. */ - } - - line = lblanks(utx->ut_line); - host = lblanks(utx->ut_host); - id = lblanks(utx->ut_id); - - if (with_display) { - if (0 && line[0] != ':' && strcmp(line, "dtlocal")) { - /* XXX useful? */ - continue; - } - - if (line[0] == ':') { - if (sscanf(line, ":%d", &d) != 1) { - d = -1; - } - } - if (d < 0 && host[0] == ':') { - if (sscanf(host, ":%d", &d) != 1) { - d = -1; - } - } - if (d < 0 && id[0] == ':') { - if (sscanf(id, ":%d", &d) != 1) { - d = -1; - } - } - - if (d < 0 || d >= dpymax || sawdpy[d]) { - continue; - } - sawdpy[d] = 1; - sprintf(tmp, ":%d", d); - } else { - /* try to eliminate repeats */ - int repeat = 0; - char *q; - - q = out; - while ((q = strstr(q, user)) != NULL) { - char *p = q + strlen(user) + strlen(":DPY"); - if (q == out || *(q-1) == ',') { - /* bounded on left. */ - if (*p == ',' || *p == '\0') { - /* bounded on right. */ - repeat = 1; - break; - } - } - q = p; - } - if (repeat) { - continue; - } - sprintf(tmp, ":DPY"); - } - - if (*out) { - strcat(out, ","); - } - strcat(out, user); - strcat(out, tmp); - - cnt++; - if (cnt >= max) { - break; - } - } - endutxent(); -#else - out = strdup(""); -#endif - return out; -} - -char **user_list(char *user_str) { - int n, i; - char *p, **list; - - p = user_str; - n = 1; - while (*p++) { - if (*p == ',') { - n++; - } - } - list = (char **) malloc((n+1)*sizeof(char *)); - - p = strtok(user_str, ","); - i = 0; - while (p) { - list[i++] = p; - p = strtok(NULL, ","); - } - list[i] = NULL; - return list; -} - -void user2uid(char *user, uid_t *uid, char **name, char **home) { - int numerical = 1; - char *q; - - *uid = (uid_t) -1; - *name = NULL; - *home = NULL; - - q = user; - while (*q) { - if (! isdigit(*q++)) { - numerical = 0; - break; - } - } - - if (numerical) { - int u = atoi(user); - - if (u < 0) { - return; - } - *uid = (uid_t) u; - } - -#if LIBVNCSERVER_HAVE_PWD_H - if (1) { - struct passwd *pw; - if (numerical) { - pw = getpwuid(*uid); - } else { - pw = getpwnam(user); - } - if (pw) { - *uid = pw->pw_uid; - *name = pw->pw_name; /* n.b. use immediately */ - *home = pw->pw_dir; - } - } -#endif -} - -int try_user_and_display(uid_t, char*); - -int lurk(char **users) { - uid_t uid; - int success = 0, dmin = -1, dmax = -1; - char *p, *logins, **u; - - if ((u = users) != NULL && *u != NULL && *(*u) == ':') { - int len; - char *tmp; - - /* extract min and max display numbers */ - tmp = *u; - if (strchr(tmp, '-')) { - if (sscanf(tmp, ":%d-%d", &dmin, &dmax) != 2) { - dmin = -1; - dmax = -1; - } - } - if (dmin < 0) { - if (sscanf(tmp, ":%d", &dmin) != 1) { - dmin = -1; - dmax = -1; - } else { - dmax = dmin; - } - } - if ((dmin < 0 || dmax < 0) || dmin > dmax || dmax > 10000) { - dmin = -1; - dmax = -1; - } - - /* get user logins regardless of having a display: */ - logins = get_login_list(0); - - /* - * now we append the list in users (might as well try - * them) this will probably allow weird ways of starting - * xservers to work. - */ - len = strlen(logins); - u++; - while (*u != NULL) { - len += strlen(*u) + strlen(":DPY,"); - u++; - } - tmp = (char *) malloc(len+1); - strcpy(tmp, logins); - - /* now concatenate them: */ - u = users+1; - while (*u != NULL) { - char *q, chk[100]; - snprintf(chk, 100, "%s:DPY", *u); - q = strstr(tmp, chk); - if (q) { - char *p = q + strlen(chk); - - if (q == tmp || *(q-1) == ',') { - /* bounded on left. */ - if (*p == ',' || *p == '\0') { - /* bounded on right. */ - u++; - continue; - } - } - } - - if (*tmp) { - strcat(tmp, ","); - } - strcat(tmp, *u); - strcat(tmp, ":DPY"); - u++; - } - free(logins); - logins = tmp; - - } else { - logins = get_login_list(1); - } - - p = strtok(logins, ","); - while (p) { - char *user, *name, *home, dpystr[10]; - char *q, *t; - int ok = 1, dn; - - t = strdup(p); /* bob:0 */ - q = strchr(t, ':'); - if (! q) { - free(t); - break; - } - *q = '\0'; - user = t; - snprintf(dpystr, 10, ":%s", q+1); - - if (users) { - u = users; - ok = 0; - while (*u != NULL) { - if (*(*u) == ':') { - u++; - continue; - } - if (!strcmp(user, *u++)) { - ok = 1; - break; - } - } - } - - user2uid(user, &uid, &name, &home); - free(t); - - if (! uid) { - ok = 0; - } - - if (! ok) { - p = strtok(NULL, ","); - continue; - } - - for (dn = dmin; dn <= dmax; dn++) { - if (dn >= 0) { - sprintf(dpystr, ":%d", dn); - } - if (try_user_and_display(uid, dpystr)) { - if (switch_user_env(uid, name, home, 0)) { - rfbLog("lurk: now user: %s @ %s\n", - name, dpystr); - started_as_root = 2; - success = 1; - } - set_env("DISPLAY", dpystr); - break; - } - } - if (success) { - break; - } - - p = strtok(NULL, ","); - } - free(logins); - return success; -} - -void lurk_loop(char *str) { - char *tstr = NULL, **users = NULL; - - if (strstr(str, "lurk=") != str) { - exit(1); - } - rfbLog("lurking for logins using: '%s'\n", str); - if (strlen(str) > strlen("lurk=")) { - char *q = strchr(str, '='); - tstr = strdup(q+1); - users = user_list(tstr); - } - - while (1) { - if (lurk(users)) { - break; - } - sleep(3); - } - if (tstr) { - free(tstr); - } - if (users) { - free(users); - } -} - -int guess_user_and_switch(char *str, int fb_mode) { - char *dstr, *d = DisplayString(dpy); - char *p, *tstr = NULL, *allowed = NULL, *logins, **users = NULL; - int dpy1, ret = 0; - - /* pick out ":N" */ - dstr = strchr(d, ':'); - if (! dstr) { - return 0; - } - if (sscanf(dstr, ":%d", &dpy1) != 1) { - return 0; - } - if (dpy1 < 0) { - return 0; - } - - if (strstr(str, "guess=") == str && strlen(str) > strlen("guess=")) { - allowed = strchr(str, '='); - allowed++; - - tstr = strdup(allowed); - users = user_list(tstr); - } - - /* loop over the utmpx entries looking for this display */ - logins = get_login_list(1); - p = strtok(logins, ","); - while (p) { - char *user, *q, *t; - int dpy2, ok = 1; - - t = strdup(p); - q = strchr(t, ':'); - if (! q) { - free(t); - break; - } - *q = '\0'; - user = t; - dpy2 = atoi(q+1); - - if (users) { - char **u = users; - ok = 0; - while (*u != NULL) { - if (!strcmp(user, *u++)) { - ok = 1; - break; - } - } - } - if (dpy1 != dpy2) { - ok = 0; - } - - if (! ok) { - free(t); - p = strtok(NULL, ","); - continue; - } - if (switch_user(user, fb_mode)) { - rfbLog("switched to guessed user: %s\n", user); - free(t); - ret = 1; - break; - } - - p = strtok(NULL, ","); - } - if (tstr) { - free(tstr); - } - if (users) { - free(users); - } - if (logins) { - free(logins); - } - return ret; -} - -int try_user_and_display(uid_t uid, char *dpystr) { - /* NO strtoks */ -#if LIBVNCSERVER_HAVE_FORK && LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_PWD_H - pid_t pid, pidw; - char *home, *name; - int st; - struct passwd *pw; - - pw = getpwuid(uid); - if (pw) { - name = pw->pw_name; - home = pw->pw_dir; - } else { - return 0; - } - - /* - * We fork here and try to open the display again as the - * new user. Unreadable XAUTHORITY could be a problem... - * This is not really needed since we have DISPLAY open but: - * 1) is a good indicator this user owns the session and 2) - * some activities do spawn new X apps, e.g. xmessage(1), etc. - */ - if ((pid = fork()) > 0) { - ; - } else if (pid == -1) { - fprintf(stderr, "could not fork\n"); - rfbLogPerror("fork"); - return 0; - } else { - /* child */ - Display *dpy2 = NULL; - int rc; - - rc = switch_user_env(uid, name, home, 0); - if (! rc) { - exit(1); - } - - fclose(stderr); - dpy2 = XOpenDisplay(dpystr); - if (dpy2) { - XCloseDisplay(dpy2); - exit(0); /* success */ - } else { - exit(2); /* fail */ - } - } - - /* see what the child says: */ - pidw = waitpid(pid, &st, 0); - if (pidw == pid && WIFEXITED(st) && WEXITSTATUS(st) == 0) { - return 1; - } -#endif /* LIBVNCSERVER_HAVE_FORK ... */ - return 0; -} - -int switch_user(char *user, int fb_mode) { - /* NO strtoks */ - int doit = 0; - uid_t uid = 0; - char *name, *home; - - if (*user == '+') { - doit = 1; - user++; - } - - if (strstr(user, "guess=") == user) { - return guess_user_and_switch(user, fb_mode); - } - - user2uid(user, &uid, &name, &home); - - if (uid == (uid_t) -1 || uid == 0) { - return 0; - } - - if (! doit && dpy) { - /* see if this display works: */ - char *dstr = DisplayString(dpy); - doit = try_user_and_display(uid, dstr); - } - - if (doit) { - int rc = switch_user_env(uid, name, home, fb_mode); - if (rc) { - started_as_root = 2; - } - return rc; - } else { - return 0; - } -} - -int switch_user_env(uid_t uid, char *name, char *home, int fb_mode) { - /* NO strtoks */ - char *xauth; - int reset_fb = 0; - -#if !LIBVNCSERVER_HAVE_SETUID - return 0; -#else - /* - * OK tricky here, we need to free the shm... otherwise - * we won't be able to delete it as the other user... - */ - if (fb_mode == 1 && using_shm) { - reset_fb = 1; - clean_shm(0); - free_tiles(); - } - if (setuid(uid) != 0) { - if (reset_fb) { - /* 2 means we did clean_shm and free_tiles */ - do_new_fb(2); - } - return 0; - } -#endif - if (reset_fb) { - do_new_fb(2); - } - - xauth = getenv("XAUTHORITY"); - if (xauth && access(xauth, R_OK) != 0) { - *(xauth-2) = '_'; /* yow */ - } - - set_env("USER", name); - set_env("LOGNAME", name); - set_env("HOME", home); - return 1; -} - -void try_to_switch_users(void) { - static time_t last_try = 0; - time_t now = time(0); - char *users, *p; - - if (getuid() && geteuid()) { - rfbLog("try_to_switch_users: not root\n"); - started_as_root = 2; - return; - } - if (!last_try) { - last_try = now; - } else if (now <= last_try + 2) { - /* try every 3 secs or so */ - return; - } - last_try = now; - - users = strdup(users_list); - - if (strstr(users, "guess=") == users) { - if (switch_user(users, 1)) { - started_as_root = 2; - } - free(users); - return; - } - - p = strtok(users, ","); - while (p) { - if (switch_user(p, 1)) { - started_as_root = 2; - rfbLog("try_to_switch_users: now %s\n", p); - break; - } - p = strtok(NULL, ","); - } - free(users); -} - -/* -- inet.c -- */ -/* - * Simple utility to map host name to dotted IP address. Ignores aliases. - * Up to caller to free returned string. - */ -char *host2ip(char *host) { - struct hostent *hp; - struct sockaddr_in addr; - char *str; - - if (! host_lookup) { - return NULL; - } - - hp = gethostbyname(host); - if (!hp) { - return NULL; - } - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = *(unsigned long *)hp->h_addr; - str = strdup(inet_ntoa(addr.sin_addr)); - return str; -} - -char *raw2host(char *raw, int len) { - char *str; -#if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H - struct hostent *hp; - - if (! host_lookup) { - return strdup("unknown"); - } - - hp = gethostbyaddr(raw, len, AF_INET); - if (!hp) { - return strdup(inet_ntoa(*((struct in_addr *)raw))); - } - str = strdup(hp->h_name); -#else - str = strdup("unknown"); -#endif - return str; -} - -char *raw2ip(char *raw) { - return strdup(inet_ntoa(*((struct in_addr *)raw))); -} - -char *ip2host(char *ip) { - char *str; -#if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H - struct hostent *hp; - in_addr_t iaddr; - - if (! host_lookup) { - return strdup("unknown"); - } - - iaddr = inet_addr(ip); - if (iaddr == htonl(INADDR_NONE)) { - return strdup("unknown"); - } - - hp = gethostbyaddr((char *)&iaddr, sizeof(in_addr_t), AF_INET); - if (!hp) { - return strdup("unknown"); - } - str = strdup(hp->h_name); -#else - str = strdup("unknown"); -#endif - return str; -} - -int dotted_ip(char *host) { - char *p = host; - while (*p != '\0') { - if (*p == '.' || isdigit(*p)) { - p++; - continue; - } - return 0; - } - return 1; -} - -int get_port(int sock, int remote) { - struct sockaddr_in saddr; - unsigned int saddr_len; - int saddr_port; - - saddr_len = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - saddr_port = -1; - if (remote) { - if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { - saddr_port = ntohs(saddr.sin_port); - } - } else { - if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { - saddr_port = ntohs(saddr.sin_port); - } - } - return saddr_port; -} - -int get_remote_port(int sock) { - return get_port(sock, 1); -} - -int get_local_port(int sock) { - return get_port(sock, 0); -} - -char *get_host(int sock, int remote) { - struct sockaddr_in saddr; - unsigned int saddr_len; - int saddr_port; - char *saddr_ip_str = NULL; - - saddr_len = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - saddr_port = -1; -#if LIBVNCSERVER_HAVE_NETINET_IN_H - if (remote) { - if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) { - saddr_ip_str = inet_ntoa(saddr.sin_addr); - } - } else { - if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) { - saddr_ip_str = inet_ntoa(saddr.sin_addr); - } - } -#endif - if (! saddr_ip_str) { - saddr_ip_str = "unknown"; - } - return strdup(saddr_ip_str); -} - -char *get_remote_host(int sock) { - return get_host(sock, 1); -} - -char *get_local_host(int sock) { - return get_host(sock, 0); -} - -char *ident_username(rfbClientPtr client) { - ClientData *cd = (ClientData *) client->clientData; - char *str, *newhost, *user = NULL, *newuser = NULL; - int len; - - if (cd) { - user = cd->username; - } - if (!user || *user == '\0') { - char msg[128]; - int n, sock, ok = 0; - - if ((sock = rfbConnectToTcpAddr(client->host, 113)) < 0) { - rfbLog("could not connect to ident: %s:%d\n", - client->host, 113); - } else { - int ret; - fd_set rfds; - struct timeval tv; - int rport = get_remote_port(client->sock); - int lport = get_local_port(client->sock); - - sprintf(msg, "%d, %d\r\n", rport, lport); - n = write(sock, msg, strlen(msg)); - - FD_ZERO(&rfds); - FD_SET(sock, &rfds); - tv.tv_sec = 4; - tv.tv_usec = 0; - ret = select(sock+1, &rfds, NULL, NULL, &tv); - - if (ret > 0) { - int i; - char *q, *p; - for (i=0; i<128; i++) { - msg[i] = '\0'; - } - usleep(250*1000); - n = read(sock, msg, 127); - close(sock); - if (n <= 0) goto badreply; - - /* 32782 , 6000 : USERID : UNIX :runge */ - q = strstr(msg, "USERID"); - if (!q) goto badreply; - q = strstr(q, ":"); - if (!q) goto badreply; - q++; - q = strstr(q, ":"); - if (!q) goto badreply; - q++; - q = lblanks(q); - p = q; - while (*p) { - if (*p == '\r' || *p == '\n') { - *p = '\0'; - } - p++; - } - ok = 1; - if (strlen(q) > 24) { - *(q+24) = '\0'; - } - newuser = strdup(q); - - badreply: - n = 0; /* avoid syntax error */ - } else { - close(sock); - } - } - if (! ok || !newuser) { - newuser = strdup("unknown-user"); - } - if (cd) { - if (cd->username) { - free(cd->username); - } - cd->username = newuser; - } - user = newuser; - } - newhost = ip2host(client->host); - len = strlen(user) + 1 + strlen(newhost) + 1; - str = (char *) malloc(len); - sprintf(str, "%s@%s", user, newhost); - free(newhost); - return str; -} - -/* -- ximage.c -- */ - -/* - * used in rfbGetScreen and rfbNewFramebuffer: and estimate to the number - * of bits per color, of course for some visuals, e.g. 565, the number - * is not the same for each color. This is just a sane default. - */ -int guess_bits_per_color(int bits_per_pixel) { - int bits_per_color; - - /* first guess, spread them "evenly" over R, G, and B */ - bits_per_color = bits_per_pixel/3; - if (bits_per_color < 1) { - bits_per_color = 1; /* 1bpp, 2bpp... */ - } - - /* choose safe values for usual cases: */ - if (bits_per_pixel == 8) { - bits_per_color = 2; - } else if (bits_per_pixel == 15 || bits_per_pixel == 16) { - bits_per_color = 5; - } else if (bits_per_pixel == 24 || bits_per_pixel == 32) { - bits_per_color = 8; - } - return bits_per_color; -} - -/* - * Kludge to interpose image gets and limit to a subset rectangle of - * the rootwin. This is the -sid option trying to work around invisible - * saveUnders menu, etc, windows. Also -clip option. - */ -int rootshift = 0; -int clipshift = 0; - -#define ADJUST_ROOTSHIFT \ - if (rootshift && subwin) { \ - d = rootwin; \ - x += off_x; \ - y += off_y; \ - } \ - if (clipshift) { \ - x += coff_x; \ - y += coff_y; \ - } - -/* - * Wrappers for Image related X calls - */ -Status XShmGetImage_wr(Display *disp, Drawable d, XImage *image, int x, int y, - unsigned long mask) { - - ADJUST_ROOTSHIFT - - /* Note: the Solaris overlay stuff is all non-shm (using_shm = 0) */ - -#if LIBVNCSERVER_HAVE_XSHM - return XShmGetImage(disp, d, image, x, y, mask); -#else - return (Status) 0; -#endif -} - -XImage *XShmCreateImage_wr(Display* disp, Visual* vis, unsigned int depth, - int format, char* data, XShmSegmentInfo* shminfo, unsigned int width, - unsigned int height) { - -#if LIBVNCSERVER_HAVE_XSHM - return XShmCreateImage(disp, vis, depth, format, data, shminfo, - width, height); -#else - return (XImage *) 0; -#endif -} - -Status XShmAttach_wr(Display *disp, XShmSegmentInfo *shminfo) { -#if LIBVNCSERVER_HAVE_XSHM - return XShmAttach(disp, shminfo); -#else - return (Status) 0; -#endif -} - -Status XShmDetach_wr(Display *disp, XShmSegmentInfo *shminfo) { -#if LIBVNCSERVER_HAVE_XSHM - return XShmDetach(disp, shminfo); -#else - return (Status) 0; -#endif -} - -Bool XShmQueryExtension_wr(Display *disp) { -#if LIBVNCSERVER_HAVE_XSHM - return XShmQueryExtension(disp); -#else - return False; -#endif -} - -/* wrapper for overlay screen reading: */ - -XImage *xreadscreen(Display *disp, Drawable d, int x, int y, - unsigned int width, unsigned int height, Bool show_cursor) { -#ifdef SOLARIS_OVERLAY - return XReadScreen(disp, d, x, y, width, height, - show_cursor); -#else -# ifdef IRIX_OVERLAY - { unsigned long hints = 0, hints_ret; - if (show_cursor) hints |= XRD_READ_POINTER; - return XReadDisplay(disp, d, x, y, width, height, - hints, &hints_ret); - } -# else - /* unused vars warning: */ - if (disp || d || x || y || width || height || show_cursor) {} - - return NULL; -# endif -#endif -} - -XImage *XGetSubImage_wr(Display *disp, Drawable d, int x, int y, - unsigned int width, unsigned int height, unsigned long plane_mask, - int format, XImage *dest_image, int dest_x, int dest_y) { - - ADJUST_ROOTSHIFT - - if (overlay && dest_x == 0 && dest_y == 0) { - size_t size = dest_image->height * dest_image->bytes_per_line; - XImage *xi; - - xi = xreadscreen(disp, d, x, y, width, height, - (Bool) overlay_cursor); - - if (! xi) return NULL; - - /* - * There is extra overhead from memcpy and free... - * this is not like the real XGetSubImage(). We hope - * this significant overhead is still small compared to - * the time to retrieve the fb data. - */ - memcpy(dest_image->data, xi->data, size); - - XDestroyImage(xi); - return (dest_image); - } - return XGetSubImage(disp, d, x, y, width, height, plane_mask, - format, dest_image, dest_x, dest_y); -} - -XImage *XGetImage_wr(Display *disp, Drawable d, int x, int y, - unsigned int width, unsigned int height, unsigned long plane_mask, - int format) { - - ADJUST_ROOTSHIFT - - if (overlay) { - return xreadscreen(disp, d, x, y, width, height, - (Bool) overlay_cursor); - } - return XGetImage(disp, d, x, y, width, height, plane_mask, format); -} - -XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth, - int format, int offset, char *data, unsigned int width, - unsigned int height, int bitmap_pad, int bytes_per_line) { - /* - * This is a kludge to get a created XImage to exactly match what - * XReadScreen returns: we noticed the rgb masks are different - * from XCreateImage with the high color visual (red mask <-> - * blue mask). Note we read from the root window(!) then free - * the data. - */ - - if (raw_fb) { /* raw_fb hack */ - XImage *xi; - xi = (XImage *) malloc(sizeof(XImage)); - memset(xi, 0, sizeof(XImage)); - xi->depth = depth; - xi->bits_per_pixel = (depth == 24) ? 32 : depth; - xi->format = format; - xi->xoffset = offset; - xi->data = data; - xi->width = width; - xi->height = height; - xi->bitmap_pad = bitmap_pad; - xi->bytes_per_line = bytes_per_line ? bytes_per_line : - xi->width * xi->bits_per_pixel / 8; - return xi; - } - - if (overlay) { - XImage *xi; - xi = xreadscreen(disp, window, 0, 0, width, height, False); - if (xi == NULL) { - return xi; - } - if (xi->data != NULL) { - free(xi->data); - } - xi->data = data; - return xi; - } - - return XCreateImage(disp, visual, depth, format, offset, data, - width, height, bitmap_pad, bytes_per_line); -} - -void copy_raw_fb(XImage *dest, int x, int y, unsigned int w, unsigned int h) { - char *src, *dst; - unsigned int line; - int pixelsize = bpp/8; - int bpl = wdpy_x * pixelsize; - - if (clipshift) { - x += coff_x; - y += coff_y; - } - if (! raw_fb_seek) { - src = raw_fb_addr + raw_fb_offset + bpl*y + pixelsize*x; - dst = dest->data; - - for (line = 0; line < h; line++) { - memcpy(dst, src, w * pixelsize); - src += bpl; - dst += dest->bytes_per_line; - } - } else{ - int n, len, del, sz = w * pixelsize; - off_t off = (off_t) (raw_fb_offset + bpl*y + pixelsize*x); - - lseek(raw_fb_fd, off, SEEK_SET); - dst = dest->data; - - for (line = 0; line < h; line++) { - len = sz; - del = 0; - while (len > 0) { - n = read(raw_fb_fd, dst + del, len); - - if (n > 0) { - del += n; - len -= n; - } else if (n == 0) { - break; - } else { - /* overkill... */ - if (errno != EINTR && errno != EAGAIN) { - break; - } - } - } - if (bpl > sz) { - off = (off_t) (bpl - sz); - lseek(raw_fb_fd, off, SEEK_CUR); - } - dst += dest->bytes_per_line; - } - } -} - -void copy_image(XImage *dest, int x, int y, unsigned int w, unsigned int h) { - /* default (w=0, h=0) is the fill the entire XImage */ - if (w < 1) { - w = dest->width; - } - if (h < 1) { - h = dest->height; - } - - if (use_snapfb && snap_fb && dest != snaprect) { - char *src, *dst; - unsigned int line; - int pixelsize = bpp/8; - - src = snap->data + snap->bytes_per_line*y + pixelsize*x; - dst = dest->data; - for (line = 0; line < h; line++) { - memcpy(dst, src, w * pixelsize); - src += snap->bytes_per_line; - dst += dest->bytes_per_line; - } - - } else if (raw_fb) { - copy_raw_fb(dest, x, y, w, h); - - } else if (using_shm && (int) w == dest->width && - (int) h == dest->height) { - XShmGetImage_wr(dpy, window, dest, x, y, AllPlanes); - - } else { - XGetSubImage_wr(dpy, window, x, y, w, h, AllPlanes, - ZPixmap, dest, 0, 0); - } -} - -#define DEBUG_SKIPPED_INPUT(dbg, str) \ - if (dbg) { \ - rfbLog("skipped input: %s\n", str); \ - } - -int keycode_state[256]; - -void init_track_keycode_state(void) { - int i; - for (i=0; i<256; i++) { - keycode_state[i] = 0; - } - get_keystate(keycode_state); -} - -void upup_downdown_warning(KeyCode key, Bool down) { - if ((down ? 1:0) == keycode_state[(int) key]) { - rfbLog("XTestFakeKeyEvent: keycode=0x%x \"%s\" is *already* " - "%s\n", key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)), - down ? "down":"up"); - } -} - -/* - * wrappers for XTestFakeKeyEvent, etc.. - * also for XTrap equivalents XESimulateXEventRequest - */ - -void XTRAP_FakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, - unsigned long delay) { - - if (! xtrap_present) { - DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTRAP"); - return; - } - /* unused vars warning: */ - if (dpy || key || down || delay) {} - -#if LIBVNCSERVER_HAVE_LIBXTRAP - XESimulateXEventRequest(trap_ctx, down ? KeyPress : KeyRelease, - key, 0, 0, 0); - if (debug_keyboard) { - upup_downdown_warning(key, down); - } - keycode_state[(int) key] = down ? 1 : 0; -#else - DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTRAP-build"); -#endif -} - -void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, - unsigned long delay) { - static int first = 1; - if (debug_keyboard) { - rfbLog("XTestFakeKeyEvent(dpy, keycode=0x%x \"%s\", %s)\n", - key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)), - down ? "down":"up"); - } - if (first) { - init_track_keycode_state(); - first = 0; - } - if (down) { - last_keyboard_keycode = -key; - } else { - last_keyboard_keycode = key; - } - - if (xtrap_input) { - XTRAP_FakeKeyEvent_wr(dpy, key, down, delay); - return; - } - - if (! xtest_present) { - 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); - if (debug_keyboard) { - upup_downdown_warning(key, down); - } - keycode_state[(int) key] = down ? 1 : 0; -#endif -} - -void XTRAP_FakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, - unsigned long delay) { - - if (! xtrap_present) { - DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTRAP"); - return; - } - /* unused vars warning: */ - if (dpy || button || is_press || delay) {} - -#if LIBVNCSERVER_HAVE_LIBXTRAP - XESimulateXEventRequest(trap_ctx, - is_press ? ButtonPress : ButtonRelease, button, 0, 0, 0); -#else - DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTRAP-build"); -#endif -} - -void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, - unsigned long delay) { - - if (xtrap_input) { - XTRAP_FakeButtonEvent_wr(dpy, button, is_press, delay); - return; - } - - if (! xtest_present) { - 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 -} - -void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y, - unsigned long delay) { - - if (! xtrap_present) { - DEBUG_SKIPPED_INPUT(debug_keyboard, "motion: no-XTRAP"); - return; - } - /* unused vars warning: */ - if (dpy || screen || x || y || delay) {} - -#if LIBVNCSERVER_HAVE_LIBXTRAP - XESimulateXEventRequest(trap_ctx, MotionNotify, 0, x, y, 0); -#else - DEBUG_SKIPPED_INPUT(debug_keyboard, "motion: no-XTRAP-build"); -#endif -} - -void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y, - unsigned long delay) { - - if (xtrap_input) { - XTRAP_FakeMotionEvent_wr(dpy, screen, x, y, delay); - return; - } - - 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 -} - -Bool XTestCompareCurrentCursorWithWindow_wr(Display* dpy, Window w) { - if (! xtest_present) { - return False; - } -#if LIBVNCSERVER_HAVE_XTEST - return XTestCompareCurrentCursorWithWindow(dpy, w); -#else - return False; -#endif -} - -Bool XTestCompareCursorWithWindow_wr(Display* dpy, Window w, Cursor cursor) { - if (! xtest_present) { - return False; - } -#if LIBVNCSERVER_HAVE_XTEST - return XTestCompareCursorWithWindow(dpy, w, cursor); -#else - return False; -#endif -} - -Bool XTestQueryExtension_wr(Display *dpy, int *ev, int *er, int *maj, - int *min) { -#if LIBVNCSERVER_HAVE_XTEST - return XTestQueryExtension(dpy, ev, er, maj, min); -#else - return False; -#endif -} - -void XTestDiscard_wr(Display *dpy) { - if (! xtest_present) { - return; - } -#if LIBVNCSERVER_HAVE_XTEST - XTestDiscard(dpy); -#endif -} - -Bool XETrapQueryExtension_wr(Display *dpy, int *ev, int *er, int *op) { -#if LIBVNCSERVER_HAVE_LIBXTRAP - return XETrapQueryExtension(dpy, (INT32 *)ev, (INT32 *)er, - (INT32 *)op); -#else - /* unused vars warning: */ - if (dpy || ev || er || op) {} - return False; -#endif -} - -int XTestGrabControl_wr(Display *dpy, Bool impervious) { - if (! xtest_present) { - return 0; - } -#if LIBVNCSERVER_HAVE_XTEST && LIBVNCSERVER_HAVE_XTESTGRABCONTROL - XTestGrabControl(dpy, impervious); - return 1; -#else - return 0; -#endif -} - -int XTRAP_GrabControl_wr(Display *dpy, Bool impervious) { - if (! xtrap_present) { - /* unused vars warning: */ - if (dpy || impervious) {} - return 0; - } -#if LIBVNCSERVER_HAVE_LIBXTRAP - else { - ReqFlags requests; - - if (! impervious) { - if (trap_ctx) { - XEFreeTC(trap_ctx); - } - trap_ctx = NULL; - return 1; - } - - if (! trap_ctx) { - trap_ctx = XECreateTC(dpy, 0, NULL); - if (! trap_ctx) { - rfbLog("DEC-XTRAP XECreateTC failed. Watch " - "out for XGrabServer from wm's\n"); - return 0; - } - XEStartTrapRequest(trap_ctx); - memset(requests, 0, sizeof(requests)); - BitTrue(requests, X_GrabServer); - BitTrue(requests, X_UngrabServer); - XETrapSetRequests(trap_ctx, True, requests); - XETrapSetGrabServer(trap_ctx, True); - } - return 1; - } -#endif - return 0; -} - -void disable_grabserver(Display *in_dpy, int change) { - int ok = 0; - static int didmsg = 0; - - if (debug_grabs) { - fprintf(stderr, "disable_grabserver/%d %.5f\n", - xserver_grabbed, dnowx()); - didmsg = 0; - } - - if (! xtrap_input) { - if (XTestGrabControl_wr(in_dpy, True)) { - if (change) { - XTRAP_GrabControl_wr(in_dpy, False); - } - if (! didmsg) { - rfbLog("GrabServer control via XTEST.\n"); - didmsg = 1; - } - ok = 1; - } else { - if (XTRAP_GrabControl_wr(in_dpy, True)) { - ok = 1; - if (! didmsg) { - rfbLog("Using DEC-XTRAP for protection" - " from XGrabServer.\n"); - didmsg = 1; - } - } - } - } else { - if (XTRAP_GrabControl_wr(in_dpy, True)) { - if (change) { - XTestGrabControl_wr(in_dpy, False); - } - if (! didmsg) { - rfbLog("GrabServer control via DEC-XTRAP.\n"); - didmsg = 1; - } - ok = 1; - } else { - if (XTestGrabControl_wr(in_dpy, True)) { - ok = 1; - if (! didmsg) { - rfbLog("DEC-XTRAP XGrabServer " - "protection not available, " - "using XTEST.\n"); - didmsg = 1; - } - } - } - } - if (! ok && ! didmsg) { - rfbLog("No XTEST or DEC-XTRAP protection from XGrabServer.\n"); - rfbLog("Deadlock if your window manager calls XGrabServer!!\n"); - } - XFlush(in_dpy); -} - -Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min) { -#if LIBVNCSERVER_HAVE_RECORD - return XRecordQueryVersion(dpy, maj, min); -#else - return False; -#endif -} - -#if LIBVNCSERVER_HAVE_RECORD -XRecordRange *rr_CA = NULL; -XRecordRange *rr_CW = NULL; -XRecordRange *rr_GS = NULL; -XRecordRange *rr_scroll[10]; -XRecordContext rc_scroll; -XRecordClientSpec rcs_scroll; -XRecordRange *rr_grab[10]; -XRecordContext rc_grab; -XRecordClientSpec rcs_grab; - -void record_grab(XPointer, XRecordInterceptData *); -#endif - -int xrecording = 0; -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; -KeySym xrecord_keysym = NoSymbol; -#define NAMEINFO 2048 -char xrecord_name_info[NAMEINFO]; - -char *xerror_string(XErrorEvent *error); -int trap_record_xerror(Display *, XErrorEvent *); -int trapped_record_xerror; -XErrorEvent *trapped_record_xerror_event; - -void xrecord_grabserver(int start) { - XErrorHandler old_handler = NULL; - int rc; - - if (debug_grabs) { - fprintf(stderr, "xrecord_grabserver%d/%d %.5f\n", - xserver_grabbed, start, dnowx()); - } - - if (! gdpy_ctrl || ! gdpy_data) { - return; - } -#if LIBVNCSERVER_HAVE_RECORD - if (!start) { - if (! rc_grab) { - return; - } - XRecordDisableContext(gdpy_ctrl, rc_grab); - XRecordFreeContext(gdpy_ctrl, rc_grab); - XFlush(gdpy_ctrl); - rc_grab = 0; - return; - } - - xserver_grabbed = 0; - - rr_grab[0] = rr_GS; - rcs_grab = XRecordAllClients; - - rc_grab = XRecordCreateContext(gdpy_ctrl, 0, &rcs_grab, 1, rr_grab, 1); - trapped_record_xerror = 0; - old_handler = XSetErrorHandler(trap_record_xerror); - - XSync(gdpy_ctrl, True); - - if (! rc_grab || trapped_record_xerror) { - XCloseDisplay(gdpy_ctrl); - XCloseDisplay(gdpy_data); - gdpy_ctrl = NULL; - gdpy_data = NULL; - XSetErrorHandler(old_handler); - return; - } - rc = XRecordEnableContextAsync(gdpy_data, rc_grab, record_grab, NULL); - if (!rc || trapped_record_xerror) { - XCloseDisplay(gdpy_ctrl); - XCloseDisplay(gdpy_data); - gdpy_ctrl = NULL; - gdpy_data = NULL; - XSetErrorHandler(old_handler); - return; - } - XSetErrorHandler(old_handler); - XFlush(gdpy_data); -#endif - if (debug_grabs) { - fprintf(stderr, "xrecord_grabserver-done: %.5f\n", dnowx()); - } -} - -void initialize_xrecord(void) { - use_xrecord = 0; - if (! xrecord_present) { - return; - } - if (nofb) { - return; - } - if (noxrecord) { - return; - } -#if LIBVNCSERVER_HAVE_RECORD - - if (rr_CA) XFree(rr_CA); - if (rr_CW) XFree(rr_CW); - if (rr_GS) XFree(rr_GS); - - rr_CA = XRecordAllocRange(); - rr_CW = XRecordAllocRange(); - rr_GS = XRecordAllocRange(); - if (!rr_CA || !rr_CW || !rr_GS) { - return; - } - /* protocol request ranges: */ - rr_CA->core_requests.first = X_CopyArea; - rr_CA->core_requests.last = X_CopyArea; - - rr_CW->core_requests.first = X_ConfigureWindow; - rr_CW->core_requests.last = X_ConfigureWindow; - - rr_GS->core_requests.first = X_GrabServer; - rr_GS->core_requests.last = X_UngrabServer; - - X_LOCK; - /* open a 2nd control connection to DISPLAY: */ - if (rdpy_data) { - XCloseDisplay(rdpy_data); - rdpy_data = NULL; - } - if (rdpy_ctrl) { - XCloseDisplay(rdpy_ctrl); - rdpy_ctrl = NULL; - } - rdpy_ctrl = XOpenDisplay(DisplayString(dpy)); - XSync(dpy, True); - XSync(rdpy_ctrl, True); - /* open datalink connection to DISPLAY: */ - rdpy_data = XOpenDisplay(DisplayString(dpy)); - if (!rdpy_ctrl || ! rdpy_data) { - X_UNLOCK; - return; - } - disable_grabserver(rdpy_ctrl, 0); - disable_grabserver(rdpy_data, 0); - - use_xrecord = 1; - - /* - * now set up the GrabServer watcher. We get GrabServer - * deadlock in XRecordCreateContext() even with XTestGrabServer - * in place, why? Not sure, so we manually watch for grabs... - */ - if (gdpy_data) { - XCloseDisplay(gdpy_data); - gdpy_data = NULL; - } - if (gdpy_ctrl) { - XCloseDisplay(gdpy_ctrl); - gdpy_ctrl = NULL; - } - xserver_grabbed = 0; - - gdpy_ctrl = XOpenDisplay(DisplayString(dpy)); - XSync(dpy, True); - XSync(gdpy_ctrl, True); - gdpy_data = XOpenDisplay(DisplayString(dpy)); - if (gdpy_ctrl && gdpy_data) { - disable_grabserver(gdpy_ctrl, 0); - disable_grabserver(gdpy_data, 0); - xrecord_grabserver(1); - } - X_UNLOCK; -#endif -} - -void shutdown_xrecord(void) { -#if LIBVNCSERVER_HAVE_RECORD - - if (debug_grabs) { - fprintf(stderr, "shutdown_xrecord%d %.5f\n", - xserver_grabbed, dnowx()); - } - - if (rr_CA) XFree(rr_CA); - if (rr_CW) XFree(rr_CW); - if (rr_GS) XFree(rr_GS); - - rr_CA = NULL; - rr_CW = NULL; - rr_GS = NULL; - - X_LOCK; - if (rdpy_ctrl && rc_scroll) { - XRecordDisableContext(rdpy_ctrl, rc_scroll); - XRecordFreeContext(rdpy_ctrl, rc_scroll); - XSync(rdpy_ctrl, False); - rc_scroll = 0; - } - - if (gdpy_ctrl && rc_grab) { - XRecordDisableContext(gdpy_ctrl, rc_grab); - XRecordFreeContext(gdpy_ctrl, rc_grab); - XSync(gdpy_ctrl, False); - rc_grab = 0; - } - - if (rdpy_data) { - XCloseDisplay(rdpy_data); - rdpy_data = NULL; - } - if (rdpy_ctrl) { - XCloseDisplay(rdpy_ctrl); - rdpy_ctrl = NULL; - } - if (gdpy_data) { - XCloseDisplay(gdpy_data); - gdpy_data = NULL; - } - if (gdpy_ctrl) { - XCloseDisplay(gdpy_ctrl); - gdpy_ctrl = NULL; - } - xserver_grabbed = 0; - X_UNLOCK; -#endif - use_xrecord = 0; - - if (debug_grabs) { - fprintf(stderr, "shutdown_xrecord-done: %.5f\n", dnowx()); - } -} - -int xrecord_skip_keysym(rfbKeySym keysym) { - KeySym sym = (KeySym) keysym; - int ok = -1, matched = 0; - - 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; - } - - /* apply various heuristics: */ - - if (IsModifierKey(sym)) { - /* Shift, Control, etc, usu. generate no scrolls */ - return 1; - } - if (sym == XK_space && scroll_term) { - /* space in a terminal is usu. full page... */ - Window win; - static Window prev_top = None; - int size = 256; - static char name[256]; - - X_LOCK; - win = query_pointer(rootwin); - X_UNLOCK; - if (win != None && win != rootwin) { - if (prev_top != None && win == prev_top) { - ; /* use cached result */ - } else { - prev_top = win; - X_LOCK; - win = descend_pointer(6, win, name, size); - X_UNLOCK; - } - if (match_str_list(name, scroll_term)) { - return 1; - } - } - } - - /* TBD use typing_rate() so */ - return 0; -} - -int xrecord_skip_button(int new, int old) { - /* unused vars warning: */ - if (new || 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 */ - - if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_Linefeed) { - return 1; /* Enter */ - } - if (sym==XK_Up || sym==XK_KP_Up || sym==XK_Down || sym==XK_KP_Down) { - return 1; /* U/D arrows */ - } - if (sym == XK_Left || sym == XK_KP_Left || sym == XK_Right || - sym == XK_KP_Right) { - return 1; /* L/R arrows */ - } - if (xrecord_vi_scroll_keysym(keysym)) { - return 1; - } - 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 now, 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(&now); - for (i=0; i < SCR_ATTR_CACHE; i++) { - - cwin = scr_attr_cache[i].win; - t = scr_attr_cache[i].time; - - if (now > 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; - double t; - int win_x, win_y, win_w, win_h; - int new_x, new_y, new_w, new_h; -} scroll_event_t; - -#define SCR_EV_MAX 128 -scroll_event_t scr_ev[SCR_EV_MAX]; -int scr_ev_cnt = 0; - -XID xrecord_seq = 0; -double xrecord_start = 0.0; - -#if LIBVNCSERVER_HAVE_RECORD -void record_CA(XPointer ptr, XRecordInterceptData *rec_data) { - xCopyAreaReq *req; - Window src = None, dst = None, c; - XWindowAttributes attr; - int src_x, src_y, dst_x, dst_y, rx, ry; - int good = 1, dx, dy, k=0, i; - unsigned int w, h; - int dba = 0, db = debug_scroll; - int cache_index, next_index, valid; - - if (dba || db) { - if (rec_data->category == XRecordFromClient) { - req = (xCopyAreaReq *) rec_data->data; - if (req->reqType == X_CopyArea) { - src = req->srcDrawable; - dst = req->dstDrawable; - } - } - } - -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); - - if (! xrecording) { - return; - } -if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); - - if (rec_data->id_base == 0) { - return; - } -if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); - - if ((XID) ptr != xrecord_seq) { - return; - } -if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); - - if (rec_data->category != XRecordFromClient) { - return; - } -if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); - - req = (xCopyAreaReq *) rec_data->data; - - if (req->reqType != X_CopyArea) { - return; - } -if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); - -/* - -xterm, gnome-terminal, others. - -Note we miss the X_ImageText8 that clears the block cursor. So there is a -short period of time with a painting error: two cursors, one above the other. - - X_ImageText8 - draw: 0x8c00017 nChars: 1, gc: 0x8c00013, x: 101, y: 585, chars=' ' - X_ClearArea - window: 0x8c00018, x: 2, y: 217, w: 10, h: 5 - X_FillPoly - draw: 0x8c00018 gc: 0x8c0000a, shape: 0, coordMode: 0, - X_FillPoly - draw: 0x8c00018 gc: 0x8c0000b, shape: 0, coordMode: 0, - X_CopyArea - src: 0x8c00017, dst: 0x8c00017, gc: 0x8c00013, srcX: 17, srcY: 15, dstX: 17, dstY: 2, w: 480, h: 572 - X_ChangeWindowAttributes - X_ClearArea - window: 0x8c00017, x: 17, y: 574, w: 480, h: 13 - X_ChangeWindowAttributes - - */ - - src = req->srcDrawable; - dst = req->dstDrawable; - src_x = req->srcX; - src_y = req->srcY; - dst_x = req->dstX; - dst_y = req->dstY; - w = req->width; - h = req->height; - - if (w*h < (unsigned int) scrollcopyrect_min_area) { - good = 0; - } else if (!src || !dst) { - good = 0; - } else if (src != dst) { - good = 0; - } else if (scr_ev_cnt >= SCR_EV_MAX) { - good = 0; - } - - dx = dst_x - src_x; - dy = dst_y - src_y; - - if (dx != 0 && dy != 0) { - good = 0; - } - - if (! good) { - return; - } -if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); - - /* - * 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) { - if (!xtranslate(src, rootwin, 0, 0, &rx, &ry, &c, 1)) { - valid = 0; - } - } - 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 > 1) fprintf(stderr, "record_CA-%d\n", k++); - - if (attr.map_state != IsViewable) { - return; - } - - -if (0 || 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; - - scr_ev[i].win = src; - scr_ev[i].frame = None; - scr_ev[i].dx = dx; - scr_ev[i].dy = dy; - scr_ev[i].x = rx + dst_x; - scr_ev[i].y = ry + dst_y; - scr_ev[i].w = w; - scr_ev[i].h = h; - 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; - scr_ev[i].win_h = attr.height; - scr_ev[i].new_x = 0; - scr_ev[i].new_y = 0; - scr_ev[i].new_w = 0; - scr_ev[i].new_h = 0; - - if (dx == 0) { - if (dy > 0) { - scr_ev[i].new_x = rx + src_x; - scr_ev[i].new_y = ry + src_y; - scr_ev[i].new_w = w; - scr_ev[i].new_h = dy; - } else { - scr_ev[i].new_x = rx + src_x; - scr_ev[i].new_y = ry + dst_y + h; - scr_ev[i].new_w = w; - scr_ev[i].new_h = -dy; - } - } else if (dy == 0) { - if (dx > 0) { - scr_ev[i].new_x = rx + src_x; - scr_ev[i].new_y = rx + src_y; - scr_ev[i].new_w = dx; - scr_ev[i].new_h = h; - } else { - scr_ev[i].new_x = rx + dst_x + w; - scr_ev[i].new_y = ry + src_y; - scr_ev[i].new_w = -dx; - scr_ev[i].new_h = h; - } - } - - scr_ev_cnt++; -} - -typedef struct cw_event { - Window win; - int x, y, w, h; -} cw_event_t; - -#define MAX_CW 128 -cw_event_t cw_events[MAX_CW]; - -void record_CW(XPointer ptr, XRecordInterceptData *rec_data) { - xConfigureWindowReq *req; - Window win = None, c; - Window src = None, dst = None; - XWindowAttributes attr; - int absent = 0x100000; - int src_x, src_y, dst_x, dst_y, rx, ry; - int good = 1, dx, dy, k=0, i, j, match, list[3]; - int f_x, f_y, f_w, f_h; - int x, y, w, h; - int x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2; - static int index = 0; - unsigned int vals[4]; - unsigned tmask; - char *data; - int dba = 0, db = debug_scroll; - int cache_index, next_index, valid; - - if (db) { - if (rec_data->category == XRecordFromClient) { - req = (xConfigureWindowReq *) rec_data->data; - if (req->reqType == X_ConfigureWindow) { - src = req->window; - } - } - } - -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); - - - if (! xrecording) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - if ((XID) ptr != xrecord_seq) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - if (rec_data->id_base == 0) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - if (rec_data->category == XRecordStartOfData) { - index = 0; - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - if (rec_data->category != XRecordFromClient) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - if (rec_data->client_swapped) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - req = (xConfigureWindowReq *) rec_data->data; - - if (req->reqType != X_ConfigureWindow) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - tmask = req->mask; - - tmask &= ~CWX; - tmask &= ~CWY; - tmask &= ~CWWidth; - tmask &= ~CWHeight; - - if (tmask) { - /* require no more than these 4 flags */ - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - f_x = req->mask & CWX; - f_y = req->mask & CWY; - f_w = req->mask & CWWidth; - f_h = req->mask & CWHeight; - - if (! f_x || ! f_y) { - if (f_w && f_h) { - ; /* netscape 4.x style */ - } else { - return; - } - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - if ( (f_w && !f_h) || (!f_w && f_h) ) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - for (i=0; i<4; i++) { - vals[i] = 0; - } - - data = (char *)req; - data += sz_xConfigureWindowReq; - - for (i=0; i<req->length; i++) { - unsigned int v; - /* - * We use unsigned int for the values. There were - * some crashes on 64bit machines with unsigned longs. - * Need to check that X protocol sends 32bit values. - */ - v = *( (unsigned int *) data); -if (db > 1) fprintf(stderr, " vals[%d] 0x%x/%d\n", i, v, v); - vals[i] = v; - data += sizeof(unsigned int); - } - - if (index >= MAX_CW) { - int i, j; - - /* FIXME, circular, etc. */ - for (i=0; i<2; i++) { - j = MAX_CW - 2 + i; - cw_events[i].win = cw_events[j].win; - cw_events[i].x = cw_events[j].x; - 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; - } - - if (! f_x && ! f_y) { - /* netscape 4.x style CWWidth,CWHeight */ - vals[2] = vals[0]; - vals[3] = vals[1]; - vals[0] = 0; - vals[1] = 0; - } - - cw_events[index].win = req->window; - - if (! f_x) { - cw_events[index].x = absent; - } else { - cw_events[index].x = (int) vals[0]; - } - if (! f_y) { - cw_events[index].y = absent; - } else { - cw_events[index].y = (int) vals[1]; - } - - if (! f_w) { - cw_events[index].w = absent; - } else { - cw_events[index].w = (int) vals[2]; - } - if (! f_h) { - cw_events[index].h = absent; - } else { - cw_events[index].h = (int) vals[3]; - } - - x = cw_events[index].x; - y = cw_events[index].y; - w = cw_events[index].w; - h = cw_events[index].h; - win = cw_events[index].win; - -if (dba || db) fprintf(stderr, " record_CW ind: %d win: 0x%lx x: %d y: %d w: %d h: %d\n", - index, win, x, y, w, h); - - index++; - - if (index < 3) { - good = 0; - } else if (w != absent && h != absent && - w*h < scrollcopyrect_min_area) { - good = 0; - } - - if (! good) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - match = 0; - for (j=index - 1; j >= 0; j--) { - if (cw_events[j].win == win) { - list[match++] = j; - } - if (match >= 3) { - break; - } - } - - if (match != 3) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - -/* - -Mozilla: - -Up arrow: window moves down a bit (dy > 0): - - X_ConfigureWindow - length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 -18, v2 760, v3 906, v4 327692, v5 48234701, v6 3, - CW-mask: CWX,CWY,CWWidth,CWHeight, - X_ConfigureWindow - length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 0, v2 506636, v3 48234701, v4 48234511, - CW-mask: CWX,CWY, - X_ConfigureWindow - length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 65579, v5 0, v6 108009, - CW-mask: CWX,CWY,CWWidth,CWHeight, - -Down arrow: window moves up a bit (dy < 0): - - X_ConfigureWindow - length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 906, v4 327692, v5 48234701, v6 262147, - CW-mask: CWX,CWY,CWWidth,CWHeight, - X_ConfigureWindow - length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 -18, v2 506636, v3 48234701, v4 48234511, - CW-mask: CWX,CWY, - X_ConfigureWindow - length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 96555, v5 48265642, v6 48265262, - CW-mask: CWX,CWY,CWWidth,CWHeight, - - -Netscape 4.x - -Up arrow: -71.76142 0.01984 X_ConfigureWindow - length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 -15, v2 785, v3 882, v4 327692, v5 159384712, v6 1769484, - CW-mask: CWX,CWY,CWWidth,CWHeight, -71.76153 0.00011 X_ConfigureWindow - length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 867, v2 329228, v3 159384712, v4 159383555, - CW-mask: CWWidth,CWHeight, - XXX,XXX -71.76157 0.00003 X_ConfigureWindow - length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 0, v2 131132, v3 159385313, v4 328759, - CW-mask: CWX,CWY, - XXX,XXX - -Down arrow: -72.93147 0.01990 X_ConfigureWindow - length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 882, v2 328972, v3 159384712, v4 159383555, - CW-mask: CWWidth,CWHeight, - XXX,XXX -72.93156 0.00009 X_ConfigureWindow - length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 -15, v2 458764, v3 159384712, v4 159383567, - CW-mask: CWX,CWY, -72.93160 0.00004 X_ConfigureWindow - length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 0, v2 785, v3 867, v4 131132, v5 159385335, v6 328759, - CW-mask: CWX,CWY,CWWidth,CWHeight, - - -sadly, probably need to handle some more... - - */ - x0 = cw_events[list[2]].x; - y0 = cw_events[list[2]].y; - w0 = cw_events[list[2]].w; - h0 = cw_events[list[2]].h; - - x1 = cw_events[list[1]].x; - y1 = cw_events[list[1]].y; - w1 = cw_events[list[1]].w; - h1 = cw_events[list[1]].h; - - x2 = cw_events[list[0]].x; - y2 = cw_events[list[0]].y; - w2 = cw_events[list[0]].w; - h2 = cw_events[list[0]].h; - - /* see NS4 XXX's above: */ - if (w2 == absent || h2 == absent) { - /* up arrow */ - if (w2 == absent) { - w2 = w1; - } - if (h2 == absent) { - h2 = h1; - } - } - if (x1 == absent || y1 == absent) { - /* up arrow */ - if (x1 == absent) { - x1 = x2; - } - if (y1 == absent) { - y1 = y2; - } - } - if (x0 == absent || y0 == absent) { - /* down arrow */ - if (x0 == absent) { - /* hmmm... what to do */ - x0 = x2; - } - if (y0 == absent) { - y0 = y2; - } - } - -if (dba) fprintf(stderr, "%d/%d/%d/%d %d/%d/%d/%d %d/%d/%d/%d\n", x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2); - - dy = y1 - y0; - dx = x1 - x0; - - src_x = x2; - src_y = y2; - w = w2; - h = h2; - - /* check w and h before we modify them */ - if (w <= 0 || h <= 0) { - good = 0; - } else if (w == absent || h == absent) { - good = 0; - } - if (! good) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - if (dy > 0) { - h -= dy; - } else { - h += dy; - src_y -= dy; - } - if (dx > 0) { - w -= dx; - } else { - w += dx; - src_x -= dx; - } - - dst_x = src_x + dx; - dst_y = src_y + dy; - - if (x0 == absent || x1 == absent || x2 == absent) { - good = 0; - } else if (y0 == absent || y1 == absent || y2 == absent) { - good = 0; - } else if (dx != 0 && dy != 0) { - good = 0; - } else if (w0 - w2 != nabs(dx)) { - 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 > 1) fprintf(stderr, "record_CW-%d\n", k++); - - /* - * 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) { - if (!xtranslate(win, rootwin, 0, 0, &rx, &ry, &c, 1)) { - valid = 0; - } - } - 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 (! valid) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - - if (attr.map_state != IsViewable) { - return; - } -if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); - -if (0 || 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; - - scr_ev[i].win = win; - scr_ev[i].frame = None; - scr_ev[i].dx = dx; - scr_ev[i].dy = dy; - scr_ev[i].x = rx + dst_x; - scr_ev[i].y = ry + dst_y; - scr_ev[i].w = w; - scr_ev[i].h = h; - 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; - scr_ev[i].win_h = attr.height; - scr_ev[i].new_x = 0; - scr_ev[i].new_y = 0; - scr_ev[i].new_w = 0; - scr_ev[i].new_h = 0; - - if (dx == 0) { - if (dy > 0) { - scr_ev[i].new_x = rx + src_x; - scr_ev[i].new_y = ry + src_y; - scr_ev[i].new_w = w; - scr_ev[i].new_h = dy; - } else { - scr_ev[i].new_x = rx + src_x; - scr_ev[i].new_y = ry + dst_y + h; - scr_ev[i].new_w = w; - scr_ev[i].new_h = -dy; - } - } else if (dy == 0) { - if (dx > 0) { - scr_ev[i].new_x = rx + src_x; - scr_ev[i].new_y = rx + src_y; - scr_ev[i].new_w = dx; - scr_ev[i].new_h = h; - } else { - scr_ev[i].new_x = rx + dst_x + w; - scr_ev[i].new_y = ry + src_y; - scr_ev[i].new_w = -dx; - scr_ev[i].new_h = h; - } - } - - /* indicate we have a new one */ - scr_ev_cnt++; - - index = 0; -} - -void record_switch(XPointer ptr, XRecordInterceptData *rec_data) { - static int first = 1; - xReq *req; - - 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); - } else if (rec_data->category == XRecordEndOfData) { - ; - } else if (rec_data->category == XRecordClientStarted) { - ; - } else if (rec_data->category == XRecordClientDied) { - ; - } else if (rec_data->category == XRecordFromServer) { - ; - } - - if (rec_data->category != XRecordFromClient) { - XRecordFreeData(rec_data); - return; - } - - req = (xReq *) rec_data->data; - - if (req->reqType == X_CopyArea) { - record_CA(ptr, rec_data); - } else if (req->reqType == X_ConfigureWindow) { - record_CW(ptr, rec_data); - } else { - ; - } - XRecordFreeData(rec_data); -} - -void record_grab(XPointer ptr, XRecordInterceptData *rec_data) { - xReq *req; - int db = 0; - - if (debug_grabs) db = 1; - - /* should handle control msgs, start/stop/etc */ - if (rec_data->category == XRecordStartOfData) { - ; - } else if (rec_data->category == XRecordEndOfData) { - ; - } else if (rec_data->category == XRecordClientStarted) { - ; - } else if (rec_data->category == XRecordClientDied) { - ; - } else if (rec_data->category == XRecordFromServer) { - ; - } - - if (rec_data->category != XRecordFromClient) { - XRecordFreeData(rec_data); - return; - } - - req = (xReq *) rec_data->data; - - if (req->reqType == X_GrabServer) { - double now = dnow() - x11vnc_start; - xserver_grabbed++; - if (db) rfbLog("X server Grabbed: %d %.5f\n", xserver_grabbed, now); - if (xserver_grabbed > 1) { - /* - * some apps do multiple grabs... very unlikely - * two apps will be doing it at same time. - */ - xserver_grabbed = 1; - } - } else if (req->reqType == X_UngrabServer) { - double now = dnow() - x11vnc_start; - xserver_grabbed--; - if (xserver_grabbed < 0) { - xserver_grabbed = 0; - } - if (db) rfbLog("X server Un-Grabbed: %d %.5f\n", xserver_grabbed, now); - } else { - ; - } - XRecordFreeData(rec_data); - - /* unused vars warning: */ - if (ptr) {} -} -#endif - -void check_xrecord_grabserver(void) { - int last_val, cnt = 0, i, max = 10; - double d; -#if LIBVNCSERVER_HAVE_RECORD - if (!gdpy_ctrl || !gdpy_data) { - return; - } - - dtime0(&d); - XFlush(gdpy_ctrl); - for (i=0; i<max; i++) { - last_val = xserver_grabbed; - XRecordProcessReplies(gdpy_data); - if (xserver_grabbed != last_val) { - cnt++; - } else if (i > 2) { - break; - } - } - if (cnt) { - XFlush(gdpy_ctrl); - } -if (debug_grabs && cnt > 0) { - d = dtime(&d); -fprintf(stderr, "check_xrecord_grabserver: cnt=%d i=%d %.4f\n", cnt, i, d); -} -#endif -} - -#if LIBVNCSERVER_HAVE_RECORD -void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) { - int ret1, ret2; - int verb = (!bequiet && !quiet); - - if (0 || debug_scroll) { - rfbLog("shutdown_record_context(0x%lx, %d, %d)\n", rc, - bequiet, reopen); - verb = 1; - } - - ret1 = XRecordDisableContext(rdpy_ctrl, rc); - if (!ret1 && verb) { - rfbLog("XRecordDisableContext(0x%lx) failed.\n", rc); - } - ret2 = XRecordFreeContext(rdpy_ctrl, rc); - if (!ret2 && verb) { - 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 && gdpy_ctrl) { - check_xrecord_grabserver(); - if (xserver_grabbed) { - rfbLog("shutdown_record_context: skip reopen," - " server grabbed\n"); - reopen = 0; - } - } - if (reopen) { - char *dpystr = DisplayString(dpy); - - if (debug_scroll) { - rfbLog("closing RECORD data connection.\n"); - } - XCloseDisplay(rdpy_data); - rdpy_data = NULL; - - if (debug_scroll) { - rfbLog("closing RECORD control connection.\n"); - } - XCloseDisplay(rdpy_ctrl); - rdpy_ctrl = NULL; - - 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; - } - XSync(dpy, False); - - disable_grabserver(rdpy_ctrl, 0); - XSync(rdpy_ctrl, True); - - rdpy_data = XOpenDisplay(dpystr); - - if (! rdpy_data) { - rfbLog("Failed to reopen RECORD data connection:" - "%s\n", dpystr); - rfbLog(" disabling RECORD scroll detection.\n"); - XCloseDisplay(rdpy_ctrl); - rdpy_ctrl = NULL; - use_xrecord = 0; - return; - } - disable_grabserver(rdpy_data, 0); - - if (debug_scroll || (! bequiet && reopen == 2)) { - rfbLog("reopened RECORD data and control display" - " connections: %s\n", dpystr); - } - } -} -#endif - -void check_xrecord_reset(int force) { - static double last_reset = 0.0; - int reset_time = 60, require_idle = 10; - int reset_time2 = 600, require_idle2 = 40; - double now; - XErrorHandler old_handler = NULL; - - if (gdpy_ctrl) { - X_LOCK; - check_xrecord_grabserver(); - X_UNLOCK; - } else { - /* more dicey if not watching grabserver */ - reset_time = reset_time2; - require_idle = require_idle2; - } - - if (!use_xrecord) { - return; - } - if (xrecording) { - return; - } - if (button_mask) { - return; - } - if (xserver_grabbed) { - return; - } - -#if LIBVNCSERVER_HAVE_RECORD - if (! rc_scroll) { - return; - } - now = dnow(); - if (last_reset == 0.0) { - last_reset = now; - return; - } - /* - * try to wait for a break in input to reopen the displays - * this is only to avoid XGrabServer deadlock on the repopens. - */ - if (force) { - ; - } else if (now < last_reset + reset_time) { - return; - } else if (now < last_pointer_click_time + require_idle) { - return; - } else if (now < last_keyboard_time + require_idle) { - return; - } - X_LOCK; - trapped_record_xerror = 0; - old_handler = XSetErrorHandler(trap_record_xerror); - - /* unlikely, but check again since we will definitely be doing it. */ - if (gdpy_ctrl) { - check_xrecord_grabserver(); - if (xserver_grabbed) { - XSetErrorHandler(old_handler); - X_UNLOCK; - return; - } - } - - shutdown_record_context(rc_scroll, 0, 1); - rc_scroll = 0; - - XSetErrorHandler(old_handler); - X_UNLOCK; - - last_reset = now; -#endif -} - -#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, c, clast; - static double create_time = 0.0; - double now; - static double last_error = 0.0; - int rc, db = debug_scroll; - int do_shutdown = 0; - int reopen_dpys = 1; - XErrorHandler old_handler = NULL; - static Window last_win = None, last_result = None; - -if (0) db = 1; - - if (nofb) { - xrecording = 0; - return; - } - if (use_threads) { - /* XXX not working */ - use_xrecord = 0; - xrecording = 0; - return; - } - - dtime0(&now); - if (now < last_error + 0.5) { - return; - } - - if (gdpy_ctrl) { - X_LOCK; - check_xrecord_grabserver(); - X_UNLOCK; - if (xserver_grabbed) { -if (db || debug_grabs) fprintf(stderr, "xrecord_watch: %d/%d out xserver_grabbed\n", start, setby); - return; - } - } - -#if LIBVNCSERVER_HAVE_RECORD - if (! start) { - int shut_reopen = 2, shut_time = 25; -if (db || debug_grabs) fprintf(stderr, "XRECORD OFF: %d/%d %.4f\n", xrecording, setby, now - x11vnc_start); - xrecording = 0; - if (! rc_scroll) { - xrecord_focus_window = None; - xrecord_wm_window = None; - xrecord_ptr_window = None; - xrecord_keysym = NoSymbol; - rcs_scroll = 0; - return; - } - - if (! do_shutdown && now > create_time + shut_time) { - /* XXX unstable if we keep a RECORD going forever */ - do_shutdown = 1; - } - - SCR_LOCK; - - if (do_shutdown) { -if (db > 1) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll); - X_LOCK; - trapped_record_xerror = 0; - old_handler = XSetErrorHandler(trap_record_xerror); - - shutdown_record_context(rc_scroll, 0, shut_reopen); - rc_scroll = 0; - - /* - * n.b. there is a grabserver issue wrt - * XRecordCreateContext() even though rdpy_ctrl - * is set imprevious to grabs. Perhaps a bug - * in the X server or library... - * - * If there are further problems, a thought - * to recreate rc_scroll right after the - * reopen. - */ - - if (! use_xrecord) { - XSetErrorHandler(old_handler); - X_UNLOCK; - SCR_UNLOCK; - return; - } - - XRecordProcessReplies(rdpy_data); - - if (trapped_record_xerror) { - RECORD_ERROR_MSG; - last_error = now; - } - - XSetErrorHandler(old_handler); - X_UNLOCK; - SCR_UNLOCK; - - } else { - if (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); - rc_scroll = 0; - - last_error = now; - - if (! use_xrecord) { - XSetErrorHandler(old_handler); - X_UNLOCK; - SCR_UNLOCK; - return; - } - } - XSetErrorHandler(old_handler); - X_UNLOCK; - } - } - - SCR_UNLOCK; - /* - * XXX if we do a XFlush(rdpy_ctrl) here we get: - * - - X Error of failed request: XRecordBadContext - Major opcode of failed request: 145 (RECORD) - Minor opcode of failed request: 5 (XRecordEnableContext) - Context in failed request: 0x2200013 - Serial number of failed request: 29 - Current serial number in output stream: 29 - - * - * need to figure out what is going on... since it may lead - * infrequent failures. - */ - xrecord_focus_window = None; - xrecord_wm_window = None; - xrecord_ptr_window = None; - xrecord_keysym = NoSymbol; - rcs_scroll = 0; - return; - } -if (db || debug_grabs) fprintf(stderr, "XRECORD ON: %d/%d %.4f\n", xrecording, setby, now - x11vnc_start); - - if (xrecording) { - return; - } - - if (do_shutdown && rc_scroll) { - static int didmsg = 0; - /* should not happen... */ - if (0 || !didmsg) { - rfbLog("warning: do_shutdown && rc_scroll\n"); - didmsg = 1; - } - xrecord_watch(0, SCR_NONE); - } - - xrecording = 0; - xrecord_focus_window = None; - xrecord_wm_window = None; - xrecord_ptr_window = None; - xrecord_keysym = NoSymbol; - xrecord_set_by_keys = 0; - xrecord_set_by_mouse = 0; - - /* get the window with focus and mouse pointer: */ - clast = None; - focus = None; - wm = None; - - X_LOCK; - SCR_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 - - wm = query_pointer(rootwin); - if (wm) { - c = wm; - } else { - c = rootwin; - } - - /* 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. */ - xrecord_name_info[0] = '\0'; - clast = descend_pointer(6, c, NULL, 0); - } else { - char *nm; - int matched_good = 0, matched_skip = 0; - - clast = descend_pointer(6, c, xrecord_name_info, NAMEINFO); -if (db) fprintf(stderr, "name_info: %s\n", xrecord_name_info); - - nm = xrecord_name_info; - - if (scroll_good_all) { - matched_good += match_str_list(nm, scroll_good_all); - } - 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; - } - } - 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"); - X_UNLOCK; - SCR_UNLOCK; - return; - } - - /* set protocol request ranges: */ - 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 */ - - if (gdpy_ctrl) { - /* - * Even though rdpy_ctrl is impervious to grabs - * at this point, we still get deadlock, why? - * It blocks in the library find_display() call. - */ - check_xrecord_grabserver(); - if (xserver_grabbed) { - XSetErrorHandler(old_handler); - X_UNLOCK; - SCR_UNLOCK; - return; - } - } - rcs_scroll = (XRecordClientSpec) clast; - rc_scroll = XRecordCreateContext(rdpy_ctrl, 0, &rcs_scroll, 1, - rr_scroll, 2); - - if (! do_shutdown) { - XSync(rdpy_ctrl, False); - } -if (db) fprintf(stderr, "NEW rc: 0x%lx\n", rc_scroll); - if (rc_scroll) { - dtime0(&create_time); - } else { - rcs_scroll = 0; - } - - } else if (! do_shutdown) { - if (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 > 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)) { - if (1 || now > last_error + 60) { - rfbLog("failed to register client 0x%lx with" - " X RECORD context rc_scroll.\n", clast); - } - last_error = now; - 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; - SCR_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); - rc_scroll = 0; - last_error = now; - - XSetErrorHandler(old_handler); - X_UNLOCK; - SCR_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++; - dtime0(&xrecord_start); - - rc = XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch, - (XPointer) xrecord_seq); - - if (!rc || trapped_record_xerror) { - if (1 || now > last_error + 60) { - rfbLog("failed to enable RECORD context " - "rc_scroll: 0x%lx rc: %d\n", rc_scroll, rc); - if (trapped_record_xerror) { - RECORD_ERROR_MSG; - } - } - shutdown_record_context(rc_scroll, 0, reopen_dpys); - rc_scroll = 0; - last_error = now; - xrecording = 0; - /* continue on for now... */ - } - XSetErrorHandler(old_handler); - - /* XXX this may cause more problems than it solves... */ - if (use_xrecord) { - XFlush(rdpy_data); - } - - X_UNLOCK; - SCR_UNLOCK; -#endif -} - -/* -- cleanup.c -- */ -/* - * Exiting and error handling routines - */ - -static int exit_flag = 0; -int exit_sig = 0; - -void clean_shm(int quick) { - int i, cnt = 0; - - if (raw_fb) quick = 1; /* raw_fb hack */ - - /* - * to avoid deadlock, etc, under quick=1 we just delete the shm - * areas and leave the X stuff hanging. - */ - if (quick) { - shm_delete(&scanline_shm); - shm_delete(&fullscreen_shm); - shm_delete(&snaprect_shm); - } else { - shm_clean(&scanline_shm, scanline); - shm_clean(&fullscreen_shm, fullscreen); - shm_clean(&snaprect_shm, snaprect); - } - - /* - * Here we have to clean up quite a few shm areas for all - * the possible tile row runs (40 for 1280), not as robust - * as one might like... sometimes need to run ipcrm(1). - */ - for(i=1; i<=ntiles_x; i++) { - if (i > tile_shm_count) { - break; - } - if (quick) { - shm_delete(&tile_row_shm[i]); - } else { - shm_clean(&tile_row_shm[i], tile_row[i]); - } - cnt++; - if (single_copytile_count && i >= single_copytile_count) { - break; - } - } - if (!quiet) { - rfbLog("deleted %d tile_row polling images.\n", cnt); - } -} - -void clean_icon_mode(void) { - if (icon_mode && icon_mode_fh) { - fprintf(icon_mode_fh, "quit\n"); - fflush(icon_mode_fh); - fclose(icon_mode_fh); - icon_mode_fh = NULL; - if (icon_mode_file) { - unlink(icon_mode_file); - icon_mode_file = NULL; - } - } -} - -/* - * Normal exiting - */ -void clean_up_exit (int ret) { - exit_flag = 1; - - if (icon_mode) { - clean_icon_mode(); - } - - /* remove the shm areas: */ - clean_shm(0); - - if (! dpy) exit(ret); /* raw_rb hack */ - - /* X keyboard cleanups */ - delete_added_keycodes(0); - - if (clear_mods == 1) { - clear_modifiers(0); - } else if (clear_mods == 2) { - clear_keys(); - } - - if (no_autorepeat) { - autorepeat(1, 0); - } - if (use_solid_bg) { - solid_bg(1); - } - X_LOCK; - XTestDiscard_wr(dpy); -#if LIBVNCSERVER_HAVE_LIBXDAMAGE - if (xdamage) { - XDamageDestroy(dpy, xdamage); - } -#endif -#if LIBVNCSERVER_HAVE_LIBXTRAP - if (trap_ctx) { - XEFreeTC(trap_ctx); - } -#endif - /* XXX rdpy_ctrl, etc. cannot close w/o blocking */ - XCloseDisplay(dpy); - X_UNLOCK; - - fflush(stderr); - exit(ret); -} - -/* X11 error handlers */ - -static XErrorHandler Xerror_def; -static XIOErrorHandler XIOerr_def; -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; - trapped_xerror_event = error; - - if (d) {} /* unused vars warning: */ - - return 0; -} - -int trap_xioerror(Display *d) { - trapped_xioerror = 1; - - if (d) {} /* unused vars warning: */ - - return 0; -} - -int trap_getimage_xerror(Display *d, XErrorEvent *error) { - trapped_getimage_xerror = 1; - trapped_xerror_event = error; - - if (d) {} /* unused vars warning: */ - - return 0; -} - -int trap_record_xerror(Display *d, XErrorEvent *error) { - trapped_record_xerror = 1; - trapped_record_xerror_event = error; - - if (d) {} /* unused vars warning: */ - - return 0; -} - -void interrupted(int); - -static int Xerror(Display *d, XErrorEvent *error) { - X_UNLOCK; - interrupted(0); - - if (d) {} /* unused vars warning: */ - - return (*Xerror_def)(d, error); -} - -static int XIOerr(Display *d) { - X_UNLOCK; - interrupted(-1); - - if (d) {} /* unused vars warning: */ - - return (*XIOerr_def)(d); -} - -char *xerrors[] = { - "Success", - "BadRequest", - "BadValue", - "BadWindow", - "BadPixmap", - "BadAtom", - "BadCursor", - "BadFont", - "BadMatch", - "BadDrawable", - "BadAccess", - "BadAlloc", - "BadColor", - "BadGC", - "BadIDChoice", - "BadName", - "BadLength", - "BadImplementation", - "unknown" -}; -int xerrors_max = BadImplementation; - -char *xerror_string(XErrorEvent *error) { - int index = -1; - if (error) { - index = (int) error->error_code; - } - if (0 <= index && index <= xerrors_max) { - return xerrors[index]; - } else { - return xerrors[xerrors_max+1]; - } -} - -char *crash_stack_command1 = NULL; -char *crash_stack_command2 = NULL; -char *crash_debug_command = NULL; -/* XXX CHECK BEFORE RELEASE */ -int crash_debug = 0; - -void initialize_crash_handler(void) { - int pid = program_pid; - crash_stack_command1 = (char *) malloc(1000); - crash_stack_command2 = (char *) malloc(1000); - crash_debug_command = (char *) 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 - */ -void interrupted (int sig) { - exit_sig = sig; - if (exit_flag) { - exit_flag++; - if (use_threads) { - usleep2(250 * 1000); - } else if (exit_flag <= 2) { - return; - } - exit(4); - } - exit_flag++; - if (sig == 0) { - fprintf(stderr, "caught X11 error:\n"); - } else if (sig == -1) { - fprintf(stderr, "caught XIO error:\n"); - } else { - fprintf(stderr, "caught signal: %d\n", sig); - } - if (sig == SIGINT) { - shut_down = 1; - return; - } - - X_UNLOCK; - - if (icon_mode) { - clean_icon_mode(); - } - /* remove the shm areas with quick=1: */ - clean_shm(1); - - if (sig == -1) { - /* not worth trying any more cleanup, X server probably gone */ - exit(3); - } - - /* X keyboard cleanups */ - delete_added_keycodes(0); - - if (clear_mods == 1) { - clear_modifiers(0); - } else if (clear_mods == 2) { - clear_keys(); - } - if (no_autorepeat) { - autorepeat(1, 0); - } - if (use_solid_bg) { - solid_bg(1); - } - - if (crash_debug) { - crash_shell(); - } - - if (sig) { - exit(2); - } -} - -Window parent_window(Window win, char **name) { - Window r, parent; - Window *list; - unsigned int nchild; - - if (name != NULL) { - *name = NULL; - } - - if (! XQueryTree(dpy, win, &r, &parent, &list, &nchild)) { - return None; - } - if (list) { - XFree(list); - } - if (parent && name) { - XFetchName(dpy, parent, name); - } - return parent; -} - -/* trapping utility to check for a valid window: */ -int valid_window(Window win, XWindowAttributes *attr_ret, int bequiet) { - XErrorHandler old_handler; - XWindowAttributes attr, *pattr; - int ok = 0; - - if (attr_ret == NULL) { - pattr = &attr; - } else { - pattr = attr_ret; - } - - if (win == None) { - return 0; - } - - trapped_xerror = 0; - old_handler = XSetErrorHandler(trap_xerror); - if (XGetWindowAttributes(dpy, win, pattr)) { - ok = 1; - } - 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; - - return ok; -} - -Bool xtranslate(Window src, Window dst, int src_x, int src_y, int *dst_x, - int *dst_y, Window *child, int bequiet) { - XErrorHandler old_handler; - Bool ok = False; - - trapped_xerror = 0; - old_handler = XSetErrorHandler(trap_xerror); - if (XTranslateCoordinates(dpy, src, dst, src_x, src_y, dst_x, - dst_y, child)) { - ok = True; - } - if (trapped_xerror && trapped_xerror_event) { - if (! quiet && ! bequiet) { - rfbLog("xtranslate: trapped XError: %s (0x%lx)\n", - xerror_string(trapped_xerror_event), src); - } - ok = False; - } - XSetErrorHandler(old_handler); - trapped_xerror = 0; - - return ok; -} - -int wait_until_mapped(Window win) { - int ms = 50, waittime = 30; - time_t start = time(0); - XWindowAttributes attr; - - while (1) { - if (! valid_window(win, NULL, 0)) { - if (time(0) > start + waittime) { - break; - } - usleep(ms * 1000); - continue; - } - if (! XGetWindowAttributes(dpy, win, &attr)) { - return 0; - } - if (attr.map_state == IsViewable) { - return 1; - } - usleep(ms * 1000); - } - return 0; -} - -int get_window_size(Window win, int *x, int *y) { - XWindowAttributes attr; - /* valid_window? */ - if (valid_window(win, &attr, 1)) { - *x = attr.width; - *y = attr.height; - return 1; - } else { - return 0; - } -} - -/* signal handlers */ -void initialize_signals(void) { - signal(SIGHUP, interrupted); - signal(SIGINT, interrupted); - signal(SIGQUIT, interrupted); - signal(SIGABRT, interrupted); - signal(SIGTERM, interrupted); - signal(SIGBUS, interrupted); - signal(SIGSEGV, interrupted); - signal(SIGFPE, interrupted); - - if (!sigpipe || *sigpipe == '\0' || !strcmp(sigpipe, "skip")) { - ; - } else if (!strcmp(sigpipe, "ignore")) { -#ifdef SIG_IGN - signal(SIGPIPE, SIG_IGN); -#endif - } else if (!strcmp(sigpipe, "exit")) { - rfbLog("initialize_signals: will exit on SIGPIPE\n"); - signal(SIGPIPE, interrupted); - } - - X_LOCK; - Xerror_def = XSetErrorHandler(Xerror); - XIOerr_def = XSetIOErrorHandler(XIOerr); - X_UNLOCK; -} - -/* -- connections.c -- */ -/* - * routines for handling incoming, outgoing, etc connections - */ - -/* - * check that all clients are in RFB_NORMAL state - */ -int all_clients_initialized(void) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int ok = 1; - - if (! screen) { - return ok; - } - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->state != RFB_NORMAL) { - ok = 0; - break; - } - } - rfbReleaseClientIterator(iter); - - return ok; -} - -char *list_clients(void) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - char *list, tmp[32]; - int count = 0; - - if (!screen) { - return strdup(""); - } - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - count++; - } - rfbReleaseClientIterator(iter); - - /* - * each client: - * <id>:<ip>:<port>:<user>:<hostname>:<input>:<loginview>, - * 8+1+16+1+5+1+24+1+256+1+5+1+1+1 - * 123.123.123.123:60000/0x11111111-rw, - * so count+1 * 400 must cover it. - */ - list = (char *) malloc((count+1)*400); - - list[0] = '\0'; - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - ClientData *cd = (ClientData *) cl->clientData; - if (*list != '\0') { - strcat(list, ","); - } - sprintf(tmp, "0x%x:", cd->uid); - strcat(list, tmp); - strcat(list, cl->host); - strcat(list, ":"); - sprintf(tmp, "%d:", cd->client_port); - strcat(list, tmp); - if (*(cd->username) == '\0') { - char *s = ident_username(cl); - if (s) free(s); - } - strcat(list, cd->username); - strcat(list, ":"); - strcat(list, cd->hostname); - strcat(list, ":"); - strcat(list, cd->input); - strcat(list, ":"); - sprintf(tmp, "%d", cd->login_viewonly); - strcat(list, tmp); - } - rfbReleaseClientIterator(iter); - return list; -} - -/* count number of clients supporting NewFBSize */ -int new_fb_size_clients(rfbScreenInfoPtr s) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int count = 0; - - if (! s) { - return 0; - } - - iter = rfbGetClientIterator(s); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->useNewFBSize) { - count++; - } - } - rfbReleaseClientIterator(iter); - return count; -} - -void close_all_clients(void) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - - if (! screen) { - return; - } - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - rfbCloseClient(cl); - rfbClientConnectionGone(cl); - } - rfbReleaseClientIterator(iter); -} - -rfbClientPtr *client_match(char *str) { - rfbClientIteratorPtr iter; - rfbClientPtr cl, *cl_list; - int i, n, host_warn = 0, hex_warn = 0; - - n = client_count + 10; - cl_list = (rfbClientPtr *) malloc(n * sizeof(rfbClientPtr)); - - i = 0; - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (strstr(str, "0x") == str) { - unsigned int in; - int id; - ClientData *cd = (ClientData *) cl->clientData; - if (sscanf(str, "0x%x", &in) != 1) { - if (hex_warn++) { - continue; - } - rfbLog("skipping invalid client hex id: %s\n", - str); - continue; - } - id = (unsigned int) in; - if (cd->uid == id) { - cl_list[i++] = cl; - } - } else { - char *rstr = str; - if (! dotted_ip(str)) { - rstr = host2ip(str); - if (rstr == NULL || *rstr == '\0') { - if (host_warn++) { - continue; - } - rfbLog("skipping bad lookup: \"%s\"\n", - str); - continue; - } - rfbLog("lookup: %s -> %s\n", str, rstr); - } - if (!strcmp(rstr, cl->host)) { - cl_list[i++] = cl; - } - if (rstr != str) { - free(rstr); - } - } - if (i >= n - 1) { - break; - } - } - rfbReleaseClientIterator(iter); - - cl_list[i] = NULL; - - return cl_list; -} - -void close_clients(char *str) { - rfbClientPtr *cl_list, *cp; - - if (!strcmp(str, "all") || !strcmp(str, "*")) { - close_all_clients(); - return; - } - - if (! screen) { - return; - } - - cl_list = client_match(str); - - cp = cl_list; - while (*cp) { - rfbCloseClient(*cp); - rfbClientConnectionGone(*cp); - cp++; - } - free(cl_list); -} - -void set_client_input(char *str) { - rfbClientPtr *cl_list, *cp; - char *p, *val; - - /* str is "match:value" */ - - if (! screen) { - return; - } - - p = strchr(str, ':'); - if (! p) { - return; - } - *p = '\0'; - p++; - val = short_kmb(p); - - cl_list = client_match(str); - - cp = cl_list; - while (*cp) { - ClientData *cd = (ClientData *) (*cp)->clientData; - cd->input[0] = '\0'; - strcat(cd->input, "_"); - strcat(cd->input, val); - cp++; - } - - free(val); - free(cl_list); -} - -void set_child_info(void) { - char pid[16]; - /* set up useful environment for child process */ - sprintf(pid, "%d", (int) getpid()); - set_env("X11VNC_PID", pid); - if (program_name) { - /* e.g. for remote control -R */ - set_env("X11VNC_PROG", program_name); - } - if (program_cmdline) { - set_env("X11VNC_CMDLINE", program_cmdline); - } - if (raw_fb_str) { - set_env("X11VNC_RAWFB_STR", raw_fb_str); - } else { - set_env("X11VNC_RAWFB_STR", ""); - } -} - -/* - * utility to run a user supplied command setting some RFB_ env vars. - * used by, e.g., accept_client() and client_gone() - */ -static int run_user_command(char *cmd, rfbClientPtr client, char *mode) { - char *old_display = NULL; - char *addr = client->host; - char str[100]; - int rc; - ClientData *cd = (ClientData *) client->clientData; - - if (addr == NULL || addr[0] == '\0') { - addr = "unknown-host"; - } - - /* set RFB_CLIENT_ID to semi unique id for command to use */ - if (cd && cd->uid) { - sprintf(str, "0x%x", cd->uid); - } else { - /* not accepted yet: */ - sprintf(str, "0x%x", clients_served); - } - set_env("RFB_CLIENT_ID", str); - - /* set RFB_CLIENT_IP to IP addr for command to use */ - set_env("RFB_CLIENT_IP", addr); - - /* set RFB_X11VNC_PID to our pid for command to use */ - sprintf(str, "%d", (int) getpid()); - set_env("RFB_X11VNC_PID", str); - - /* set RFB_CLIENT_PORT to peer port for command to use */ - if (cd && cd->client_port > 0) { - sprintf(str, "%d", cd->client_port); - } else { - sprintf(str, "%d", get_remote_port(client->sock)); - } - set_env("RFB_CLIENT_PORT", str); - - set_env("RFB_MODE", mode); - - /* - * now do RFB_SERVER_IP and RFB_SERVER_PORT (i.e. us!) - * This will establish a 5-tuple (including tcp) the external - * program can potentially use to work out the virtual circuit - * for this connection. - */ - if (cd && cd->server_ip) { - set_env("RFB_SERVER_IP", cd->server_ip); - } else { - char *sip = get_local_host(client->sock); - set_env("RFB_SERVER_IP", sip); - if (sip) free(sip); - } - - if (cd && cd->server_port > 0) { - sprintf(str, "%d", cd->server_port); - } else { - sprintf(str, "%d", get_local_port(client->sock)); - } - set_env("RFB_SERVER_PORT", str); - - /* - * Better set DISPLAY to the one we are polling, if they - * want something trickier, they can handle on their own - * via environment, etc. - */ - if (getenv("DISPLAY")) { - old_display = strdup(getenv("DISPLAY")); - } - - if (raw_fb && ! dpy) { /* raw_fb hack */ - set_env("DISPLAY", "rawfb"); - } else { - set_env("DISPLAY", DisplayString(dpy)); - } - - /* - * work out the number of clients (have to use client_count - * since there is deadlock in rfbGetClientIterator) - */ - sprintf(str, "%d", client_count); - set_env("RFB_CLIENT_COUNT", str); - - if (no_external_cmds) { - rfbLogEnable(1); - rfbLog("cannot run external commands in -nocmds mode:\n"); - rfbLog(" \"%s\"\n", cmd); - rfbLog(" exiting.\n"); - clean_up_exit(1); - } - rfbLog("running command:\n"); - rfbLog(" %s\n", cmd); - - /* XXX need to close port 5900, etc.. */ - rc = system(cmd); - - if (rc >= 256) { - rc = rc/256; - } - rfbLog("command returned: %d\n", rc); - - if (old_display) { - set_env("DISPLAY", old_display); - free(old_display); - } - - return rc; -} - -/* - * callback for when a client disconnects - */ -static void client_gone(rfbClientPtr client) { - - client_count--; - if (client_count < 0) client_count = 0; - - speeds_net_rate_measured = 0; - speeds_net_latency_measured = 0; - - rfbLog("client_count: %d\n", client_count); - - if (no_autorepeat && client_count == 0) { - autorepeat(1, 0); - } - if (use_solid_bg && client_count == 0) { - solid_bg(1); - } - if (gone_cmd && *gone_cmd != '\0') { - rfbLog("client_gone: using cmd for: %s\n", client->host); - run_user_command(gone_cmd, client, "gone"); - } - - if (client->clientData) { - ClientData *cd = (ClientData *) client->clientData; - if (cd) { - if (cd->server_ip) { - free(cd->server_ip); - cd->server_ip = NULL; - } - if (cd->hostname) { - free(cd->hostname); - cd->hostname = NULL; - } - if (cd->username) { - free(cd->username); - cd->username = NULL; - } - } - free(client->clientData); - client->clientData = NULL; - } - - if (inetd) { - rfbLog("viewer exited.\n"); - clean_up_exit(0); - } - if (connect_once) { - /* - * This non-exit is done for a bad passwd to be consistent - * with our RFB_CLIENT_REFUSE behavior in new_client() (i.e. - * we disconnect after 1 successful connection). - */ - if ((client->state == RFB_PROTOCOL_VERSION || - client->state == RFB_AUTHENTICATION) && accepted_client) { - rfbLog("connect_once: invalid password or early " - "disconnect.\n"); - rfbLog("connect_once: waiting for next connection.\n"); - accepted_client = 0; - return; - } - if (shared && client_count > 0) { - rfbLog("connect_once: other shared clients still " - "connected, not exiting.\n"); - return; - } - - rfbLog("viewer exited.\n"); - clean_up_exit(0); - } -} - -/* - * Simple routine to limit access via string compare. A power user will - * want to compile libvncserver with libwrap support and use /etc/hosts.allow. - */ -static int check_access(char *addr) { - int allowed = 0; - char *p, *list; - - if (deny_all) { - rfbLog("check_access: new connections are currently " - "blocked.\n"); - return 0; - } - if (addr == NULL || *addr == '\0') { - rfbLog("check_access: denying empty host IP address string.\n"); - return 0; - } - - if (allow_list == NULL) { - /* set to "" to possibly append allow_once */ - allow_list = strdup(""); - } - if (*allow_list == '\0' && allow_once == NULL) { - /* no constraints, accept it */ - return 1; - } - - if (strchr(allow_list, '/')) { - /* a file of IP addresess or prefixes */ - int len, len2 = 0; - struct stat sbuf; - FILE *in; - char line[1024], *q; - - if (stat(allow_list, &sbuf) != 0) { - rfbLogEnable(1); - rfbLog("check_access: failure stating file: %s\n", - allow_list); - rfbLogPerror("stat"); - clean_up_exit(1); - } - len = sbuf.st_size + 1; /* 1 more for '\0' at end */ - if (allow_once) { - len2 = strlen(allow_once) + 2; - len += len2; - } - list = (char *) malloc(len); - list[0] = '\0'; - - in = fopen(allow_list, "r"); - if (in == NULL) { - rfbLogEnable(1); - rfbLog("check_access: cannot open: %s\n", allow_list); - rfbLogPerror("fopen"); - clean_up_exit(1); - } - while (fgets(line, 1024, in) != NULL) { - if ( (q = strchr(line, '#')) != NULL) { - *q = '\0'; - } - if (strlen(list) + strlen(line) >= - (size_t) (len - len2)) { - /* file grew since our stat() */ - break; - } - strcat(list, line); - } - fclose(in); - if (allow_once) { - strcat(list, "\n"); - strcat(list, allow_once); - strcat(list, "\n"); - } - } else { - int len = strlen(allow_list) + 1; - if (allow_once) { - len += strlen(allow_once) + 1; - } - list = (char *) malloc(len); - list[0] = '\0'; - strcat(list, allow_list); - if (allow_once) { - strcat(list, ","); - strcat(list, allow_once); - } - } - - if (allow_once) { - free(allow_once); - allow_once = NULL; - } - - p = strtok(list, ", \t\n\r"); - while (p) { - char *chk, *q, *r = NULL; - if (*p == '\0') { - p = strtok(NULL, ", \t\n\r"); - continue; - } - if (! dotted_ip(p)) { - r = host2ip(p); - if (r == NULL || *r == '\0') { - rfbLog("check_access: bad lookup \"%s\"\n", p); - p = strtok(NULL, ", \t\n\r"); - continue; - } - rfbLog("check_access: lookup %s -> %s\n", p, r); - chk = r; - } else { - chk = p; - } - - q = strstr(addr, chk); - if (chk[strlen(chk)-1] != '.') { - if (!strcmp(addr, chk)) { - if (chk != p) { - rfbLog("check_access: client %s " - "matches host %s=%s\n", addr, - chk, p); - } else { - rfbLog("check_access: client %s " - "matches host %s\n", addr, chk); - } - allowed = 1; - } else if(!strcmp(chk, "localhost") && - !strcmp(addr, "127.0.0.1")) { - allowed = 1; - } - } else if (q == addr) { - rfbLog("check_access: client %s matches pattern %s\n", - addr, chk); - allowed = 1; - } - p = strtok(NULL, ", \t\n\r"); - if (r) { - free(r); - } - if (allowed) { - break; - } - } - free(list); - return allowed; -} - -/* - * x11vnc's first (and only) visible widget: accept/reject dialog window. - * We go through this pain to avoid dependency on libXt... - */ -static int ugly_accept_window(char *addr, char *userhost, int X, int Y, - int timeout, char *mode) { - -#define t2x2_width 16 -#define t2x2_height 16 -static unsigned char t2x2_bits[] = { - 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, - 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, - 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33}; - - Window awin; - GC gc; - XSizeHints hints; - XGCValues values; - static XFontStruct *font_info = NULL; - static Pixmap ico = 0; - unsigned long valuemask = 0; - static char dash_list[] = {20, 40}; - int list_length = sizeof(dash_list); - - Atom wm_protocols; - Atom wm_delete_window; - - XEvent ev; - long evmask = ExposureMask | KeyPressMask | ButtonPressMask - | StructureNotifyMask; - double waited = 0.0; - - /* strings and geometries y/n */ - KeyCode key_y, key_n, key_v; - char strh[100]; - char stri[100]; - char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button"; - char str2_b[] = "To reject: press \"n\" or click the \"No\" button"; - char str3_b[] = "View only: press \"v\" or click the \"View\" button"; - char str1_m[] = "To accept: click the \"Yes\" button"; - char str2_m[] = "To reject: click the \"No\" button"; - char str3_m[] = "View only: click the \"View\" button"; - char str1_k[] = "To accept: press \"y\""; - char str2_k[] = "To reject: press \"n\""; - char str3_k[] = "View only: press \"v\""; - char *str1, *str2, *str3; - char str_y[] = "Yes"; - char str_n[] = "No"; - char str_v[] = "View"; - int x, y, w = 345, h = 175, ret = 0; - int X_sh = 20, Y_sh = 30, dY = 20; - int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20; - int No_x = 75, No_y = 0, No_w = 45, No_h = 20; - int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20; - - if (raw_fb && ! dpy) return 0; /* raw_fb hack */ - - if (!strcmp(mode, "mouse_only")) { - str1 = str1_m; - str2 = str2_m; - str3 = str3_m; - } else if (!strcmp(mode, "key_only")) { - str1 = str1_k; - str2 = str2_k; - str3 = str3_k; - h -= dY; - } else { - str1 = str1_b; - str2 = str2_b; - str3 = str3_b; - } - if (view_only) { - h -= dY; - } - - /* XXX handle coff_x/coff_y? */ - if (X < -dpy_x) { - x = (dpy_x - w)/2; /* large negative: center */ - if (x < 0) x = 0; - } else if (X < 0) { - x = dpy_x + X - w; /* from lower right */ - } else { - x = X; /* from upper left */ - } - - if (Y < -dpy_y) { - y = (dpy_y - h)/2; - if (y < 0) y = 0; - } else if (Y < 0) { - y = dpy_y + Y - h; - } else { - y = Y; - } - - X_LOCK; - - awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4, - BlackPixel(dpy, scr), WhitePixel(dpy, scr)); - - wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False); - wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - XSetWMProtocols(dpy, awin, &wm_delete_window, 1); - - if (! ico) { - ico = XCreateBitmapFromData(dpy, awin, (char *) t2x2_bits, - t2x2_width, t2x2_height); - } - - hints.flags = PPosition | PSize | PMinSize; - hints.x = x; - hints.y = y; - hints.width = w; - hints.height = h; - hints.min_width = w; - hints.min_height = h; - - XSetStandardProperties(dpy, awin, "new x11vnc client", "x11vnc query", - ico, NULL, 0, &hints); - - XSelectInput(dpy, awin, evmask); - - if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) { - rfbLogEnable(1); - rfbLog("ugly_accept_window: cannot locate font fixed.\n"); - X_UNLOCK; - clean_up_exit(1); - } - - gc = XCreateGC(dpy, awin, valuemask, &values); - XSetFont(dpy, gc, font_info->fid); - XSetForeground(dpy, gc, BlackPixel(dpy, scr)); - XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); - XSetDashes(dpy, gc, 0, dash_list, list_length); - - XMapWindow(dpy, awin); - XFlush(dpy); - - snprintf(strh, 100, "x11vnc: accept connection from %s?", addr); - snprintf(stri, 100, " (%s)", userhost); - key_y = XKeysymToKeycode(dpy, XStringToKeysym("y")); - key_n = XKeysymToKeycode(dpy, XStringToKeysym("n")); - key_v = XKeysymToKeycode(dpy, XStringToKeysym("v")); - - while (1) { - int out = -1, x, y, tw, k; - - if (XCheckWindowEvent(dpy, awin, evmask, &ev)) { - ; /* proceed to handling */ - } else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) { - ; /* proceed to handling */ - } else { - int ms = 100; /* sleep a bit */ - usleep(ms * 1000); - waited += ((double) ms)/1000.; - if (timeout && (int) waited >= timeout) { - rfbLog("accept_client: popup timed out after " - "%d seconds.\n", timeout); - out = 0; - ev.type = 0; - } else { - continue; - } - } - - switch(ev.type) { - case Expose: - while (XCheckTypedEvent(dpy, Expose, &ev)) { - ; - } - k=0; - - /* instructions */ - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - strh, strlen(strh)); - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - stri, strlen(stri)); - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str1, strlen(str1)); - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str2, strlen(str2)); - if (! view_only) { - XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY, - str3, strlen(str3)); - } - - if (!strcmp(mode, "key_only")) { - break; - } - - /* buttons */ - Ye_y = Y_sh+k*dY; - No_y = Y_sh+k*dY; - Vi_y = Y_sh+k*dY; - XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h); - XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h); - if (! view_only) { - XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y, - Vi_w, Vi_h); - } - - tw = XTextWidth(font_info, str_y, strlen(str_y)); - tw = (Ye_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5, - str_y, strlen(str_y)); - - tw = XTextWidth(font_info, str_n, strlen(str_n)); - tw = (No_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5, - str_n, strlen(str_n)); - - if (! view_only) { - tw = XTextWidth(font_info, str_v, - strlen(str_v)); - tw = (Vi_w - tw)/2; - if (tw < 0) tw = 1; - XDrawString(dpy, awin, gc, Vi_x+tw, - Vi_y+Vi_h-5, str_v, strlen(str_v)); - } - - break; - - case ClientMessage: - if (ev.xclient.message_type == wm_protocols && - (Atom) ev.xclient.data.l[0] == wm_delete_window) { - out = 0; - } - break; - - case ButtonPress: - x = ev.xbutton.x; - y = ev.xbutton.y; - if (!strcmp(mode, "key_only")) { - ; - } else if (x > No_x && x < No_x+No_w && y > No_y - && y < No_y+No_h) { - out = 0; - } else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y - && y < Ye_y+Ye_h) { - out = 1; - } else if (! view_only && x > Vi_x && x < Vi_x+Vi_w - && y > Vi_y && y < Vi_y+Ye_h) { - out = 2; - } - break; - - case KeyPress: - if (!strcmp(mode, "mouse_only")) { - ; - } else if (ev.xkey.keycode == key_y) { - out = 1; - } else if (ev.xkey.keycode == key_n) { - out = 0; - } else if (! view_only && ev.xkey.keycode == key_v) { - out = 2; - } - break; - default: - break; - } - if (out != -1) { - ret = out; - XSelectInput(dpy, awin, 0); - XUnmapWindow(dpy, awin); - XFreeGC(dpy, gc); - XDestroyWindow(dpy, awin); - XFlush(dpy); - break; - } - } - X_UNLOCK; - - return ret; -} - -/* - * process a "yes:0,no:*,view:3" type action list comparing to command - * return code rc. * means the default action with no other match. - */ -static int action_match(char *action, int rc) { - char *p, *q, *s = strdup(action); - int cases[4], i, result; - char *labels[4]; - - labels[1] = "yes"; - labels[2] = "no"; - labels[3] = "view"; - - rfbLog("accept_client: process action line: %s\n", - action); - - for (i=1; i <= 3; i++) { - cases[i] = -2; - } - - p = strtok(s, ","); - while (p) { - if ((q = strchr(p, ':')) != NULL) { - int in, k; - *q = '\0'; - q++; - if (strstr(p, "yes") == p) { - k = 1; - } else if (strstr(p, "no") == p) { - k = 2; - } else if (strstr(p, "view") == p) { - k = 3; - } else { - rfbLogEnable(1); - rfbLog("invalid action line: %s\n", action); - clean_up_exit(1); - } - if (*q == '*') { - cases[k] = -1; - } else if (sscanf(q, "%d", &in) == 1) { - if (in < 0) { - rfbLogEnable(1); - rfbLog("invalid action line: %s\n", - action); - clean_up_exit(1); - } - cases[k] = in; - } else { - rfbLogEnable(1); - rfbLog("invalid action line: %s\n", action); - clean_up_exit(1); - } - } else { - rfbLogEnable(1); - rfbLog("invalid action line: %s\n", action); - clean_up_exit(1); - } - p = strtok(NULL, ","); - } - free(s); - - result = -1; - for (i=1; i <= 3; i++) { - if (cases[i] == -1) { - rfbLog("accept_client: default action is case=%d %s\n", - i, labels[i]); - result = i; - break; - } - } - if (result == -1) { - rfbLog("accept_client: no default action\n"); - } - for (i=1; i <= 3; i++) { - if (cases[i] >= 0 && cases[i] == rc) { - rfbLog("accept_client: matched action is case=%d %s\n", - i, labels[i]); - result = i; - break; - } - } - if (result < 0) { - rfbLog("no action match: %s rc=%d set to no\n", action, rc); - result = 2; - } - return result; -} - -/* - * Simple routine to prompt the user on the X display whether an incoming - * client should be allowed to connect or not. If a gui is involved it - * will be running in the environment/context of the X11 DISPLAY. - * - * The command supplied via -accept is run as is (i.e. no string - * substitution) with the RFB_CLIENT_IP environment variable set to the - * incoming client's numerical IP address. - * - * If the external command exits with 0 the client is accepted, otherwise - * the client is rejected. - * - * Some builtins are provided: - * - * xmessage: use homebrew xmessage(1) for the external command. - * popup: use internal X widgets for prompting. - * - */ -static int accept_client(rfbClientPtr client) { - - char xmessage[200], *cmd = NULL; - char *addr = client->host; - char *action = NULL; - - if (accept_cmd == NULL || *accept_cmd == '\0') { - return 1; /* no command specified, so we accept */ - } - - if (addr == NULL || addr[0] == '\0') { - addr = "unknown-host"; - } - - if (strstr(accept_cmd, "popup") == accept_cmd) { - /* use our builtin popup button */ - - /* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */ - - int ret, timeout = 120; - int x = -64000, y = -64000; - char *p, *mode; - char *userhost = ident_username(client); - - /* extract timeout */ - if ((p = strchr(accept_cmd, ':')) != NULL) { - int in; - if (sscanf(p+1, "%d", &in) == 1) { - timeout = in; - } - } - /* extract geometry */ - if ((p = strpbrk(accept_cmd, "+-")) != NULL) { - int x1, y1; - if (sscanf(p, "+%d+%d", &x1, &y1) == 2) { - x = x1; - y = y1; - } else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) { - x = x1; - y = -y1; - } else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) { - x = -x1; - y = y1; - } else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) { - x = -x1; - y = -y1; - } - } - - /* find mode: mouse, key, or both */ - if (strstr(accept_cmd, "popupmouse") == accept_cmd) { - mode = "mouse_only"; - } else if (strstr(accept_cmd, "popupkey") == accept_cmd) { - mode = "key_only"; - } else { - mode = "both"; - } - - rfbLog("accept_client: using builtin popup for: %s\n", addr); - if ((ret = ugly_accept_window(addr, userhost, x, y, timeout, - mode))) { - free(userhost); - if (ret == 2) { - rfbLog("accept_client: viewonly: %s\n", addr); - client->viewOnly = TRUE; - } - rfbLog("accept_client: popup accepted: %s\n", addr); - return 1; - } else { - free(userhost); - rfbLog("accept_client: popup rejected: %s\n", addr); - return 0; - } - - } else if (!strcmp(accept_cmd, "xmessage")) { - /* make our own command using xmessage(1) */ - - if (view_only) { - sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center" - " 'x11vnc: accept connection from %s?'", addr); - } else { - sprintf(xmessage, "xmessage -buttons yes:0,no:2," - "view-only:3 -center" " 'x11vnc: accept connection" - " from %s?'", addr); - action = "yes:0,no:*,view:3"; - } - cmd = xmessage; - - } else { - /* use the user supplied command: */ - - cmd = accept_cmd; - - /* extract any action prefix: yes:N,no:M,view:K */ - if (strstr(accept_cmd, "yes:") == accept_cmd) { - char *p; - if ((p = strpbrk(accept_cmd, " \t")) != NULL) { - int i; - cmd = p; - p = accept_cmd; - for (i=0; i<200; i++) { - if (*p == ' ' || *p == '\t') { - xmessage[i] = '\0'; - break; - } - xmessage[i] = *p; - p++; - } - xmessage[200-1] = '\0'; - action = xmessage; - } - } - } - - if (cmd) { - int rc; - - rfbLog("accept_client: using cmd for: %s\n", addr); - rc = run_user_command(cmd, client, "accept"); - - if (action) { - int result; - - if (rc < 0) { - rfbLog("accept_client: cannot use negative " - "rc: %d, action %s\n", rc, action); - result = 2; - } else { - result = action_match(action, rc); - } - - if (result == 1) { - rc = 0; - } else if (result == 2) { - rc = 1; - } else if (result == 3) { - rc = 0; - rfbLog("accept_client: viewonly: %s\n", addr); - client->viewOnly = TRUE; - } else { - rc = 1; /* NOTREACHED */ - } - } - - if (rc == 0) { - rfbLog("accept_client: accepted: %s\n", addr); - return 1; - } else { - rfbLog("accept_client: rejected: %s\n", addr); - return 0; - } - } else { - rfbLog("accept_client: no command, rejecting %s\n", addr); - return 0; - } - - /* return 0; NOTREACHED */ -} - -/* - * For the -connect <file> option: periodically read the file looking for - * a connect string. If one is found set client_connect to it. - */ -static void check_connect_file(char *file) { - FILE *in; - char line[VNC_CONNECT_MAX], host[VNC_CONNECT_MAX]; - static int first_warn = 1, truncate_ok = 1; - static time_t last_time = 0; - time_t now = time(0); - - if (last_time == 0) { - last_time = now; - } - if (now - last_time < 1) { - /* check only once a second */ - return; - } - last_time = now; - - if (! truncate_ok) { - /* check if permissions changed */ - if (access(file, W_OK) == 0) { - truncate_ok = 1; - } else { - return; - } - } - - in = fopen(file, "r"); - if (in == NULL) { - if (first_warn) { - rfbLog("check_connect_file: fopen failure: %s\n", file); - rfbLogPerror("fopen"); - first_warn = 0; - } - return; - } - - if (fgets(line, VNC_CONNECT_MAX, in) != NULL) { - if (sscanf(line, "%s", host) == 1) { - if (strlen(host) > 0) { - char *str = strdup(host); - if (strlen(str) > 38) { - char trim[100]; - trim[0] = '\0'; - strncat(trim, str, 38); - rfbLog("read connect file: %s ...\n", - trim); - } else { - rfbLog("read connect file: %s\n", str); - } - client_connect = str; - } - } - } - fclose(in); - - /* truncate file */ - in = fopen(file, "w"); - if (in != NULL) { - fclose(in); - } else { - /* disable if we cannot truncate */ - rfbLog("check_connect_file: could not truncate %s, " - "disabling checking.\n", file); - truncate_ok = 0; - } -} - -/* - * Do a reverse connect for a single "host" or "host:port" - */ -static int do_reverse_connect(char *str) { - rfbClientPtr cl; - char *host, *p; - int rport = 5500, len = strlen(str); - - if (len < 1) { - return 0; - } - if (len > 1024) { - rfbLog("reverse_connect: string too long: %d bytes\n", len); - return 0; - } - if (!screen) { - rfbLog("reverse_connect: screen not setup yet.\n"); - return 0; - } - - /* copy in to host */ - host = (char *) malloc(len+1); - if (! host) { - rfbLog("reverse_connect: could not malloc string %d\n", len); - return 0; - } - strncpy(host, str, len); - host[len] = '\0'; - - /* extract port, if any */ - if ((p = strchr(host, ':')) != NULL) { - rport = atoi(p+1); - *p = '\0'; - } - - cl = rfbReverseConnection(screen, host, rport); - free(host); - - if (cl == NULL) { - rfbLog("reverse_connect: %s failed\n", str); - return 0; - } else { - rfbLog("reverse_connect: %s/%s OK\n", str, cl->host); - return 1; - } -} - -/* - * Break up comma separated list of hosts and call do_reverse_connect() - */ -static void reverse_connect(char *str) { - char *p, *tmp = strdup(str); - int sleep_between_host = 300; - int sleep_min = 1500, sleep_max = 4500, n_max = 5; - int n, tot, t, dt = 100, cnt = 0; - - p = strtok(tmp, ", \t\r\n"); - while (p) { - if ((n = do_reverse_connect(p)) != 0) { - rfbPE(-1); - } - cnt += n; - - p = strtok(NULL, ", \t\r\n"); - if (p) { - t = 0; - while (t < sleep_between_host) { - usleep(dt * 1000); - rfbPE(-1); - t += dt; - } - } - } - free(tmp); - - if (cnt == 0) { - return; - } - - /* - * XXX: we need to process some of the initial handshaking - * events, otherwise the client can get messed up (why??) - * so we send rfbProcessEvents() all over the place. - */ - - n = cnt; - if (n >= n_max) { - n = n_max; - } - t = sleep_max - sleep_min; - tot = sleep_min + ((n-1) * t) / (n_max-1); - - t = 0; - while (t < tot) { - rfbPE(-1); - usleep(dt * 1000); - t += dt; - } -} - -/* - * Routines for monitoring the VNC_CONNECT property for changes. - * The vncconnect(1) will set it on our X display. - */ -void set_vnc_connect_prop(char *str) { - XChangeProperty(dpy, rootwin, vnc_connect_prop, XA_STRING, 8, - PropModeReplace, (unsigned char *)str, strlen(str)); -} - -void read_vnc_connect_prop(void) { - Atom type; - int format, slen, dlen; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - int db = 1; - - vnc_connect_str[0] = '\0'; - slen = 0; - - if (! vnc_connect || vnc_connect_prop == None) { - /* not active or problem with VNC_CONNECT atom */ - return; - } - - /* read the property value into vnc_connect_str: */ - do { - if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), - vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > VNC_CONNECT_MAX) { - /* too big */ - rfbLog("warning: truncating large VNC_CONNECT" - " string > %d bytes.\n", VNC_CONNECT_MAX); - XFree(data); - break; - } - memcpy(vnc_connect_str+slen, data, dlen); - slen += dlen; - vnc_connect_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - vnc_connect_str[VNC_CONNECT_MAX] = '\0'; - if (! db) { - ; - } else if (strstr(vnc_connect_str, "ans=stop:N/A,ans=quit:N/A,ans=")) { - ; - } else if (strstr(vnc_connect_str, "qry=stop,quit,exit")) { - ; - } else if (strstr(vnc_connect_str, "ack=") == vnc_connect_str) { - ; - } else if (quiet && strstr(vnc_connect_str, "qry=ping") == - vnc_connect_str) { - ; - } else if (strstr(vnc_connect_str, "cmd=") && - strstr(vnc_connect_str, "passwd")) { - rfbLog("read VNC_CONNECT: *\n"); - } else if (strlen(vnc_connect_str) > 38) { - char trim[100]; - trim[0] = '\0'; - strncat(trim, vnc_connect_str, 38); - rfbLog("read VNC_CONNECT: %s ...\n", trim); - - } else { - rfbLog("read VNC_CONNECT: %s\n", vnc_connect_str); - } -} - -/* - * check if client_connect has been set, if so make the reverse connections. - */ -static void send_client_connect(void) { - if (client_connect != NULL) { - char *str = client_connect; - if (strstr(str, "cmd=") == str || strstr(str, "qry=") == str) { - process_remote_cmd(client_connect, 0); - } else if (strstr(str, "ans=") == str - || strstr(str, "aro=") == str) { - ; - } else if (strstr(str, "ack=") == str) { - ; - } else { - reverse_connect(client_connect); - } - free(client_connect); - client_connect = NULL; - } -} - -/* - * monitor the various input methods - */ -void check_connect_inputs(void) { - - /* flush any already set: */ - send_client_connect(); - - /* connect file: */ - if (client_connect_file != NULL) { - check_connect_file(client_connect_file); - } - send_client_connect(); - - /* VNC_CONNECT property (vncconnect program) */ - if (vnc_connect && *vnc_connect_str != '\0') { - client_connect = strdup(vnc_connect_str); - vnc_connect_str[0] = '\0'; - } - send_client_connect(); -} - -void check_gui_inputs(void) { - int i, nmax = 0, n = 0, nfds; - int socks[ICON_MODE_SOCKS]; - fd_set fds; - struct timeval tv; - char buf[VNC_CONNECT_MAX+1]; - ssize_t nbytes; - - for (i=0; i<ICON_MODE_SOCKS; i++) { - if (icon_mode_socks[i] >= 0) { - socks[n++] = i; - if (icon_mode_socks[i] > nmax) { - nmax = icon_mode_socks[i]; - } - } - } - - if (! n) { - return; - } - - FD_ZERO(&fds); - for (i=0; i<n; i++) { - FD_SET(icon_mode_socks[socks[i]], &fds); - } - tv.tv_sec = 0; - tv.tv_usec = 0; - - nfds = select(nmax+1, &fds, NULL, NULL, &tv); - if (nfds <= 0) { - return; - } - - for (i=0; i<n; i++) { - int k, fd = icon_mode_socks[socks[i]]; - char *p; - if (! FD_ISSET(fd, &fds)) { - continue; - } - for (k=0; k<=VNC_CONNECT_MAX; k++) { - buf[k] = '\0'; - } - nbytes = read(fd, buf, VNC_CONNECT_MAX); - if (nbytes <= 0) { - close(fd); - icon_mode_socks[socks[i]] = -1; - continue; - } - - p = strtok(buf, "\r\n"); - while (p) { - if (strstr(p, "cmd=") == p || - strstr(p, "qry=") == p) { - char *str = process_remote_cmd(p, 1); - if (! str) { - str = strdup(""); - } - nbytes = write(fd, str, strlen(str)); - write(fd, "\n", 1); - free(str); - if (nbytes < 0) { - close(fd); - icon_mode_socks[socks[i]] = -1; - break; - } - } - p = strtok(NULL, "\r\n"); - } - } -} - -/* - * libvncserver callback for when a new client connects - */ -enum rfbNewClientAction new_client(rfbClientPtr client) { - ClientData *cd; - - last_event = last_input = time(0); - - if (inetd) { - /* - * Set this so we exit as soon as connection closes, - * otherwise client_gone is only called after RFB_CLIENT_ACCEPT - */ - client->clientGoneHook = client_gone; - } - - clients_served++; - - if (connect_once) { - if (screen->dontDisconnect && screen->neverShared) { - if (! shared && accepted_client) { - rfbLog("denying additional client: %s\n", - client->host); - return(RFB_CLIENT_REFUSE); - } - } - } - if (! check_access(client->host)) { - rfbLog("denying client: %s does not match %s\n", client->host, - allow_list ? allow_list : "(null)" ); - return(RFB_CLIENT_REFUSE); - } - if (! accept_client(client)) { - rfbLog("denying client: %s local user rejected connection.\n", - client->host); - rfbLog("denying client: accept_cmd=\"%s\"\n", - accept_cmd ? accept_cmd : "(null)" ); - return(RFB_CLIENT_REFUSE); - } - - client->clientData = (void *) calloc(sizeof(ClientData), 1); - cd = (ClientData *) client->clientData; - - cd->uid = clients_served; - - cd->client_port = get_remote_port(client->sock); - cd->server_port = get_local_port(client->sock); - cd->server_ip = get_local_host(client->sock); - cd->hostname = ip2host(client->host); - cd->username = strdup(""); - - cd->input[0] = '-'; - cd->login_viewonly = -1; - - client->clientGoneHook = client_gone; - - if (client_count) { - speeds_net_rate_measured = 0; - speeds_net_latency_measured = 0; - } - 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, 0); - } - if (use_solid_bg && client_count == 1) { - solid_bg(0); - } - - if (pad_geometry) { - install_padded_fb(pad_geometry); - } - - cd->timer = dnow(); - cd->send_cmp_rate = 0.0; - cd->send_raw_rate = 0.0; - cd->latency = 0.0; - cd->cmp_bytes_sent = 0; - cd->raw_bytes_sent = 0; - - accepted_client = 1; - last_client = time(0); - - return(RFB_CLIENT_ACCEPT); -} - -void start_client_info_sock(char *host_port_cookie) { - char *host = NULL, *cookie = NULL, *p; - char *str = strdup(host_port_cookie); - int i, port, sock, next = -1; - static time_t start_time[ICON_MODE_SOCKS]; - time_t oldest = 0; - int db = 0; - - for (i = 0; i < ICON_MODE_SOCKS; i++) { - if (icon_mode_socks[i] < 0) { - next = i; - break; - } - if (oldest == 0 || start_time[i] < oldest) { - next = i; - oldest = start_time[i]; - } - } - - p = strtok(str, ":"); - i = 0; - while (p) { - if (i == 0) { - host = strdup(p); - } else if (i == 1) { - port = atoi(p); - } else if (i == 2) { - cookie = strdup(p); - } - i++; - p = strtok(NULL, ":"); - } - free(str); - - if (db) fprintf(stderr, "%s/%d/%s next=%d\n", host, port, cookie, next); - - if (host && port && cookie) { - if (*host == '\0') { - free(host); - host = strdup("localhost"); - } - sock = rfbConnectToTcpAddr(host, port); - if (sock < 0) { - usleep(200 * 1000); - sock = rfbConnectToTcpAddr(host, port); - } - if (sock >= 0) { - char *lst = list_clients(); - icon_mode_socks[next] = sock; - start_time[next] = time(0); - write(sock, "COOKIE:", strlen("COOKIE:")); - write(sock, cookie, strlen(cookie)); - write(sock, "\n", strlen("\n")); - write(sock, "none\n", strlen("none\n")); - write(sock, "none\n", strlen("none\n")); - write(sock, lst, strlen(lst)); - write(sock, "\n", strlen("\n")); - if (db) { - fprintf(stderr, "list: %s\n", lst); - } - free(lst); - rfbLog("client_info_sock to: %s:%d\n", host, port); - } else { - rfbLog("failed client_info_sock: %s:%d\n", host, port); - } - } else { - rfbLog("malformed client_info_sock: %s\n", host_port_cookie); - } - - if (host) free(host); - if (cookie) free(cookie); -} - -void send_client_info(char *str) { - int i; - static char *pstr = NULL; - static int len = 128; - - if (!str || strlen(str) == 0) { - return; - } - - if (!pstr) { - pstr = (char *)malloc(len); - } - if (strlen(str) + 2 > (size_t) len) { - free(pstr); - len *= 2; - pstr = (char *)malloc(len); - } - strcpy(pstr, str); - strcat(pstr, "\n"); - - if (icon_mode_fh) { - fprintf(icon_mode_fh, "%s", pstr); - fflush(icon_mode_fh); - } - - for (i=0; i<ICON_MODE_SOCKS; i++) { - int len, n, sock = icon_mode_socks[i]; - char *buf = pstr; - - if (sock < 0) { - continue; - } - - len = strlen(pstr); - while (len > 0) { - n = write(sock, buf, len); - if (n > 0) { - buf += n; - len -= n; - continue; - } - - if (n < 0 && errno == EINTR) { - continue; - } - close(sock); - icon_mode_socks[i] = -1; - break; - } - } -} - -void check_new_clients(void) { - static int last_count = 0; - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int i, send_info = 0; - - if (client_count == last_count) { - return; - } - - if (! all_clients_initialized()) { - return; - } - - last_count = client_count; - - if (! screen) { - return; - } - if (! client_count) { - send_client_info("none"); - return; - } - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - ClientData *cd = (ClientData *) cl->clientData; - - if (cd->login_viewonly < 0) { - /* this is a general trigger to initialize things */ - if (cl->viewOnly) { - cd->login_viewonly = 1; - if (allowed_input_view_only) { - cl->viewOnly = FALSE; - cd->input[0] = '\0'; - strncpy(cd->input, - allowed_input_view_only, CILEN); - } - } else { - cd->login_viewonly = 0; - if (allowed_input_normal) { - cd->input[0] = '\0'; - strncpy(cd->input, - allowed_input_normal, CILEN); - } - } - } - } - rfbReleaseClientIterator(iter); - - if (icon_mode_fh) { - send_info++; - } - for (i = 0; i < ICON_MODE_SOCKS; i++) { - if (send_info || icon_mode_socks[i] >= 0) { - send_info++; - break; - } - } - if (send_info) { - char *str = list_clients(); - send_client_info(str); - free(str); - } -} - -/* -- keyboard.c -- */ -/* - * Routine to retreive current state keyboard. 1 means down, 0 up. - */ -void get_keystate(int *keystate) { - int i, k; - char keys[32]; - - /* n.b. caller decides to X_LOCK or not. */ - XQueryKeymap(dpy, keys); - for (i=0; i<32; i++) { - char c = keys[i]; - - for (k=0; k < 8; k++) { - if (c & 0x1) { - keystate[8*i + k] = 1; - } else { - keystate[8*i + k] = 0; - } - c = c >> 1; - } - } -} - -/* - * Try to KeyRelease any non-Lock modifiers that are down. - */ -void clear_modifiers(int init) { - static KeyCode keycodes[256]; - static KeySym keysyms[256]; - static char *keystrs[256]; - static int kcount = 0, first = 1; - int keystate[256]; - int i, j, minkey, maxkey, syms_per_keycode; - KeySym *keymap; - KeySym keysym; - KeyCode keycode; - - /* n.b. caller decides to X_LOCK or not. */ - if (first) { - /* - * we store results in static arrays, to aid interrupted - * case, but modifiers could have changed during session... - */ - XDisplayKeycodes(dpy, &minkey, &maxkey); - - keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), - &syms_per_keycode); - - for (i = minkey; i <= maxkey; i++) { - for (j = 0; j < syms_per_keycode; j++) { - keysym = keymap[ (i - minkey) * syms_per_keycode + j ]; - if (keysym == NoSymbol || ! ismodkey(keysym)) { - continue; - } - keycode = XKeysymToKeycode(dpy, keysym); - if (keycode == NoSymbol) { - continue; - } - keycodes[kcount] = keycode; - keysyms[kcount] = keysym; - keystrs[kcount] = strdup(XKeysymToString(keysym)); - kcount++; - } - } - XFree((void *) keymap); - first = 0; - } - if (init) { - return; - } - - get_keystate(keystate); - for (i=0; i < kcount; i++) { - keysym = keysyms[i]; - keycode = keycodes[i]; - - if (! keystate[(int) keycode]) { - continue; - } - - if (clear_mods) { - rfbLog("clear_modifiers: up: %-10s (0x%x) " - "keycode=0x%x\n", keystrs[i], keysym, keycode); - } - XTestFakeKeyEvent_wr(dpy, keycode, False, CurrentTime); - } - XFlush(dpy); -} - -KeySym simple_mods[] = { - XK_Shift_L, XK_Shift_R, - XK_Control_L, XK_Control_R, - XK_Meta_L, XK_Meta_R, - XK_Alt_L, XK_Alt_R, - XK_Super_L, XK_Super_R, - XK_Hyper_L, XK_Hyper_R, - XK_Mode_switch, - NoSymbol -}; -#define NSIMPLE_MODS 13 - -int track_mod_state(rfbKeySym keysym, rfbBool down, rfbBool set) { - KeySym sym = (KeySym) keysym; - static rfbBool isdown[NSIMPLE_MODS]; - static int first = 1; - int i, cnt = 0; - - /* - * simple tracking method for the modifier state without - * contacting the Xserver. Ignores, of course what keys are - * pressed on the physical display. - * - * This is unrelated to our mod_tweak and xkb stuff. - * Just a simple thing for wireframe/scroll heuristics, - * sloppy keys etc. - */ - - if (first) { - for (i=0; i<NSIMPLE_MODS; i++) { - isdown[i] = FALSE; - } - first = 0; - } - - if (sym != NoSymbol) { - for (i=0; i<NSIMPLE_MODS; i++) { - if (sym == simple_mods[i]) { - if (set) { - isdown[i] = down; - return 1; - } else { - if (isdown[i]) { - return 1; - } else { - return 0; - } - } - break; - } - } - /* not a modifier */ - if (set) { - return 0; - } else { - return -1; - } - } - - /* called with NoSymbol: return number currently pressed: */ - for (i=0; i<NSIMPLE_MODS; i++) { - if (isdown[i]) { - cnt++; - } - } - return cnt; -} - -/* - * Attempt to set all keys to Up position. Can mess up typing at the - * physical keyboard so use with caution. - */ -void clear_keys(void) { - int k, keystate[256]; - - /* n.b. caller decides to X_LOCK or not. */ - get_keystate(keystate); - for (k=0; k<256; k++) { - if (keystate[k]) { - KeyCode keycode = (KeyCode) k; - rfbLog("clear_keys: keycode=%d\n", keycode); - XTestFakeKeyEvent_wr(dpy, keycode, False, CurrentTime); - } - } - XFlush(dpy); -} - -/* - * Kludge for -norepeat option: we turn off keystroke autorepeat in - * the X server when clients are connected. This may annoy people at - * the physical display. We do this because 'key down' and 'key up' - * user input events may be separated by 100s of ms due to screen fb - * processing or link latency, thereby inducing the X server to apply - * autorepeat when it should not. Since the *client* is likely doing - * 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; - XGetKeyboardControl(dpy, &kstate); - X_UNLOCK; - return kstate.global_auto_repeat; -} - -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; - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - if (restore) { - if (save_auto_repeat < 0) { - return; /* nothing to restore */ - } - global_auto_repeat = get_autorepeat_state(); - X_LOCK; - /* read state and skip restore if equal (e.g. no clients) */ - if (global_auto_repeat == save_auto_repeat) { - X_UNLOCK; - return; - } - - kctrl.auto_repeat_mode = save_auto_repeat; - XChangeKeyboardControl(dpy, KBAutoRepeatMode, &kctrl); - XFlush(dpy); - X_UNLOCK; - - if (! bequiet && ! quiet) { - rfbLog("Restored X server key autorepeat to: %d\n", - save_auto_repeat); - } - } else { - global_auto_repeat = get_autorepeat_state(); - 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; - XChangeKeyboardControl(dpy, KBAutoRepeatMode, &kctrl); - XFlush(dpy); - X_UNLOCK; - - 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 (first) { - for (n=0; n < 0x100; n++) { - added_keysyms[n] = NoSymbol; - } - first = 0; - } - - if (raw_fb && ! dpy) return 0; /* raw_fb hack */ - - if (keysym == NoSymbol) { - return 0; - } - /* there can be a race before MappingNotify */ - for (n=0; n < 0x100; n++) { - if (added_keysyms[n] == keysym) { - return n; - } - } - - XDisplayKeycodes(dpy, &minkey, &maxkey); - keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), - &syms_per_keycode); - - for (kc = minkey+1; kc <= maxkey; kc++) { - int i, j, didmsg = 0, is_empty = 1; - char *str; - KeySym new[8]; - - for (n=0; n < syms_per_keycode; n++) { - if (keymap[ (kc-minkey) * syms_per_keycode + n] - != NoSymbol) { - is_empty = 0; - break; - } - } - if (! is_empty) { - continue; - } - - for (i=0; i<8; i++) { - new[i] = NoSymbol; - } - if (add_keysyms == 2) { - new[0] = keysym; /* XXX remove me */ - } else { - for(i=0; i < syms_per_keycode; i++) { - new[i] = keysym; - if (i >= 7) break; - } - } - - XChangeKeyboardMapping(dpy, kc, syms_per_keycode, - new, 1); - - 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; - ret = kc; - break; - } - XFree(keymap); - return ret; -} - -void delete_keycode(KeyCode kc, int bequiet) { - int minkey, maxkey, syms_per_keycode, i; - KeySym *keymap; - KeySym ksym, new[8]; - char *str; - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - XDisplayKeycodes(dpy, &minkey, &maxkey); - keymap = XGetKeyboardMapping(dpy, minkey, (maxkey - minkey + 1), - &syms_per_keycode); - - for (i=0; i<8; i++) { - new[i] = NoSymbol; - } - - XChangeKeyboardMapping(dpy, kc, syms_per_keycode, new, 1); - - 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); -} - -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, bequiet); - added_keysyms[kc] = NoSymbol; - } - } -} - -/* - * The following is for an experimental -remap option to allow the user - * to remap keystrokes. It is currently confusing wrt modifiers... - */ -typedef struct keyremap { - KeySym before; - KeySym after; - int isbutton; - struct keyremap *next; -} keyremap_t; - -static keyremap_t *keyremaps = NULL; - -void add_remap(char *line) { - char str1[256], str2[256]; - KeySym ksym1, ksym2; - int isbtn = 0; - unsigned int i; - static keyremap_t *current = NULL; - keyremap_t *remap; - - if (sscanf(line, "%s %s", str1, str2) != 2) { - rfbLogEnable(1); - rfbLog("remap: invalid 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) { - if (sscanf(str2, "Button%u", &i) == 1) { - ksym2 = (KeySym) i; - isbtn = 1; - } - } - if (ksym1 == NoSymbol || ksym2 == NoSymbol) { - rfbLog("warning: skipping invalid 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]; - - if (keyremaps != NULL) { - /* free last remapping */ - keyremap_t *next_remap, *curr_remap = keyremaps; - while (curr_remap != NULL) { - next_remap = curr_remap->next; - free(curr_remap); - curr_remap = next_remap; - } - keyremaps = NULL; - } - if (infile == NULL || *infile == '\0') { - /* just unset remapping */ - return; - } - - in = fopen(infile, "r"); - if (in == NULL) { - /* assume cmd line key1-key2,key3-key4 */ - if (strstr(infile, "DEAD") == infile) { - ; - } else if (!strchr(infile, '-')) { - rfbLogEnable(1); - rfbLog("remap: cannot open: %s\n", infile); - rfbLogPerror("fopen"); - clean_up_exit(1); - } - if ((in = tmpfile()) == NULL) { - rfbLogEnable(1); - 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 == '-') { - fprintf(in, " "); - } else if (*p == ',' || *p == ' ' || *p == '\t') { - fprintf(in, "\n"); - } else { - fprintf(in, "%c", *p); - } - p++; - } - fprintf(in, "\n"); - fflush(in); - rewind(in); - } - - while (fgets(line, 256, in) != NULL) { - p = lblanks(line); - if (*p == '\0') { - continue; - } - if (strchr(line, '#')) { - continue; - } - - if (strstr(p, "DEAD") == p) { - add_dead_keysyms(p); - continue; - } - if ((q = strchr(line, '-')) != NULL) { - /* allow Keysym1-Keysym2 notation */ - *q = ' '; - } - add_remap(p); - } - fclose(in); -} - -/* - * preliminary support for using the Xkb (XKEYBOARD) extension for handling - * user input. inelegant, slow, and incomplete currently... but initial - * tests show it is useful for some setups. - */ -typedef struct keychar { - KeyCode code; - int group; - int level; -} keychar_t; - -/* max number of key groups and shift levels we consider */ -#define GRP 4 -#define LVL 8 -static int lvl_max, grp_max, kc_min, kc_max; -static KeySym xkbkeysyms[0x100][GRP][LVL]; -static unsigned int xkbstate[0x100][GRP][LVL]; -static unsigned int xkbignore[0x100][GRP][LVL]; -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; - -int sloppy_key_check(int key, rfbBool down, rfbKeySym keysym, int *new) { - if (!sloppy_keys) { - return 0; - } - - if (!down && !keycode_state[key] && !IsModifierKey(keysym)) { - int i, cnt = 0, downkey; - int nmods_down = track_mod_state(NoSymbol, FALSE, FALSE); - int mods_down[256]; - - if (nmods_down) { - /* tracking to skip down modifier keycodes. */ - for(i=0; i<256; i++) { - mods_down[i] = 0; - } - i = 0; - while (simple_mods[i] != NoSymbol) { - KeySym ksym = simple_mods[i]; - KeyCode k = XKeysymToKeycode(dpy, ksym); - if (k != NoSymbol && keycode_state[(int) k]) { - mods_down[(int) k] = 1; - } - - i++; - } - } - /* - * the keycode is already up... look for a single one - * (non modifier) that is down - */ - for (i=0; i<256; i++) { - if (keycode_state[i]) { - if (nmods_down && mods_down[i]) { - continue; - } - cnt++; - downkey = i; - } - } - if (cnt == 1) { - if (debug_keyboard) { - fprintf(stderr, " sloppy_keys: %d/0x%x " - "-> %d/0x%x (nmods: %d)\n", (int) key, - (int) key, downkey, downkey, nmods_down); - } - *new = downkey; - return 1; - } - } - return 0; -} - -#if !LIBVNCSERVER_HAVE_XKEYBOARD - -/* empty functions for no xkb */ -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) { - KeySym ks; - int kc, grp, lvl, k; - unsigned int state; - -/* - * Here is a guide: - -Workarounds arrays: - -multi_key[] indicates which keycodes have Multi_key (Compose) - bound to them. -mode_switch[] indicates which keycodes have Mode_switch (AltGr) - bound to them. -shift_keys[] indicates which keycodes have Shift bound to them. -skipkeycode[] indicates which keycodes are to be skipped - for any lookups from -skip_keycodes option. - -Groups and Levels, here is an example: - - ^ -------- - | L2 | A AE | - shift | | - level L1 | a ae | - -------- - G1 G2 - - group -> - -Traditionally this it all a key could do. L1 vs. L2 selected via Shift -and G1 vs. G2 selected via Mode_switch. Up to 4 Keysyms could be bound -to a key. See initialize_modtweak() for an example of using that type -of keymap from XGetKeyboardMapping(). - -Xkb gives us up to 4 groups and 63 shift levels per key, with the -situation being potentially different for each key. This is complicated, -and I don't claim to understand it all, but in the following we just think -of ranging over the group and level indices as covering all of the cases. -This gives us an accurate view of the keymap. The main tricky part -is mapping between group+level and modifier state. - -On current linux/XFree86 setups (Xkb is enabled by default) the -information from XGetKeyboardMapping() (evidently the compat map) -is incomplete and inaccurate, so we are really forced to use the -Xkb API. - -xkbkeysyms[] For a (keycode,group,level) holds the KeySym (0 for none) -xkbstate[] For a (keycode,group,level) holds the corresponding - modifier state needed to get that KeySym -xkbignore[] For a (keycode,group,level) which modifiers can be - ignored (the 0 bits can be ignored). -xkbmodifiers[] For the KeySym bound to this (keycode,group,level) store - the modifier mask. - * - */ - - /* initialize all the arrays: */ - for (kc = 0; kc < 0x100; kc++) { - multi_key[kc] = 0; - mode_switch[kc] = 0; - skipkeycode[kc] = 0; - shift_keys[kc] = 0; - - for (grp = 0; grp < GRP; grp++) { - for (lvl = 0; lvl < LVL; lvl++) { - xkbkeysyms[kc][grp][lvl] = NoSymbol; - xkbmodifiers[kc][grp][lvl] = -1; - xkbstate[kc][grp][lvl] = -1; - } - } - } - - /* - * the array is 256*LVL*GRP, but we can make the searched region - * smaller by computing the actual ranges. - */ - lvl_max = 0; - grp_max = 0; - 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: - */ - if (debug_keyboard > 1) { - rfbLog("initialize_xkb_modtweak: XKB keycode -> keysyms " - "mapping info:\n"); - } - for (kc = 0; kc < 0x100; kc++) { - for (grp = 0; grp < GRP; grp++) { - for (lvl = 0; lvl < LVL; lvl++) { - unsigned int ms, mods; - int state_save = -1, mods_save; - KeySym ks2; - - /* look up the Keysym, if any */ - ks = XkbKeycodeToKeysym(dpy, kc, grp, lvl); - xkbkeysyms[kc][grp][lvl] = ks; - - /* if no Keysym, on to next */ - if (ks == NoSymbol) { - continue; - } - /* - * for various workarounds, note where these special - * keys are bound to. - */ - if (ks == XK_Multi_key) { - multi_key[kc] = lvl+1; - } - if (ks == XK_Mode_switch) { - mode_switch[kc] = lvl+1; - } - if (ks == XK_Shift_L || ks == XK_Shift_R) { - 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: - */ - if (grp > grp_max) { - grp_max = grp; - } - if (lvl > lvl_max) { - lvl_max = lvl; - } - if (kc > kc_max) { - kc_max = kc; - } - if (kc < kc_min) { - kc_min = kc; - } - - /* - * lookup on *keysym* (i.e. not kc, grp, lvl) - * and get the modifier mask. this is 0 for - * most keysyms, only non zero for modifiers. - */ - ms = XkbKeysymToModifiers(dpy, ks); - xkbmodifiers[kc][grp][lvl] = ms; - - /* - * Amusing heuristic (may have bugs). There are - * 8 modifier bits, so 256 possible modifier - * states. We loop over all of them for this - * keycode (simulating Key "events") and ask - * XkbLookupKeySym to tell us the Keysym. Once it - * matches the Keysym we have for this (keycode, - * group, level), gotten via XkbKeycodeToKeysym() - * above, we then (hopefully...) know that state - * of modifiers needed to generate this keysym. - * - * Yes... keep your fingers crossed. - * - * Note that many of the 256 states give the - * Keysym, we just need one, and we take the - * first one found. - */ - state = 0; - while(state < 256) { - if (XkbLookupKeySym(dpy, kc, state, &mods, - &ks2)) { - - /* save these for workaround below */ - if (state_save == -1) { - state_save = state; - mods_save = mods; - } - if (ks2 == ks) { - /* - * zero the irrelevant bits - * by anding with mods. - */ - xkbstate[kc][grp][lvl] - = state & mods; - /* - * also remember the irrelevant - * bits since it is handy. - */ - xkbignore[kc][grp][lvl] = mods; - - break; - } - } - state++; - } - if (xkbstate[kc][grp][lvl] == (unsigned int) -1 - && grp == 1) { - /* - * Hack on Solaris 9 for Mode_switch - * for Group2 characters. We force the - * Mode_switch modifier bit on. - * XXX Need to figure out better what is - * happening here. Is compat on somehow?? - */ - unsigned int ms2; - ms2 = XkbKeysymToModifiers(dpy, XK_Mode_switch); - - xkbstate[kc][grp][lvl] - = (state_save & mods_save) | ms2; - - xkbignore[kc][grp][lvl] = mods_save | ms2; - } - - if (debug_keyboard > 1) { - fprintf(stderr, " %03d G%d L%d mod=%s ", - kc, grp+1, lvl+1, bitprint(ms, 8)); - fprintf(stderr, "state=%s ", - bitprint(xkbstate[kc][grp][lvl], 8)); - fprintf(stderr, "ignore=%s ", - bitprint(xkbignore[kc][grp][lvl], 8)); - fprintf(stderr, " ks=0x%08lx \"%s\"\n", - ks, XKeysymToString(ks)); - } - } - } - } - - /* - * 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 - * ambiguities in the reverse map: Keysym -> KeyCode + Modstate, - * so if we can ignore them so much the better. Presumably the - * user can never generate them from the physical keyboard. - * There may be other reasons to deaden some keys. - */ - if (skip_keycodes != NULL) { - char *p, *str = strdup(skip_keycodes); - p = strtok(str, ", \t\n\r"); - while (p) { - k = 1; - if (sscanf(p, "%d", &k) != 1 || k < 0 || k >= 0x100) { - rfbLogEnable(1); - rfbLog("invalid skip_keycodes: %s %s\n", - skip_keycodes, p); - clean_up_exit(1); - } - skipkeycode[k] = 1; - p = strtok(NULL, ", \t\n\r"); - } - free(str); - } - if (debug_keyboard > 1) { - fprintf(stderr, "grp_max=%d lvl_max=%d\n", grp_max, lvl_max); - } -} - -/* - * Called on user keyboard input. Try to solve the reverse mapping - * problem: KeySym (from VNC client) => KeyCode(s) to press to generate - * it. The one-to-many KeySym => KeyCode mapping makes it difficult, as - * does working out what changes to the modifier keypresses are needed. - */ -static void xkb_tweak_keyboard(rfbBool down, rfbKeySym keysym, - rfbClientPtr client) { - - 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; - - if (client) {} /* unused vars warning: */ - - X_LOCK; - - if (debug_keyboard) { - char *str = XKeysymToString(keysym); - - 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"); - } - - /* - * set everything to not-yet-found. - * 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. - */ - for (i = 0; i < 0x100; i++) { - kc_f[i] = -1; - grp_f[i] = -1; - lvl_f[i] = -1; - state_f[i] = -1; - ignore_f[i] = -1; - } - found = 0; - - /* - * loop over all (keycode, group, level) triples looking for - * matching keysyms. Amazingly this isn't slow (but maybe if - * you type really fast...). Hash lookup into a linked list of - * (keycode,grp,lvl) triples would be the way to improve this - * in the future if needed. - */ - for (kc = kc_min; kc <= kc_max; kc++) { - for (grp = 0; grp < grp_max+1; grp++) { - for (lvl = 0; lvl < lvl_max+1; lvl++) { - if (keysym != xkbkeysyms[kc][grp][lvl]) { - continue; - } - /* got a keysym match */ - state = xkbstate[kc][grp][lvl]; - - if (debug_keyboard > 1) { - fprintf(stderr, " got match kc=%03d=0x%02x G%d" - " L%d ks=0x%x \"%s\" (basesym: \"%s\")\n", - kc, kc, grp+1, lvl+1, keysym, - XKeysymToString(keysym), XKeysymToString( - XKeycodeToKeysym(dpy, kc, 0))); - fprintf(stderr, " need state: %s\n", - bitprint(state, 8)); - fprintf(stderr, " ignorable : %s\n", - bitprint(xkbignore[kc][grp][lvl], 8)); - } - - /* save it if state is OK and not told to skip */ - if (state == (unsigned int) -1) { - 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 (found > 0 && kc == kc_f[found-1]) { - /* ignore repeats for same keycode */ - continue; - } - kc_f[found] = kc; - grp_f[found] = grp; - lvl_f[found] = lvl; - state_f[found] = state; - ignore_f[found] = xkbignore[kc][grp][lvl]; - found++; - } - } - } - -#define PKBSTATE \ - fprintf(stderr, " --- current mod state:\n"); \ - fprintf(stderr, " mods : %s\n", bitprint(kbstate.mods, 8)); \ - fprintf(stderr, " base_mods : %s\n", bitprint(kbstate.base_mods, 8)); \ - fprintf(stderr, " latch_mods: %s\n", bitprint(kbstate.latched_mods, 8)); \ - fprintf(stderr, " lock_mods : %s\n", bitprint(kbstate.locked_mods, 8)); \ - fprintf(stderr, " compat : %s\n", bitprint(kbstate.compat_state, 8)); - - /* - * Now get the current state of the keyboard from the X server. - * This seems to be the safest way to go as opposed to our - * keeping track of the modifier state on our own. Again, - * this is fortunately not too slow. - */ - - if (debug_keyboard > 1) { - /* get state early for debug output */ - XkbGetState(dpy, XkbUseCoreKbd, &kbstate); - got_kbstate = 1; - PKBSTATE - } - - if (!found && add_keysyms && keysym && ! IsModifierKey(keysym)) { - int new_kc = add_keysym(keysym); - if (new_kc != 0) { - found = 1; - kc_f[0] = new_kc; - grp_f[0] = 0; - lvl_f[0] = 0; - state_f[0] = 0; - } - } - - if (!found && debug_keyboard) { - char *str = XKeysymToString(keysym); - fprintf(stderr, " *** NO key found for: 0x%x %s " - "*keystroke ignored*\n", keysym, str ? str : "null"); - } - if (!found) { - X_UNLOCK; - return; - } - - /* - * 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. - */ - - 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]; - - /* - * XXX since we now internally track - * keycode_state[], we could throw that into - * the score as well. I.e. if it is already - * down, it is pointless to think we can - * press it down further! E.g. - * myscore += 1000 * keycode_state[kc_f[l]]; - * Also could watch multiple modifier - * problem, e.g. Shift+key -> Alt - * keycode = 125 on my keyboard. - */ - - 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 { - /* up */ - 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) { - int l; - /* - * If it is already down, that is - * a great hint. Use it. - * - * note: keycode_state in internal and - * ignores someone pressing keys on the - * physical display (but is updated - * periodically to clean out stale info). - */ - for (l=0; l < found; l++) { - int key = (int) kc_f[l]; - if (keycode_state[key]) { - Kc_f = kc_f[l]; - 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; - char *str; - fprintf(stderr, " *** found more than one keycode: "); - for (l = 0; l < found; l++) { - fprintf(stderr, "%03d ", kc_f[l]); - } - for (l = 0; l < found; l++) { - str = XKeysymToString(XKeycodeToKeysym(dpy,kc_f[l],0)); - fprintf(stderr, " \"%s\"", str ? str : "null"); - } - fprintf(stderr, ", picked this one: %03d (last down: %03d)\n", - Kc_f, Kc_last_down); - } - - if (sloppy_keys) { - int new_kc; - if (sloppy_key_check(Kc_f, down, keysym, &new_kc)) { - Kc_f = new_kc; - } - } - - if (down) { - /* - * need to set up the mods for tweaking and other workarounds - */ - int needmods[8], sentmods[8], Ilist[8], keystate[256]; - int involves_multi_key, shift_is_down; - int i, j, b, curr, need; - unsigned int ms; - 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; - } - - /* - * needmods[] whether or not that modifier bit needs - * something done to it. - * < 0 means no, - * 0 means needs to go up. - * 1 means needs to go down. - * - * -1, -2, -3 are used for debugging info to indicate - * why nothing needs to be done with the modifier, see below. - * - * sentmods[] is the corresponding keycode to use - * to acheive the needmods[] requirement for the bit. - */ - - for (i=0; i<8; i++) { - needmods[i] = -1; - sentmods[i] = 0; - } - - /* - * Loop over the 8 modifier bits and check if the current - * setting is what we need it to be or whether it should - * be changed (by us sending some keycode event) - * - * If nothing needs to be done to it record why: - * -1 the modifier bit is ignored. - * -2 the modifier bit is ignored, but is correct anyway. - * -3 the modifier bit is correct. - */ - - b = 0x1; - for (i=0; i<8; i++) { - curr = b & kbstate.mods; - need = b & state; - - if (! (b & xkbignore[Kc_f][Grp_f][Lvl_f])) { - /* irrelevant modifier bit */ - needmods[i] = -1; - if (curr == need) needmods[i] = -2; - } else if (curr == need) { - /* already correct */ - needmods[i] = -3; - } else if (! curr && need) { - /* need it down */ - needmods[i] = 1; - } else if (curr && ! need) { - /* need it up */ - needmods[i] = 0; - } - - b = b << 1; - } - - /* - * Again we dynamically probe the X server for information, - * this time for the state of all the keycodes. Useful - * info, and evidently is not too slow... - */ - get_keystate(keystate); - - /* - * We try to determine if Shift is down (since that can - * screw up ISO_Level3_Shift manipulations). - */ - shift_is_down = 0; - - for (kc = kc_min; kc <= kc_max; kc++) { - if (skipkeycode[kc] && debug_keyboard) { - fprintf(stderr, " xxx skipping keycode: " - "%d\n", kc); - } - if (skipkeycode[kc]) { - continue; - } - if (shift_keys[kc] && keystate[kc]) { - shift_is_down = kc; - break; - } - } - - /* - * Now loop over the modifier bits and try to deduce the - * keycode presses/release require to match the desired - * state. - */ - for (i=0; i<8; i++) { - if (needmods[i] < 0 && debug_keyboard > 1) { - int k = -needmods[i] - 1; - char *words[] = {"ignorable", - "bitset+ignorable", "bitset"}; - fprintf(stderr, " +++ needmods: mod=%d is " - "OK (%s)\n", i, words[k]); - } - if (needmods[i] < 0) { - continue; - } - - b = 1 << i; - - if (debug_keyboard > 1) { - fprintf(stderr, " +++ needmods: mod=%d %s " - "need it to be: %d %s\n", i, bitprint(b, 8), - needmods[i], needmods[i] ? "down" : "up"); - } - - /* - * 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 (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; - - kc = kc_vec[kci]; - - ms = xkbmodifiers[kc][grp][lvl]; - if (! ms || ms != (unsigned int) b) { - 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; - } - - ks = xkbkeysyms[kc][grp][lvl]; - if (! ks) { - 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"); - } - - 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++) { - /* - * reverse order is useful for tweaking - * ISO_Level3_Shift before Shift, but assumes they - * are in that order (i.e. Shift is first bit). - */ - int reverse = 1; - if (reverse) { - Ilist[i] = 7 - i; - } else { - Ilist[i] = i; - } - } - - /* - * check to see if Multi_key is bound to one of the Mods - * we have to tweak - */ - involves_multi_key = 0; - for (j=0; j<8; j++) { - i = Ilist[j]; - if (sentmods[i] == 0) continue; - dn = (Bool) needmods[i]; - if (!dn) continue; - if (multi_key[sentmods[i]]) { - involves_multi_key = i+1; - } - } - - if (involves_multi_key && shift_is_down && needmods[0] < 0) { - /* - * Workaround for Multi_key and shift. - * Assumes Shift is bit 1 (needmods[0]) - */ - if (debug_keyboard) { - fprintf(stderr, " ^^^ trying to avoid " - "inadvertent Multi_key from Shift " - "(doing %03d up now)\n", shift_is_down); - } - XTestFakeKeyEvent_wr(dpy, shift_is_down, False, - CurrentTime); - } else { - involves_multi_key = 0; - } - - for (j=0; j<8; j++) { - /* do the Mod ups */ - i = Ilist[j]; - if (sentmods[i] == 0) continue; - dn = (Bool) needmods[i]; - if (dn) continue; - XTestFakeKeyEvent_wr(dpy, sentmods[i], dn, CurrentTime); - } - for (j=0; j<8; j++) { - /* next, do the Mod downs */ - i = Ilist[j]; - if (sentmods[i] == 0) continue; - dn = (Bool) needmods[i]; - if (!dn) continue; - XTestFakeKeyEvent_wr(dpy, sentmods[i], dn, CurrentTime); - } - - if (involves_multi_key) { - /* - * Reverse workaround for Multi_key and shift. - */ - if (debug_keyboard) { - fprintf(stderr, " vvv trying to avoid " - "inadvertent Multi_key from Shift " - "(doing %03d down now)\n", shift_is_down); - } - XTestFakeKeyEvent_wr(dpy, shift_is_down, True, - CurrentTime); - } - - /* - * With the above modifier work done, send the actual keycode: - */ - XTestFakeKeyEvent_wr(dpy, Kc_f, (Bool) down, CurrentTime); - - /* - * Now undo the modifier work: - */ - for (j=7; j>=0; j--) { - /* reverse Mod downs we did */ - i = Ilist[j]; - if (sentmods[i] == 0) continue; - dn = (Bool) needmods[i]; - if (!dn) continue; - XTestFakeKeyEvent_wr(dpy, sentmods[i], !dn, - CurrentTime); - } - for (j=7; j>=0; j--) { - /* finally reverse the Mod ups we did */ - i = Ilist[j]; - if (sentmods[i] == 0) continue; - dn = (Bool) needmods[i]; - if (dn) continue; - XTestFakeKeyEvent_wr(dpy, sentmods[i], !dn, - CurrentTime); - } - - } else { /* for up case, hopefully just need to pop it up: */ - - XTestFakeKeyEvent_wr(dpy, Kc_f, (Bool) down, CurrentTime); - } - X_UNLOCK; -} -#endif - -/* - * For tweaking modifiers wrt the Alt-Graph key, etc. - */ -#define LEFTSHIFT 1 -#define RIGHTSHIFT 2 -#define ALTGR 4 -static char mod_state = 0; - -static char modifiers[0x100]; -static KeyCode keycodes[0x100]; -static KeyCode left_shift_code, right_shift_code, altgr_code, iso_level3_code; - -/* workaround for X11R5, Latin 1 only */ -#ifndef XConvertCase -#define XConvertCase(sym, lower, upper) \ -*(lower) = sym; \ -*(upper) = sym; \ -if (sym >> 8 == 0) { \ - if ((sym >= XK_A) && (sym <= XK_Z)) \ - *(lower) += (XK_a - XK_A); \ - else if ((sym >= XK_a) && (sym <= XK_z)) \ - *(upper) -= (XK_a - XK_A); \ - else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) \ - *(lower) += (XK_agrave - XK_Agrave); \ - else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis)) \ - *(upper) -= (XK_agrave - XK_Agrave); \ - else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) \ - *(lower) += (XK_oslash - XK_Ooblique); \ - else if ((sym >= XK_oslash) && (sym <= XK_thorn)) \ - *(upper) -= (XK_oslash - XK_Ooblique); \ -} -#endif - -char *short_kmb(char *str) { - int i, saw_k = 0, saw_m = 0, saw_b = 0, n = 10; - char *p, tmp[10]; - - for (i=0; i<n; i++) { - tmp[i] = '\0'; - } - - p = str; - i = 0; - while (*p) { - if ((*p == 'K' || *p == 'k') && !saw_k) { - tmp[i++] = 'K'; - saw_k = 1; - } else if ((*p == 'M' || *p == 'm') && !saw_m) { - tmp[i++] = 'M'; - saw_m = 1; - } else if ((*p == 'B' || *p == 'b') && !saw_b) { - tmp[i++] = 'B'; - saw_b = 1; - } - p++; - } - return(strdup(tmp)); -} - -void initialize_allowed_input(void) { - char *str; - - if (allowed_input_normal) { - free(allowed_input_normal); - allowed_input_normal = NULL; - } - if (allowed_input_view_only) { - free(allowed_input_view_only); - allowed_input_view_only = NULL; - } - - if (! allowed_input_str) { - allowed_input_normal = strdup("KMB"); - allowed_input_view_only = strdup(""); - } else { - char *p, *str = strdup(allowed_input_str); - p = strchr(str, ','); - if (p) { - allowed_input_view_only = strdup(p+1); - *p = '\0'; - allowed_input_normal = strdup(str); - } else { - allowed_input_normal = strdup(str); - allowed_input_view_only = strdup(""); - } - free(str); - } - - /* shorten them */ - str = short_kmb(allowed_input_normal); - free(allowed_input_normal); - allowed_input_normal = str; - - str = short_kmb(allowed_input_view_only); - free(allowed_input_view_only); - allowed_input_view_only = str; - - if (screen) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - ClientData *cd = (ClientData *) cl->clientData; - - if (cd->input[0] == '=') { - ; /* custom setting */ - } else if (cd->login_viewonly) { - if (*allowed_input_view_only != '\0') { - cl->viewOnly = FALSE; - cd->input[0] = '\0'; - strncpy(cd->input, - allowed_input_view_only, CILEN); - } else { - cl->viewOnly = TRUE; - } - } else { - if (allowed_input_normal) { - cd->input[0] = '\0'; - strncpy(cd->input, - allowed_input_normal, CILEN); - } - } - } - rfbReleaseClientIterator(iter); - } -} - -void initialize_keyboard_and_pointer(void) { - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - if (use_modifier_tweak) { - initialize_modtweak(); - } - if (remap_file != NULL) { - initialize_remap(remap_file); - } - - initialize_pointer_map(pointer_remap); - - clear_modifiers(1); - if (clear_mods == 1) { - clear_modifiers(0); - } -} - -void initialize_modtweak(void) { - KeySym keysym, *keymap; - int i, j, minkey, maxkey, syms_per_keycode; - - if (use_xkb_modtweak) { - initialize_xkb_modtweak(); - return; - } - memset(modifiers, -1, sizeof(modifiers)); - for (i=0; i<0x100; i++) { - keycodes[i] = NoSymbol; - } - - X_LOCK; - 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; - } - } - for (i = minkey; i <= maxkey; i++) { - if (debug_keyboard) { - if (i == minkey) { - rfbLog("initialize_modtweak: keycode -> " - "keysyms mapping info:\n"); - } - fprintf(stderr, " %03d ", i); - } - for (j = 0; j < syms_per_keycode; j++) { - if (debug_keyboard) { - char *sym; -#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"); - } - } - if (j >= 4) { - /* - * Something wacky in the keymapping. - * Ignore these non Shift/AltGr chords - * for now... n.b. we try to automatically - * switch to -xkb for this case. - */ - continue; - } - keysym = keymap[ (i - minkey) * syms_per_keycode + j ]; - if ( keysym >= ' ' && keysym < 0x100 - && i == XKeysymToKeycode(dpy, keysym) ) { - keycodes[keysym] = i; - modifiers[keysym] = j; - } - } - } - - left_shift_code = XKeysymToKeycode(dpy, XK_Shift_L); - right_shift_code = XKeysymToKeycode(dpy, XK_Shift_R); - altgr_code = XKeysymToKeycode(dpy, XK_Mode_switch); - iso_level3_code = NoSymbol; -#ifdef XK_ISO_Level3_Shift - iso_level3_code = XKeysymToKeycode(dpy, XK_ISO_Level3_Shift); -#endif - - XFree ((void *) keymap); - - X_UNLOCK; -} - -/* - * does the actual tweak: - */ -static void tweak_mod(signed char mod, rfbBool down) { - rfbBool is_shift = mod_state & (LEFTSHIFT|RIGHTSHIFT); - Bool dn = (Bool) down; - KeyCode altgr = altgr_code; - - if (mod < 0) { - if (debug_keyboard) { - rfbLog("tweak_mod: Skip: down=%d index=%d\n", down, - (int) mod); - } - return; - } - if (debug_keyboard) { - rfbLog("tweak_mod: Start: down=%d index=%d mod_state=0x%x" - " is_shift=%d\n", down, (int) mod, (int) mod_state, - is_shift); - } - - if (use_iso_level3 && iso_level3_code) { - altgr = iso_level3_code; - } - - X_LOCK; - if (is_shift && mod != 1) { - if (mod_state & LEFTSHIFT) { - XTestFakeKeyEvent_wr(dpy, left_shift_code, !dn, CurrentTime); - } - if (mod_state & RIGHTSHIFT) { - XTestFakeKeyEvent_wr(dpy, right_shift_code, !dn, CurrentTime); - } - } - if ( ! is_shift && mod == 1 ) { - XTestFakeKeyEvent_wr(dpy, left_shift_code, dn, CurrentTime); - } - if ( altgr && (mod_state & ALTGR) && mod != 2 ) { - XTestFakeKeyEvent_wr(dpy, altgr, !dn, CurrentTime); - } - if ( altgr && ! (mod_state & ALTGR) && mod == 2 ) { - XTestFakeKeyEvent_wr(dpy, altgr, dn, CurrentTime); - } - X_UNLOCK; - if (debug_keyboard) { - rfbLog("tweak_mod: Finish: down=%d index=%d mod_state=0x%x" - " is_shift=%d\n", down, (int) mod, (int) mod_state, - is_shift); - } -} - -/* - * tweak the modifier under -modtweak - */ -static void modifier_tweak_keyboard(rfbBool down, rfbKeySym keysym, - rfbClientPtr client) { - KeyCode k; - int tweak = 0; - - if (use_xkb_modtweak) { - xkb_tweak_keyboard(down, keysym, client); - return; - } - if (debug_keyboard) { - rfbLog("modifier_tweak_keyboard: %s keysym=0x%x\n", - down ? "down" : "up", (int) keysym); - } - -#define ADJUSTMOD(sym, state) \ - if (keysym == sym) { \ - if (down) { \ - mod_state |= state; \ - } else { \ - mod_state &= ~state; \ - } \ - } - - ADJUSTMOD(XK_Shift_L, LEFTSHIFT) - ADJUSTMOD(XK_Shift_R, RIGHTSHIFT) - ADJUSTMOD(XK_Mode_switch, ALTGR) - - if ( down && keysym >= ' ' && keysym < 0x100 ) { - tweak = 1; - tweak_mod(modifiers[keysym], True); - k = keycodes[keysym]; - } else { - X_LOCK; - k = XKeysymToKeycode(dpy, (KeySym) keysym); - X_UNLOCK; - } - if (k == NoSymbol && add_keysyms && ! IsModifierKey(keysym)) { - int new_kc = add_keysym(keysym); - if (new_kc) { - k = new_kc; - } - } - - if (sloppy_keys) { - int new_kc; - if (sloppy_key_check((int) k, down, keysym, &new_kc)) { - k = (KeyCode) new_kc; - } - } - - if (debug_keyboard) { - rfbLog("modifier_tweak_keyboard: KeySym 0x%x \"%s\" -> " - "KeyCode 0x%x%s\n", (int) keysym, XKeysymToString(keysym), - (int) k, k ? "" : " *ignored*"); - } - if ( k != NoSymbol ) { - X_LOCK; - XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime); - X_UNLOCK; - } - - if ( tweak ) { - tweak_mod(modifiers[keysym], False); - } -} - -typedef struct allowed_input { - int keystroke; - int motion; - int button; -} allowed_input_t; - -void get_allowed_input(rfbClientPtr client, allowed_input_t *input) { - ClientData *cd; - char *str; - - input->keystroke = 0; - input->motion = 0; - input->button = 0; - - if (! client) { - return; - } - - cd = (ClientData *) client->clientData; - - if (cd->input[0] != '-') { - str = cd->input; - } else if (client->viewOnly) { - if (allowed_input_view_only) { - str = allowed_input_view_only; - } else { - str = ""; - } - } else { - if (allowed_input_normal) { - str = allowed_input_normal; - } else { - str = "KMB"; - } - } - - while (*str) { - if (*str == 'K') { - input->keystroke = 1; - } else if (*str == 'M') { - input->motion = 1; - } else if (*str == 'B') { - input->button = 1; - } - str++; - } -} - -/* for -pipeinput mode */ -void pipe_keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { - int can_input = 0, uid; - allowed_input_t input; - char *name; - ClientData *cd = (ClientData *) client->clientData; - - if (pipeinput_fh == NULL) { - return; - } - - if (! view_only) { - get_allowed_input(client, &input); - if (input.motion || input.button) { - can_input = 1; /* XXX distinguish later */ - } - } - uid = cd->uid; - if (! can_input) { - uid = -uid; - } - - X_LOCK; - name = XKeysymToString(keysym); - X_UNLOCK; - - fprintf(pipeinput_fh, "Keysym %d %d %u %s %s\n", uid, down, - keysym, name, down ? "KeyPress" : "KeyRelease"); - - fflush(pipeinput_fh); - check_pipeinput(); -} - -typedef struct keyevent { - rfbKeySym sym; - rfbBool down; - double time; -} keyevent_t; - -#define KEY_HIST 256 -int key_history_idx = -1; -keyevent_t key_history[KEY_HIST]; - -double typing_rate(double time_window, int *repeating) { - double dt = 1.0, now = dnow(); - KeySym key = NoSymbol; - int i, idx, cnt = 0, repeat_keys = 0; - - if (key_history_idx == -1) { - if (repeating) { - *repeating = 0; - } - return 0.0; - } - if (time_window > 0.0) { - dt = time_window; - } - for (i=0; i<KEY_HIST; i++) { - idx = key_history_idx - i; - if (idx < 0) { - idx += KEY_HIST; - } - if (! key_history[idx].down) { - continue; - } - if (now > key_history[idx].time + dt) { - break; - } - cnt++; - if (key == NoSymbol) { - key = key_history[idx].sym; - repeat_keys = 1; - } else if (key == key_history[idx].sym) { - repeat_keys++; - } - } - - if (repeating) { - if (repeat_keys >= 2) { - *repeating = repeat_keys; - } else { - *repeating = 0; - } - } - - /* - * n.b. keyrate could seem very high with libvncserver buffering them - * so avoid using small dt. - */ - return ((double) cnt)/dt; -} - -int skip_cr_when_scaling(char *mode) { - int got = 0; - - if (!scaling) { - return 0; - } - - if (scaling_copyrect != scaling_copyrect0) { - /* user override via -scale: */ - if (! scaling_copyrect) { - return 1; - } else { - return 0; - } - } - if (*mode == 's') { - got = got_scrollcopyrect; - } else if (*mode == 'w') { - got = got_wirecopyrect; - } - if (scaling_copyrect || got) { - int lat, rate; - int link = link_rate(&lat, &rate); - if (link == LR_DIALUP) { - return 1; - } else if (rate < 25) { - /* the fill-in of the repair may be too slow */ - return 1; - } else { - return 0; - } - } else { - return 1; - } -} - -/* - * key event handler. See the above functions for contortions for - * running under -modtweak. - */ -static rfbClientPtr last_keyboard_client = NULL; - -void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) { - KeyCode k; - int idx, isbutton = 0; - allowed_input_t input; - time_t now = time(0); - double tnow; - static int skipped_last_down; - static rfbBool last_down; - static rfbKeySym last_keysym = NoSymbol; - static rfbKeySym max_keyrepeat_last_keysym = NoSymbol; - static double max_keyrepeat_last_time = 0.0; - - dtime0(&tnow); - got_keyboard_calls++; - - if (debug_keyboard) { - char *str; - X_LOCK; - str = XKeysymToString(keysym); - rfbLog("# keyboard(%s, 0x%x \"%s\") %.4f\n", down ? "down":"up", - (int) keysym, str ? str : "null", tnow - 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 = tnow; - - last_rfb_down = down; - last_rfb_keysym = keysym; - last_rfb_keytime = tnow; - last_rfb_key_accepted = FALSE; - - if (key_history_idx == -1) { - for (idx=0; idx<KEY_HIST; idx++) { - key_history[idx].sym = NoSymbol; - key_history[idx].down = FALSE; - key_history[idx].time = 0.0; - } - } - idx = ++key_history_idx; - if (key_history_idx >= KEY_HIST) { - key_history_idx = 0; - idx = 0; - } - key_history[idx].sym = keysym; - key_history[idx].down = down; - key_history[idx].time = tnow; - - if (down && (keysym == XK_Alt_L || keysym == XK_Super_L)) { - int i, k, run = 0, ups = 0; - double delay = 1.0; - KeySym ks; - for (i=0; i<16; i++) { - k = idx - i; - if (k < 0) k += KEY_HIST; - if (!key_history[k].down) { - ups++; - continue; - } - ks = key_history[k].sym; - if (key_history[k].time < tnow - delay) { - break; - } else if (ks == keysym && ks == XK_Alt_L) { - run++; - } else if (ks == keysym && ks == XK_Super_L) { - run++; - } else { - break; - } - } - if (ups < 2) { - ; - } else if (run == 3 && keysym == XK_Alt_L) { - rfbLog("3*Alt_L, calling: refresh_screen(0)\n"); - refresh_screen(0); - } else if (run == 4 && keysym == XK_Alt_L) { - rfbLog("4*Alt_L, setting: do_copy_screen\n"); - do_copy_screen = 1; - } else if (run == 5 && keysym == XK_Alt_L) { - ; - } else if (run == 3 && keysym == XK_Super_L) { - rfbLog("3*Super_L, calling: set_xdamage_mark()\n"); - set_xdamage_mark(0, 0, dpy_x, dpy_y); - } else if (run == 4 && keysym == XK_Super_L) { - rfbLog("4*Super_L, calling: check_xrecord_reset()\n"); - check_xrecord_reset(1); - } else if (run == 5 && keysym == XK_Super_L) { - rfbLog("5*Super_L, calling: push_black_screen(0)\n"); - push_black_screen(0); - } - } - - if (!down && skipped_last_down) { - int db = debug_scroll; - if (keysym == max_keyrepeat_last_keysym) { - skipped_last_down = 0; - if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s " - "%.4f %.4f\n", keysym, down ? "down":"up ", - tnow - x11vnc_start, tnow - max_keyrepeat_last_time); - return; - } - } - if (down && max_keyrepeat_time > 0.0) { - int skip = 0; - int db = debug_scroll; - - if (max_keyrepeat_last_keysym != NoSymbol && - max_keyrepeat_last_keysym != keysym) { - ; - } else { - if (tnow < max_keyrepeat_last_time+max_keyrepeat_time) { - skip = 1; - } - } - max_keyrepeat_time = 0.0; - if (skip) { - if (db) rfbLog("--- scroll keyrate skipping 0x%lx %s " - "%.4f %.4f\n", keysym, down ? "down":"up ", - tnow - x11vnc_start, tnow - max_keyrepeat_last_time); - max_keyrepeat_last_keysym = keysym; - skipped_last_down = 1; - return; - } else { - if (db) rfbLog("--- scroll keyrate KEEPING 0x%lx %s " - "%.4f %.4f\n", keysym, down ? "down":"up ", - tnow - x11vnc_start, tnow - max_keyrepeat_last_time); - } - } - max_keyrepeat_last_keysym = keysym; - max_keyrepeat_last_time = tnow; - skipped_last_down = 0; - last_rfb_key_accepted = TRUE; - - 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 = now; - last_keyboard_input = now; - - last_keysym = keysym; - - last_rfb_down = down; - last_rfb_keysym = keysym; - last_rfb_keytime = tnow; - - got_user_input++; - got_keyboard_input++; - } - return; - } - } - - if (view_only) { - return; - } - get_allowed_input(client, &input); - if (! input.keystroke) { - return; - } - - track_mod_state(keysym, down, TRUE); /* ignores remaps */ - - last_keyboard_client = client; - last_event = last_input = now; - last_keyboard_input = now; - - last_keysym = keysym; - - last_rfb_down = down; - last_rfb_keysym = keysym; - last_rfb_keytime = tnow; - - got_user_input++; - got_keyboard_input++; - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - if (keyremaps) { - keyremap_t *remap = keyremaps; - while (remap != NULL) { - if (remap->before == keysym) { - keysym = remap->after; - isbutton = remap->isbutton; - if (debug_keyboard) { - X_LOCK; - rfbLog("keyboard(): remapping keysym: " - "0x%x \"%s\" -> 0x%x \"%s\"\n", - (int) remap->before, - XKeysymToString(remap->before), - (int) remap->after, - remap->isbutton ? "button" : - XKeysymToString(remap->after)); - X_UNLOCK; - } - break; - } - remap = remap->next; - } - } - - if (use_xrecord && ! xrecording && down) { - - if (!strcmp(scroll_copyrect, "never")) { - ; - } else if (!strcmp(scroll_copyrect, "mouse")) { - ; - } else if (skip_cr_when_scaling("scroll")) { - ; - } else if (! xrecord_skip_keysym(keysym)) { - snapshot_stack_list(0, 0.25); - xrecord_watch(1, SCR_KEY); - xrecord_set_by_keys = 1; - xrecord_keysym = keysym; - } else { - if (debug_scroll) { - char *str = XKeysymToString(keysym); - rfbLog("xrecord_skip_keysym: %s\n", - str ? str : "NoSymbol"); - } - } - } - - if (isbutton) { - int mask, button = (int) keysym; - if (! down) { - return; /* nothing to send */ - } - if (debug_keyboard) { - rfbLog("keyboard(): remapping keystroke to button %d" - " click\n", button); - } - dtime0(&last_key_to_button_remap_time); - - X_LOCK; - /* - * 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_modifier_tweak) { - modifier_tweak_keyboard(down, keysym, client); - X_LOCK; - XFlush(dpy); - X_UNLOCK; - return; - } - - X_LOCK; - - k = XKeysymToKeycode(dpy, (KeySym) keysym); - - if (k == NoSymbol && add_keysyms && ! IsModifierKey(keysym)) { - int new_kc = add_keysym(keysym); - if (new_kc) { - k = new_kc; - } - } - if (debug_keyboard) { - char *str = XKeysymToString(keysym); - rfbLog("keyboard(): KeySym 0x%x \"%s\" -> KeyCode 0x%x%s\n", - (int) keysym, str ? str : "null", (int) k, - k ? "" : " *ignored*"); - } - - if ( k != NoSymbol ) { - XTestFakeKeyEvent_wr(dpy, k, (Bool) down, CurrentTime); - XFlush(dpy); - } - - X_UNLOCK; -} - -/* -- pointer.c -- */ -/* - * pointer event (motion and button click) handling routines. - */ -typedef struct ptrremap { - KeySym keysym; - KeyCode keycode; - int end; - int button; - int down; - int up; -} prtremap_t; - -MUTEX(pointerMutex); -#define MAX_BUTTONS 5 -#define MAX_BUTTON_EVENTS 50 -static prtremap_t pointer_map[MAX_BUTTONS+1][MAX_BUTTON_EVENTS]; - -/* - * For parsing the -buttonmap sections, e.g. "4" or ":Up+Up+Up:" - */ -static void buttonparse(int from, char **s) { - char *q; - int to, i; - int modisdown[256]; - - q = *s; - - for (i=0; i<256; i++) { - modisdown[i] = 0; - } - - if (*q == ':') { - /* :sym1+sym2+...+symN: format */ - int l = 0, n = 0; - char list[1000]; - char *t, *kp = q + 1; - KeyCode kcode; - - while (*(kp+l) != ':' && *(kp+l) != '\0') { - /* loop to the matching ':' */ - l++; - if (l >= 1000) { - rfbLog("buttonparse: keysym list too long: " - "%s\n", q); - break; - } - } - *(kp+l) = '\0'; - strncpy(list, kp, l); - list[l] = '\0'; - rfbLog("remap button %d using \"%s\"\n", from, list); - - /* loop over tokens separated by '+' */ - t = strtok(list, "+"); - while (t) { - KeySym ksym; - unsigned int ui; - int i; - if (n >= MAX_BUTTON_EVENTS - 20) { - rfbLog("buttonparse: too many button map " - "events: %s\n", list); - break; - } - if (sscanf(t, "0x%x", &ui) == 1) { - ksym = (KeySym) ui; /* hex value */ - } else { - X_LOCK; - ksym = XStringToKeysym(t); /* string value */ - X_UNLOCK; - } - if (ksym == NoSymbol) { - /* see if Button<N> "keysym" was used: */ - if (sscanf(t, "Button%d", &i) == 1) { - rfbLog(" event %d: button %d\n", - from, n+1, i); - if (i == 0) i = -1; /* bah */ - pointer_map[from][n].keysym = NoSymbol; - pointer_map[from][n].keycode = NoSymbol; - pointer_map[from][n].button = i; - pointer_map[from][n].end = 0; - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 0; - } else { - rfbLog("buttonparse: ignoring unknown " - "keysym: %s\n", t); - n--; - } - } else { - /* - * XXX may not work with -modtweak or -xkb - */ - X_LOCK; - kcode = XKeysymToKeycode(dpy, ksym); - - pointer_map[from][n].keysym = ksym; - pointer_map[from][n].keycode = kcode; - pointer_map[from][n].button = 0; - pointer_map[from][n].end = 0; - if (! ismodkey(ksym) ) { - /* do both down then up */ - pointer_map[from][n].down = 1; - pointer_map[from][n].up = 1; - } else { - if (modisdown[kcode]) { - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 1; - modisdown[kcode] = 0; - } else { - pointer_map[from][n].down = 1; - pointer_map[from][n].up = 0; - modisdown[kcode] = 1; - } - } - rfbLog(" event %d: keysym %s (0x%x) -> " - "keycode 0x%x down=%d up=%d\n", n+1, - XKeysymToString(ksym), ksym, kcode, - pointer_map[from][n].down, - pointer_map[from][n].up); - X_UNLOCK; - } - t = strtok(NULL, "+"); - n++; - } - - /* we must release any modifiers that are still down: */ - for (i=0; i<256; i++) { - kcode = (KeyCode) i; - if (n >= MAX_BUTTON_EVENTS) { - rfbLog("buttonparse: too many button map " - "events: %s\n", list); - break; - } - if (modisdown[kcode]) { - pointer_map[from][n].keysym = NoSymbol; - pointer_map[from][n].keycode = kcode; - pointer_map[from][n].button = 0; - pointer_map[from][n].end = 0; - pointer_map[from][n].down = 0; - pointer_map[from][n].up = 1; - modisdown[kcode] = 0; - n++; - } - } - - /* advance the source pointer position */ - (*s) += l+2; - } else { - /* single digit format */ - char str[2]; - str[0] = *q; - str[1] = '\0'; - - to = atoi(str); - if (to < 1) { - rfbLog("skipping invalid remap button \"%d\" for button" - " %d from string \"%s\"\n", - to, from, str); - } else { - rfbLog("remap button %d using \"%s\"\n", from, str); - rfbLog(" button: %d -> %d\n", from, to); - pointer_map[from][0].keysym = NoSymbol; - pointer_map[from][0].keycode = NoSymbol; - pointer_map[from][0].button = to; - pointer_map[from][0].end = 0; - pointer_map[from][0].down = 0; - pointer_map[from][0].up = 0; - } - /* advance the source pointer position */ - (*s)++; - } -} - -/* - * process the -buttonmap string - */ -void initialize_pointer_map(char *pointer_remap) { - unsigned char map[MAX_BUTTONS]; - int i, k; - /* - * This routine counts the number of pointer buttons on the X - * server (to avoid problems, even crashes, if a client has more - * buttons). And also initializes any pointer button remapping - * from -buttonmap option. - */ - - X_LOCK; - num_buttons = XGetPointerMapping(dpy, map, MAX_BUTTONS); - X_UNLOCK; - - if (num_buttons < 0) { - num_buttons = 0; - } - - /* FIXME: should use info in map[] */ - for (i=1; i<= MAX_BUTTONS; i++) { - for (k=0; k < MAX_BUTTON_EVENTS; k++) { - pointer_map[i][k].end = 1; - } - pointer_map[i][0].keysym = NoSymbol; - pointer_map[i][0].keycode = NoSymbol; - pointer_map[i][0].button = i; - pointer_map[i][0].end = 0; - pointer_map[i][0].down = 0; - pointer_map[i][0].up = 0; - } - - if (pointer_remap && *pointer_remap != '\0') { - /* -buttonmap, format is like: 12-21=2 */ - char *p, *q, *remap = strdup(pointer_remap); - int n; - - if ((p = strchr(remap, '=')) != NULL) { - /* undocumented max button number */ - n = atoi(p+1); - *p = '\0'; - if (n < num_buttons || num_buttons == 0) { - num_buttons = n; - } else { - rfbLog("warning: increasing number of mouse " - "buttons from %d to %d\n", num_buttons, n); - num_buttons = n; - } - } - if ((q = strchr(remap, '-')) != NULL) { - /* - * The '-' separates the 'from' and 'to' lists, - * then it is kind of like tr(1). - */ - char str[2]; - int from; - - rfbLog("remapping pointer buttons using string:\n"); - rfbLog(" \"%s\"\n", remap); - - p = remap; - q++; - i = 0; - str[1] = '\0'; - while (*p != '-') { - str[0] = *p; - from = atoi(str); - buttonparse(from, &q); - p++; - } - } - free(remap); - } -} - -typedef struct bout { - int x1, y1, x2, y2; -} blackout_t; -int blackouts; -blackout_t blackr[]; - -/* - * For use in the -wireframe stuff, save the stacking order of the direct - * children of the root window. Ideally done before we send ButtonPress - * to the X server. - */ -void snapshot_stack_list(int free_only, double allowed_age) { - static double last_snap = 0.0, last_free = 0.0; - double now; - int num, rc, i, j; - unsigned int ui; - Window r, w; - Window *list; - - 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) { - /* we really don't free it, just reset to zero windows */ - stack_list_num = 0; - last_free = now; - return; - } - - if (stack_list_num && now < last_snap + allowed_age) { - return; - } - - stack_list_num = 0; - last_free = now; - - X_LOCK; - rc = XQueryTree(dpy, rootwin, &r, &w, &list, &ui); - num = (int) ui; - - if (! rc) { - stack_list_num = 0; - last_free = now; - last_snap = 0.0; - X_UNLOCK; - return; - } - - last_snap = now; - if (num > stack_list_len + blackouts) { - int n = 2*num; - free(stack_list); - stack_list = (winattr_t *) malloc(n*sizeof(winattr_t)); - stack_list_len = n; - } - j = 0; - for (i=0; i<num; i++) { - stack_list[j].win = list[i]; - stack_list[j].fetched = 0; - stack_list[j].valid = 0; - stack_list[j].time = now; - j++; - } - for (i=0; i<blackouts; i++) { - stack_list[j].win = 0x1; - stack_list[j].fetched = 1; - stack_list[j].valid = 1; - stack_list[j].x = blackr[i].x1; - stack_list[j].y = blackr[i].y1; - stack_list[j].width = blackr[i].x2 - blackr[i].x1; - stack_list[j].height = blackr[i].y2 - blackr[i].y1; - stack_list[j].time = now; - stack_list[j].map_state = IsViewable; - stack_list[j].rx = -1; - stack_list[j].ry = -1; - j++; - -if (0) fprintf(stderr, "blackr: %d %dx%d+%d+%d\n", i, - stack_list[j-1].width, stack_list[j-1].height, - stack_list[j-1].x, stack_list[j-1].y); - - } - stack_list_num = num + blackouts; - if (debug_wireframe > 1) { - fprintf(stderr, "snapshot_stack_list: num=%d len=%d\n", - stack_list_num, stack_list_len); - } - - 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 (win != None && win < 10) { - ; /* special, blackout */ - } else 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. - */ -static void update_x11_pointer_position(int x, int y) { - int rc; - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - X_LOCK; - if (use_xwarppointer) { - /* - * off_x and off_y not needed with XWarpPointer since - * window is used: - */ - XWarpPointer(dpy, None, window, 0, 0, 0, 0, x + coff_x, - y + coff_y); - } else { - XTestFakeMotionEvent_wr(dpy, scr, x + off_x + coff_x, - y + off_y + coff_y, CurrentTime); - } - X_UNLOCK; - - if (cursor_x != x || cursor_y != y) { - last_pointer_motion_time = dnow(); - } - - cursor_x = x; - cursor_y = y; - - /* record the x, y position for the rfb screen as well. */ - cursor_position(x, y); - - /* change the cursor shape if necessary */ - rc = set_cursor(x, y, get_which_cursor()); - cursor_changes += rc; - - 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 snapped, xr_mouse = 1, i; - - last_event = last_input = last_pointer_input = time(0); - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - if (mask != button_mask) { - last_pointer_click_time = dnow(); - } - - if (nofb) { - xr_mouse = 0; - } else if (!strcmp(scroll_copyrect, "never")) { - xr_mouse = 0; - } else if (!strcmp(scroll_copyrect, "keys")) { - xr_mouse = 0; - } else if (skip_cr_when_scaling("scroll")) { - xr_mouse = 0; - } else if (xrecord_skip_button(mask, button_mask)) { - xr_mouse = 0; - } - - if (mask && use_xrecord && ! xrecording && xr_mouse) { - 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, &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 { - got_wm_frame = 0; - } - } - 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; - int btn4 = (1<<3); - int btn5 = (1<<4); - - if (near_scrollbar_edge(x, y, w, h, px, py)) { - ok = 1; - } - if (mask & (btn4|btn5)) { - /* scroll wheel mouse */ - ok = 1; - } - if (mwin != None) { - /* skinny internal window */ - 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) { - xrecord_watch(1, SCR_MOUSE); - snapshot_stack_list(0, 0.50); - snapped = 1; - if (button_mask) { - xrecord_set_by_mouse = 1; - } else { - update_stack_list(); - xrecord_set_by_mouse = 2; - } - } - } - - if (mask && !button_mask) { - /* button down, snapshot the stacking list before flushing */ - if (wireframe && !wireframe_in_progress && - strcmp(wireframe_copyrect, "never")) { - if (! snapped) { - snapshot_stack_list(0, 0.0); - } - } - } - - 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)) ) { - if (debug_pointer) { - rfbLog("pointer(): mask change: mask: 0x%x -> " - "0x%x button: %d\n", button_mask, mask,i+1); - } - do_button_mask_change(mask, i+1); /* button # is i+1 */ - } - } - - X_UNLOCK; - - /* - * Remember the button state for next time and also for the - * -nodragging case: - */ - button_mask_prev = button_mask; - button_mask = mask; -} - -/* for -pipeinput */ - - -void pipe_pointer(int mask, int x, int y, rfbClientPtr client) { - int can_input = 0, uid; - allowed_input_t input; - ClientData *cd = (ClientData *) client->clientData; - char hint[MAX_BUTTONS * 20]; - - if (pipeinput_fh == NULL) { - return; - } - - if (! view_only) { - get_allowed_input(client, &input); - if (input.motion || input.button) { - can_input = 1; /* XXX distinguish later */ - } - } - uid = cd->uid; - if (! can_input) { - uid = -uid; - } - - hint[0] = '\0'; - if (mask == button_mask) { - strcat(hint, "None"); - } else { - int i, old, new, m = 1, cnt = 0; - for (i=0; i<MAX_BUTTONS; i++) { - char s[20]; - - old = button_mask & m; - new = mask & m; - m = m << 1; - - if (old == new) { - continue; - } - if (hint[0] != '\0') { - strcat(hint, ","); - } - if (new && ! old) { - sprintf(s, "ButtonPress-%d", i+1); - cnt++; - } else if (! new && old) { - sprintf(s, "ButtonRelease-%d", i+1); - cnt++; - } - strcat(hint, s); - } - if (! cnt) { - strcpy(hint, "None"); - } - } - - fprintf(pipeinput_fh, "Pointer %d %d %d %d %s\n", uid, x, y, - mask, hint); - fflush(pipeinput_fh); - check_pipeinput(); -} - -/* - * Actual callback from libvncserver when it gets a pointer event. - * This may queue pointer events rather than sending them immediately - * to the X server. (see update_x11_pointer*()) - */ -void pointer(int mask, int x, int y, rfbClientPtr client) { - allowed_input_t input; - int sent = 0, buffer_it = 0; - double now; - - if (mask >= 0) { - got_pointer_calls++; - } - - 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; - } else { - 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) " - "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) { - /* map from rfb size to X11 size: */ - x = ((double) x / scaled_x) * dpy_x; - x = nfix(x, dpy_x); - y = ((double) y / scaled_y) * dpy_y; - y = nfix(y, dpy_y); - } - - if (pipeinput_fh != NULL && mask >= 0) { - pipe_pointer(mask, x, y, client); - if (! pipeinput_tee) { - if (! view_only || raw_fb) { /* raw_fb hack */ - got_user_input++; - got_keyboard_input++; - last_pointer_client = client; - } - if (view_only && raw_fb) { - /* raw_fb hack track button state */ - button_mask_prev = button_mask; - button_mask = mask; - } - return; - } - } - - if (view_only) { - return; - } - - now = dnow(); - - 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; - - last_pointer_time = now; - - if (blackout_ptr && blackouts) { - int b, ok = 1; - /* see if it goes into the blacked out region */ - for (b=0; b < blackouts; b++) { - if (x < blackr[b].x1 || x > blackr[b].x2) { - continue; - } - if (y < blackr[b].y1 || y > blackr[b].y2) { - continue; - } - /* x1 <= x <= x2 and y1 <= y <= y2 */ - ok = 0; - break; - } - if (! ok) { - if (debug_pointer) { - rfbLog("pointer(): blackout_ptr skipping " - "x=%d y=%d in rectangle %d,%d %d,%d\n", x, y, - blackr[b].x1, blackr[b].y1, - blackr[b].x2, blackr[b].y2); - } - return; - } - } - } - - /* - * The following is hopefully an improvement wrt response during - * pointer user input (window drags) for the threaded case. - * See check_user_input() for the more complicated things we do - * in the non-threaded case. - */ - if ((use_threads && pointer_mode != 1) || pointer_flush_delay > 0.0) { -# define NEV 32 - /* storage for the event queue */ - static int mutex_init = 0; - static int nevents = 0; - static int ev[NEV][3]; - int i; - /* timer things */ - static double dt = 0.0, tmr = 0.0, maxwait = 0.4; - - if (! mutex_init) { - INIT_MUTEX(pointerMutex); - 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); - - /* - * If the framebuffer is being copied in another thread - * (scan_for_updates()), we will queue up to 32 pointer - * events for later. The idea is by delaying these input - * events, the screen is less likely to change during the - * copying period, and so will give rise to less window - * "tearing". - * - * Tearing is not completely eliminated because we do - * not suspend work in the other libvncserver threads. - * Maybe that is a possibility with a mutex... - */ - if (buffer_it) { - /* - * mask = -1 is an all-clear signal from - * scan_for_updates(). - * - * dt is a timer in seconds; we only queue for so long. - */ - dt += dtime(&tmr); - - if (nevents < NEV && dt < maxwait) { - i = nevents++; - ev[i][0] = mask; - ev[i][1] = x; - ev[i][2] = y; - if (! input.button) { - ev[i][0] = -1; - } - if (! input.motion) { - ev[i][1] = -1; - ev[i][2] = -1; - } - UNLOCK(pointerMutex); - if (debug_pointer) { - rfbLog("pointer(): deferring event %d" - " %.4f\n", i, tmr - x11vnc_start); - } - return; - } - } - - /* 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 %.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) { - 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; - } - } - } - nevents = 0; /* reset everything */ - dt = 0.0; - dtime0(&tmr); - - UNLOCK(pointerMutex); - } - if (mask < 0) { /* -1 just means flush the event queue */ - if (debug_pointer) { - rfbLog("pointer(): flush only. %.4f\n", - dnow() - x11vnc_start); - } - return; - } - - /* update the X display with the event: */ - if (input.motion) { - update_x11_pointer_position(x, y); - sent = 1; - } - if (input.button) { - if (mask != button_mask) { - button_change_x = cursor_x; - button_change_y = cursor_y; - } - update_x11_pointer_mask(mask); - sent = 1; - } - - if (nofb && sent) { - /* - * nofb is for, e.g. Win2VNC, where fastest pointer - * updates are desired. - */ - 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; - } -} - -int check_pipeinput(void) { - if (! pipeinput_fh) { - return 1; - } - if (ferror(pipeinput_fh)) { - rfbLog("pipeinput pipe has ferror. %p\n", pipeinput_fh); - - if (pipeinput_opts && strstr(pipeinput_opts, "reopen")) { - rfbLog("restarting -pipeinput pipe...\n"); - initialize_pipeinput(); - if (pipeinput_fh) { - return 1; - } else { - return 0; - } - } else { - rfbLog("closing -pipeinput pipe...\n"); - pclose(pipeinput_fh); - pipeinput_fh = NULL; - return 0; - } - } - return 1; -} - -void initialize_pipeinput(void) { - char *p; - - if (pipeinput_fh != NULL) { - rfbLog("closing pipeinput stream: %p\n", pipeinput_fh); - pclose(pipeinput_fh); - pipeinput_fh = NULL; - } - - pipeinput_tee = 0; - if (pipeinput_opts) { - free(pipeinput_opts); - pipeinput_opts = NULL; - } - - if (! pipeinput_str) { - return; - } - - /* look for options: tee, reopen, ... */ - p = strchr(pipeinput_str, ':'); - if (p != NULL) { - char *str, *opt, *q; - int got = 0; - *p = '\0'; - str = strdup(pipeinput_str); - opt = strdup(pipeinput_str); - *p = ':'; - q = strtok(str, ","); - while (q) { - if (!strcmp(q, "key") || !strcmp(q, "keycodes")) { - got = 1; - } - if (!strcmp(q, "reopen")) { - got = 1; - } - if (!strcmp(q, "tee")) { - pipeinput_tee = 1; - got = 1; - } - q = strtok(NULL, ","); - } - if (got) { - pipeinput_opts = opt; - } else { - free(opt); - } - free(str); - p++; - } else { - p = pipeinput_str; - } - - set_child_info(); - if (no_external_cmds) { - rfbLogEnable(1); - rfbLog("cannot run external commands in -nocmds mode:\n"); - rfbLog(" \"%s\"\n", p); - rfbLog(" exiting.\n"); - clean_up_exit(1); - } - rfbLog("pipeinput: starting: \"%s\"...\n", p); - pipeinput_fh = popen(p, "w"); - - if (! pipeinput_fh) { - rfbLog("popen(\"%s\", \"w\") failed.\n", p); - rfbLogPerror("popen"); - rfbLog("Disabling -pipeinput mode.\n"); - return; - } - - fprintf(pipeinput_fh, "%s", -"# \n" -"# Format of the -pipeinput stream:\n" -"# --------------------------------\n" -"#\n" -"# Lines like these beginning with '#' are to be ignored.\n" -"#\n" -"# Pointer events (mouse motion and button clicks) come in the form:\n" -"#\n" -"#\n" -"# Pointer <client#> <x> <y> <mask> <hint>\n" -"#\n" -"#\n" -"# The <client#> is a decimal integer uniquely identifying the client\n" -"# that generated the event. If it is negative that means this event\n" -"# would have been discarded since the client was viewonly.\n" -"#\n" -"# <x> and <y> are decimal integers reflecting the position on the screen\n" -"# the event took place at.\n" -"#\n" -"# <mask> is the button mask indicating the button press state, as normal\n" -"# 0 means no buttons pressed, 1 means button 1 is down 3 (11) means buttons\n" -"# 1 and 2 are down, etc.\n" -"#\n" -"# <hint> is a string containing no spaces and may be ignored.\n" -"# It contains some interpretation about what has happened.\n" -"# It can be:\n" -"#\n" -"# None (nothing to report)\n" -"# ButtonPress-N (this event will cause button-1 to be pressed) \n" -"# ButtonRelease-N (this event will cause button-1 to be released) \n" -"#\n" -"# if two more more buttons change state in one event they are listed\n" -"# separated by commas.\n" -"#\n" -"# One might parse a Pointer line with:\n" -"#\n" -"# int client, x, y, mask; char *hint;\n" -"# sscanf(line, \"Pointer %d %d %d %s\", &client, &x, &y, &mask, &hint);\n" -"#\n" -"#\n" -"# Keysym events (keyboard presses and releases) come in the form:\n" -"#\n" -"#\n" -"# Keysym <client#> <down> <keysym#> <keysym-name> <hint>\n" -"#\n" -"#\n" -"# The <client#> is as with Pointer.\n" -"#\n" -"# <down> is a decimal either 1 or 0 indicating KeyPress or KeyRelease,\n" -"# respectively.\n" -"#\n" -"# <keysym#> is a decimal integer incidating the Keysym of the event.\n" -"#\n" -"# <keysym-name> is the corresponding Keysym name.\n" -"#\n" -"# See the file /usr/include/X11/keysymdef.h for the mappings.\n" -"# You basically remove the leading 'XK_' prefix from the macro name in\n" -"# that file to get the Keysym name.\n" -"#\n" -"# One might parse a Keysym line with:\n" -"#\n" -"# int client, down, keysym; char *name, *hint;\n" -"# sscanf(line, \"Keysym %d %d %s %s\", &client, &down, &keysym, &name, &hint);\n" -"#\n" -"# The <hint> value is currently just None, KeyPress, or KeyRelease.\n" -"#\n" -"# In the future <hint> will provide a hint for the sequence of KeyCodes\n" -"# (i.e. keyboard scancodes) that x11vnc would inject to an X display to\n" -"# simulate the Keysym.\n" -"#\n" -"# You see, some Keysyms will require more than one injected Keycode to\n" -"# generate the symbol. E.g. the Keysym \"ampersand\" going down usually\n" -"# requires a Shift key going down, then the key with the \"&\" on it going\n" -"# down, and, perhaps, the Shift key going up (that is how x11vnc does it).\n" -"#\n" -"# The Keysym => Keycode(s) stuff gets pretty messy. Hopefully the Keysym\n" -"# info will be enough for most purposes (having identical keyboards on\n" -"# both sides helps).\n" -"#\n" -"# Here comes your stream. The following token will always indicate the\n" -"# end of this informational text:\n" -"# END_OF_TOP\n" -); - fflush(pipeinput_fh); - if (raw_fb_str) { - /* the pipe program may actually create the fb */ - sleep(1); - } -} - -/* -- xkb_bell.c -- */ -/* - * Bell event handling. Requires XKEYBOARD extension. - */ -int xkb_base_event_type = 0; - -#if LIBVNCSERVER_HAVE_XKEYBOARD -/* - * check for XKEYBOARD, set up xkb_base_event_type - */ -void initialize_xkb(void) { - int ir, reason; - int op, ev, er, maj, min; - - if (xkbcompat) { - xkb_present = 0; - } else if (! XkbQueryExtension(dpy, &op, &ev, &er, &maj, &min)) { - if (! quiet) { - rfbLog("warning: XKEYBOARD extension not present.\n"); - } - xkb_present = 0; - } else { - xkb_present = 1; - } - - if (! xkb_present) { - return; - } - - if (! XkbOpenDisplay(DisplayString(dpy), &xkb_base_event_type, &ir, - NULL, NULL, &reason) ) { - if (! quiet) { - rfbLog("warning: disabling XKEYBOARD. XkbOpenDisplay" - " failed.\n"); - } - xkb_base_event_type = 0; - xkb_present = 0; - } -} - -void initialize_watch_bell(void) { - if (! xkb_present) { - if (! quiet) { - rfbLog("warning: disabling bell. XKEYBOARD ext. " - "not present.\n"); - } - watch_bell = 0; - sound_bell = 0; - return; - } - - XkbSelectEvents(dpy, XkbUseCoreKbd, XkbBellNotifyMask, 0); - - if (! watch_bell) { - return; - } - if (! XkbSelectEvents(dpy, XkbUseCoreKbd, XkbBellNotifyMask, - XkbBellNotifyMask) ) { - if (! quiet) { - rfbLog("warning: disabling bell. XkbSelectEvents" - " failed.\n"); - } - watch_bell = 0; - sound_bell = 0; - } -} - -/* - * We call this periodically to process any bell events that have - * taken place. - */ -void check_bell_event(void) { - XEvent xev; - XkbAnyEvent *xkb_ev; - int got_bell = 0; - - if (! xkb_base_event_type) { - return; - } - - /* caller does X_LOCK */ - if (! XCheckTypedEvent(dpy, xkb_base_event_type, &xev)) { - return; - } - if (! watch_bell) { - /* we return here to avoid xkb events piling up */ - return; - } - - xkb_ev = (XkbAnyEvent *) &xev; - if (xkb_ev->xkb_type == XkbBellNotify) { - got_bell = 1; - } - - if (got_bell && sound_bell) { - if (! all_clients_initialized()) { - rfbLog("check_bell_event: not sending bell: " - "uninitialized clients\n"); - } else { - if (screen && client_count) { - rfbSendBell(screen); - } - } - } -} -#else -void initialize_watch_bell(void) { - watch_bell = 0; - sound_bell = 0; -} -void check_bell_event(void) {} -#endif - -/* -- xrandr.h -- */ - -time_t last_subwin_trap = 0; -int subwin_trap_count = 0; - -XErrorHandler old_getimage_handler; -#define XRANDR_SET_TRAP_RET(x,y) \ - if (subwin || xrandr) { \ - trapped_getimage_xerror = 0; \ - old_getimage_handler = XSetErrorHandler(trap_getimage_xerror); \ - if (check_xrandr_event(y)) { \ - trapped_getimage_xerror = 0; \ - XSetErrorHandler(old_getimage_handler); \ - return(x); \ - } \ - } -#define XRANDR_CHK_TRAP_RET(x,y) \ - if (subwin || xrandr) { \ - if (trapped_getimage_xerror) { \ - if (subwin) { \ - static int last = 0; \ - subwin_trap_count++; \ - if (time(0) > last_subwin_trap + 60) { \ - rfbLog("trapped GetImage xerror" \ - " in SUBWIN mode. [%d]\n", \ - subwin_trap_count); \ - last_subwin_trap = time(0); \ - last = subwin_trap_count; \ - } \ - if (subwin_trap_count - last > 30) { \ - /* window probably iconified */ \ - usleep(1000*1000); \ - } \ - } else { \ - rfbLog("trapped GetImage xerror" \ - " in XRANDR mode.\n"); \ - } \ - trapped_getimage_xerror = 0; \ - XSetErrorHandler(old_getimage_handler); \ - check_xrandr_event(y); \ - X_UNLOCK; \ - return(x); \ - } \ - } - -/* -- xrandr.c -- */ - -void initialize_xrandr(void) { - if (xrandr_present) { -#if LIBVNCSERVER_HAVE_LIBXRANDR - Rotation rot; - - X_LOCK; - xrandr_width = XDisplayWidth(dpy, scr); - xrandr_height = XDisplayHeight(dpy, scr); - XRRRotations(dpy, scr, &rot); - xrandr_rotation = (int) rot; - if (xrandr) { - XRRSelectInput(dpy, rootwin, RRScreenChangeNotifyMask); - } else { - XRRSelectInput(dpy, rootwin, 0); - } - X_UNLOCK; -#endif - } else if (xrandr) { - rfbLog("-xrandr mode specified, but no RANDR support on\n"); - rfbLog(" display or in client library. Disabling -xrandr " - "mode.\n"); - xrandr = 0; - } -} - -void handle_xrandr_change(int, int); - -int handle_subwin_resize(char *msg) { - int new_x, new_y; - int i, check = 10, ms = 250; /* 2.5 secs total... */ - - if (msg) {} /* unused vars warning: */ - if (! subwin) { - return 0; /* hmmm... */ - } - if (! valid_window(subwin, NULL, 0)) { - rfbLogEnable(1); - rfbLog("subwin 0x%lx went away!\n", subwin); - X_UNLOCK; - clean_up_exit(1); - } - if (! get_window_size(subwin, &new_x, &new_y)) { - rfbLogEnable(1); - rfbLog("could not get size of subwin 0x%lx\n", subwin); - X_UNLOCK; - clean_up_exit(1); - } - if (wdpy_x == new_x && wdpy_y == new_y) { - /* no change */ - return 0; - } - - /* window may still be changing (e.g. drag resize) */ - for (i=0; i < check; i++) { - int newer_x, newer_y; - usleep(ms * 1000); - - if (! get_window_size(subwin, &newer_x, &newer_y)) { - rfbLogEnable(1); - rfbLog("could not get size of subwin 0x%lx\n", subwin); - clean_up_exit(1); - } - if (new_x == newer_x && new_y == newer_y) { - /* go for it... */ - break; - } else { - rfbLog("subwin 0x%lx still changing size...\n", subwin); - new_x = newer_x; - new_y = newer_y; - } - } - - rfbLog("subwin 0x%lx new size: x: %d -> %d, y: %d -> %d\n", - subwin, wdpy_x, new_x, wdpy_y, new_y); - rfbLog("calling handle_xrandr_change() for resizing\n"); - - X_UNLOCK; - handle_xrandr_change(new_x, new_y); - return 1; -} - -int known_xrandr_mode(char *); - -void handle_xrandr_change(int new_x, int new_y) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - - /* sanity check xrandr_mode */ - if (! xrandr_mode) { - xrandr_mode = strdup("default"); - } else if (! known_xrandr_mode(xrandr_mode)) { - free(xrandr_mode); - xrandr_mode = strdup("default"); - } - rfbLog("xrandr_mode: %s\n", xrandr_mode); - if (!strcmp(xrandr_mode, "exit")) { - close_all_clients(); - rfbLog(" shutting down due to XRANDR event.\n"); - clean_up_exit(0); - } - if (!strcmp(xrandr_mode, "newfbsize") && screen) { - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->useNewFBSize) { - continue; - } - rfbLog(" closing client %s (no useNewFBSize" - " support).\n", cl->host); - rfbCloseClient(cl); - rfbClientConnectionGone(cl); - } - rfbReleaseClientIterator(iter); - } - - /* default, resize, and newfbsize create a new fb: */ - rfbLog("check_xrandr_event: trying to create new framebuffer...\n"); - if (new_x < wdpy_x || new_y < wdpy_y) { - check_black_fb(); - } - do_new_fb(1); - rfbLog("check_xrandr_event: fb WxH: %dx%d\n", wdpy_x, wdpy_y); -} - -int check_xrandr_event(char *msg) { - XEvent xev; - if (subwin) { - return handle_subwin_resize(msg); - } -#if LIBVNCSERVER_HAVE_LIBXRANDR - if (! xrandr || ! xrandr_present) { - return 0; - } - if (xrandr_base_event_type && XCheckTypedEvent(dpy, - xrandr_base_event_type + RRScreenChangeNotify, &xev)) { - int do_change; - XRRScreenChangeNotifyEvent *rev; - - rev = (XRRScreenChangeNotifyEvent *) &xev; - rfbLog("check_xrandr_event():\n"); - rfbLog("Detected XRANDR event at location '%s':\n", msg); - rfbLog(" serial: %d\n", (int) rev->serial); - rfbLog(" timestamp: %d\n", (int) rev->timestamp); - rfbLog(" cfg_timestamp: %d\n", (int) rev->config_timestamp); - rfbLog(" size_id: %d\n", (int) rev->size_index); - rfbLog(" sub_pixel: %d\n", (int) rev->subpixel_order); - rfbLog(" rotation: %d\n", (int) rev->rotation); - rfbLog(" width: %d\n", (int) rev->width); - rfbLog(" height: %d\n", (int) rev->height); - rfbLog(" mwidth: %d mm\n", (int) rev->mwidth); - rfbLog(" mheight: %d mm\n", (int) rev->mheight); - rfbLog("\n"); - rfbLog("check_xrandr_event: previous WxH: %dx%d\n", - wdpy_x, wdpy_y); - if (wdpy_x == rev->width && wdpy_y == rev->height && - xrandr_rotation == (int) rev->rotation) { - rfbLog("check_xrandr_event: no change detected.\n"); - do_change = 0; - } else { - do_change = 1; - } - - xrandr_width = rev->width; - xrandr_height = rev->height; - xrandr_timestamp = rev->timestamp; - xrandr_cfg_time = rev->config_timestamp; - xrandr_rotation = (int) rev->rotation; - - rfbLog("check_xrandr_event: updating config...\n"); - XRRUpdateConfiguration(&xev); - - if (do_change) { - X_UNLOCK; - handle_xrandr_change(rev->width, rev->height); - } - rfbLog("check_xrandr_event: current WxH: %dx%d\n", - XDisplayWidth(dpy, scr), XDisplayHeight(dpy, scr)); - rfbLog("check_xrandr_event(): returning control to" - " caller...\n"); - return do_change; - } -#endif - return 0; -} - -int known_xrandr_mode(char *s) { -/* - * default: - * resize: the default - * exit: shutdown clients and exit. - * newfbsize: shutdown clients that do not support NewFBSize encoding. - */ - if (strcmp(s, "default") && strcmp(s, "resize") && - strcmp(s, "exit") && strcmp(s, "newfbsize")) { - return 0; - } else { - return 1; - } -} - -int known_sigpipe_mode(char *s) { -/* - * skip, ignore, exit - */ - if (strcmp(s, "skip") && strcmp(s, "ignore") && - strcmp(s, "exit")) { - return 0; - } else { - return 1; - } -} - -/* -- selection.c -- */ -/* - * Selection/Cutbuffer/Clipboard handlers. - */ - -static int own_selection = 0; /* whether we currently own PRIMARY or not */ -static int set_cutbuffer = 0; /* to avoid bouncing the CutText right back */ -static int sel_waittime = 15; /* some seconds to skip before first send */ -static Window selwin; /* special window for our selection */ - -/* - * This is where we keep our selection: the string sent TO us from VNC - * clients, and the string sent BY us to requesting X11 clients. - */ -static char *xcut_str = NULL; - -/* - * Our callbacks instruct us to check for changes in the cutbuffer - * and PRIMARY selection on the local X11 display. - * - * We store the new cutbuffer and/or PRIMARY selection data in this - * constant sized array selection_str[]. - * TODO: check if malloc does not cause performance issues (esp. WRT - * SelectionNotify handling). - */ -#define PROP_MAX (131072L) -static char selection_str[PROP_MAX+1]; - -/* - * An X11 (not VNC) client on the local display has requested the selection - * from us (because we are the current owner). - * - * n.b.: our caller already has the X_LOCK. - */ -static void selection_request(XEvent *ev) { - XSelectionEvent notify_event; - XSelectionRequestEvent *req_event; - XErrorHandler old_handler; - unsigned int length; - unsigned char *data; -#ifndef XA_LENGTH - unsigned long XA_LENGTH = XInternAtom(dpy, "LENGTH", True); -#endif - - req_event = &(ev->xselectionrequest); - notify_event.type = SelectionNotify; - notify_event.display = req_event->display; - notify_event.requestor = req_event->requestor; - notify_event.selection = req_event->selection; - notify_event.target = req_event->target; - notify_event.time = req_event->time; - - if (req_event->property == None) { - notify_event.property = req_event->target; - } else { - notify_event.property = req_event->property; - } - if (xcut_str) { - length = strlen(xcut_str); - } else { - length = 0; - } - - /* the window may have gone away, so trap errors */ - trapped_xerror = 0; - old_handler = XSetErrorHandler(trap_xerror); - - if (ev->xselectionrequest.target == XA_LENGTH) { - /* length request */ - - XChangeProperty(ev->xselectionrequest.display, - ev->xselectionrequest.requestor, - ev->xselectionrequest.property, - ev->xselectionrequest.target, 32, PropModeReplace, - (unsigned char *) &length, sizeof(unsigned int)); - - } else { - /* data request */ - - data = (unsigned char *)xcut_str; - - XChangeProperty(ev->xselectionrequest.display, - ev->xselectionrequest.requestor, - ev->xselectionrequest.property, - ev->xselectionrequest.target, 8, PropModeReplace, - data, length); - } - - if (! trapped_xerror) { - XSendEvent(req_event->display, req_event->requestor, False, 0, - (XEvent *)¬ify_event); - } - if (trapped_xerror) { - rfbLog("selection_request: ignored XError while sending " - "PRIMARY selection to 0x%x.\n", req_event->requestor); - } - XSetErrorHandler(old_handler); - trapped_xerror = 0; - - XFlush(dpy); -} - -int check_sel_direction(char *dir, char *label, char *sel, int len) { - int db = 0, ok = 1; - if (sel_direction) { - if (strstr(sel_direction, "debug")) { - db = 1; - } - if (strcmp(sel_direction, "debug")) { - if (strstr(sel_direction, dir) == NULL) { - ok = 0; - } - } - } - if (db) { - char str[40]; - int n = 40; - strncpy(str, sel, n); - str[n-1] = '\0'; - if (len < n) { - str[len] = '\0'; - } - rfbLog("%s: %s...\n", label, str); - if (ok) { - rfbLog("%s: %s-ing it.\n", label, dir); - } else { - rfbLog("%s: NOT %s-ing it.\n", label, dir); - } - } - return ok; -} - -/* - * CUT_BUFFER0 property on the local display has changed, we read and - * store it and send it out to any connected VNC clients. - * - * n.b.: our caller already has the X_LOCK. - */ -static void cutbuffer_send(void) { - Atom type; - int format, slen, dlen, len; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - selection_str[0] = '\0'; - slen = 0; - - /* read the property value into selection_str: */ - do { - if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), - XA_CUT_BUFFER0, nitems/4, PROP_MAX/16, False, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > PROP_MAX) { - /* too big */ - rfbLog("warning: truncating large CUT_BUFFER0" - " selection > %d bytes.\n", PROP_MAX); - XFree(data); - break; - } - memcpy(selection_str+slen, data, dlen); - slen += dlen; - selection_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - selection_str[PROP_MAX] = '\0'; - - if (! all_clients_initialized()) { - rfbLog("cutbuffer_send: no send: uninitialized clients\n"); - return; /* some clients initializing, cannot send */ - } - - /* now send it to any connected VNC clients (rfbServerCutText) */ - if (!screen) { - return; - } - len = strlen(selection_str); - if (check_sel_direction("send", "cutbuffer_send", selection_str, len)) { - rfbSendServerCutText(screen, selection_str, len); - } -} - -/* - * "callback" for our SelectionNotify polling. We try to determine if - * the PRIMARY selection has changed (checking length and first CHKSZ bytes) - * and if it has we store it and send it off to any connected VNC clients. - * - * n.b.: our caller already has the X_LOCK. - * - * TODO: if we were willing to use libXt, we could perhaps get selection - * timestamps to speed up the checking... XtGetSelectionValue(). - * - * Also: XFIXES has XFixesSelectSelectionInput(). - */ -#define CHKSZ 32 -static void selection_send(XEvent *ev) { - Atom type; - int format, slen, dlen, oldlen, newlen, toobig = 0, len; - static int err = 0, sent_one = 0; - char before[CHKSZ], after[CHKSZ]; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - /* - * remember info about our last value of PRIMARY (or CUT_BUFFER0) - * so we can check for any changes below. - */ - oldlen = strlen(selection_str); - strncpy(before, selection_str, CHKSZ); - - selection_str[0] = '\0'; - slen = 0; - - /* read in the current value of PRIMARY: */ - do { - if (XGetWindowProperty(dpy, ev->xselection.requestor, - ev->xselection.property, nitems/4, PROP_MAX/16, True, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > PROP_MAX) { - /* too big */ - toobig = 1; - XFree(data); - if (err) { /* cut down on messages */ - break; - } else { - err = 5; - } - rfbLog("warning: truncating large PRIMARY" - " selection > %d bytes.\n", PROP_MAX); - break; - } - memcpy(selection_str+slen, data, dlen); - slen += dlen; - selection_str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); - - if (! toobig) { - err = 0; - } else if (err) { - err--; - } - - if (! sent_one) { - /* try to force a send first time in */ - oldlen = -1; - sent_one = 1; - } - - /* look for changes in the new value */ - newlen = strlen(selection_str); - strncpy(after, selection_str, CHKSZ); - - if (oldlen == newlen && strncmp(before, after, CHKSZ) == 0) { - /* evidently no change */ - return; - } - if (newlen == 0) { - /* do not bother sending a null string out */ - return; - } - - if (! all_clients_initialized()) { - rfbLog("selection_send: no send: uninitialized clients\n"); - return; /* some clients initializing, cannot send */ - } - - /* now send it to any connected VNC clients (rfbServerCutText) */ - if (!screen) { - return; - } - - len = newlen; - if (check_sel_direction("send", "selection_send", selection_str, len)) { - rfbSendServerCutText(screen, selection_str, len); - } -} - -/* -- xevents.c -- */ - -void initialize_vnc_connect_prop() { - vnc_connect_str[0] = '\0'; - vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False); -} - -void initialize_xevents(void) { - static int did_xselect_input = 0; - static int did_xcreate_simple_window = 0; - static int did_vnc_connect_prop = 0; - static int did_xfixes = 0; - static int did_xdamage = 0; - static int did_xrandr = 0; - - if ((watch_selection || vnc_connect) && !did_xselect_input) { - /* - * register desired event(s) for notification. - * PropertyChangeMask is for CUT_BUFFER0 changes. - * XXX: does this cause a flood of other stuff? - */ - X_LOCK; - XSelectInput(dpy, rootwin, PropertyChangeMask); - X_UNLOCK; - did_xselect_input = 1; - } - if (watch_selection && !did_xcreate_simple_window) { - /* create fake window for our selection ownership, etc */ - - X_LOCK; - selwin = XCreateSimpleWindow(dpy, rootwin, 0, 0, 1, 1, 0, 0, 0); - X_UNLOCK; - did_xcreate_simple_window = 1; - } - - if (xrandr && !did_xrandr) { - initialize_xrandr(); - did_xrandr = 1; - } - if (vnc_connect && !did_vnc_connect_prop) { - initialize_vnc_connect_prop(); - did_vnc_connect_prop = 1; - } - if (xfixes_present && use_xfixes && !did_xfixes) { - initialize_xfixes(); - did_xfixes = 1; - } - if (xdamage_present && !did_xdamage) { - initialize_xdamage(); - did_xdamage = 1; - } -} - -void print_xevent_bases(void) { - fprintf(stderr, "X event bases: xkb=%d, xtest=%d, xrandr=%d, " - "xfixes=%d, xdamage=%d, xtrap=%d\n", xkb_base_event_type, - xtest_base_event_type, xrandr_base_event_type, - xfixes_base_event_type, xdamage_base_event_type, - xtrap_base_event_type); - fprintf(stderr, " MapNotify=%d, ClientMsg=%d PropNotify=%d " - "SelNotify=%d, SelRequest=%d\n", MappingNotify, ClientMessage, - PropertyNotify, SelectionNotify, SelectionRequest); - fprintf(stderr, " SelClear=%d, Expose=%d\n", SelectionClear, Expose); -} - - -void get_prop(char *str, int len, Atom prop) { - Atom type; - int format, slen, dlen, i; - unsigned long nitems = 0, bytes_after = 0; - unsigned char* data = NULL; - - for (i=0; i<len; i++) { - str[i] = '\0'; - } - if (prop == None) { - return; - } - - slen = 0; - - do { - if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), - prop, nitems/4, len/16, False, - AnyPropertyType, &type, &format, &nitems, &bytes_after, - &data) == Success) { - - dlen = nitems * (format/8); - if (slen + dlen > len) { - /* too big */ - XFree(data); - break; - } - memcpy(str+slen, data, dlen); - slen += dlen; - str[slen] = '\0'; - XFree(data); - } - } while (bytes_after > 0); -} - -void bust_grab(int reset) { - static int bust_count = 0; - static time_t last_bust = 0; - time_t now = time(0); - KeyCode key; - int button, x, y, nb; - - if (now > last_bust + 180) { - bust_count = 0; - } - if (reset) { - bust_count = 0; - return; - } - - x = 0; - y = 0; - button = 0; - key = NoSymbol; - - nb = 8; - if (bust_count >= 3 * nb) { - fprintf(stderr, "too many bust_grab's %d for me\n", bust_count); - exit(0); - } - if (bust_count % nb == 0) { - button = 1; - } else if (bust_count % nb == 1) { - button = 1; - } else if (bust_count % nb == 2) { - key = XKeysymToKeycode(dpy, XK_Escape); - } else if (bust_count % nb == 3) { - button = 3; - } else if (bust_count % nb == 4) { - key = XKeysymToKeycode(dpy, XK_space); - } else if (bust_count % nb == 5) { - x = bust_count * 23; - y = bust_count * 17; - } else if (bust_count % nb == 5) { - button = 2; - } else if (bust_count % nb == 6) { - key = XKeysymToKeycode(dpy, XK_a); - } - - if (key == NoSymbol) { - key = XKeysymToKeycode(dpy, XK_a); - if (key == NoSymbol) { - button = 1; - } - } - - bust_count++; - - if (button) { - /* try button press+release */ - fprintf(stderr, "**bust_grab: button%d %.4f\n", - button, dnowx()); - XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); - XFlush(dpy); - usleep(50 * 1000); - XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); - } else if (x > 0) { - /* try button motion*/ - int scr = DefaultScreen(dpy); - - fprintf(stderr, "**bust_grab: x=%d y=%d %.4f\n", x, y, - dnowx()); - XTestFakeMotionEvent_wr(dpy, scr, x, y, CurrentTime); - XFlush(dpy); - usleep(50 * 1000); - - /* followed by button press */ - button = 1; - fprintf(stderr, "**bust_grab: button%d\n", button); - XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); - XFlush(dpy); - usleep(50 * 1000); - XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); - } else { - /* try Escape or Space press+release */ - fprintf(stderr, "**bust_grab: keycode: %d %.4f\n", - (int) key, dnowx()); - XTestFakeKeyEvent_wr(dpy, key, True, CurrentTime); - XFlush(dpy); - usleep(50 * 1000); - XTestFakeKeyEvent_wr(dpy, key, False, CurrentTime); - } - XFlush(dpy); - last_bust = time(0); -} - -typedef struct _grabwatch { - int pid; - int tick; - unsigned long time; - time_t change; -} grabwatch_t; -#define GRABWATCH 16 - -int grab_npids = 1; - -int process_watch(char *str, int parent, int db) { - int i, pid, ticker, npids; - char diff[128]; - unsigned long xtime; - static grabwatch_t watches[GRABWATCH]; - static int first = 1; - time_t now = time(0); - static time_t last_bust = 0; - int too_long, problems = 0; - - if (first) { - for (i=0; i < GRABWATCH; i++) { - watches[i].pid = 0; - watches[i].tick = 0; - watches[i].time = 0; - watches[i].change = 0; - } - first = 0; - } - - /* record latest value of prop */ - if (str && *str != '\0') { - if (sscanf(str, "%d/%d/%lu/%s", &pid, &ticker, &xtime, diff) - == 4) { - int got = -1, free = -1; - - if (db) fprintf(stderr, "grab_buster %d - %d - %lu - %s" - "\n", pid, ticker, xtime, diff); - - if (pid == parent && !strcmp(diff, "QUIT")) { - /* that's it. */ - return 0; - } - if (pid == 0 || ticker == 0 || xtime == 0) { - /* bad prop read. */ - goto badtickerstr; - } - for (i=0; i < GRABWATCH; i++) { - if (watches[i].pid == pid) { - got = i; - break; - } - if (free == -1 && watches[i].pid == 0) { - free = i; - } - } - if (got == -1) { - if (free == -1) { - /* bad news */; - free = GRABWATCH - 1; - } - watches[free].pid = pid; - watches[free].tick = ticker; - watches[free].time = xtime; - watches[free].change = now; - if (db) fprintf(stderr, "grab_buster free slot: %d\n", free); - } else { - if (db) fprintf(stderr, "grab_buster got slot: %d\n", got); - if (watches[got].tick != ticker) { - watches[got].change = now; - } - if (watches[got].time != xtime) { - watches[got].change = now; - } - watches[got].tick = ticker; - watches[got].time = xtime; - } - } else { - if (db) fprintf(stderr, "grab_buster bad prop str: %s\n", str); - } - } - - badtickerstr: - - too_long = grab_buster_delay; - if (too_long < 3 * sync_tod_delay) { - too_long = 3 * sync_tod_delay; - } - - npids = 0; - for (i=0; i < GRABWATCH; i++) { - if (watches[i].pid) { - npids++; - } - } - grab_npids = npids; - if (npids > 4) { - npids = 4; - } - - /* now check everyone we are tracking */ - for (i=0; i < GRABWATCH; i++) { - int fac = 1; - if (!watches[i].pid) { - continue; - } - if (watches[i].change == 0) { - watches[i].change = now; /* just to be sure */ - continue; - } - - pid = watches[i].pid; - - if (pid != parent) { - fac = 2; - } - if (npids > 0) { - fac *= npids; - } - - if (now > watches[i].change + fac*too_long) { - int process_alive = 1; - - fprintf(stderr, "grab_buster: problem with pid: " - "%d - %d/%d/%d\n", pid, (int) now, - (int) watches[i].change, too_long); - - if (kill((pid_t) pid, 0) != 0) { - if (1 || errno == ESRCH) { - process_alive = 0; - } - } - - if (!process_alive) { - watches[i].pid = 0; - watches[i].tick = 0; - watches[i].time = 0; - watches[i].change = 0; - fprintf(stderr, "grab_buster: pid gone: %d\n", - pid); - if (pid == parent) { - /* that's it */ - return 0; - } - } else { - int sleep = sync_tod_delay * 1000 * 1000; - - bust_grab(0); - problems++; - last_bust = now; - usleep(1 * sleep); - break; - } - } - } - - if (!problems) { - bust_grab(1); - } - return 1; -} - -void grab_buster_watch(int parent, char *dstr) { - Atom ticker_atom = None; - int sleep = sync_tod_delay * 921 * 1000; - char propval[200]; - int ev, er, maj, min; - int db = 0; - - if (grab_buster > 1) { - db = 1; - } - - /* overwrite original dpy, we let orig connection sit unused. */ - dpy = XOpenDisplay(dstr); - if (!dpy) { - fprintf(stderr, "grab_buster_watch: could not reopen: %s\n", - dstr); - return; - } - rfbLogEnable(0); - - /* check for XTEST, etc, and then disable grabs for us */ - if (! XTestQueryExtension_wr(dpy, &ev, &er, &maj, &min)) { - xtest_present = 0; - } else { - xtest_present = 1; - } - if (! XETrapQueryExtension_wr(dpy, &ev, &er, &maj)) { - xtrap_present = 0; - } else { - xtrap_present = 1; - } - - if (! xtest_present && ! xtrap_present) { - fprintf(stderr, "grab_buster_watch: no grabserver " - "protection on display: %s\n", dstr); - return; - } - disable_grabserver(dpy, 0); - - usleep(3 * sleep); - - ticker_atom = XInternAtom(dpy, "X11VNC_TICKER", False); - if (! ticker_atom) { - fprintf(stderr, "grab_buster_watch: no ticker atom\n"); - return; - } - - while(1) { - int slp = sleep; - if (grab_npids > 1) { - slp = slp / 8; - } - usleep(slp); - usleep((int) (0.60 * rfac() * slp)); - - if (kill((pid_t) parent, 0) != 0) { - break; - } - - get_prop(propval, 128, ticker_atom); - if (db) fprintf(stderr, "got_prop: %s\n", propval); - - if (!process_watch(propval, parent, db)) { - break; - } - } -} - -void spawn_grab_buster(void) { -#if LIBVNCSERVER_HAVE_FORK - pid_t pid; - int parent = (int) getpid(); - char *dstr = strdup(DisplayString(dpy)); - - XCloseDisplay(dpy); - dpy = NULL; - - if ((pid = fork()) > 0) { - grab_buster_pid = pid; - if (! quiet) { - rfbLog("grab_buster pid is: %d\n", (int) pid); - } - } else if (pid == -1) { - fprintf(stderr, "spawn_grab_buster: could not fork\n"); - rfbLogPerror("fork"); - } else { - grab_buster_watch(parent, dstr); - exit(0); - } - - dpy = XOpenDisplay(dstr); - if (!dpy) { - rfbLog("failed to reopen display %s in spawn_grab_buster\n", - dstr); - exit(1); - } -#endif -} - -void sync_tod_with_servertime(void) { - static Atom ticker_atom = None; - XEvent xev; - char diff[128]; - static int seq = 0; - static unsigned long xserver_ticks = 1; - int i, db = 0; - - if (! ticker_atom) { - ticker_atom = XInternAtom(dpy, "X11VNC_TICKER", False); - } - if (! ticker_atom) { - return; - } - - XSync(dpy, False); - while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { - ; - } - - snprintf(diff, 128, "%d/%08d/%lu/%.6f", (int) getpid(), seq++, - xserver_ticks, servertime_diff); - XChangeProperty(dpy, rootwin, ticker_atom, XA_STRING, 8, - PropModeReplace, (unsigned char *) diff, strlen(diff)); - XSync(dpy, False); - - for (i=0; i < 10; i++) { - int k, got = 0; - - for (k=0; k < 5; k++) { - while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { - if (xev.xproperty.atom == ticker_atom) { - double stime; - - xserver_ticks = xev.xproperty.time; - stime = (double) xev.xproperty.time; - stime = stime/1000.0; - servertime_diff = dnow() - stime; - if (db) rfbLog("set servertime_diff: " - "%.6f\n", servertime_diff); - got = 1; - } - } - } - if (got) { - break; - } - usleep(1000); - } -} - -void check_keycode_state(void) { - static time_t last_check = 0; - int delay = 10, noinput = 3; - time_t now = time(0); - - if (! client_count) { - return; - } - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - /* - * periodically update our model of the keycode_state[] - * by correlating with the Xserver. wait for a pause in - * keyboard input to be on the safe side. the idea here - * is to remove stale keycode state, not to be perfectly - * in sync with the Xserver at every instant of time. - */ - if (now > last_check + delay && now > last_keyboard_input + noinput) { - init_track_keycode_state(); - last_check = now; - } -} - -void check_autorepeat(void) { - 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. - */ -void check_xevents(void) { - XEvent xev; - int tmp, have_clients = 0; - static int sent_some_sel = 0; - static time_t last_request = 0; - static time_t last_call = 0; - static time_t last_bell = 0; - static time_t last_init_check = 0; - static time_t last_sync = 0; - static time_t last_time_sync = 0; - time_t now = time(0); - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - if (now > last_init_check+1) { - last_init_check = now; - initialize_xevents(); - } - - if (screen && screen->clientHead) { - have_clients = 1; - } - - X_LOCK; - /* - * There is a bug where we have to wait before sending text to - * the client... so instead of sending right away we wait a - * the few seconds. - */ - if (have_clients && watch_selection && !sent_some_sel - && now > last_client + sel_waittime) { - if (XGetSelectionOwner(dpy, XA_PRIMARY) == None) { - cutbuffer_send(); - } - sent_some_sel = 1; - } - if (! have_clients) { - /* - * If we don't have clients we can miss the X server - * going away until a client connects. - */ - static time_t last_X_ping = 0; - if (now > last_X_ping + 5) { - last_X_ping = now; - XGetSelectionOwner(dpy, XA_PRIMARY); - } - } - - if (now > last_call+1) { - /* we only check these once a second or so. */ - int n = 0; - while (XCheckTypedEvent(dpy, MappingNotify, &xev)) { - XRefreshKeyboardMapping((XMappingEvent *) &xev); - n++; - } - if (n && use_modifier_tweak) { - X_UNLOCK; - initialize_modtweak(); - X_LOCK; - } - if (xtrap_base_event_type) { - int base = xtrap_base_event_type; - while (XCheckTypedEvent(dpy, base, &xev)) { - ; - } - } - if (xtest_base_event_type) { - int base = xtest_base_event_type; - while (XCheckTypedEvent(dpy, base, &xev)) { - ; - } - } - /* - * we can get ClientMessage from our XSendEvent() call in - * selection_request(). - */ - while (XCheckTypedEvent(dpy, ClientMessage, &xev)) { - ; - } - } - - /* check for CUT_BUFFER0 and VNC_CONNECT changes: */ - if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { - if (xev.type == PropertyNotify) { - if (xev.xproperty.atom == XA_CUT_BUFFER0) { - /* - * Go retrieve CUT_BUFFER0 and send it. - * - * set_cutbuffer is a flag to try to avoid - * processing our own cutbuffer changes. - */ - if (have_clients && watch_selection - && ! set_cutbuffer) { - cutbuffer_send(); - sent_some_sel = 1; - } - set_cutbuffer = 0; - } else if (vnc_connect && vnc_connect_prop != None - && xev.xproperty.atom == vnc_connect_prop) { - - /* - * Go retrieve VNC_CONNECT string. - */ - read_vnc_connect_prop(); - } - } - } - - /* do this now that we have just cleared PropertyNotify */ - tmp = 0; - if (rfac() < 0.6) { - tmp = 1; - } - if (now > last_time_sync + sync_tod_delay + tmp) { - sync_tod_with_servertime(); - last_time_sync = now; - } - -#if LIBVNCSERVER_HAVE_LIBXRANDR - if (xrandr) { - check_xrandr_event("check_xevents"); - } -#endif -#if LIBVNCSERVER_HAVE_LIBXFIXES - if (xfixes_present && use_xfixes && xfixes_base_event_type) { - if (XCheckTypedEvent(dpy, xfixes_base_event_type + - XFixesCursorNotify, &xev)) { - got_xfixes_cursor_notify++; - } - } -#endif - - /* check for our PRIMARY request notification: */ - if (watch_primary) { - if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) { - if (xev.type == SelectionNotify && - xev.xselection.requestor == selwin && - xev.xselection.selection == XA_PRIMARY && - xev.xselection.property != None && - xev.xselection.target == XA_STRING) { - - /* go retrieve PRIMARY and check it */ - if (now > last_client + sel_waittime - || sent_some_sel) { - selection_send(&xev); - } - } - } - if (now > last_request) { - /* - * Every second or two, request PRIMARY, unless we - * already own it or there is no owner or we have - * no clients. - * TODO: even at this low rate we should look into - * and performance problems in odds cases, etc. - */ - last_request = now; - if (! own_selection && have_clients && - XGetSelectionOwner(dpy, XA_PRIMARY) != None) { - XConvertSelection(dpy, XA_PRIMARY, XA_STRING, - XA_STRING, selwin, CurrentTime); - } - } - } - - if (own_selection) { - /* we own PRIMARY, see if someone requested it: */ - if (XCheckTypedEvent(dpy, SelectionRequest, &xev)) { - if (xev.type == SelectionRequest && - xev.xselectionrequest.selection == XA_PRIMARY) { - selection_request(&xev); - } - } - - /* we own PRIMARY, see if we no longer own it: */ - if (XCheckTypedEvent(dpy, SelectionClear, &xev)) { - if (xev.type == SelectionClear && - xev.xselectionclear.selection == XA_PRIMARY) { - - own_selection = 0; - if (xcut_str) { - free(xcut_str); - xcut_str = NULL; - } - } - } - } - - if (watch_bell || now > last_bell+1) { - last_bell = now; - check_bell_event(); - } - if (tray_request != None) { - static time_t last_tray_request = 0; - if (now > last_tray_request + 2) { - last_tray_request = now; - if (tray_embed(tray_request, tray_unembed)) { - tray_window = tray_request; - tray_request = None; - } - } - } - -#ifndef DEBUG_XEVENTS -#define DEBUG_XEVENTS 1 -#endif -#if DEBUG_XEVENTS - if (debug_xevents) { - static time_t last_check = 0; - static time_t reminder = 0; - static int freq = 0; - - if (! freq) { - if (getenv("X11VNC_REMINDER_RATE")) { - freq = atoi(getenv("X11VNC_REMINDER_RATE")); - } else { - freq = 300; - } - } - - if (now > last_check + 1) { - int ev_type_max = 300, ev_size = 400; - XEvent xevs[400]; - int i, tot = XEventsQueued(dpy, QueuedAlready); - - if (reminder == 0 || (tot && now > reminder + freq)) { - print_xevent_bases(); - reminder = now; - } - last_check = now; - - if (tot) { - fprintf(stderr, "Total events queued: %d\n", - tot); - } - for (i=1; i<ev_type_max; i++) { - int k, n = 0; - while (XCheckTypedEvent(dpy, i, xevs+n)) { - if (++n >= ev_size) { - break; - } - } - if (n) { - fprintf(stderr, " %d%s events of type" - " %d queued\n", n, - (n >= ev_size) ? "+" : "", i); - } - for (k=n-1; k >= 0; k--) { - XPutBackEvent(dpy, xevs+k); - } - } - } - } -#endif - - if (now > last_sync + 1200) { - /* kludge for any remaining event leaks */ - int bugout = use_xdamage ? 500 : 50; - int qlen, i; - if (last_sync != 0) { - qlen = XEventsQueued(dpy, QueuedAlready); - if (qlen >= bugout) { - rfbLog("event leak: %d queued, " - " calling XSync(dpy, True)\n", qlen); - rfbLog(" for diagnostics run: 'x11vnc -R" - " debug_xevents:1'\n"); - XSync(dpy, True); - } - } - last_sync = now; - - /* clear these, we don't want any events on them */ - if (rdpy_ctrl) { - qlen = XEventsQueued(rdpy_ctrl, QueuedAlready); - for (i=0; i<qlen; i++) { - XNextEvent(rdpy_ctrl, &xev); - } - } - if (gdpy_ctrl) { - qlen = XEventsQueued(gdpy_ctrl, QueuedAlready); - for (i=0; i<qlen; i++) { - XNextEvent(gdpy_ctrl, &xev); - } - } - } - X_UNLOCK; - - last_call = now; -} - -/* - * hook called when a VNC client sends us some "XCut" text (rfbClientCutText). - */ -void xcut_receive(char *text, int len, rfbClientPtr cl) { - allowed_input_t input; - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - if (!watch_selection) { - return; - } - if (view_only) { - return; - } - if (text == NULL || len == 0) { - return; - } - get_allowed_input(cl, &input); - if (!input.keystroke && !input.motion && !input.button) { - /* maybe someday KMBC for cut text... */ - return; - } - - if (! check_sel_direction("recv", "xcut_receive", text, len)) { - return; - } - - X_LOCK; - - /* associate this text with PRIMARY (and SECONDARY...) */ - if (! own_selection) { - own_selection = 1; - /* we need to grab the PRIMARY selection */ - XSetSelectionOwner(dpy, XA_PRIMARY, selwin, CurrentTime); - XFlush(dpy); - } - - /* duplicate the text string for our own use. */ - if (xcut_str != NULL) { - free(xcut_str); - xcut_str = NULL; - } - xcut_str = (char *) malloc((size_t) (len+1)); - strncpy(xcut_str, text, len); - xcut_str[len] = '\0'; /* make sure null terminated */ - - /* copy this text to CUT_BUFFER0 as well: */ - XChangeProperty(dpy, rootwin, XA_CUT_BUFFER0, XA_STRING, 8, - PropModeReplace, (unsigned char *) text, len); - XFlush(dpy); - - X_UNLOCK; - - set_cutbuffer = 1; -} - -/* -- remote.c -- */ - -/* - * for the wild-n-crazy -remote/-R interface. - */ -int send_remote_cmd(char *cmd, int query, int wait) { - FILE *in = NULL; - - if (client_connect_file) { - in = fopen(client_connect_file, "w"); - if (in == NULL) { - fprintf(stderr, "send_remote_cmd: could not open " - "connect file \"%s\" for writing\n", - client_connect_file); - perror("fopen"); - return 1; - } - } else if (vnc_connect_prop == None) { - initialize_vnc_connect_prop(); - if (vnc_connect_prop == None) { - fprintf(stderr, "send_remote_cmd: could not obtain " - "VNC_CONNECT X property\n"); - return 1; - } - } - - if (in != NULL) { - fprintf(stderr, ">>> sending remote command: \"%s\"\n via" - " connect file: %s\n", cmd, client_connect_file); - fprintf(in, "%s\n", cmd); - fclose(in); - } else { - fprintf(stderr, ">>> sending remote command: \"%s\" via" - " VNC_CONNECT X property.\n", cmd); - set_vnc_connect_prop(cmd); - XFlush(dpy); - } - - if (query || wait) { - char line[VNC_CONNECT_MAX]; - int rc=1, i=0, max=70, ms_sl=50; - - if (!strcmp(cmd, "cmd=stop")) { - max = 20; - } - for (i=0; i<max; i++) { - usleep(ms_sl * 1000); - if (client_connect_file) { - char *q; - in = fopen(client_connect_file, "r"); - if (in == NULL) { - fprintf(stderr, "send_remote_cmd: could" - " not open connect file \"%s\" for" - " writing\n", client_connect_file); - perror("fopen"); - return 1; - } - fgets(line, VNC_CONNECT_MAX, in); - fclose(in); - q = line; - while (*q != '\0') { - if (*q == '\n') *q = '\0'; - q++; - } - } else { - read_vnc_connect_prop(); - strncpy(line, vnc_connect_str, VNC_CONNECT_MAX); - } - if (strcmp(cmd, line)){ - if (query) { - fprintf(stdout, "%s\n", line); - fflush(stdout); - } - rc = 0; - break; - } - } - if (rc) { - fprintf(stderr, "error: could not connect to " - "an x11vnc server at %s (rc=%d)\n", - client_connect_file ? client_connect_file - : DisplayString(dpy), rc); - } - return rc; - } - return 0; -} - -int do_remote_query(char *remote_cmd, char *query_cmd, int remote_sync, - int qdefault) { - char *rcmd = NULL, *qcmd = NULL; - int rc = 1; - - if (qdefault && !query_cmd) { - query_cmd = remote_cmd; - remote_cmd = NULL; - } - - if (remote_cmd) { - rcmd = (char *) malloc(strlen(remote_cmd) + 5); - strcpy(rcmd, "cmd="); - strcat(rcmd, remote_cmd); - } - if (query_cmd) { - qcmd = (char *) malloc(strlen(query_cmd) + 5); - strcpy(qcmd, "qry="); - strcat(qcmd, query_cmd); - } - if (qdefault) { - char *res; - if (!qcmd) { - return 1; - } - res = process_remote_cmd(qcmd, 1); - fprintf(stdout, "%s\n", res); - fflush(stdout); - return 0; - } - - if (rcmd && qcmd) { - rc = send_remote_cmd(rcmd, 0, 1); - if (rc) { - free(rcmd); - free(qcmd); - return(rc); - } - rc = send_remote_cmd(qcmd, 1, 1); - } else if (rcmd) { - rc = send_remote_cmd(rcmd, 0, remote_sync); - free(rcmd); - } else if (qcmd) { - rc = send_remote_cmd(qcmd, 1, 1); - free(qcmd); - } - return rc; -} - -char *add_item(char *instr, char *item) { - char *p, *str; - int len, saw_item = 0; - - if (! instr || *instr == '\0') { - str = strdup(item); - return str; - } - len = strlen(instr) + 1 + strlen(item) + 1; - str = (char *) malloc(len); - str[0] = '\0'; - - /* n.b. instr will be modified; caller replaces with returned string */ - p = strtok(instr, ","); - while (p) { - if (!strcmp(p, item)) { - if (saw_item) { - p = strtok(NULL, ","); - continue; - } - saw_item = 1; - } else if (*p == '\0') { - p = strtok(NULL, ","); - continue; - } - if (str[0]) { - strcat(str, ","); - } - strcat(str, p); - p = strtok(NULL, ","); - } - if (! saw_item) { - if (str[0]) { - strcat(str, ","); - } - strcat(str, item); - } - return str; -} - -char *delete_item(char *instr, char *item) { - char *p, *str; - int len; - - if (! instr || *instr == '\0') { - str = strdup(""); - return str; - } - len = strlen(instr) + 1; - str = (char *) malloc(len); - str[0] = '\0'; - - /* n.b. instr will be modified; caller replaces with returned string */ - p = strtok(instr, ","); - while (p) { - if (!strcmp(p, item) || *p == '\0') { - p = strtok(NULL, ","); - continue; - } - if (str[0]) { - strcat(str, ","); - } - strcat(str, p); - p = strtok(NULL, ","); - } - return str; -} - -void if_8bpp_do_new_fb(void) { - if (bpp == 8) { - do_new_fb(0); - } else { - rfbLog(" bpp(%d) is not 8bpp, not resetting fb\n", bpp); - } -} - -void check_black_fb(void) { - if (!screen) { - return; - } - if (new_fb_size_clients(screen) != client_count) { - rfbLog("trying to send a black fb for non-newfbsize" - " clients %d != %d\n", client_count, - new_fb_size_clients(screen)); - push_black_screen(4); - } -} - -int check_httpdir(void) { - if (http_dir) { - return 1; - } else { - char *prog = NULL, *httpdir, *q; - struct stat sbuf; - int len; - - rfbLog("check_httpdir: trying to guess httpdir...\n"); - if (program_name[0] == '/') { - prog = strdup(program_name); - } else { - char cwd[1024]; - getcwd(cwd, 1024); - len = strlen(cwd) + 1 + strlen(program_name) + 1; - prog = (char *) malloc(len); - snprintf(prog, len, "%s/%s", cwd, program_name); - if (stat(prog, &sbuf) != 0) { - char *path = strdup(getenv("PATH")); - char *p, *base; - base = strrchr(program_name, '/'); - if (base) { - base++; - } else { - base = program_name; - } - - p = strtok(path, ":"); - while(p) { - free(prog); - len = strlen(p) + 1 + strlen(base) + 1; - prog = (char *) malloc(len); - snprintf(prog, len, "%s/%s", p, base); - if (stat(prog, &sbuf) == 0) { - break; - } - p = strtok(NULL, ":"); - } - free(path); - } - } - /* - * /path/to/bin/x11vnc - * /path/to/bin/../share/x11vnc/classes - * 12345678901234567 - */ - if ((q = strrchr(prog, '/')) == NULL) { - rfbLog("check_httpdir: bad program path: %s\n", prog); - free(prog); - return 0; - } - - len = strlen(prog) + 17 + 1; - *q = '\0'; - httpdir = (char *) malloc(len); - snprintf(httpdir, len, "%s/../share/x11vnc/classes", prog); - free(prog); - - if (stat(httpdir, &sbuf) == 0) { - /* good enough for me */ - rfbLog("check_httpdir: guessed directory:\n"); - rfbLog(" %s\n", httpdir); - http_dir = httpdir; - return 1; - } else { - /* try some hardwires: */ - if (stat("/usr/local/share/x11vnc/classes", - &sbuf) == 0) { - http_dir = - strdup("/usr/local/share/x11vnc/classes"); - return 1; - } - if (stat("/usr/share/x11vnc/classes", &sbuf) == 0) { - http_dir = strdup("/usr/share/x11vnc/classes"); - return 1; - } - rfbLog("check_httpdir: bad guess:\n"); - rfbLog(" %s\n", httpdir); - return 0; - } - } -} - -void http_connections(int on) { - if (!screen) { - return; - } - if (on) { - rfbLog("http_connections: turning on http service.\n"); - screen->httpInitDone = FALSE; - screen->httpDir = http_dir; - if (check_httpdir()) { - rfbHttpInitSockets(screen); - } - } else { - rfbLog("http_connections: turning off http service.\n"); - if (screen->httpListenSock > -1) { - close(screen->httpListenSock); - } - screen->httpListenSock = -1; - screen->httpDir = NULL; - } -} - -void reset_httpport(int old, int new) { - int hp = new; - if (hp < 0) { - rfbLog("reset_httpport: invalid httpport: %d\n", hp); - } else if (hp == old) { - rfbLog("reset_httpport: unchanged httpport: %d\n", hp); - } else if (inetd) { - rfbLog("reset_httpport: cannot set httpport: %d" - " in inetd.\n", hp); - } else if (screen) { - screen->httpPort = hp; - screen->httpInitDone = FALSE; - if (screen->httpListenSock > -1) { - close(screen->httpListenSock); - } - rfbLog("reset_httpport: setting httpport %d -> %d.\n", - old == -1 ? hp : old, hp); - rfbHttpInitSockets(screen); - } -} - -void reset_rfbport(int old, int new) { - int rp = new; - if (rp < 0) { - rfbLog("reset_rfbport: invalid rfbport: %d\n", rp); - } else if (rp == old) { - rfbLog("reset_rfbport: unchanged rfbport: %d\n", rp); - } else if (inetd) { - rfbLog("reset_rfbport: cannot set rfbport: %d" - " in inetd.\n", rp); - } else if (screen) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int maxfd; - if (rp == 0) { - screen->autoPort = TRUE; - } else { - screen->autoPort = FALSE; - } - screen->port = rp; - screen->socketState = RFB_SOCKET_INIT; - - if (screen->listenSock > -1) { - close(screen->listenSock); - } - - rfbLog("reset_rfbport: setting rfbport %d -> %d.\n", - old == -1 ? rp : old, rp); - rfbInitSockets(screen); - - maxfd = screen->maxFd; - if (screen->udpSock > 0 && screen->udpSock > maxfd) { - maxfd = screen->udpSock; - } - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->sock > -1) { - FD_SET(cl->sock, &(screen->allFds)); - if (cl->sock > maxfd) { - maxfd = cl->sock; - } - } - } - rfbReleaseClientIterator(iter); - - screen->maxFd = maxfd; - - set_vnc_desktop_name(); - } -} - -/* - * Do some sanity checking of the permissions on the XAUTHORITY and the - * -connect file. This is -privremote. What should be done is check - * for an empty host access list, currently we lazily do not bring in - * libXau yet. - */ -int remote_control_access_ok(void) { - struct stat sbuf; - - if (client_connect_file) { - if (stat(client_connect_file, &sbuf) == 0) { - if (sbuf.st_mode & S_IWOTH) { - rfbLog("connect file is writable by others.\n"); - rfbLog(" %s\n", client_connect_file); - return 0; - } - if (sbuf.st_mode & S_IWGRP) { - rfbLog("connect file is writable by group.\n"); - rfbLog(" %s\n", client_connect_file); - return 0; - } - } - } - - if (dpy) { - char tmp[1000]; - char *home, *xauth; - char *dpy_str = DisplayString(dpy); - Display *dpy2; - XHostAddress *xha; - Bool enabled; - int n; - - home = get_home_dir(); - if (getenv("XAUTHORITY") != NULL) { - xauth = getenv("XAUTHORITY"); - } else if (home) { - int len = 1000 - strlen("/.Xauthority") - 1; - strncpy(tmp, home, len); - strcat(tmp, "/.Xauthority"); - xauth = tmp; - } else { - rfbLog("cannot determine default XAUTHORITY.\n"); - return 0; - } - if (home) { - free(home); - } - if (stat(xauth, &sbuf) == 0) { - if (sbuf.st_mode & S_IWOTH) { - rfbLog("XAUTHORITY is writable by others!!\n"); - rfbLog(" %s\n", xauth); - return 0; - } - if (sbuf.st_mode & S_IWGRP) { - rfbLog("XAUTHORITY is writable by group!!\n"); - rfbLog(" %s\n", xauth); - return 0; - } - if (sbuf.st_mode & S_IROTH) { - rfbLog("XAUTHORITY is readable by others.\n"); - rfbLog(" %s\n", xauth); - return 0; - } - if (sbuf.st_mode & S_IRGRP) { - rfbLog("XAUTHORITY is readable by group.\n"); - rfbLog(" %s\n", xauth); - return 0; - } - } - - xha = XListHosts(dpy, &n, &enabled); - if (! enabled) { - rfbLog("X access control is disabled, X clients can\n"); - rfbLog(" connect from any host. Run 'xhost -'\n"); - return 0; - } - if (xha) { - int i; - rfbLog("The following hosts can connect w/o X11 " - "auth:\n"); - for (i=0; i<n; i++) { - if (xha[i].family == FamilyInternet) { - char *str = raw2host(xha[i].address, - xha[i].length); - char *ip = raw2ip(xha[i].address); - rfbLog(" %s/%s\n", str, ip); - free(str); - free(ip); - } else { - rfbLog(" unknown-%d\n", i+1); - } - } - XFree(xha); - return 0; - } - - if (getenv("XAUTHORITY")) { - xauth = strdup(getenv("XAUTHORITY")); - } else { - xauth = NULL; - } - set_env("XAUTHORITY", "/impossible/xauthfile"); - - fprintf(stderr, "\nChecking if display %s requires " - "XAUTHORITY\n", dpy_str); - fprintf(stderr, " -- (ignore any Xlib: errors that" - " follow) --\n"); - dpy2 = XOpenDisplay(dpy_str); - fflush(stderr); - fprintf(stderr, " -- (done checking) --\n\n"); - - if (xauth) { - set_env("XAUTHORITY", xauth); - free(xauth); - } else { - xauth = getenv("XAUTHORITY"); - if (xauth) { - *(xauth-2) = '_'; /* yow */ - } - } - if (dpy2) { - rfbLog("XAUTHORITY is not required on display.\n"); - rfbLog(" %s\n", DisplayString(dpy)); - XCloseDisplay(dpy2); - return 0; - } - - } - return 1; -} - -/* - * Huge, ugly switch to handle all remote commands and queries - * -remote/-R and -query/-Q. - */ -char *process_remote_cmd(char *cmd, int stringonly) { -#if REMOTE_CONTROL - char *p = cmd; - char *co = ""; - char buf[VNC_CONNECT_MAX]; - int bufn = VNC_CONNECT_MAX; - int query = 0; - static char *prev_cursors_mode = NULL; - - if (!query_default && !accept_remote_cmds) { - rfbLog("remote commands disabled: %s\n", cmd); - return NULL; - } - - if (!query_default && priv_remote) { - if (! remote_control_access_ok()) { - rfbLog("** Disabling remote commands in -privremote " - "mode.\n"); - accept_remote_cmds = 0; - return NULL; - } - } - - strcpy(buf, ""); - if (strstr(cmd, "cmd=") == cmd) { - p += strlen("cmd="); - } else if (strstr(cmd, "qry=") == cmd) { - query = 1; - if (strchr(cmd, ',')) { - /* comma separated batch mode */ - char *s, *q, *res; - char tmp[512]; - strcpy(buf, ""); - s = strdup(cmd + strlen("qry=")); - q = strtok(s, ","); - while (q) { - strcpy(tmp, "qry="); - strncat(tmp, q, 500); - res = process_remote_cmd(tmp, 1); - if (res && strlen(buf)+strlen(res) - >= VNC_CONNECT_MAX - 1) { - rfbLog("overflow in process_remote_cmd:" - " %s -- %s\n", buf, res); - free(res); - break; - } - if (res) { - strcat(buf, res); - free(res); - } - q = strtok(NULL, ","); - if (q) { - strcat(buf, ","); - } - } - free(s); - goto qry; - } - p += strlen("qry="); - } else { - rfbLog("ignoring malformed command: %s\n", cmd); - goto done; - } - - /* allow var=val usage */ - if (!strchr(p, ':')) { - char *q = strchr(p, '='); - if (q) *q = ':'; - } - - /* always call like: COLON_CHECK("foobar:") */ -#define COLON_CHECK(str) \ - if (strstr(p, str) != p) { \ - co = ":"; \ - if (! query) { \ - goto done; \ - } \ - } else { \ - char *q = strchr(p, ':'); \ - if (query && q != NULL) { \ - *(q+1) = '\0'; \ - } \ - } - -#define NOTAPP \ - if (query) { \ - if (strchr(p, ':')) { \ - snprintf(buf, bufn, "ans=%sN/A", p); \ - } else { \ - snprintf(buf, bufn, "ans=%s:N/A", p); \ - } \ - goto qry; \ - } - -#define NOTAPPRO \ - if (query) { \ - if (strchr(p, ':')) { \ - snprintf(buf, bufn, "aro=%sN/A", p); \ - } else { \ - snprintf(buf, bufn, "aro=%s:N/A", p); \ - } \ - goto qry; \ - } - -/* - * Maybe add: passwdfile logfile bg rfbauth passwd... - */ - if (!strcmp(p, "stop") || !strcmp(p, "quit") || - !strcmp(p, "exit") || !strcmp(p, "shutdown")) { - NOTAPP - close_all_clients(); - rfbLog("remote_cmd: setting shut_down flag\n"); - shut_down = 1; - - } else if (!strcmp(p, "ping")) { - query = 1; - if (rfb_desktop_name) { - snprintf(buf, bufn, "ans=%s:%s", p, rfb_desktop_name); - } else { - snprintf(buf, bufn, "ans=%s:%s", p, "unknown"); - } - goto qry; - - } else if (!strcmp(p, "blacken") || !strcmp(p, "zero")) { - NOTAPP - push_black_screen(4); - } else if (!strcmp(p, "refresh")) { - NOTAPP - refresh_screen(1); - } else if (!strcmp(p, "reset")) { - NOTAPP - do_new_fb(1); - } else if (strstr(p, "zero:") == p) { /* skip-cmd-list */ - int x1, y1, x2, y2; - NOTAPP - p += strlen("zero:"); - if (sscanf(p, "%d,%d,%d,%d", &x1, &y1, &x2, &y2) == 4) { - int mark = 1; - rfbLog("zeroing rect: %s\n", p); - if (x1 < 0 || x2 < 0) { - x1 = nabs(x1); - x2 = nabs(x2); - mark = 0; /* hack for testing */ - } - - zero_fb(x1, y1, x2, y2); - if (mark) { - mark_rect_as_modified(x1, y1, x2, y2, 0); - } - push_sleep(4); - } - } else if (strstr(p, "damagefb:") == p) { /* skip-cmd-list */ - int delay; - NOTAPP - p += strlen("damagefb:"); - if (sscanf(p, "%d", &delay) == 1) { - rfbLog("damaging client fb's for %d secs " - "(by not marking rects.)\n", delay); - damage_time = time(0); - damage_delay = delay; - } - - } else if (strstr(p, "close") == p) { - NOTAPP - COLON_CHECK("close:") - p += strlen("close:"); - close_clients(p); - } else if (strstr(p, "disconnect") == p) { - NOTAPP - COLON_CHECK("disconnect:") - p += strlen("disconnect:"); - close_clients(p); - - } else if (strstr(p, "id") == p) { - int ok = 0; - Window twin; - COLON_CHECK("id:") - if (query) { - snprintf(buf, bufn, "ans=%s%s0x%lx", p, co, - rootshift ? 0 : subwin); - goto qry; - } - p += strlen("id:"); - if (*p == '\0' || !strcmp("root", p)) { - /* back to root win */ - twin = 0x0; - ok = 1; - } else if (!strcmp("pick", p)) { - twin = 0x0; - if (safe_remote_only) { - rfbLog("unsafe: '-id pick'\n"); - } else if (pick_windowid(&twin)) { - ok = 1; - } - } else if (! scan_hexdec(p, &twin)) { - rfbLog("-id: skipping incorrect hex/dec number:" - " %s\n", p); - } else { - ok = 1; - } - if (ok) { - if (twin && ! valid_window(twin, NULL, 0)) { - rfbLog("skipping invalid sub-window: 0x%lx\n", - twin); - } else { - subwin = twin; - rootshift = 0; - check_black_fb(); - do_new_fb(1); - } - } - } else if (strstr(p, "sid") == p) { - int ok = 0; - Window twin; - COLON_CHECK("sid:") - if (query) { - snprintf(buf, bufn, "ans=%s%s0x%lx", p, co, - !rootshift ? 0 : subwin); - goto qry; - } - p += strlen("sid:"); - if (*p == '\0' || !strcmp("root", p)) { - /* back to root win */ - twin = 0x0; - ok = 1; - } else if (!strcmp("pick", p)) { - twin = 0x0; - if (safe_remote_only) { - rfbLog("unsafe: '-sid pick'\n"); - } else if (pick_windowid(&twin)) { - ok = 1; - } - } else if (! scan_hexdec(p, &twin)) { - rfbLog("-sid: skipping incorrect hex/dec number: %s\n", p); - } else { - ok = 1; - } - if (ok) { - if (twin && ! valid_window(twin, NULL, 0)) { - rfbLog("skipping invalid sub-window: 0x%lx\n", - twin); - } else { - subwin = twin; - rootshift = 1; - check_black_fb(); - do_new_fb(1); - } - } - } else if (strstr(p, "waitmapped") == p) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - subwin_wait_mapped); - goto qry; - } - subwin_wait_mapped = 1; - } else if (strstr(p, "nowaitmapped") == p) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - !subwin_wait_mapped); - goto qry; - } - subwin_wait_mapped = 0; - - } 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, - NONUL(clip_str)); - goto qry; - } - p += strlen("clip:"); - if (clip_str) free(clip_str); - clip_str = strdup(p); - - /* OK, this requires a new fb... */ - do_new_fb(1); - - } else if (!strcmp(p, "flashcmap")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, flash_cmap); - goto qry; - } - rfbLog("remote_cmd: turning on flashcmap mode.\n"); - flash_cmap = 1; - } else if (!strcmp(p, "noflashcmap")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !flash_cmap); - goto qry; - } - rfbLog("remote_cmd: turning off flashcmap mode.\n"); - flash_cmap = 0; - - } else if (strstr(p, "shiftcmap") == p) { - COLON_CHECK("shiftcmap:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, shift_cmap); - goto qry; - } - p += strlen("shiftcmap:"); - shift_cmap = atoi(p); - rfbLog("remote_cmd: set -shiftcmap %d\n", shift_cmap); - do_new_fb(1); - - } else if (!strcmp(p, "truecolor")) { - int orig = force_indexed_color; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - !force_indexed_color); - goto qry; - } - rfbLog("remote_cmd: turning off notruecolor mode.\n"); - force_indexed_color = 0; - if (orig != force_indexed_color) { - if_8bpp_do_new_fb(); - } - } else if (!strcmp(p, "notruecolor")) { - int orig = force_indexed_color; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - force_indexed_color); - goto qry; - } - rfbLog("remote_cmd: turning on notruecolor mode.\n"); - force_indexed_color = 1; - if (orig != force_indexed_color) { - if_8bpp_do_new_fb(); - } - - } else if (!strcmp(p, "overlay")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, overlay); - goto qry; - } - rfbLog("remote_cmd: turning on -overlay mode.\n"); - if (!overlay_present) { - rfbLog("skipping: overlay extension not present.\n"); - } else if (overlay) { - rfbLog("skipping: already in -overlay mode.\n"); - } else { - int reset_mem = 0; - /* here we go... */ - if (using_shm) { - rfbLog("setting -noshm mode.\n"); - using_shm = 0; - reset_mem = 1; - } - overlay = 1; - do_new_fb(reset_mem); - } - } else if (!strcmp(p, "nooverlay")) { - int orig = overlay; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !overlay); - goto qry; - } - rfbLog("remote_cmd: turning off overlay mode\n"); - overlay = 0; - if (!overlay_present) { - rfbLog("warning: overlay extension not present.\n"); - } else if (!orig) { - rfbLog("skipping: already not in -overlay mode.\n"); - } else { - /* here we go... */ - do_new_fb(0); - } - - } else if (!strcmp(p, "overlay_cursor") || - !strcmp(p, "overlay_yescursor") || - !strcmp(p, "nooverlay_nocursor")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, overlay_cursor); - goto qry; - } - rfbLog("remote_cmd: turning on overlay_cursor mode.\n"); - overlay_cursor = 1; - if (!overlay_present) { - rfbLog("warning: overlay extension not present.\n"); - } else if (!overlay) { - rfbLog("warning: not in -overlay mode.\n"); - } else { - rfbLog("You may want to run -R noshow_cursor or\n"); - rfbLog(" -R cursor:none to disable any extra " - "cursors.\n"); - } - } else if (!strcmp(p, "nooverlay_cursor") || - !strcmp(p, "nooverlay_yescursor") || - !strcmp(p, "overlay_nocursor")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !overlay_cursor); - goto qry; - } - rfbLog("remote_cmd: turning off overlay_cursor mode\n"); - overlay_cursor = 0; - if (!overlay_present) { - rfbLog("warning: overlay extension not present.\n"); - } else if (!overlay) { - rfbLog("warning: not in -overlay mode.\n"); - } else { - rfbLog("You may want to run -R show_cursor or\n"); - rfbLog(" -R cursor:... to re-enable any cursors.\n"); - } - - } else if (strstr(p, "visual") == p) { - COLON_CHECK("visual:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(visual_str)); - goto qry; - } - p += strlen("visual:"); - if (visual_str) free(visual_str); - visual_str = strdup(p); - - /* OK, this requires a new fb... */ - do_new_fb(0); - - } else if (!strcmp(p, "scale") || - strstr(p, "scale:") == p) { /* skip-cmd-list */ - COLON_CHECK("scale:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(scale_str)); - goto qry; - } - p += strlen("scale:"); - if (scale_str) free(scale_str); - scale_str = strdup(p); - - /* OK, this requires a new fb... */ - check_black_fb(); - do_new_fb(0); - - } else if (!strcmp(p, "scale_cursor") || - strstr(p, "scale_cursor:") == p) { /* skip-cmd-list */ - COLON_CHECK("scale_cursor:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(scale_cursor_str)); - goto qry; - } - p += strlen("scale_cursor:"); - if (scale_cursor_str) free(scale_cursor_str); - if (*p == '\0') { - scale_cursor_str = NULL; - } else { - scale_cursor_str = strdup(p); - } - setup_cursors_and_push(); - - } else if (!strcmp(p, "viewonly")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, view_only); - goto qry; - } - rfbLog("remote_cmd: enable viewonly mode.\n"); - view_only = 1; - } else if (!strcmp(p, "noviewonly")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !view_only); - goto qry; - } - rfbLog("remote_cmd: disable viewonly mode.\n"); - view_only = 0; - if (raw_fb) set_raw_fb_params(0); - - } else if (!strcmp(p, "shared")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, shared); goto qry; - } - rfbLog("remote_cmd: enable sharing.\n"); - shared = 1; - if (screen) { - screen->alwaysShared = TRUE; - screen->neverShared = FALSE; - } - } else if (!strcmp(p, "noshared")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !shared); goto qry; - } - rfbLog("remote_cmd: disable sharing.\n"); - shared = 0; - if (screen) { - screen->alwaysShared = FALSE; - screen->neverShared = TRUE; - } - - } else if (!strcmp(p, "forever")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, 1-connect_once); - goto qry; - } - rfbLog("remote_cmd: enable -forever mode.\n"); - connect_once = 0; - } else if (!strcmp(p, "noforever") || !strcmp(p, "once")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, connect_once); - goto qry; - } - rfbLog("remote_cmd: disable -forever mode.\n"); - connect_once = 1; - - } else if (strstr(p, "timeout") == p) { - int to; - COLON_CHECK("timeout:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, - first_conn_timeout); - goto qry; - } - p += strlen("timeout:"); - to = atoi(p); - if (to > 0 ) { - to = -to; - } - first_conn_timeout = to; - rfbLog("remote_cmd: set -timeout to %d\n", -to); - -#if 0 - } else if (!strcmp(p, "filexfer")) { - /* does this work after rfbInitServer? */ - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, filexfer); - goto qry; - } - rfbLog("remote_cmd: enabling -filexfer.\n"); - filexfer = 1; - rfbRegisterTightVNCFileTransferExtension(); -#endif - - } else if (!strcmp(p, "deny") || !strcmp(p, "lock")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, deny_all); - goto qry; - } - rfbLog("remote_cmd: denying new connections.\n"); - deny_all = 1; - } else if (!strcmp(p, "nodeny") || !strcmp(p, "unlock")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !deny_all); - goto qry; - } - rfbLog("remote_cmd: allowing new connections.\n"); - deny_all = 0; - - } else if (strstr(p, "connect") == p) { - NOTAPP - COLON_CHECK("connect:") - p += strlen("connect:"); - /* this is a reverse connection */ - reverse_connect(p); - - } else if (strstr(p, "allowonce") == p) { - COLON_CHECK("allowonce:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(allow_once)); - goto qry; - } - p += strlen("allowonce:"); - allow_once = strdup(p); - rfbLog("remote_cmd: set allow_once %s\n", allow_once); - - } else if (strstr(p, "allow") == p) { - char *before, *old; - COLON_CHECK("allow:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(allow_list)); - goto qry; - } - p += strlen("allow:"); - if (allow_list && strchr(allow_list, '/')) { - rfbLog("remote_cmd: cannot use allow:host\n"); - rfbLog("in '-allow %s' mode.\n", allow_list); - goto done; - } - if (allow_list) { - before = strdup(allow_list); - } else { - before = strdup(""); - } - - old = allow_list; - if (*p == '+') { - p++; - allow_list = add_item(allow_list, p); - } else if (*p == '-') { - p++; - allow_list = delete_item(allow_list, p); - } else { - allow_list = strdup(p); - } - - if (strcmp(before, allow_list)) { - rfbLog("remote_cmd: modified allow_list:\n"); - rfbLog(" from: \"%s\"\n", before); - rfbLog(" to: \"%s\"\n", allow_list); - } - if (old) free(old); - free(before); - - } else if (!strcmp(p, "localhost")) { - char *before, *old; - if (query) { - int state = 0; - char *s = allow_list; - if (s && (!strcmp(s, "127.0.0.1") || - !strcmp(s, "localhost"))) { - state = 1; - } - snprintf(buf, bufn, "ans=%s:%d", p, state); - goto qry; - } - if (allow_list) { - before = strdup(allow_list); - } else { - before = strdup(""); - } - old = allow_list; - - allow_list = strdup("127.0.0.1"); - - if (strcmp(before, allow_list)) { - rfbLog("remote_cmd: modified allow_list:\n"); - rfbLog(" from: \"%s\"\n", before); - rfbLog(" to: \"%s\"\n", allow_list); - } - if (old) free(old); - free(before); - - if (listen_str) { - free(listen_str); - } - listen_str = strdup("localhost"); - - screen->listenInterface = htonl(INADDR_LOOPBACK); - rfbLog("listening on loopback network only.\n"); - rfbLog("allow list is: '%s'\n", NONUL(allow_list)); - reset_rfbport(-1, screen->port); - if (screen->httpListenSock > -1) { - reset_httpport(-1, screen->httpPort); - } - } else if (!strcmp(p, "nolocalhost")) { - char *before, *old; - if (query) { - int state = 0; - char *s = allow_list; - if (s && (!strcmp(s, "127.0.0.1") || - !strcmp(s, "localhost"))) { - state = 1; - } - snprintf(buf, bufn, "ans=%s:%d", p, !state); - goto qry; - } - if (allow_list) { - before = strdup(allow_list); - } else { - before = strdup(""); - } - old = allow_list; - - allow_list = strdup(""); - - if (strcmp(before, allow_list)) { - rfbLog("remote_cmd: modified allow_list:\n"); - rfbLog(" from: \"%s\"\n", before); - rfbLog(" to: \"%s\"\n", allow_list); - } - if (old) free(old); - free(before); - - if (listen_str) { - free(listen_str); - } - listen_str = NULL; - - screen->listenInterface = htonl(INADDR_ANY); - rfbLog("listening on ALL network interfaces.\n"); - rfbLog("allow list is: '%s'\n", NONUL(allow_list)); - reset_rfbport(-1, screen->port); - if (screen->httpListenSock > -1) { - reset_httpport(-1, screen->httpPort); - } - - } else if (strstr(p, "listen") == p) { - char *before; - int ok, mod = 0; - - COLON_CHECK("listen:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(listen_str)); - goto qry; - } - if (listen_str) { - before = strdup(listen_str); - } else { - before = strdup(""); - } - p += strlen("listen:"); - - listen_str = strdup(p); - - if (strcmp(before, listen_str)) { - rfbLog("remote_cmd: modified listen_str:\n"); - rfbLog(" from: \"%s\"\n", before); - rfbLog(" to: \"%s\"\n", listen_str); - mod = 1; - } - - ok = 1; - if (listen_str == NULL || *listen_str == '\0' || - !strcmp(listen_str, "any")) { - screen->listenInterface = htonl(INADDR_ANY); - } else if (!strcmp(listen_str, "localhost")) { - screen->listenInterface = htonl(INADDR_LOOPBACK); - } else { - struct hostent *hp; - in_addr_t iface = inet_addr(listen_str); - if (iface == htonl(INADDR_NONE)) { - if (!host_lookup) { - ok = 0; - } else if (!(hp = gethostbyname(listen_str))) { - ok = 0; - } else { - iface = *(unsigned long *)hp->h_addr; - } - } - if (ok) { - screen->listenInterface = iface; - } - } - - if (ok && mod) { - int is_loopback = 0; - in_addr_t iface = screen->listenInterface; - - if (allow_list) { - if (!strcmp(allow_list, "127.0.0.1") || - !strcmp(allow_list, "localhost")) { - is_loopback = 1; - } - } - if (iface != htonl(INADDR_LOOPBACK)) { - if (is_loopback) { - rfbLog("re-setting -allow list to all " - "hosts for non-loopback listening.\n"); - if (allow_list) { - free(allow_list); - } - allow_list = NULL; - } - } else { - if (!is_loopback) { - if (allow_list) { - free(allow_list); - } - rfbLog("setting -allow list to 127.0.0.1\n"); - allow_list = strdup("127.0.0.1"); - } - } - } - if (ok) { - rfbLog("allow list is: '%s'\n", NONUL(allow_list)); - reset_rfbport(-1, screen->port); - if (screen->httpListenSock > -1) { - reset_httpport(-1, screen->httpPort); - } - free(before); - } else { - rfbLog("invalid listen string: %s\n", listen_str); - free(listen_str); - listen_str = before; - } - } else if (!strcmp(p, "lookup")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, host_lookup); - goto qry; - } - rfbLog("remote_cmd: enabling hostname lookup.\n"); - host_lookup = 1; - } else if (!strcmp(p, "nolookup")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !host_lookup); - goto qry; - } - rfbLog("remote_cmd: disabling hostname lookup.\n"); - host_lookup = 0; - - } else if (strstr(p, "accept") == p) { - int doit = 1; - COLON_CHECK("accept:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(accept_cmd)); - goto qry; - } - p += strlen("accept:"); - if (safe_remote_only) { - if (icon_mode && !strcmp(p, "")) { /* skip-cmd-list */ - ; - } else if (icon_mode && !strcmp(p, "popup")) { /* skip-cmd-list */ - ; - } else { - rfbLog("unsafe: %s\n", p); - doit = 0; - } - } - - if (doit) { - if (accept_cmd) free(accept_cmd); - accept_cmd = strdup(p); - } - - } else if (strstr(p, "gone") == p) { - COLON_CHECK("gone:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(gone_cmd)); - goto qry; - } - if (safe_remote_only) { - rfbLog("unsafe: %s\n", p); - } else { - p += strlen("gone:"); - if (gone_cmd) free(gone_cmd); - gone_cmd = strdup(p); - } - - } else if (!strcmp(p, "shm")) { - int orig = using_shm; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, using_shm); - goto qry; - } - rfbLog("remote_cmd: turning off noshm mode.\n"); - using_shm = 1; - if (raw_fb) set_raw_fb_params(0); - - if (orig != using_shm) { - do_new_fb(1); - } else { - rfbLog(" already in shm mode.\n"); - } - } else if (!strcmp(p, "noshm")) { - int orig = using_shm; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !using_shm); - goto qry; - } - rfbLog("remote_cmd: turning on noshm mode.\n"); - using_shm = 0; - if (orig != using_shm) { - do_new_fb(1); - } else { - rfbLog(" already in noshm mode.\n"); - } - - } else if (!strcmp(p, "flipbyteorder")) { - int orig = flip_byte_order; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, flip_byte_order); - goto qry; - } - rfbLog("remote_cmd: turning on flipbyteorder mode.\n"); - flip_byte_order = 1; - if (orig != flip_byte_order) { - if (! using_shm) { - do_new_fb(1); - } else { - rfbLog(" using shm, not resetting fb\n"); - } - } - } else if (!strcmp(p, "noflipbyteorder")) { - int orig = flip_byte_order; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !flip_byte_order); - goto qry; - } - rfbLog("remote_cmd: turning off flipbyteorder mode.\n"); - flip_byte_order = 0; - if (orig != flip_byte_order) { - if (! using_shm) { - do_new_fb(1); - } else { - rfbLog(" using shm, not resetting fb\n"); - } - } - - } else if (!strcmp(p, "onetile")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, single_copytile); - goto qry; - } - rfbLog("remote_cmd: enable -onetile mode.\n"); - single_copytile = 1; - } else if (!strcmp(p, "noonetile")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !single_copytile); - goto qry; - } - rfbLog("remote_cmd: disable -onetile mode.\n"); - if (tile_shm_count < ntiles_x) { - rfbLog(" this has no effect: tile_shm_count=%d" - " ntiles_x=%d\n", tile_shm_count, ntiles_x); - - } - single_copytile = 0; - - } else if (strstr(p, "solid_color") == p) { - /* - * n.b. this solid stuff perhaps should reflect - * safe_remote_only but at least the command names - * are fixed. - */ - char *new; - int doit = 1; - COLON_CHECK("solid_color:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(solid_str)); - goto qry; - } - p += strlen("solid_color:"); - if (*p != '\0') { - new = strdup(p); - } else { - new = strdup(solid_default); - } - rfbLog("remote_cmd: solid %s -> %s\n", NONUL(solid_str), new); - - if (solid_str) { - if (!strcmp(solid_str, new)) { - doit = 0; - } - free(solid_str); - } - solid_str = new; - use_solid_bg = 1; - if (raw_fb) set_raw_fb_params(0); - - if (doit && client_count) { - solid_bg(0); - } - } else if (!strcmp(p, "solid")) { - int orig = use_solid_bg; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, use_solid_bg); - goto qry; - } - rfbLog("remote_cmd: enable -solid mode\n"); - if (! solid_str) { - solid_str = strdup(solid_default); - } - use_solid_bg = 1; - if (raw_fb) set_raw_fb_params(0); - if (client_count && !orig) { - solid_bg(0); - } - } else if (!strcmp(p, "nosolid")) { - int orig = use_solid_bg; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !use_solid_bg); - goto qry; - } - rfbLog("remote_cmd: disable -solid mode\n"); - use_solid_bg = 0; - if (client_count && orig) { - solid_bg(1); - } - - } else if (strstr(p, "blackout") == p) { - char *before, *old; - COLON_CHECK("blackout:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(blackout_str)); - goto qry; - } - p += strlen("blackout:"); - if (blackout_str) { - before = strdup(blackout_str); - } else { - before = strdup(""); - } - old = blackout_str; - if (*p == '+') { - p++; - blackout_str = add_item(blackout_str, p); - } else if (*p == '-') { - p++; - blackout_str = delete_item(blackout_str, p); - } else { - blackout_str = strdup(p); - } - if (strcmp(before, blackout_str)) { - rfbLog("remote_cmd: changing -blackout\n"); - rfbLog(" from: %s\n", before); - rfbLog(" to: %s\n", blackout_str); - if (0 && !strcmp(blackout_str, "") && - single_copytile_orig != single_copytile) { - rfbLog("resetting single_copytile to: %d\n", - single_copytile_orig); - single_copytile = single_copytile_orig; - } - initialize_blackouts_and_xinerama(); - } - if (old) free(old); - free(before); - - } else if (!strcmp(p, "xinerama")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, xinerama); - goto qry; - } - rfbLog("remote_cmd: enable xinerama mode. (if applicable).\n"); - xinerama = 1; - initialize_blackouts_and_xinerama(); - } else if (!strcmp(p, "noxinerama")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !xinerama); - goto qry; - } - rfbLog("remote_cmd: disable xinerama mode. (if applicable).\n"); - xinerama = 0; - initialize_blackouts_and_xinerama(); - - } else if (!strcmp(p, "xtrap")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, xtrap_input); - goto qry; - } - rfbLog("remote_cmd: enable xtrap input mode." - "(if applicable).\n"); - if (! xtrap_input) { - xtrap_input = 1; - disable_grabserver(dpy, 1); - } - - } else if (!strcmp(p, "noxtrap")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !xtrap_input); - goto qry; - } - rfbLog("remote_cmd: disable xtrap input mode." - "(if applicable).\n"); - if (xtrap_input) { - xtrap_input = 0; - disable_grabserver(dpy, 1); - } - - } else if (!strcmp(p, "xrandr")) { - int orig = xrandr; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, xrandr); goto qry; - } - if (xrandr_present) { - rfbLog("remote_cmd: enable xrandr mode.\n"); - xrandr = 1; - if (raw_fb) set_raw_fb_params(0); - if (! xrandr_mode) { - xrandr_mode = strdup("default"); - } - if (orig != xrandr) { - initialize_xrandr(); - } - } else { - rfbLog("remote_cmd: XRANDR ext. not present.\n"); - } - } else if (!strcmp(p, "noxrandr")) { - int orig = xrandr; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !xrandr); goto qry; - } - xrandr = 0; - if (xrandr_present) { - rfbLog("remote_cmd: disable xrandr mode.\n"); - if (orig != xrandr) { - initialize_xrandr(); - } - } else { - rfbLog("remote_cmd: XRANDR ext. not present.\n"); - } - } else if (strstr(p, "xrandr_mode") == p) { - int orig = xrandr; - COLON_CHECK("xrandr_mode:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(xrandr_mode)); - goto qry; - } - p += strlen("xrandr_mode:"); - if (!strcmp("none", p)) { - xrandr = 0; - } else { - if (known_xrandr_mode(p)) { - if (xrandr_mode) free(xrandr_mode); - xrandr_mode = strdup(p); - } else { - rfbLog("skipping unknown xrandr mode: %s\n", p); - goto done; - } - xrandr = 1; - } - if (xrandr_present) { - if (xrandr) { - rfbLog("remote_cmd: enable xrandr mode.\n"); - } else { - rfbLog("remote_cmd: disable xrandr mode.\n"); - } - if (! xrandr_mode) { - xrandr_mode = strdup("default"); - } - if (orig != xrandr) { - initialize_xrandr(); - } - } else { - rfbLog("remote_cmd: XRANDR ext. not present.\n"); - } - - } else if (strstr(p, "padgeom") == p) { - COLON_CHECK("padgeom:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(pad_geometry)); - goto qry; - } - p += strlen("padgeom:"); - if (!strcmp("force", p) || !strcmp("do",p) || !strcmp("go",p)) { - rfbLog("remote_cmd: invoking install_padded_fb()\n"); - install_padded_fb(pad_geometry); - } else { - if (pad_geometry) free(pad_geometry); - pad_geometry = strdup(p); - rfbLog("remote_cmd: set padgeom to: %s\n", - pad_geometry); - } - - } else if (!strcmp(p, "quiet") || !strcmp(p, "q")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, quiet); goto qry; - } - rfbLog("remote_cmd: turning on quiet mode.\n"); - quiet = 1; - } else if (!strcmp(p, "noquiet")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !quiet); goto qry; - } - rfbLog("remote_cmd: turning off quiet mode.\n"); - quiet = 0; - - } else if (!strcmp(p, "modtweak")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, use_modifier_tweak); - goto qry; - } - rfbLog("remote_cmd: enabling -modtweak mode.\n"); - if (! use_modifier_tweak) { - use_modifier_tweak = 1; - initialize_modtweak(); - } - use_modifier_tweak = 1; - - } else if (!strcmp(p, "nomodtweak")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - !use_modifier_tweak); - goto qry; - } - rfbLog("remote_cmd: enabling -nomodtweak mode.\n"); - got_nomodtweak = 1; - use_modifier_tweak = 0; - - } else if (!strcmp(p, "xkb")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, use_xkb_modtweak); - goto qry; - } - if (! xkb_present) { - rfbLog("remote_cmd: cannot enable -xkb " - "modtweak mode (not supported on X display)\n"); - goto done; - } - rfbLog("remote_cmd: enabling -xkb modtweak mode" - " (if supported).\n"); - if (! use_modifier_tweak || ! use_xkb_modtweak) { - use_modifier_tweak = 1; - use_xkb_modtweak = 1; - initialize_modtweak(); - } - use_modifier_tweak = 1; - use_xkb_modtweak = 1; - - } else if (!strcmp(p, "noxkb")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !use_xkb_modtweak); - goto qry; - } - if (! xkb_present) { - rfbLog("remote_cmd: cannot disable -xkb " - "modtweak mode (not supported on X display)\n"); - goto done; - } - 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:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(skip_keycodes)); - goto qry; - } - p += strlen("skip_keycodes:"); - rfbLog("remote_cmd: setting xkb -skip_keycodes" - " to:\n\t'%s'\n", p); - if (! xkb_present) { - rfbLog("remote_cmd: warning xkb not present\n"); - } else if (! use_xkb_modtweak) { - rfbLog("remote_cmd: turning on xkb.\n"); - use_xkb_modtweak = 1; - if (! use_modifier_tweak) { - rfbLog("remote_cmd: turning on modtweak.\n"); - use_modifier_tweak = 1; - } - } - if (skip_keycodes) free(skip_keycodes); - skip_keycodes = strdup(p); - initialize_modtweak(); - - } else if (!strcmp(p, "sloppy_keys")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, sloppy_keys); - goto qry; - } - sloppy_keys += 1; - rfbLog("remote_cmd: set sloppy_keys to: %d\n", sloppy_keys); - } else if (!strcmp(p, "nosloppy_keys")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !sloppy_keys); - goto qry; - } - sloppy_keys = 0; - rfbLog("remote_cmd: set sloppy_keys to: %d\n", sloppy_keys); - - } 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); - goto qry; - } - rfbLog("remote_cmd: enabling -add_keysyms mode.\n"); - add_keysyms = 1; - - } else if (!strcmp(p, "noadd_keysyms")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !add_keysyms); - goto qry; - } - rfbLog("remote_cmd: disabling -add_keysyms mode.\n"); - add_keysyms = 0; - - } else if (!strcmp(p, "clear_mods")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, clear_mods == 1); - goto qry; - } - rfbLog("remote_cmd: enabling -clear_mods mode.\n"); - clear_mods = 1; - clear_modifiers(0); - - } else if (!strcmp(p, "noclear_mods")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - !(clear_mods == 1)); - goto qry; - } - rfbLog("remote_cmd: disabling -clear_mods mode.\n"); - clear_mods = 0; - - } else if (!strcmp(p, "clear_keys")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - clear_mods == 2); - goto qry; - } - rfbLog("remote_cmd: enabling -clear_keys mode.\n"); - clear_mods = 2; - clear_keys(); - - } else if (!strcmp(p, "noclear_keys")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - !(clear_mods == 2)); - goto qry; - } - rfbLog("remote_cmd: disabling -clear_keys mode.\n"); - clear_mods = 0; - - } else if (strstr(p, "remap") == p) { - char *before, *old; - COLON_CHECK("remap:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(remap_file)); - goto qry; - } - p += strlen("remap:"); - if ((*p == '+' || *p == '-') && remap_file && - strchr(remap_file, '/')) { - rfbLog("remote_cmd: cannot use remap:+/-\n"); - rfbLog("in '-remap %s' mode.\n", remap_file); - goto done; - } - if (remap_file) { - before = strdup(remap_file); - } else { - before = strdup(""); - } - old = remap_file; - if (*p == '+') { - p++; - remap_file = add_item(remap_file, p); - } else if (*p == '-') { - p++; - remap_file = delete_item(remap_file, p); - if (! strchr(remap_file, '-')) { - *remap_file = '\0'; - } - } else { - remap_file = strdup(p); - } - if (strcmp(before, remap_file)) { - rfbLog("remote_cmd: changed -remap\n"); - rfbLog(" from: %s\n", before); - rfbLog(" to: %s\n", remap_file); - initialize_remap(remap_file); - } - if (old) free(old); - free(before); - - } else if (!strcmp(p, "repeat")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !no_autorepeat); - goto qry; - } - rfbLog("remote_cmd: enabling -repeat mode.\n"); - autorepeat(1, 0); /* restore initial setting */ - no_autorepeat = 0; - - } else if (!strcmp(p, "norepeat")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, no_autorepeat); - goto qry; - } - rfbLog("remote_cmd: enabling -norepeat mode.\n"); - no_autorepeat = 1; - if (no_repeat_countdown >= 0) { - no_repeat_countdown = 2; - } - if (client_count && ! view_only) { - autorepeat(0, 0); /* disable if any clients */ - } - - } else if (!strcmp(p, "fb")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !nofb); - goto qry; - } - if (nofb) { - rfbLog("remote_cmd: disabling nofb mode.\n"); - rfbLog(" you may need to these turn back on:\n"); - rfbLog(" xfixes, xdamage, solid, flashcmap\n"); - rfbLog(" overlay, shm, noonetile, nap, cursor\n"); - rfbLog(" cursorpos, cursorshape, bell.\n"); - nofb = 0; - set_nofb_params(1); - do_new_fb(1); - } - } else if (!strcmp(p, "nofb")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, nofb); - goto qry; - } - if (!nofb) { - rfbLog("remote_cmd: enabling nofb mode.\n"); - if (main_fb) { - push_black_screen(4); - } - nofb = 1; - sound_bell = 0; - initialize_watch_bell(); - set_nofb_params(0); - do_new_fb(1); - } - - } else if (!strcmp(p, "bell")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, sound_bell); - goto qry; - } - rfbLog("remote_cmd: enabling bell (if supported).\n"); - initialize_watch_bell(); - sound_bell = 1; - - } else if (!strcmp(p, "nobell")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !sound_bell); - goto qry; - } - rfbLog("remote_cmd: disabling bell.\n"); - initialize_watch_bell(); - sound_bell = 0; - - } else if (!strcmp(p, "sel")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, watch_selection); - goto qry; - } - rfbLog("remote_cmd: enabling watch selection+primary.\n"); - watch_selection = 1; - watch_primary = 1; - - } else if (!strcmp(p, "nosel")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !watch_selection); - goto qry; - } - rfbLog("remote_cmd: disabling watch selection+primary.\n"); - watch_selection = 0; - watch_primary = 0; - - } else if (!strcmp(p, "primary")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, watch_primary); - goto qry; - } - rfbLog("remote_cmd: enabling watch_primary.\n"); - watch_primary = 1; - - } else if (!strcmp(p, "noprimary")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !watch_primary); - goto qry; - } - rfbLog("remote_cmd: disabling watch_primary.\n"); - watch_primary = 0; - - } else if (strstr(p, "seldir") == p) { - COLON_CHECK("seldir:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(sel_direction)); - goto qry; - } - p += strlen("seldir:"); - rfbLog("remote_cmd: setting -seldir to %s\n", p); - if (sel_direction) free(sel_direction); - sel_direction = strdup(p); - - } else if (!strcmp(p, "set_no_cursor")) { /* skip-cmd-list */ - rfbLog("remote_cmd: calling set_no_cursor()\n"); - set_no_cursor(); - - } else if (!strcmp(p, "cursorshape")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - cursor_shape_updates); - goto qry; - } - rfbLog("remote_cmd: turning on cursorshape mode.\n"); - - set_no_cursor(); - cursor_shape_updates = 1; - restore_cursor_shape_updates(screen); - first_cursor(); - } else if (!strcmp(p, "nocursorshape")) { - int i, max = 5; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - !cursor_shape_updates); - goto qry; - } - rfbLog("remote_cmd: turning off cursorshape mode.\n"); - - set_no_cursor(); - for (i=0; i<max; i++) { - /* XXX: try to force empty cursor back to client */ - rfbPE(-1); - } - cursor_shape_updates = 0; - disable_cursor_shape_updates(screen); - first_cursor(); - - } else if (!strcmp(p, "cursorpos")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - cursor_pos_updates); - goto qry; - } - rfbLog("remote_cmd: turning on cursorpos mode.\n"); - cursor_pos_updates = 1; - } else if (!strcmp(p, "nocursorpos")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, - !cursor_pos_updates); - goto qry; - } - rfbLog("remote_cmd: turning off cursorpos mode.\n"); - cursor_pos_updates = 0; - - } else if (strstr(p, "cursor") == p) { - COLON_CHECK("cursor:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(multiple_cursors_mode)); - goto qry; - } - p += strlen("cursor:"); - if (multiple_cursors_mode) { - if (prev_cursors_mode) free(prev_cursors_mode); - prev_cursors_mode = strdup(multiple_cursors_mode); - free(multiple_cursors_mode); - } - multiple_cursors_mode = strdup(p); - - rfbLog("remote_cmd: changed -cursor mode " - "to: %s\n", multiple_cursors_mode); - - if (strcmp(multiple_cursors_mode, "none") && !show_cursor) { - show_cursor = 1; - rfbLog("remote_cmd: changed show_cursor " - "to: %d\n", show_cursor); - } - initialize_cursors_mode(); - first_cursor(); - - } else if (!strcmp(p, "show_cursor")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, show_cursor); - goto qry; - } - rfbLog("remote_cmd: enabling show_cursor.\n"); - show_cursor = 1; - if (multiple_cursors_mode && !strcmp(multiple_cursors_mode, - "none")) { - free(multiple_cursors_mode); - if (prev_cursors_mode) { - multiple_cursors_mode = - strdup(prev_cursors_mode); - } else { - multiple_cursors_mode = strdup("default"); - } - rfbLog("remote_cmd: changed -cursor mode " - "to: %s\n", multiple_cursors_mode); - } - initialize_cursors_mode(); - first_cursor(); - } else if (!strcmp(p, "noshow_cursor") || !strcmp(p, "nocursor")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !show_cursor); - goto qry; - } - if (prev_cursors_mode) free(prev_cursors_mode); - prev_cursors_mode = strdup(multiple_cursors_mode); - - rfbLog("remote_cmd: disabling show_cursor.\n"); - show_cursor = 0; - initialize_cursors_mode(); - first_cursor(); - - } else if (strstr(p, "arrow") == p) { - COLON_CHECK("arrow:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, alt_arrow); - goto qry; - } - p += strlen("arrow:"); - alt_arrow = atoi(p); - rfbLog("remote_cmd: setting alt_arrow: %d.\n", alt_arrow); - setup_cursors_and_push(); - - } else if (!strcmp(p, "xfixes")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, use_xfixes); - goto qry; - } - if (! xfixes_present) { - rfbLog("remote_cmd: cannot enable xfixes " - "(not supported on X display)\n"); - goto done; - } - rfbLog("remote_cmd: enabling -xfixes" - " (if supported).\n"); - use_xfixes = 1; - initialize_xfixes(); - first_cursor(); - } else if (!strcmp(p, "noxfixes")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !use_xfixes); - goto qry; - } - if (! xfixes_present) { - rfbLog("remote_cmd: disabling xfixes " - "(but not supported on X display)\n"); - goto done; - } - rfbLog("remote_cmd: disabling -xfixes.\n"); - use_xfixes = 0; - initialize_xfixes(); - first_cursor(); - - } else if (!strcmp(p, "xdamage")) { - int orig = use_xdamage; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, use_xdamage); - goto qry; - } - if (! xdamage_present) { - rfbLog("remote_cmd: cannot enable xdamage hints " - "(not supported on X display)\n"); - goto done; - } - rfbLog("remote_cmd: enabling xdamage hints" - " (if supported).\n"); - use_xdamage = 1; - if (use_xdamage != orig) { - initialize_xdamage(); - create_xdamage_if_needed(); - } - } else if (!strcmp(p, "noxdamage")) { - int orig = use_xdamage; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !use_xdamage); - goto qry; - } - if (! xdamage_present) { - rfbLog("remote_cmd: disabling xdamage hints " - "(but not supported on X display)\n"); - goto done; - } - rfbLog("remote_cmd: disabling xdamage hints.\n"); - use_xdamage = 0; - if (use_xdamage != orig) { - initialize_xdamage(); - destroy_xdamage_if_needed(); - } - - } else if (strstr(p, "xd_area") == p) { - int a; - COLON_CHECK("xd_area:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, - xdamage_max_area); - goto qry; - } - p += strlen("xd_area:"); - a = atoi(p); - if (a >= 0) { - rfbLog("remote_cmd: setting xdamage_max_area " - "%d -> %d.\n", xdamage_max_area, a); - xdamage_max_area = a; - } - } else if (strstr(p, "xd_mem") == p) { - double a; - COLON_CHECK("xd_mem:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%.3f", p, co, - xdamage_memory); - goto qry; - } - p += strlen("xd_mem:"); - a = atof(p); - if (a >= 0.0) { - rfbLog("remote_cmd: setting xdamage_memory " - "%.3f -> %.3f.\n", xdamage_memory, a); - xdamage_memory = a; - } - - } else if (strstr(p, "alphacut") == p) { - int a; - COLON_CHECK("alphacut:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, - alpha_threshold); - goto qry; - } - p += strlen("alphacut:"); - a = atoi(p); - if (a < 0) a = 0; - if (a > 256) a = 256; /* allow 256 for testing. */ - if (alpha_threshold != a) { - rfbLog("remote_cmd: setting alphacut " - "%d -> %d.\n", alpha_threshold, a); - if (a == 256) { - rfbLog("note: alphacut=256 leads to completely" - " transparent cursors.\n"); - } - alpha_threshold = a; - setup_cursors_and_push(); - } - } else if (strstr(p, "alphafrac") == p) { - double a; - COLON_CHECK("alphafrac:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%f", p, co, - alpha_frac); - goto qry; - } - p += strlen("alphafrac:"); - a = atof(p); - if (a < 0.0) a = 0.0; - if (a > 1.0) a = 1.0; - if (alpha_frac != a) { - rfbLog("remote_cmd: setting alphafrac " - "%f -> %f.\n", alpha_frac, a); - alpha_frac = a; - setup_cursors_and_push(); - } - } else if (strstr(p, "alpharemove") == p) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, alpha_remove); - goto qry; - } - if (!alpha_remove) { - rfbLog("remote_cmd: enable alpharemove\n"); - alpha_remove = 1; - setup_cursors_and_push(); - } - } else if (strstr(p, "noalpharemove") == p) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !alpha_remove); - goto qry; - } - if (alpha_remove) { - rfbLog("remote_cmd: disable alpharemove\n"); - alpha_remove = 0; - setup_cursors_and_push(); - } - } else if (strstr(p, "alphablend") == p) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, alpha_blend); - goto qry; - } - if (!alpha_blend) { - rfbLog("remote_cmd: enable alphablend\n"); - alpha_remove = 0; - alpha_blend = 1; - setup_cursors_and_push(); - } - } else if (strstr(p, "noalphablend") == p) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !alpha_blend); - goto qry; - } - if (alpha_blend) { - rfbLog("remote_cmd: disable alphablend\n"); - alpha_blend = 0; - setup_cursors_and_push(); - } - - } 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, "noxwarppointer") == p || - strstr(p, "noxwarp") == p) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !use_xwarppointer); - goto qry; - } - rfbLog("remote_cmd: turning off xwarppointer mode.\n"); - use_xwarppointer = 0; - - } else if (strstr(p, "buttonmap") == p) { - COLON_CHECK("buttonmap:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(pointer_remap)); - goto qry; - } - p += strlen("buttonmap:"); - if (pointer_remap) free(pointer_remap); - pointer_remap = strdup(p); - - rfbLog("remote_cmd: setting -buttonmap to:\n\t'%s'\n", p); - initialize_pointer_map(p); - - } else if (!strcmp(p, "dragging")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, show_dragging); - goto qry; - } - rfbLog("remote_cmd: enabling mouse dragging mode.\n"); - show_dragging = 1; - } else if (!strcmp(p, "nodragging")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !show_dragging); - goto qry; - } - rfbLog("remote_cmd: enabling mouse nodragging mode.\n"); - show_dragging = 0; - - } else if (strstr(p, "wireframe_mode") == p) { - COLON_CHECK("wireframe_mode:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - wireframe_str ? wireframe_str : WIREFRAME_PARMS); - goto qry; - } - p += strlen("wireframe_mode:"); - if (*p) { - if (wireframe_str) { - free(wireframe_str); - } - wireframe_str = strdup(p); - parse_wireframe(); - } - rfbLog("remote_cmd: enabling -wireframe mode.\n"); - wireframe = 1; - } else if (strstr(p, "wireframe:") == p) { /* skip-cmd-list */ - COLON_CHECK("wireframe:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, wireframe); - goto qry; - } - p += strlen("wireframe:"); - if (*p) { - if (wireframe_str) { - free(wireframe_str); - } - wireframe_str = strdup(p); - parse_wireframe(); - } - rfbLog("remote_cmd: enabling -wireframe mode.\n"); - wireframe = 1; - } else if (strstr(p, "wf:") == p) { /* skip-cmd-list */ - COLON_CHECK("wf:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, wireframe); - goto qry; - } - p += strlen("wf:"); - if (*p) { - if (wireframe_str) { - free(wireframe_str); - } - wireframe_str = strdup(p); - parse_wireframe(); - } - rfbLog("remote_cmd: enabling -wireframe mode.\n"); - wireframe = 1; - } else if (!strcmp(p, "wireframe") || !strcmp(p, "wf")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, wireframe); - goto qry; - } - rfbLog("remote_cmd: enabling -wireframe mode.\n"); - wireframe = 1; - } else if (!strcmp(p, "nowireframe") || !strcmp(p, "nowf")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !wireframe); - goto qry; - } - rfbLog("remote_cmd: enabling -nowireframe mode.\n"); - wireframe = 0; - } else if (strstr(p, "wirecopyrect") == p) { - COLON_CHECK("wirecopyrect:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(wireframe_copyrect)); - goto qry; - } - p += strlen("wirecopyrect:"); - - set_wirecopyrect_mode(p); - rfbLog("remote_cmd: changed -wirecopyrect mode " - "to: %s\n", NONUL(wireframe_copyrect)); - got_wirecopyrect = 1; - } else if (strstr(p, "wcr") == p) { - COLON_CHECK("wcr:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(wireframe_copyrect)); - goto qry; - } - p += strlen("wcr:"); - - set_wirecopyrect_mode(p); - rfbLog("remote_cmd: changed -wirecopyrect mode " - "to: %s\n", NONUL(wireframe_copyrect)); - got_wirecopyrect = 1; - } else if (!strcmp(p, "nowirecopyrect") || !strcmp(p, "nowcr")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%s", p, - NONUL(wireframe_copyrect)); - goto qry; - } - - set_wirecopyrect_mode("never"); - rfbLog("remote_cmd: changed -wirecopyrect mode " - "to: %s\n", NONUL(wireframe_copyrect)); - - } else if (strstr(p, "scr_area") == p) { - COLON_CHECK("scr_area:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, - scrollcopyrect_min_area); - goto qry; - } - p += strlen("scr_area:"); - - scrollcopyrect_min_area = atoi(p); - 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_term") == p) { - char *s = scroll_term_str; - if (!s || *s == '\0') s = scroll_term_str0; - COLON_CHECK("scr_term:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s)); - goto qry; - } - p += strlen("scr_term:"); - if (scroll_term_str) { - free(scroll_term_str); - } - - scroll_term_str = strdup(p); - rfbLog("remote_cmd: changed -scr_term to: %s\n", - scroll_term_str); - initialize_scroll_term(); - - } else if (strstr(p, "scr_keyrepeat") == p) { - char *s = max_keyrepeat_str; - if (!s || *s == '\0') s = max_keyrepeat_str0; - COLON_CHECK("scr_keyrepeat:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, NONUL(s)); - goto qry; - } - p += strlen("scr_keyrepeat:"); - if (max_keyrepeat_str) { - free(max_keyrepeat_str); - } - - max_keyrepeat_str = strdup(p); - rfbLog("remote_cmd: changed -scr_keyrepeat to: %s\n", - max_keyrepeat_str); - initialize_max_keyrepeat(); - - } 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) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(scroll_copyrect)); - goto qry; - } - p += strlen("scrollcopyrect:"); - - set_scrollcopyrect_mode(p); - rfbLog("remote_cmd: changed -scrollcopyrect mode " - "to: %s\n", NONUL(scroll_copyrect)); - got_scrollcopyrect = 1; - } 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, - NONUL(scroll_copyrect)); - goto qry; - } - p += strlen("scr:"); - - set_scrollcopyrect_mode(p); - rfbLog("remote_cmd: changed -scrollcopyrect mode " - "to: %s\n", NONUL(scroll_copyrect)); - got_scrollcopyrect = 1; - } else if (!strcmp(p, "noscrollcopyrect") || !strcmp(p, "noscr")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%s", p, - NONUL(scroll_copyrect)); - goto qry; - } - - set_scrollcopyrect_mode("never"); - rfbLog("remote_cmd: changed -scrollcopyrect mode " - "to: %s\n", NONUL(scroll_copyrect)); - - } else if (strstr(p, "fixscreen") == p) { - COLON_CHECK("fixscreen:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(screen_fixup_str)); - goto qry; - } - p += strlen("fixscreen:"); - if (screen_fixup_str) { - free(screen_fixup_str); - } - screen_fixup_str = strdup(p); - parse_fixscreen(); - rfbLog("remote_cmd: set -fixscreen %s.\n", - NONUL(screen_fixup_str)); - - } else if (!strcmp(p, "noxrecord")) { - int orig = noxrecord; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, noxrecord); - goto qry; - } - noxrecord = 1; - rfbLog("set noxrecord to: %d\n", noxrecord); - if (orig != noxrecord) { - shutdown_xrecord(); - } - } else if (!strcmp(p, "xrecord")) { - int orig = noxrecord; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !noxrecord); - goto qry; - } - noxrecord = 0; - rfbLog("set noxrecord to: %d\n", noxrecord); - if (orig != noxrecord) { - initialize_xrecord(); - } - } else if (!strcmp(p, "reset_record")) { - NOTAPP - if (use_xrecord) { - rfbLog("resetting RECORD\n"); - check_xrecord_reset(1); - } else { - rfbLog("RECORD is disabled, not resetting.\n"); - } - - } else if (strstr(p, "pointer_mode") == p) { - int pm; - COLON_CHECK("pointer_mode:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, pointer_mode); - goto qry; - } - p += strlen("pointer_mode:"); - pm = atoi(p); - if (pm < 0 || pm > pointer_mode_max) { - rfbLog("remote_cmd: pointer_mode out of range:" - " 1-%d: %d\n", pointer_mode_max, pm); - } else { - rfbLog("remote_cmd: setting pointer_mode %d\n", pm); - pointer_mode = pm; - } - } else if (strstr(p, "pm") == p) { - int pm; - COLON_CHECK("pm:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, pointer_mode); - goto qry; - } - p += strlen("pm:"); - pm = atoi(p); - if (pm < 0 || pm > pointer_mode_max) { - rfbLog("remote_cmd: pointer_mode out of range:" - " 1-%d: %d\n", pointer_mode_max, pm); - } else { - rfbLog("remote_cmd: setting pointer_mode %d\n", pm); - pointer_mode = pm; - } - - } else if (strstr(p, "input_skip") == p) { - int is; - COLON_CHECK("input_skip:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, ui_skip); - goto qry; - } - p += strlen("input_skip:"); - is = atoi(p); - rfbLog("remote_cmd: setting input_skip %d\n", is); - ui_skip = is; - - } else if (strstr(p, "input") == p) { - int doit = 1; - COLON_CHECK("input:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(allowed_input_str)); - goto qry; - } - p += strlen("input:"); - if (allowed_input_str && !strcmp(p, allowed_input_str)) { - doit = 0; - } - rfbLog("remote_cmd: setting input %s\n", p); - if (allowed_input_str) free(allowed_input_str); - if (*p == '\0') { - allowed_input_str = NULL; - } else { - allowed_input_str = strdup(p); - } - if (doit) { - initialize_allowed_input(); - } - } else if (strstr(p, "client_input") == p) { - NOTAPP - COLON_CHECK("client_input:") - p += strlen("client_input:"); - set_client_input(p); - - } else if (strstr(p, "speeds") == p) { - COLON_CHECK("speeds:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(speeds_str)); - goto qry; - } - p += strlen("speeds:"); - if (speeds_str) free(speeds_str); - speeds_str = strdup(p); - - rfbLog("remote_cmd: setting -speeds to:\n\t'%s'\n", p); - initialize_speeds(); - - } else if (strstr(p, "wmdt") == p) { - COLON_CHECK("wmdt:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(wmdt_str)); - goto qry; - } - p += strlen("wmdt:"); - if (wmdt_str) free(wmdt_str); - wmdt_str = strdup(p); - - rfbLog("remote_cmd: setting -wmdt to: %s\n", p); - - } else if (!strcmp(p, "debug_pointer") || !strcmp(p, "dp")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, debug_pointer); - goto qry; - } - rfbLog("remote_cmd: turning on debug_pointer.\n"); - debug_pointer = 1; - } else if (!strcmp(p, "nodebug_pointer") || !strcmp(p, "nodp")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !debug_pointer); - goto qry; - } - rfbLog("remote_cmd: turning off debug_pointer.\n"); - debug_pointer = 0; - - } else if (!strcmp(p, "debug_keyboard") || !strcmp(p, "dk")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, debug_keyboard); - goto qry; - } - rfbLog("remote_cmd: turning on debug_keyboard.\n"); - debug_keyboard = 1; - } else if (!strcmp(p, "nodebug_keyboard") || !strcmp(p, "nodk")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !debug_keyboard); - goto qry; - } - rfbLog("remote_cmd: turning off debug_keyboard.\n"); - debug_keyboard = 0; - - } else if (strstr(p, "deferupdate") == p) { - int d; - COLON_CHECK("deferupdate:") - if (query) { - if (!screen) { - d = defer_update; - } else { - d = screen->deferUpdateTime; - } - snprintf(buf, bufn, "ans=%s%s%d", p, co, d); - goto qry; - } - p += strlen("deferupdate:"); - d = atoi(p); - if (d < 0) d = 0; - rfbLog("remote_cmd: setting defer to %d ms.\n", d); - screen->deferUpdateTime = d; - got_defer = 1; - - } else if (strstr(p, "defer") == p) { - int d; - COLON_CHECK("defer:") - if (query) { - if (!screen) { - d = defer_update; - } else { - d = screen->deferUpdateTime; - } - snprintf(buf, bufn, "ans=%s%s%d", p, co, d); - goto qry; - } - p += strlen("defer:"); - d = atoi(p); - if (d < 0) d = 0; - rfbLog("remote_cmd: setting defer to %d ms.\n", d); - screen->deferUpdateTime = d; - got_defer = 1; - - } else if (strstr(p, "wait_ui") == p) { - double w; - COLON_CHECK("wait_ui:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%.2f", p, co, wait_ui); - goto qry; - } - p += strlen("wait_ui:"); - w = atof(p); - if (w <= 0) w = 1.0; - rfbLog("remote_cmd: setting wait_ui factor %.2f -> %.2f\n", - wait_ui, w); - wait_ui = w; - - } else if (!strcmp(p, "wait_bog")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, wait_bog); - goto qry; - } - wait_bog = 1; - rfbLog("remote_cmd: setting wait_bog to %d\n", wait_bog); - } else if (!strcmp(p, "nowait_bog")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !wait_bog); - goto qry; - } - wait_bog = 0; - rfbLog("remote_cmd: setting wait_bog to %d\n", wait_bog); - - } else if (strstr(p, "slow_fb") == p) { - double w; - COLON_CHECK("slow_fb:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%.2f", p, co, slow_fb); - goto qry; - } - p += strlen("slow_fb:"); - w = atof(p); - if (w <= 0) w = 0.0; - rfbLog("remote_cmd: setting slow_fb factor %.2f -> %.2f\n", - slow_fb, w); - slow_fb = w; - - } else if (strstr(p, "wait") == p) { - int w; - COLON_CHECK("wait:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, waitms); - goto qry; - } - p += strlen("wait:"); - w = atoi(p); - if (w < 0) w = 0; - rfbLog("remote_cmd: setting wait %d -> %d ms.\n", waitms, w); - waitms = w; - - } else if (strstr(p, "readtimeout") == p) { - int w, orig = rfbMaxClientWait; - COLON_CHECK("readtimeout:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, - rfbMaxClientWait/1000); - goto qry; - } - p += strlen("readtimeout:"); - w = atoi(p) * 1000; - if (w <= 0) w = 0; - rfbLog("remote_cmd: setting rfbMaxClientWait %d -> " - "%d msec.\n", orig, w); - rfbMaxClientWait = w; - - } else if (!strcmp(p, "nap")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, take_naps); - goto qry; - } - rfbLog("remote_cmd: turning on nap mode.\n"); - take_naps = 1; - } else if (!strcmp(p, "nonap")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !take_naps); - goto qry; - } - rfbLog("remote_cmd: turning off nap mode.\n"); - take_naps = 0; - - } else if (strstr(p, "sb") == p) { - int w; - COLON_CHECK("sb:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, screen_blank); - goto qry; - } - p += strlen("sb:"); - w = atoi(p); - if (w < 0) w = 0; - rfbLog("remote_cmd: setting screen_blank %d -> %d sec.\n", - screen_blank, w); - screen_blank = w; - } else if (strstr(p, "screen_blank") == p) { - int w; - COLON_CHECK("screen_blank:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, screen_blank); - goto qry; - } - p += strlen("screen_blank:"); - w = atoi(p); - if (w < 0) w = 0; - rfbLog("remote_cmd: setting screen_blank %d -> %d sec.\n", - screen_blank, w); - screen_blank = w; - - } else if (strstr(p, "fs") == p) { - COLON_CHECK("fs:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%f", p, co, fs_frac); - goto qry; - } - p += strlen("fs:"); - fs_frac = atof(p); - rfbLog("remote_cmd: setting -fs frac to %f\n", fs_frac); - - } else if (strstr(p, "gaps") == p) { - int g; - COLON_CHECK("gaps:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, gaps_fill); - goto qry; - } - p += strlen("gaps:"); - g = atoi(p); - if (g < 0) g = 0; - rfbLog("remote_cmd: setting gaps_fill %d -> %d.\n", - gaps_fill, g); - gaps_fill = g; - } else if (strstr(p, "grow") == p) { - int g; - COLON_CHECK("grow:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, grow_fill); - goto qry; - } - p += strlen("grow:"); - g = atoi(p); - if (g < 0) g = 0; - rfbLog("remote_cmd: setting grow_fill %d -> %d.\n", - grow_fill, g); - grow_fill = g; - } else if (strstr(p, "fuzz") == p) { - int f; - COLON_CHECK("fuzz:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, tile_fuzz); - goto qry; - } - p += strlen("fuzz:"); - f = atoi(p); - if (f < 0) f = 0; - rfbLog("remote_cmd: setting tile_fuzz %d -> %d.\n", - tile_fuzz, f); - grow_fill = f; - - } else if (!strcmp(p, "snapfb")) { - int orig = use_snapfb; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, use_snapfb); - goto qry; - } - rfbLog("remote_cmd: turning on snapfb mode.\n"); - use_snapfb = 1; - if (orig != use_snapfb) { - do_new_fb(1); - } - } else if (!strcmp(p, "nosnapfb")) { - int orig = use_snapfb; - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !use_snapfb); - goto qry; - } - rfbLog("remote_cmd: turning off snapfb mode.\n"); - use_snapfb = 0; - if (orig != use_snapfb) { - do_new_fb(1); - } - - } else if (strstr(p, "rawfb") == p) { - COLON_CHECK("rawfb:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(raw_fb_str)); - goto qry; - } - p += strlen("rawfb:"); - if (raw_fb_str) free(raw_fb_str); - raw_fb_str = strdup(p); - if (safe_remote_only && strstr(p, "setup:") == p) { /* skip-cmd-list */ - /* n.b. we still allow filename, shm, of rawfb */ - fprintf(stderr, "unsafe rawfb setup: %s\n", p); - exit(1); - } - - rfbLog("remote_cmd: setting -rawfb to:\n\t'%s'\n", p); - - if (*raw_fb_str == '\0') { - free(raw_fb_str); - raw_fb_str = NULL; - rfbLog("restoring per-rawfb settings...\n"); - set_raw_fb_params(1); - } - rfbLog("hang on tight, here we go...\n"); - do_new_fb(1); - - } else if (strstr(p, "progressive") == p) { - int f; - COLON_CHECK("progressive:") - if (query) { - if (!screen) { - f = 0; - } else { - f = screen->progressiveSliceHeight; - } - snprintf(buf, bufn, "ans=%s%s%d", p, co, f); - goto qry; - } - p += strlen("progressive:"); - f = atoi(p); - if (f < 0) f = 0; - rfbLog("remote_cmd: setting progressive %d -> %d.\n", - screen->progressiveSliceHeight, f); - screen->progressiveSliceHeight = f; - - } else if (strstr(p, "rfbport") == p) { - int rp, orig = screen ? screen->port : 5900; - COLON_CHECK("rfbport:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, orig); - goto qry; - } - p += strlen("rfbport:"); - rp = atoi(p); - reset_rfbport(orig, rp); - - } else if (!strcmp(p, "http")) { - if (query) { - int ls = screen ? screen->httpListenSock : -1; - snprintf(buf, bufn, "ans=%s:%d", p, (ls > -1)); - goto qry; - } - if (screen->httpListenSock > -1) { - rfbLog("already listening for http connections.\n"); - } else { - rfbLog("turning on listening for http connections.\n"); - if (check_httpdir()) { - http_connections(1); - } - } - } else if (!strcmp(p, "nohttp")) { - if (query) { - int ls = screen ? screen->httpListenSock : -1; - snprintf(buf, bufn, "ans=%s:%d", p, !(ls > -1)); - goto qry; - } - if (screen->httpListenSock < 0) { - rfbLog("already not listening for http connections.\n"); - } else { - rfbLog("turning off listening for http connections.\n"); - if (check_httpdir()) { - http_connections(0); - } - } - - } else if (strstr(p, "httpport") == p) { - int hp, orig = screen ? screen->httpPort : 0; - COLON_CHECK("httpport:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, orig); - goto qry; - } - p += strlen("httpport:"); - hp = atoi(p); - reset_httpport(orig, hp); - - } else if (strstr(p, "httpdir") == p) { - COLON_CHECK("httpdir:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%s", p, co, - NONUL(http_dir)); - goto qry; - } - p += strlen("httpdir:"); - if (http_dir && !strcmp(http_dir, p)) { - rfbLog("no change in httpdir: %s\n", http_dir); - } else { - if (http_dir) { - free(http_dir); - } - http_dir = strdup(p); - http_connections(0); - if (*p != '\0') { - http_connections(1); - } - } - - } else if (!strcmp(p, "enablehttpproxy")) { - if (query) { - int ht = screen ? screen->httpEnableProxyConnect : 0; - snprintf(buf, bufn, "ans=%s:%d", p, ht != 0); - goto qry; - } - rfbLog("turning on enablehttpproxy.\n"); - screen->httpEnableProxyConnect = 1; - } else if (!strcmp(p, "noenablehttpproxy")) { - if (query) { - int ht = screen ? screen->httpEnableProxyConnect : 0; - snprintf(buf, bufn, "ans=%s:%d", p, ht == 0); - goto qry; - } - rfbLog("turning off enablehttpproxy.\n"); - screen->httpEnableProxyConnect = 0; - - } else if (!strcmp(p, "alwaysshared")) { - if (query) { - int t = screen ? screen->alwaysShared : 0; - snprintf(buf, bufn, "ans=%s:%d", p, t != 0); - goto qry; - } - rfbLog("turning on alwaysshared.\n"); - screen->alwaysShared = 1; - } else if (!strcmp(p, "noalwaysshared")) { - if (query) { - int t = screen ? screen->alwaysShared : 0; - snprintf(buf, bufn, "ans=%s:%d", p, t == 0); - goto qry; - } - rfbLog("turning off alwaysshared.\n"); - screen->alwaysShared = 0; - - } else if (!strcmp(p, "nevershared")) { - if (query) { - int t = screen ? screen->neverShared : 1; - snprintf(buf, bufn, "ans=%s:%d", p, t != 0); - goto qry; - } - rfbLog("turning on nevershared.\n"); - screen->neverShared = 1; - } else if (!strcmp(p, "noalwaysshared")) { - if (query) { - int t = screen ? screen->neverShared : 1; - snprintf(buf, bufn, "ans=%s:%d", p, t == 0); - goto qry; - } - rfbLog("turning off nevershared.\n"); - screen->neverShared = 0; - - } else if (!strcmp(p, "dontdisconnect")) { - if (query) { - int t = screen ? screen->dontDisconnect : 1; - snprintf(buf, bufn, "ans=%s:%d", p, t != 0); - goto qry; - } - rfbLog("turning on dontdisconnect.\n"); - screen->dontDisconnect = 1; - } else if (!strcmp(p, "nodontdisconnect")) { - if (query) { - int t = screen ? screen->dontDisconnect : 1; - snprintf(buf, bufn, "ans=%s:%d", p, t == 0); - goto qry; - } - rfbLog("turning off dontdisconnect.\n"); - screen->dontDisconnect = 0; - - } 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, - NONUL(rfb_desktop_name)); - goto qry; - } - p += strlen("desktop:"); - if (rfb_desktop_name) { - free(rfb_desktop_name); - } - rfb_desktop_name = strdup(p); - screen->desktopName = rfb_desktop_name; - rfbLog("remote_cmd: setting desktop name to %s\n", - rfb_desktop_name); - - } else if (!strcmp(p, "debug_xevents")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, debug_xevents); - goto qry; - } - debug_xevents = 1; - rfbLog("set debug_xevents to: %d\n", debug_xevents); - } else if (!strcmp(p, "nodebug_xevents")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !debug_xevents); - goto qry; - } - debug_xevents = 0; - rfbLog("set debug_xevents to: %d\n", debug_xevents); - } else if (strstr(p, "debug_xevents") == p) { - COLON_CHECK("debug_xevents:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, debug_xevents); - goto qry; - } - p += strlen("debug_xevents:"); - debug_xevents = atoi(p); - rfbLog("set debug_xevents to: %d\n", debug_xevents); - - } else if (!strcmp(p, "debug_xdamage")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, debug_xdamage); - goto qry; - } - debug_xdamage = 1; - rfbLog("set debug_xdamage to: %d\n", debug_xdamage); - } else if (!strcmp(p, "nodebug_xdamage")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !debug_xdamage); - goto qry; - } - debug_xdamage = 0; - rfbLog("set debug_xdamage to: %d\n", debug_xdamage); - } else if (strstr(p, "debug_xdamage") == p) { - COLON_CHECK("debug_xdamage:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, debug_xdamage); - goto qry; - } - p += strlen("debug_xdamage:"); - debug_xdamage = atoi(p); - rfbLog("set debug_xdamage to: %d\n", debug_xdamage); - - } else if (!strcmp(p, "debug_wireframe")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, debug_wireframe); - goto qry; - } - debug_wireframe = 1; - rfbLog("set debug_wireframe to: %d\n", debug_wireframe); - } else if (!strcmp(p, "nodebug_wireframe")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !debug_wireframe); - goto qry; - } - debug_wireframe = 0; - rfbLog("set debug_wireframe to: %d\n", debug_wireframe); - } else if (strstr(p, "debug_wireframe") == p) { - COLON_CHECK("debug_wireframe:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, - debug_wireframe); - goto qry; - } - p += strlen("debug_wireframe:"); - debug_wireframe = atoi(p); - rfbLog("set debug_wireframe to: %d\n", debug_wireframe); - - } else if (!strcmp(p, "debug_scroll")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, debug_scroll); - goto qry; - } - debug_scroll = 1; - rfbLog("set debug_scroll to: %d\n", debug_scroll); - } else if (!strcmp(p, "nodebug_scroll")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !debug_scroll); - goto qry; - } - debug_scroll = 0; - rfbLog("set debug_scroll to: %d\n", debug_scroll); - } else if (strstr(p, "debug_scroll") == p) { - COLON_CHECK("debug_scroll:") - if (query) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, - debug_scroll); - goto qry; - } - p += strlen("debug_scroll:"); - 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, "debug_grabs")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, debug_grabs); - goto qry; - } - debug_grabs = 1; - rfbLog("set debug_grabs to: %d\n", debug_grabs); - } else if (!strcmp(p, "nodebug_grabs")) { - if (query) { - snprintf(buf, bufn, "ans=%s:%d", p, !debug_grabs); - goto qry; - } - debug_grabs = 0; - rfbLog("set debug_grabs to: %d\n", debug_grabs); - - } 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) { - snprintf(buf, bufn, "ans=%s%s%d", p, co, hack_val); - goto qry; - } - p += strlen("hack:"); - 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 (strstr(p, "client_info_sock") == p) { /* skip-cmd-list */ - NOTAPP - p += strlen("client_info_sock:"); - if (*p != '\0') { - start_client_info_sock(p); - } - - } else if (strstr(p, "noop") == p) { - NOTAPP - rfbLog("remote_cmd: noop\n"); - - } else if (icon_mode && !query && strstr(p, "passwd") == p) { /* skip-cmd-list */ - char **passwds_new = (char **) malloc(3*sizeof(char *)); - char **passwds_old = (char **) screen->authPasswdData; - - COLON_CHECK("passwd:") - p += strlen("passwd:"); - - passwds_new[0] = strdup(p); - - if (screen->authPasswdData && - screen->passwordCheck == rfbCheckPasswordByList) { - passwds_new[1] = passwds_old[1]; - } else { - passwds_new[1] = NULL; - screen->passwordCheck = rfbCheckPasswordByList; - } - passwds_new[2] = NULL; - - screen->authPasswdData = (void*) passwds_new; - if (*p == '\0') { - screen->authPasswdData = (void*) NULL; - } - rfbLog("remote_cmd: changed full access passwd.\n"); - - } else if (icon_mode && !query && strstr(p, "viewpasswd") == p) { /* skip-cmd-list */ - char **passwds_new = (char **) malloc(3*sizeof(char *)); - char **passwds_old = (char **) screen->authPasswdData; - - COLON_CHECK("viewpasswd:") - p += strlen("viewpasswd:"); - - passwds_new[1] = strdup(p); - - if (screen->authPasswdData && - screen->passwordCheck == rfbCheckPasswordByList) { - passwds_new[0] = passwds_old[0]; - } else { - char *tmp = (char *) malloc(4 + CHALLENGESIZE); - rfbRandomBytes((unsigned char*)tmp); - passwds_new[0] = tmp; - screen->passwordCheck = rfbCheckPasswordByList; - } - passwds_new[2] = NULL; - - if (*p == '\0') { - passwds_new[1] = NULL; - } - - screen->authPasswdData = (void*) passwds_new; - rfbLog("remote_cmd: changed view only passwd.\n"); - - } else if (strstr(p, "trayembed") == p) { /* skip-cmd-list */ - unsigned long id; - NOTAPP - - COLON_CHECK("trayembed:") - p += strlen("trayembed:"); - if (scan_hexdec(p, &id)) { - tray_request = (Window) id; - tray_unembed = 0; - rfbLog("remote_cmd: will try to embed 0x%x in" - " the system tray.\n", id); - } - } else if (strstr(p, "trayunembed") == p) { /* skip-cmd-list */ - unsigned long id; - NOTAPP - - COLON_CHECK("trayunembed:") - p += strlen("trayunembed:"); - if (scan_hexdec(p, &id)) { - tray_request = (Window) id; - tray_unembed = 1; - rfbLog("remote_cmd: will try to unembed 0x%x out" - " of the system tray.\n", id); - } - - - } else if (query) { - /* read-only variables that can only be queried: */ - - if (!strcmp(p, "display")) { - if (raw_fb) { - snprintf(buf, bufn, "aro=%s:rawfb:%p", - p, raw_fb_addr); - } else if (! dpy) { - snprintf(buf, bufn, "aro=%s:", p); - } else { - char *d; - d = DisplayString(dpy); - if (! d) d = "unknown"; - if (*d == ':') { - snprintf(buf, bufn, "aro=%s:%s%s", p, - this_host(), d); - } else { - snprintf(buf, bufn, "aro=%s:%s", p, d); - } - } - } else if (!strcmp(p, "vncdisplay")) { - snprintf(buf, bufn, "aro=%s:%s", p, - NONUL(vnc_desktop_name)); - } else if (!strcmp(p, "desktopname")) { - snprintf(buf, bufn, "aro=%s:%s", p, - NONUL(rfb_desktop_name)); - } else if (!strcmp(p, "guess_desktop")) { - snprintf(buf, bufn, "aro=%s:%s", p, - NONUL(guess_desktop())); - } else if (!strcmp(p, "http_url")) { - if (!screen) { - snprintf(buf, bufn, "aro=%s:", p); - } else if (screen->httpListenSock > -1) { - snprintf(buf, bufn, "aro=%s:http://%s:%d", p, - NONUL(screen->thisHost), screen->httpPort); - } else { - snprintf(buf, bufn, "aro=%s:%s", p, - "http_not_active"); - } - } else if (!strcmp(p, "auth") || !strcmp(p, "xauth")) { - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(auth_file)); - } else if (!strcmp(p, "users")) { - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(users_list)); - } else if (!strcmp(p, "rootshift")) { - snprintf(buf, bufn, "aro=%s:%d", p, rootshift); - } else if (!strcmp(p, "clipshift")) { - snprintf(buf, bufn, "aro=%s:%d", p, clipshift); - } else if (!strcmp(p, "scale_str")) { - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(scale_str)); - } else if (!strcmp(p, "scaled_x")) { - snprintf(buf, bufn, "aro=%s:%d", p, scaled_x); - } else if (!strcmp(p, "scaled_y")) { - snprintf(buf, bufn, "aro=%s:%d", p, scaled_y); - } else if (!strcmp(p, "scale_numer")) { - snprintf(buf, bufn, "aro=%s:%d", p, scale_numer); - } else if (!strcmp(p, "scale_denom")) { - snprintf(buf, bufn, "aro=%s:%d", p, scale_denom); - } else if (!strcmp(p, "scale_fac")) { - snprintf(buf, bufn, "aro=%s:%f", p, scale_fac); - } else if (!strcmp(p, "scaling_blend")) { - snprintf(buf, bufn, "aro=%s:%d", p, scaling_blend); - } else if (!strcmp(p, "scaling_nomult4")) { - snprintf(buf, bufn, "aro=%s:%d", p, scaling_nomult4); - } else if (!strcmp(p, "scaling_pad")) { - snprintf(buf, bufn, "aro=%s:%d", p, scaling_pad); - } else if (!strcmp(p, "scaling_interpolate")) { - snprintf(buf, bufn, "aro=%s:%d", p, - scaling_interpolate); - } else if (!strcmp(p, "inetd")) { - snprintf(buf, bufn, "aro=%s:%d", p, inetd); - } else if (!strcmp(p, "privremote")) { - snprintf(buf, bufn, "aro=%s:%d", p, priv_remote); - } else if (!strcmp(p, "unsafe")) { - snprintf(buf, bufn, "aro=%s:%d", p, !safe_remote_only); - } else if (!strcmp(p, "safer")) { - snprintf(buf, bufn, "aro=%s:%d", p, more_safe); - } else if (!strcmp(p, "nocmds")) { - snprintf(buf, bufn, "aro=%s:%d", p, no_external_cmds); - } else if (!strcmp(p, "passwdfile")) { - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(passwdfile)); - } else if (!strcmp(p, "using_shm")) { - snprintf(buf, bufn, "aro=%s:%d", p, !using_shm); - } else if (!strcmp(p, "logfile") || !strcmp(p, "o")) { - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(logfile)); - } else if (!strcmp(p, "flag")) { - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(flagfile)); - } else if (!strcmp(p, "rc")) { - char *s = rc_rcfile; - if (rc_rcfile_default) { - s = NULL; - } - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(s)); - } else if (!strcmp(p, "norc")) { - snprintf(buf, bufn, "aro=%s:%d", p, got_norc); - } else if (!strcmp(p, "h") || !strcmp(p, "help") || - !strcmp(p, "V") || !strcmp(p, "version") || - !strcmp(p, "lastmod")) { - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(lastmod)); - } else if (!strcmp(p, "bg")) { - snprintf(buf, bufn, "aro=%s:%d", p, opts_bg); - } else if (!strcmp(p, "sigpipe")) { - snprintf(buf, bufn, "aro=%s:%s", p, NONUL(sigpipe)); - } else if (!strcmp(p, "threads")) { - snprintf(buf, bufn, "aro=%s:%d", p, use_threads); - } else if (!strcmp(p, "readrate")) { - snprintf(buf, bufn, "aro=%s:%d", p, get_read_rate()); - } else if (!strcmp(p, "netrate")) { - snprintf(buf, bufn, "aro=%s:%d", p, get_net_rate()); - } else if (!strcmp(p, "netlatency")) { - snprintf(buf, bufn, "aro=%s:%d", p, get_net_latency()); - } else if (!strcmp(p, "pipeinput")) { - snprintf(buf, bufn, "aro=%s:%s", p, - NONUL(pipeinput_str)); - } else if (!strcmp(p, "clients")) { - char *str = list_clients(); - snprintf(buf, bufn, "aro=%s:%s", p, str); - free(str); - } else if (!strcmp(p, "client_count")) { - snprintf(buf, bufn, "aro=%s:%d", p, client_count); - } else if (!strcmp(p, "pid")) { - snprintf(buf, bufn, "aro=%s:%d", p, (int) getpid()); - } else if (!strcmp(p, "ext_xtest")) { - snprintf(buf, bufn, "aro=%s:%d", p, xtest_present); - } else if (!strcmp(p, "ext_xtrap")) { - snprintf(buf, bufn, "aro=%s:%d", p, xtrap_present); - } else if (!strcmp(p, "ext_xrecord")) { - snprintf(buf, bufn, "aro=%s:%d", p, xrecord_present); - } else if (!strcmp(p, "ext_xkb")) { - snprintf(buf, bufn, "aro=%s:%d", p, xkb_present); - } else if (!strcmp(p, "ext_xshm")) { - snprintf(buf, bufn, "aro=%s:%d", p, xshm_present); - } else if (!strcmp(p, "ext_xinerama")) { - snprintf(buf, bufn, "aro=%s:%d", p, xinerama_present); - } else if (!strcmp(p, "ext_overlay")) { - snprintf(buf, bufn, "aro=%s:%d", p, overlay_present); - } else if (!strcmp(p, "ext_xfixes")) { - snprintf(buf, bufn, "aro=%s:%d", p, xfixes_present); - } else if (!strcmp(p, "ext_xdamage")) { - snprintf(buf, bufn, "aro=%s:%d", p, xdamage_present); - } else if (!strcmp(p, "ext_xrandr")) { - snprintf(buf, bufn, "aro=%s:%d", p, xrandr_present); - } else if (!strcmp(p, "rootwin")) { - snprintf(buf, bufn, "aro=%s:0x%x", p, - (unsigned int) rootwin); - } else if (!strcmp(p, "num_buttons")) { - snprintf(buf, bufn, "aro=%s:%d", p, num_buttons); - } else if (!strcmp(p, "button_mask")) { - snprintf(buf, bufn, "aro=%s:%d", p, button_mask); - } else if (!strcmp(p, "mouse_x")) { - snprintf(buf, bufn, "aro=%s:%d", p, cursor_x); - } else if (!strcmp(p, "mouse_y")) { - snprintf(buf, bufn, "aro=%s:%d", p, cursor_y); - } else if (!strcmp(p, "bpp")) { - snprintf(buf, bufn, "aro=%s:%d", p, bpp); - } else if (!strcmp(p, "depth")) { - snprintf(buf, bufn, "aro=%s:%d", p, depth); - } else if (!strcmp(p, "indexed_color")) { - snprintf(buf, bufn, "aro=%s:%d", p, indexed_color); - } else if (!strcmp(p, "dpy_x")) { - snprintf(buf, bufn, "aro=%s:%d", p, dpy_x); - } else if (!strcmp(p, "dpy_y")) { - snprintf(buf, bufn, "aro=%s:%d", p, dpy_y); - } else if (!strcmp(p, "wdpy_x")) { - snprintf(buf, bufn, "aro=%s:%d", p, wdpy_x); - } else if (!strcmp(p, "wdpy_y")) { - snprintf(buf, bufn, "aro=%s:%d", p, wdpy_y); - } else if (!strcmp(p, "off_x")) { - snprintf(buf, bufn, "aro=%s:%d", p, off_x); - } else if (!strcmp(p, "off_y")) { - snprintf(buf, bufn, "aro=%s:%d", p, off_y); - } else if (!strcmp(p, "cdpy_x")) { - snprintf(buf, bufn, "aro=%s:%d", p, cdpy_x); - } else if (!strcmp(p, "cdpy_y")) { - snprintf(buf, bufn, "aro=%s:%d", p, cdpy_y); - } else if (!strcmp(p, "coff_x")) { - snprintf(buf, bufn, "aro=%s:%d", p, coff_x); - } else if (!strcmp(p, "coff_y")) { - snprintf(buf, bufn, "aro=%s:%d", p, coff_y); - } else if (!strcmp(p, "rfbauth")) { - NOTAPPRO - } else if (!strcmp(p, "passwd")) { - NOTAPPRO - } else if (!strcmp(p, "viewpasswd")) { - NOTAPPRO - } else { - NOTAPP - } - goto qry; - } else { - char tmp[100]; - NOTAPP - rfbLog("remote_cmd: warning unknown\n"); - strncpy(tmp, p, 90); - rfbLog("command \"%s\"\n", tmp); - goto done; - } - - done: - - if (*buf == '\0') { - sprintf(buf, "ack=1"); - } - - qry: - - if (stringonly) { - return strdup(buf); - } else if (client_connect_file) { - FILE *out = fopen(client_connect_file, "w"); - if (out != NULL) { - fprintf(out, "%s\n", buf); - fclose(out); - usleep(20*1000); - } - } else { - if (dpy) { /* raw_fb hack */ - set_vnc_connect_prop(buf); - XFlush(dpy); - } - } -#endif - return NULL; -} - -/* -- xdamage.c -- */ - -sraRegionPtr *xdamage_regions = NULL; -int xdamage_ticker = 0; - -/* for stats */ -int XD_skip = 0, XD_tot = 0, XD_des = 0; - -void record_desired_xdamage_rect(int x, int y, int w, int h) { - /* - * Unfortunately we currently can't trust an xdamage event - * to correspond to real screen damage. E.g. focus-in for - * mozilla (depending on wm) will mark the whole toplevel - * area as damaged, when only the border has changed. - * Similar things for terminal windows. - * - * This routine uses some heuristics to detect small enough - * damage regions that we will not have a performance problem - * if we believe them even though they are wrong. We record - * the corresponding tiles the damage regions touch. - */ - 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; - /* - * 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; - } - - if (!always_accept && area > xdamage_max_area) { - return; - } - - dt_x = w / tile_x; - dt_y = h / tile_y; - - 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?). - */ - return; - } - - 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); - nt_x2 = nfix((x+w)/tile_x, ntiles_x); - nt_y1 = nfix( (y)/tile_y, ntiles_y); - nt_y2 = nfix((y+h)/tile_y, ntiles_y); - - /* - * loop over the rectangle of tiles (1 tile for a small - * input rect). - */ - for (ix = nt_x1; ix <= nt_x2; ix++) { - for (iy = nt_y1; iy <= nt_y2; iy++) { - nt = ix + iy * ntiles_x; - cnt++; - if (! tile_has_xdamage_diff[nt]) { - XD_des++; - tile_has_xdamage_diff[nt] = 1; - } - /* not used: */ - tile_row_has_xdamage_diff[iy] = 1; - xdamage_tile_count++; - } - } - } - if (debug_xdamage > 1) { - fprintf(stderr, "xdamage: desired: %dx%d+%d+%d\tA: %6d tiles=" - "%02d-%02d/%02d-%02d tilecnt: %d\n", w, h, x, y, - w * h, nt_x1, nt_x2, nt_y1, nt_y2, cnt); - } -} - -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) { -if (0) fprintf(stderr, "add_region_xdamage: prev_tick: %d reg %p\n", prev_tick, (void *)reg); - sraRgnOr(reg, new_region); - } -} - -void clear_xdamage_mark_region(sraRegionPtr markregion, int flush) { -#if LIBVNCSERVER_HAVE_LIBXDAMAGE - XEvent ev; - sraRegionPtr tmpregion; - int count = 0; - - 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)) { - count++; - } - /* clear the whole damage region */ - XDamageSubtract(dpy, xdamage, None, None); - X_UNLOCK; - - if (debug_tiles || debug_xdamage) { - fprintf(stderr, "clear_xdamage_mark_region: %d\n", count); - } - - 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, 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 32 - int dup_x[DUPSZ], dup_y[DUPSZ], dup_w[DUPSZ], dup_h[DUPSZ]; - double tm, dt; - - if (scancnt) {} /* unused vars warning: */ - - if (! xdamage_present || ! use_xdamage) { - return 0; - } - if (! xdamage) { - return 0; - } - if (! xdamage_base_event_type) { - return 0; - } - - dtime0(&tm); - - nreg = (xdamage_memory * NSCAN) + 1; - - 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? - * Could check QLength and if huge just mark the whole - * screen. - */ - ecount++; - if (ev.type != xdamage_base_event_type + XDamageNotify) { - break; - } - dev = (XDamageNotifyEvent *) &ev; - if (dev->damage != xdamage) { - continue; /* not ours! */ - } - - x = dev->area.x; - y = dev->area.y; - w = dev->area.width; - h = dev->area.height; - - /* - * we try to manually remove some duplicates because - * certain activities can lead to many 10's of dups - * in a row. The region work can be costly and reg is - * later used in xdamage_hint_skip loops, so it is good - * to skip them if possible. - */ - dup = 0; - for (i=0; i < dup_max; i++) { - if (dup_x[i] == x && dup_y[i] == y && dup_w[i] == w && - dup_h[i] == h) { - dup = 1; - break; - } - } - if (dup) { - dcount++; - continue; - } - if (dup_max < DUPSZ) { - next = dup_max; - dup_max++; - } else { - next = (next+1) % DUPSZ; - } - dup_x[next] = x; - dup_y[next] = y; - dup_w[next] = w; - dup_h[next] = h; - - /* translate if needed */ - if (clipshift) { - /* set coords relative to fb origin */ - if (0 && rootshift) { - /* - * Note: not needed because damage is - * relative to subwin, not rootwin. - */ - x = x - off_x; - y = y - off_y; - } - if (clipshift) { - x = x - coff_x; - y = y - coff_y; - } - - x2 = x + w; /* upper point */ - x = nfix(x, dpy_x); /* place both in fb area */ - x2 = nfix(x2, dpy_x+1); - w = x2 - x; /* recompute w */ - - y2 = y + h; - y = nfix(y, dpy_y); - y2 = nfix(y2, dpy_y+1); - h = y2 - y; - - if (w <= 0 || h <= 0) { - continue; - } - } - if (debug_xdamage > 2) { - fprintf(stderr, "xdamage: -> event %dx%d+%d+%d area:" - " %d dups: %d %s\n", w, h, x, y, w*h, dcount, - (w*h > xdamage_max_area) ? "TOO_BIG" : ""); - } - - record_desired_xdamage_rect(x, y, w, h); - - tmpregion = sraRgnCreateRect(x, y, x + w, y + h); - sraRgnOr(reg, tmpregion); - sraRgnDestroy(tmpregion); - rect_count++; - ccount++; - } - /* clear the whole damage region for next time. XXX check */ - 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; - } - if (now > last_rpt + 15) { - double rat = -1.0; - - if (XD_tot) { - rat = ((double) XD_skip)/XD_tot; - } - 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); - } - - XD_skip = 0; - XD_tot = 0; - XD_des = 0; - rect_count = 0; - last_rpt = now; - } -#endif - return 0; -} - -int xdamage_hint_skip(int y) { - static sraRegionPtr scanline = NULL; - sraRegionPtr reg, tmpl; - int ret, i, n, nreg; - - if (! xdamage_present || ! use_xdamage) { - return 0; /* cannot skip */ - } - if (! xdamage_regions) { - return 0; /* cannot skip */ - } - - if (! scanline) { - /* keep it around to avoid malloc etc, recreate */ - scanline = sraRgnCreate(); - } - - tmpl = sraRgnCreateRect(0, y, dpy_x, y+1); - - nreg = (xdamage_memory * NSCAN) + 1; - ret = 1; - for (i=0; i<nreg; i++) { - /* go back thru the history starting at most recent */ - n = (xdamage_ticker + nreg - i) % nreg; - reg = xdamage_regions[n]; - if (sraRgnEmpty(reg)) { - /* checking for emptiness is very fast */ - continue; - } - sraRgnMakeEmpty(scanline); - sraRgnOr(scanline, tmpl); - if (sraRgnAnd(scanline, reg)) { - ret = 0; - break; - } - } - sraRgnDestroy(tmpl); - - return ret; -} - -void initialize_xdamage(void) { - sraRegionPtr *ptr; - int i, nreg; - - if (! xdamage_present) { - use_xdamage = 0; - } - if (xdamage_regions) { - ptr = xdamage_regions; - while (*ptr != NULL) { - sraRgnDestroy(*ptr); - ptr++; - } - free(xdamage_regions); - xdamage_regions = NULL; - } - if (use_xdamage) { - nreg = (xdamage_memory * NSCAN) + 2; - xdamage_regions = (sraRegionPtr *) - malloc(nreg * sizeof(sraRegionPtr)); - for (i = 0; i < nreg; i++) { - ptr = xdamage_regions+i; - if (i == nreg - 1) { - *ptr = NULL; - } else { - *ptr = sraRgnCreate(); - sraRgnMakeEmpty(*ptr); - } - } - /* set so will be 0 in first collect_xdamage call */ - xdamage_ticker = -1; - } -} - -void create_xdamage_if_needed(void) { - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - -#if LIBVNCSERVER_HAVE_LIBXDAMAGE - if (! xdamage) { - X_LOCK; - xdamage = XDamageCreate(dpy, window, XDamageReportRawRectangles); - XDamageSubtract(dpy, xdamage, None, None); - X_UNLOCK; - rfbLog("created xdamage object: 0x%lx\n", xdamage); - } -#endif -} - -void destroy_xdamage_if_needed(void) { - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - -#if LIBVNCSERVER_HAVE_LIBXDAMAGE - if (xdamage) { - XEvent ev; - X_LOCK; - XDamageDestroy(dpy, xdamage); - XFlush(dpy); - if (xdamage_base_event_type) { - while (XCheckTypedEvent(dpy, - xdamage_base_event_type+XDamageNotify, &ev)) { - ; - } - } - X_UNLOCK; - rfbLog("destroyed xdamage object: 0x%lx\n", xdamage); - xdamage = 0; - } -#endif -} - -void check_xdamage_state(void) { - if (! xdamage_present) { - return; - } - /* - * Create or destroy the Damage object as needed, we don't want - * one if no clients are connected. - */ - if (client_count && use_xdamage) { - create_xdamage_if_needed(); - if (xdamage_scheduled_mark > 0.0 && dnow() > - xdamage_scheduled_mark) { - if (xdamage_scheduled_mark_region) { - mark_region_for_xdamage( - xdamage_scheduled_mark_region); - sraRgnDestroy(xdamage_scheduled_mark_region); - xdamage_scheduled_mark_region = NULL; - } else { - mark_for_xdamage(0, 0, dpy_x, dpy_y); - } - xdamage_scheduled_mark = 0.0; - } - } else { - destroy_xdamage_if_needed(); - } -} - -/* -- cursor.c -- */ -/* - * Here begins a bit of a mess to experiment with multiple cursors - * drawn on the remote background ... - */ -typedef struct cursor_info { - char *data; /* data and mask pointers */ - char *mask; - int wx, wy; /* size of cursor */ - int sx, sy; /* shift to its centering point */ - int reverse; /* swap black and white */ - rfbCursorPtr rfb; -} cursor_info_t; - -void curs_copy(cursor_info_t *dest, cursor_info_t *src) { - if (src->data != NULL) { - dest->data = strdup(src->data); - } else { - dest->data = NULL; - } - if (src->mask != NULL) { - dest->mask = strdup(src->mask); - } else { - dest->mask = NULL; - } - dest->wx = src->wx; - dest->wy = src->wy; - dest->sx = src->sx; - dest->sy = src->sy; - dest->reverse = src->reverse; - dest->rfb = src->rfb; -} - -/* empty cursor */ -static char* curs_empty_data = -" " -" "; - -static char* curs_empty_mask = -" " -" "; -static cursor_info_t cur_empty = {NULL, NULL, 2, 2, 0, 0, 0, NULL}; - -/* dot cursor */ -static char* curs_dot_data = -" " -" x"; - -static char* curs_dot_mask = -" " -" x"; -static cursor_info_t cur_dot = {NULL, NULL, 2, 2, 0, 0, 0, NULL}; - - -/* main cursor */ -static char* curs_arrow_data = -" " -" x " -" xx " -" xxx " -" xxxx " -" xxxxx " -" xxxxxx " -" xxxxxxx " -" xxxxxxxx " -" xxxxx " -" xx xx " -" x xx " -" xx " -" xx " -" xx " -" " -" " -" "; - -static char* curs_arrow_mask = -"xx " -"xxx " -"xxxx " -"xxxxx " -"xxxxxx " -"xxxxxxx " -"xxxxxxxx " -"xxxxxxxxx " -"xxxxxxxxxx " -"xxxxxxxxxx " -"xxxxxxx " -"xxx xxxx " -"xx xxxx " -" xxxx " -" xxxx " -" xx " -" " -" "; -static cursor_info_t cur_arrow = {NULL, NULL, 18, 18, 0, 0, 1, NULL}; - -static char* curs_arrow2_data = -" " -" x " -" xx " -" xxx " -" xxxx " -" xxxxx " -" xxxxxx " -" xxxxxxx " -" xxxxxxxx " -" xxxxx " -" xx xx " -" x xx " -" xx " -" xx " -" xx " -" " -" " -" "; - -static char* curs_arrow2_mask = -"xx " -"xxx " -"xxxx " -"xxxxx " -"xxxxxx " -"xxxxxxx " -"xxxxxxxx " -"xxxxxxxxx " -"xxxxxxxxxx " -"xxxxxxxxxx " -"xxxxxxx " -"xxx xxxx " -"xx xxxx " -" xxxx " -" xxxx " -" xx " -" " -" "; -static cursor_info_t cur_arrow2 = {NULL, NULL, 18, 18, 0, 0, 0, NULL}; - -static char* curs_arrow3_data = -" " -" xx " -" xxxx " -" xxxxx " -" xxxxxxx " -" xxxxxxxx " -" xxxxxxxxxx " -" xxxxx " -" xxxxx " -" xx x " -" xx x " -" x x " -" x x " -" x " -" x " -" "; - -static char* curs_arrow3_mask = -"xxx " -"xxxxx " -"xxxxxxx " -" xxxxxxxx " -" xxxxxxxxxx " -" xxxxxxxxxxxx " -" xxxxxxxxxxxx " -" xxxxxxxxxxx " -" xxxxxxx " -" xxxxxxx " -" xxxx xxx " -" xxx xxx " -" xxx xxx " -" xxx xxx " -" xxx" -" xx"; - -static cursor_info_t cur_arrow3 = {NULL, NULL, 16, 16, 0, 0, 1, NULL}; - -static char* curs_arrow4_data = -" " -" xx " -" xxxx " -" xxxxx " -" xxxxxxx " -" xxxxxxxx " -" xxxxxxxxxx " -" xxxxx " -" xxxxx " -" xx x " -" xx x " -" x x " -" x x " -" x " -" x " -" "; - -static char* curs_arrow4_mask = -"xxx " -"xxxxx " -"xxxxxxx " -" xxxxxxxx " -" xxxxxxxxxx " -" xxxxxxxxxxxx " -" xxxxxxxxxxxx " -" xxxxxxxxxxx " -" xxxxxxx " -" xxxxxxx " -" xxxx xxx " -" xxx xxx " -" xxx xxx " -" xxx xxx " -" xxx" -" xx"; - -static cursor_info_t cur_arrow4 = {NULL, NULL, 16, 16, 0, 0, 0, NULL}; - -static char* curs_arrow5_data = -"x " -" xx " -" xxxx " -" xxxxx " -" xxxxxxx " -" xxx " -" xx x " -" x x " -" x x " -" x " -" x " -" x " -" x " -" x " -" x"; - -static char* curs_arrow5_mask = -"xx " -"xxxx " -" xxxxx " -" xxxxxxx " -" xxxxxxxx " -" xxxxxxxx " -" xxxxx " -" xxxxxx " -" xx xxx " -" x xxx " -" xxx " -" xxx " -" xxx " -" xxx" -" xx"; - -static cursor_info_t cur_arrow5 = {NULL, NULL, 15, 15, 0, 0, 1, NULL}; - -static char* curs_arrow6_data = -"x " -" xx " -" xxxx " -" xxxxx " -" xxxxxxx " -" xxx " -" xx x " -" x x " -" x x " -" x " -" x " -" x " -" x " -" x " -" x"; - -static char* curs_arrow6_mask = -"xx " -"xxxx " -" xxxxx " -" xxxxxxx " -" xxxxxxxx " -" xxxxxxxx " -" xxxxx " -" xxxxxx " -" xx xxx " -" x xxx " -" xxx " -" xxx " -" xxx " -" xxx" -" xx"; - -static cursor_info_t cur_arrow6 = {NULL, NULL, 15, 15, 0, 0, 0, NULL}; - -int alt_arrow_max = 6; -/* - * It turns out we can at least detect mouse is on the root window so - * show it (under -cursor X) with this familiar cursor... - */ -static char* curs_root_data = -" " -" " -" xxx xxx " -" xxxx xxxx " -" xxxxx xxxxx " -" xxxxx xxxxx " -" xxxxxxxxxx " -" xxxxxxxx " -" xxxxxx " -" xxxxxx " -" xxxxxxxx " -" xxxxxxxxxx " -" xxxxx xxxxx " -" xxxxx xxxxx " -" xxxx xxxx " -" xxx xxx " -" " -" "; - -static char* curs_root_mask = -" " -" xxxx xxxx " -" xxxxx xxxxx " -" xxxxxx xxxxxx " -" xxxxxxx xxxxxxx " -" xxxxxxxxxxxxxx " -" xxxxxxxxxxxx " -" xxxxxxxxxx " -" xxxxxxxx " -" xxxxxxxx " -" xxxxxxxxxx " -" xxxxxxxxxxxx " -" xxxxxxxxxxxxxx " -" xxxxxxx xxxxxxx " -" xxxxxx xxxxxx " -" xxxxx xxxxx " -" xxxx xxxx " -" "; -static cursor_info_t cur_root = {NULL, NULL, 18, 18, 8, 8, 1, NULL}; - -static char* curs_fleur_data = -" " -" xx " -" xxxx " -" xxxxxx " -" xx " -" x xx x " -" xx xx xx " -" xxxxxxxxxxxxxx " -" xxxxxxxxxxxxxx " -" xx xx xx " -" x xx x " -" xx " -" xxxxxx " -" xxxx " -" xx " -" "; - -static char* curs_fleur_mask = -" xxxx " -" xxxxx " -" xxxxxx " -" xxxxxxxx " -" x xxxxxx x " -" xxx xxxx xxx " -"xxxxxxxxxxxxxxxx" -"xxxxxxxxxxxxxxxx" -"xxxxxxxxxxxxxxxx" -"xxxxxxxxxxxxxxxx" -" xxx xxxx xxx " -" x xxxxxx x " -" xxxxxxxx " -" xxxxxx " -" xxxx " -" xxxx "; - -static cursor_info_t cur_fleur = {NULL, NULL, 16, 16, 8, 8, 1, NULL}; - -static char* curs_plus_data = -" " -" xx " -" xx " -" xx " -" xx " -" xxxxxxxxxx " -" xxxxxxxxxx " -" xx " -" xx " -" xx " -" xx " -" "; - -static char* curs_plus_mask = -" xxxx " -" xxxx " -" xxxx " -" xxxx " -"xxxxxxxxxxxx" -"xxxxxxxxxxxx" -"xxxxxxxxxxxx" -"xxxxxxxxxxxx" -" xxxx " -" xxxx " -" xxxx " -" xxxx "; -static cursor_info_t cur_plus = {NULL, NULL, 12, 12, 5, 6, 1, NULL}; - -static char* curs_xterm_data = -" " -" xxx xxx " -" xxx " -" x " -" x " -" x " -" x " -" x " -" x " -" x " -" x " -" x " -" x " -" xxx " -" xxx xxx " -" "; - -static char* curs_xterm_mask = -" xxxx xxxx " -" xxxxxxxxx " -" xxxxxxxxx " -" xxxxx " -" xxx " -" xxx " -" xxx " -" xxx " -" xxx " -" xxx " -" xxx " -" xxx " -" xxxxx " -" xxxxxxxxx " -" xxxxxxxxx " -" xxxx xxxx "; -static cursor_info_t cur_xterm = {NULL, NULL, 16, 16, 8, 8, 1, NULL}; - -enum cursor_names { - CURS_EMPTY = 0, - CURS_DOT, - - CURS_ARROW, - CURS_ROOT, - CURS_WM, - CURS_TERM, - CURS_PLUS, - - CURS_DYN1, - CURS_DYN2, - CURS_DYN3, - CURS_DYN4, - CURS_DYN5, - CURS_DYN6, - CURS_DYN7, - CURS_DYN8, - CURS_DYN9, - CURS_DYN10, - CURS_DYN11, - CURS_DYN12, - CURS_DYN13, - CURS_DYN14, - CURS_DYN15, - CURS_DYN16 -}; - -#define CURS_DYN_MIN CURS_DYN1 -#define CURS_DYN_MAX CURS_DYN16 -#define CURS_DYN_NUM (CURS_DYN_MAX - CURS_DYN_MIN + 1) - -#define CURS_MAX 32 -static cursor_info_t *cursors[CURS_MAX]; - -void setup_cursors_and_push(void) { - setup_cursors(); - first_cursor(); -} - -void first_cursor(void) { - if (! screen) { - return; - } - if (! show_cursor) { - screen->cursor = NULL; - } else { - got_xfixes_cursor_notify++; - set_rfb_cursor(get_which_cursor()); - set_cursor_was_changed(screen); - } -} - -void setup_cursors(void) { - rfbCursorPtr rfb_curs; - char *scale = NULL; - int i, j, n = 0; - static int first = 1; - - rfbLog("setting up %d cursors...\n", CURS_MAX); - - if (first) { - for (i=0; i<CURS_MAX; i++) { - cursors[i] = NULL; - } - } - first = 0; - - if (screen) { - screen->cursor = NULL; - LOCK(screen->cursorMutex); - } - - for (i=0; i<CURS_MAX; i++) { - cursor_info_t *ci; - if (cursors[i]) { - /* clear out any existing ones: */ - ci = cursors[i]; - if (ci->rfb) { - /* this is the rfbCursor part: */ - if (ci->rfb->richSource) { - free(ci->rfb->richSource); - ci->rfb->richSource = NULL; - } - if (ci->rfb->source) { - free(ci->rfb->source); - ci->rfb->source = NULL; - } - if (ci->rfb->mask) { - free(ci->rfb->mask); - ci->rfb->mask = NULL; - } - free(ci->rfb); - ci->rfb = NULL; - } - if (ci->data) { - free(ci->data); - ci->data = NULL; - } - if (ci->mask) { - free(ci->mask); - ci->mask = NULL; - } - free(ci); - ci = NULL; - } - - /* create new struct: */ - ci = (cursor_info_t *) malloc(sizeof(cursor_info_t)); - ci->data = NULL; - ci->mask = NULL; - ci->wx = 0; - ci->wy = 0; - ci->sx = 0; - ci->sy = 0; - ci->reverse = 0; - ci->rfb = NULL; - cursors[i] = ci; - } - - /* clear any xfixes cursor cache (no freeing is done) */ - get_xfixes_cursor(1); - - /* manually fill in the data+masks: */ - cur_empty.data = curs_empty_data; - cur_empty.mask = curs_empty_mask; - - cur_dot.data = curs_dot_data; - cur_dot.mask = curs_dot_mask; - - cur_arrow.data = curs_arrow_data; - cur_arrow.mask = curs_arrow_mask; - cur_arrow2.data = curs_arrow2_data; - cur_arrow2.mask = curs_arrow2_mask; - cur_arrow3.data = curs_arrow3_data; - cur_arrow3.mask = curs_arrow3_mask; - cur_arrow4.data = curs_arrow4_data; - cur_arrow4.mask = curs_arrow4_mask; - cur_arrow5.data = curs_arrow5_data; - cur_arrow5.mask = curs_arrow5_mask; - cur_arrow6.data = curs_arrow6_data; - cur_arrow6.mask = curs_arrow6_mask; - - cur_root.data = curs_root_data; - cur_root.mask = curs_root_mask; - - cur_plus.data = curs_plus_data; - cur_plus.mask = curs_plus_mask; - - cur_fleur.data = curs_fleur_data; - cur_fleur.mask = curs_fleur_mask; - - cur_xterm.data = curs_xterm_data; - cur_xterm.mask = curs_xterm_mask; - - curs_copy(cursors[CURS_EMPTY], &cur_empty); n++; - curs_copy(cursors[CURS_DOT], &cur_dot); n++; - - if (alt_arrow < 1 || alt_arrow > alt_arrow_max) { - alt_arrow = 1; - } - if (alt_arrow == 1) { - curs_copy(cursors[CURS_ARROW], &cur_arrow); n++; - } else if (alt_arrow == 2) { - curs_copy(cursors[CURS_ARROW], &cur_arrow2); n++; - } else if (alt_arrow == 3) { - curs_copy(cursors[CURS_ARROW], &cur_arrow3); n++; - } else if (alt_arrow == 4) { - curs_copy(cursors[CURS_ARROW], &cur_arrow4); n++; - } else if (alt_arrow == 5) { - curs_copy(cursors[CURS_ARROW], &cur_arrow5); n++; - } else if (alt_arrow == 6) { - curs_copy(cursors[CURS_ARROW], &cur_arrow6); n++; - } else { - alt_arrow = 1; - curs_copy(cursors[CURS_ARROW], &cur_arrow); n++; - } - - curs_copy(cursors[CURS_ROOT], &cur_root); n++; - curs_copy(cursors[CURS_WM], &cur_fleur); n++; - curs_copy(cursors[CURS_TERM], &cur_xterm); n++; - curs_copy(cursors[CURS_PLUS], &cur_plus); n++; - - if (scale_cursor_str) { - scale = scale_cursor_str; - } else if (scaling && scale_str) { - scale = scale_str; - } - /* scale = NULL zeroes everything */ - parse_scale_string(scale, &scale_cursor_fac, &scaling_cursor, - &scaling_cursor_blend, &j, &j, &scaling_cursor_interpolate, - &scale_cursor_numer, &scale_cursor_denom); - - for (i=0; i<n; i++) { - /* create rfbCursors for the special cursors: */ - - cursor_info_t *ci = cursors[i]; - - if (scaling_cursor && scale_cursor_fac != 1.0) { - int w, h, x, y, k; - unsigned long *pixels; - - w = ci->wx; - h = ci->wy; - - pixels = (unsigned long *) malloc(w * h - * sizeof(unsigned long)); - - k = 0; - for (y=0; y<h; y++) { - for (x=0; x<w; x++) { - char d = ci->data[k]; - char m = ci->mask[k]; - unsigned long *p; - - p = pixels + k; - - /* set alpha on */ - *p = 0xff000000; - - if (d == ' ' && m == ' ') { - /* alpha off */ - *p = 0x00000000; - } else if (d != ' ') { - /* body */ - if (ci->reverse) { - *p |= 0x00000000; - } else { - *p |= 0x00ffffff; - } - } else if (m != ' ') { - /* edge */ - if (ci->reverse) { - *p |= 0x00ffffff; - } else { - *p |= 0x00000000; - } - } - k++; - } - } - - rfb_curs = pixels2curs(pixels, w, h, ci->sx, ci->sy, - bpp/8); - - free(pixels); - - } else { - - /* standard X cursor */ - rfb_curs = rfbMakeXCursor(ci->wx, ci->wy, - ci->data, ci->mask); - - if (ci->reverse) { - rfb_curs->foreRed = 0x0000; - rfb_curs->foreGreen = 0x0000; - rfb_curs->foreBlue = 0x0000; - rfb_curs->backRed = 0xffff; - rfb_curs->backGreen = 0xffff; - rfb_curs->backBlue = 0xffff; - } - rfb_curs->alphaSource = NULL; - - rfb_curs->xhot = ci->sx; - rfb_curs->yhot = ci->sy; - rfb_curs->cleanup = FALSE; - rfb_curs->cleanupSource = FALSE; - rfb_curs->cleanupMask = FALSE; - rfb_curs->cleanupRichSource = FALSE; - - if (bpp == 8 && indexed_color) { - /* - * use richsource in PseudoColor for better - * looking cursors (i.e. two-color). - */ - int x, y, k = 0, bw; - int black = 0, white = 1; - char d, m; - - if (dpy) { /* raw_fb hack */ - black = BlackPixel(dpy, scr); - white = WhitePixel(dpy, scr); - } - - rfb_curs->richSource = (unsigned char *) - calloc(ci->wx * ci->wy, 1); - - for (y = 0; y < ci->wy; y++) { - for (x = 0; x < ci->wx; x++) { - d = *(ci->data + k); - m = *(ci->mask + k); - if (d == ' ' && m == ' ') { - k++; - continue; - } else if (m != ' ' && d == ' ') { - bw = black; - } else { - bw = white; - } - if (ci->reverse) { - if (bw == black) { - bw = white; - } else { - bw = black; - } - } - *(rfb_curs->richSource+k) = - (unsigned char) bw; - k++; - } - } - } - } - ci->rfb = rfb_curs; - } - if (screen) { - UNLOCK(screen->cursorMutex); - } - rfbLog(" done.\n"); -} - -typedef struct win_str_info { - char *wm_name; - char *res_name; - char *res_class; -} win_str_info_t; - -/* - * Descends window tree at pointer until the window cursor matches the current - * cursor. So far only used to detect if mouse is on root background or not. - * (returns 0 in that case, 1 otherwise). - * - */ -void tree_descend_cursor(int *depth, Window *w, win_str_info_t *winfo) { - Window r, c; - int i, rx, ry, wx, wy; - unsigned int mask; - Window wins[10]; - int descend, maxtries = 10; - char *name, *s = multiple_cursors_mode; - static XClassHint *classhint = NULL; - int nm_info = 1; - XErrorHandler old_handler; - - X_LOCK; - - if (!strcmp(s, "default") || !strcmp(s, "X") || !strcmp(s, "arrow")) { - nm_info = 0; - } - - *(winfo->wm_name) = '\0'; - *(winfo->res_name) = '\0'; - *(winfo->res_class) = '\0'; - - - /* some times a window can go away before we get to it */ - trapped_xerror = 0; - old_handler = XSetErrorHandler(trap_xerror); - - c = window; - descend = -1; - - while (c) { - wins[++descend] = c; - if (descend >= maxtries - 1) { - break; - } - if ( XTestCompareCurrentCursorWithWindow_wr(dpy, c) ) { - break; - } - /* TBD: query_pointer() */ - XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &mask); - } - - if (nm_info) { - int got_wm_name = 0, got_res_name = 0, got_res_class = 0; - - if (! classhint) { - classhint = XAllocClassHint(); - } - - for (i = descend; i >=0; i--) { - c = wins[i]; - if (! c) { - continue; - } - - if (! got_wm_name && XFetchName(dpy, c, &name)) { - if (name) { - if (*name != '\0') { - strcpy(winfo->wm_name, name); - got_wm_name = 1; - } - XFree(name); - } - } - if (classhint && (! got_res_name || ! got_res_class)) { - if (XGetClassHint(dpy, c, classhint)) { - char *p; - p = classhint->res_name; - if (p) { - if (*p != '\0' && ! got_res_name) { - strcpy(winfo->res_name, p); - got_res_name = 1; - } - XFree(p); - classhint->res_name = NULL; - } - p = classhint->res_class; - if (p) { - if (*p != '\0' && ! got_res_class) { - strcpy(winfo->res_class, p); - got_res_class = 1; - } - XFree(p); - classhint->res_class = NULL; - } - } - } - } - } - - XSetErrorHandler(old_handler); - trapped_xerror = 0; - - X_UNLOCK; - - *depth = descend; - *w = wins[descend]; -} - -void initialize_xfixes(void) { -#if LIBVNCSERVER_HAVE_LIBXFIXES - if (xfixes_present) { - X_LOCK; - if (use_xfixes) { - XFixesSelectCursorInput(dpy, rootwin, - XFixesDisplayCursorNotifyMask); - } else { - XFixesSelectCursorInput(dpy, rootwin, 0); - } - X_UNLOCK; - } -#endif -} - -rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h, - int xhot, int yhot, int Bpp) { - rfbCursorPtr c; - static unsigned long black = 0, white = 1; - static int first = 1; - char *bitmap, *rich, *alpha; - char *pixels_new = NULL; - int n_opaque, n_trans, n_alpha, len, histo[256]; - int send_alpha = 0, alpha_shift, thresh; - int i, x, y; - - if (first && dpy) { /* raw_fb hack */ - X_LOCK; - black = BlackPixel(dpy, scr); - white = WhitePixel(dpy, scr); - X_UNLOCK; - first = 0; - } - - if (scaling_cursor && scale_cursor_fac != 1.0) { - int W, H; - char *pixels_use = (char *) pixels; - unsigned int *pixels32 = NULL; - - W = w; - H = h; - - w = scale_round(W, scale_cursor_fac); - h = scale_round(H, scale_cursor_fac); - - pixels_new = (char *) malloc(4*w*h); - - if (sizeof(unsigned long) == 8) { - int i, j, k = 0; - /* - * to avoid 64bpp code in scale_rect() we knock - * down to unsigned int on 64bit machines: - */ - pixels32 = (unsigned int*) malloc(4*W*H); - for (j=0; j<H; j++) { - for (i=0; i<W; i++) { - *(pixels32+k) = 0xffffffff & (*(pixels+k)); - k++; - } - } - pixels_use = (char *) pixels32; - } - - scale_rect(scale_cursor_fac, scaling_cursor_blend, - scaling_cursor_interpolate, - 4, pixels_use, 4*W, pixels_new, 4*w, - W, H, w, h, 0, 0, W, H, 0); - - if (sizeof(unsigned long) == 8) { - int i, j, k = 0; - unsigned long *pixels64; - unsigned int* source = (unsigned int*) pixels_new; - /* - * now knock it back up to unsigned long: - */ - pixels64 = (unsigned long*) malloc(8*w*h); - for (j=0; j<h; j++) { - for (i=0; i<w; i++) { - *(pixels64+k) = (unsigned long) (*(source+k)); - k++; - } - } - free(pixels_new); - pixels_new = (char *) pixels64; - if (pixels32) { - free(pixels32); - pixels32 = NULL; - } - } - - pixels = (unsigned long *) pixels_new; - - xhot = scale_round(xhot, scale_cursor_fac); - yhot = scale_round(yhot, scale_cursor_fac); - } - - len = w * h; - /* for bitmap data */ - bitmap = (char *) malloc(len+1); - bitmap[len] = '\0'; - - /* for rich cursor pixel data */ - rich = (char *)calloc(Bpp*len, 1); - alpha = (char *)calloc(1*len, 1); - - n_opaque = 0; - n_trans = 0; - n_alpha = 0; - for (i=0; i<256; i++) { - histo[i] = 0; - } - - i = 0; - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - unsigned long a; - - a = 0xff000000 & (*(pixels+i)); - a = a >> 24; /* alpha channel */ - if (a > 0) { - n_alpha++; - } - histo[a]++; - if (a < (unsigned int) alpha_threshold) { - n_trans++; - } else { - n_opaque++; - } - i++; - } - } - if (alpha_blend) { - send_alpha = 0; - if (Bpp == 4) { - send_alpha = 1; - } - alpha_shift = 24; - if (main_red_shift == 24 || main_green_shift == 24 || - main_blue_shift == 24) { - alpha_shift = 0; /* XXX correct? */ - } - } - if (n_opaque >= alpha_frac * n_alpha) { - thresh = alpha_threshold; - } else { - n_opaque = 0; - for (i=255; i>=0; i--) { - n_opaque += histo[i]; - thresh = i; - if (n_opaque >= alpha_frac * n_alpha) { - break; - } - } - } - - i = 0; - for (y = 0; y < h; y++) { - for (x = 0; x < w; x++) { - unsigned long r, g, b, a; - unsigned int ui; - char *p; - - a = 0xff000000 & (*(pixels+i)); - a = a >> 24; /* alpha channel */ - - - if (a < (unsigned int) thresh) { - bitmap[i] = ' '; - } else { - bitmap[i] = 'x'; - } - - r = 0x00ff0000 & (*(pixels+i)); - g = 0x0000ff00 & (*(pixels+i)); - b = 0x000000ff & (*(pixels+i)); - r = r >> 16; /* red */ - g = g >> 8; /* green */ - b = b >> 0; /* blue */ - - if (alpha_remove && a != 0) { - r = (255 * r) / a; - g = (255 * g) / a; - b = (255 * b) / a; - if (r > 255) r = 255; - if (g > 255) g = 255; - if (b > 255) b = 255; - } - - if (indexed_color) { - /* - * Choose black or white for - * PseudoColor case. - */ - int value = (r+g+b)/3; - if (value > 127) { - ui = white; - } else { - ui = black; - } - } else { - /* - * Otherwise map the RGB data onto - * the framebuffer format: - */ - r = (main_red_max * r)/255; - g = (main_green_max * g)/255; - b = (main_blue_max * b)/255; - ui = 0; - ui |= (r << main_red_shift); - ui |= (g << main_green_shift); - ui |= (b << main_blue_shift); - if (send_alpha) { - ui |= (a << alpha_shift); - } - } - - /* insert value into rich source: */ - p = rich + Bpp*i; - - if (Bpp == 1) { - *((unsigned char *)p) - = (unsigned char) ui; - } else if (Bpp == 2) { - *((unsigned short *)p) - = (unsigned short) ui; - } else if (Bpp == 3) { - *((unsigned char *)p) - = (unsigned char) ((ui & 0x0000ff) >> 0); - *((unsigned char *)(p+1)) - = (unsigned char) ((ui & 0x00ff00) >> 8); - *((unsigned char *)(p+2)) - = (unsigned char) ((ui & 0xff0000) >> 16); - } else if (Bpp == 4) { - *((unsigned int *)p) - = (unsigned int) ui; - } - - /* insert alpha value into alpha source: */ - p = alpha + i; - *((unsigned char *)p) = (unsigned char) a; - - i++; - } - } - - /* create the cursor with the bitmap: */ - c = rfbMakeXCursor(w, h, bitmap, bitmap); - free(bitmap); - - if (pixels_new) { - free(pixels_new); - } - - /* set up the cursor parameters: */ - c->xhot = xhot; - c->yhot = yhot; - c->cleanup = FALSE; - c->cleanupSource = FALSE; - c->cleanupMask = FALSE; - c->cleanupRichSource = FALSE; - c->richSource = (unsigned char *) rich; - - if (alpha_blend && !indexed_color) { - c->alphaSource = (unsigned char *) alpha; - c->alphaPreMultiplied = TRUE; - } else { - free(alpha); - c->alphaSource = NULL; - } - return c; -} - -int get_xfixes_cursor(int init) { - static unsigned long last_cursor = 0; - static int last_index = 0; - static time_t curs_times[CURS_MAX]; - static unsigned long curs_index[CURS_MAX]; - int which = CURS_ARROW; - - if (init) { - /* zero out our cache (cursors are not freed) */ - int i; - for (i=0; i<CURS_MAX; i++) { - curs_times[i] = 0; - curs_index[i] = 0; - } - last_cursor = 0; - last_index = 0; - return -1; - } - - if (xfixes_present) { -#if LIBVNCSERVER_HAVE_LIBXFIXES - int use, oldest, i; - time_t oldtime, now; - XFixesCursorImage *xfc; - - if (! got_xfixes_cursor_notify && xfixes_base_event_type) { - /* try again for XFixesCursorNotify event */ - XEvent xev; - X_LOCK; - if (XCheckTypedEvent(dpy, xfixes_base_event_type + - XFixesCursorNotify, &xev)) { - got_xfixes_cursor_notify++; - } - X_UNLOCK; - } - if (! got_xfixes_cursor_notify) { - /* evidently no cursor change, just return last one */ - if (last_index) { - return last_index; - } else { - return CURS_ARROW; - } - } - got_xfixes_cursor_notify = 0; - - /* retrieve the cursor info + pixels from server: */ - X_LOCK; - xfc = XFixesGetCursorImage(dpy); - X_UNLOCK; - if (! xfc) { - /* failure. */ - return(which); - } - - if (xfc->cursor_serial == last_cursor) { - /* same serial index: no change */ - X_LOCK; - XFree(xfc); - X_UNLOCK; - if (last_index) { - return last_index; - } else { - return CURS_ARROW; - } - } - - oldest = CURS_DYN_MIN; - if (screen && screen->cursor == cursors[oldest]->rfb) { - oldest++; - } - oldtime = curs_times[oldest]; - now = time(0); - for (i = CURS_DYN_MIN; i <= CURS_DYN_MAX; i++) { - if (screen && screen->cursor == cursors[i]->rfb) { - ; - } else if (curs_times[i] < oldtime) { - /* watch for oldest one to overwrite */ - oldest = i; - oldtime = curs_times[i]; - } - if (xfc->cursor_serial == curs_index[i]) { - /* - * got a hit with an existing cursor, - * use that one. - */ - last_cursor = curs_index[i]; - curs_times[i] = now; - last_index = i; - X_LOCK; - XFree(xfc); - X_UNLOCK; - return last_index; - } - } - - /* we need to create the cursor and overwrite oldest */ - use = oldest; - if (cursors[use]->rfb) { - /* clean up oldest if it exists */ - if (cursors[use]->rfb->richSource) { - free(cursors[use]->rfb->richSource); - cursors[use]->rfb->richSource = NULL; - } - if (cursors[use]->rfb->alphaSource) { - free(cursors[use]->rfb->alphaSource); - cursors[use]->rfb->alphaSource = NULL; - } - if (cursors[use]->rfb->source) { - free(cursors[use]->rfb->source); - cursors[use]->rfb->source = NULL; - } - if (cursors[use]->rfb->mask) { - free(cursors[use]->rfb->mask); - cursors[use]->rfb->mask = NULL; - } - free(cursors[use]->rfb); - cursors[use]->rfb = NULL; - } - - /* place cursor into our collection */ - cursors[use]->rfb = pixels2curs(xfc->pixels, xfc->width, - xfc->height, xfc->xhot, xfc->yhot, bpp/8); - - /* update time and serial index: */ - curs_times[use] = now; - curs_index[use] = xfc->cursor_serial; - last_index = use; - last_cursor = xfc->cursor_serial; - - which = last_index; - - X_LOCK; - XFree(xfc); - X_UNLOCK; -#endif - } - return(which); -} - -int known_cursors_mode(char *s) { -/* - * default: see initialize_cursors_mode() for default behavior. - * arrow: unchanging white arrow. - * Xn*: show X on root background. Optional n sets treedepth. - * some: do the heuristics for root, wm, term detection. - * most: if display have overlay or xfixes, show all cursors, - * otherwise do the same as "some" - * none: show no cursor. - */ - if (strcmp(s, "default") && strcmp(s, "arrow") && *s != 'X' && - strcmp(s, "some") && strcmp(s, "most") && strcmp(s, "none")) { - return 0; - } else { - return 1; - } -} - -void initialize_cursors_mode(void) { - char *s = multiple_cursors_mode; - if (!s || !known_cursors_mode(s)) { - rfbLog("unknown cursors mode: %s\n", s); - rfbLog("resetting cursors mode to \"default\"\n"); - if (multiple_cursors_mode) free(multiple_cursors_mode); - multiple_cursors_mode = strdup("default"); - s = multiple_cursors_mode; - } - if (!strcmp(s, "none")) { - show_cursor = 0; - } else { - /* we do NOT set show_cursor = 1, let the caller do that */ - } - - show_multiple_cursors = 0; - if (show_cursor) { - if (!strcmp(s, "default")) { - if(multiple_cursors_mode) free(multiple_cursors_mode); - multiple_cursors_mode = strdup("X"); - s = multiple_cursors_mode; - } - if (*s == 'X' || !strcmp(s, "some") || !strcmp(s, "most")) { - show_multiple_cursors = 1; - } else { - show_multiple_cursors = 0; - /* hmmm, some bug going back to arrow mode.. */ - set_rfb_cursor(CURS_ARROW); - } - if (screen) { - set_cursor_was_changed(screen); - } - } else { - if (screen) { - screen->cursor = NULL; /* dangerous? */ - set_cursor_was_changed(screen); - } - } -} - -int get_which_cursor(void) { - int which = CURS_ARROW; - - if (show_multiple_cursors) { - int depth; - static win_str_info_t winfo; - static int first = 1, depth_cutoff = -1; - Window win; - XErrorHandler old_handler; - int mode = 0; - - if (drag_in_progress || button_mask) { - /* XXX not exactly what we want for menus */ - return -1; - } - - if (!strcmp(multiple_cursors_mode, "arrow")) { - /* should not happen... */ - return CURS_ARROW; - } else if (!strcmp(multiple_cursors_mode, "default")) { - mode = 0; - } else if (!strcmp(multiple_cursors_mode, "X")) { - mode = 1; - } else if (!strcmp(multiple_cursors_mode, "some")) { - mode = 2; - } else if (!strcmp(multiple_cursors_mode, "most")) { - mode = 3; - } - - if (mode == 3 && xfixes_present && use_xfixes) { - return get_xfixes_cursor(0); - } - - if (depth_cutoff < 0) { - int din; - if (sscanf(multiple_cursors_mode, "X%d", &din) == 1) { - depth_cutoff = din; - } else { - depth_cutoff = 0; - } - } - - if (first) { - winfo.wm_name = (char *) malloc(1024); - winfo.res_name = (char *) malloc(1024); - winfo.res_class = (char *) malloc(1024); - } - first = 0; - - tree_descend_cursor(&depth, &win, &winfo); - - if (depth <= depth_cutoff && !subwin) { - which = CURS_ROOT; - - } else if (mode == 2 || mode == 3) { - int which0 = which; - - /* apply crude heuristics to choose a cursor... */ - if (win) { - int ratio = 10, x, y; - unsigned int w, h, bw, d; - Window r; - - trapped_xerror = 0; - X_LOCK; - old_handler = XSetErrorHandler(trap_xerror); - - /* "narrow" windows are WM */ - if (XGetGeometry(dpy, win, &r, &x, &y, &w, &h, - &bw, &d)) { - if (w > ratio * h || h > ratio * w) { - which = CURS_WM; - } - } - XSetErrorHandler(old_handler); - X_UNLOCK; - trapped_xerror = 0; - } - if (which == which0) { - /* the string "term" mean I-beam. */ - char *name, *class; - lowercase(winfo.res_name); - lowercase(winfo.res_class); - name = winfo.res_name; - class = winfo.res_class; - if (strstr(name, "term")) { - which = CURS_TERM; - } else if (strstr(class, "term")) { - which = CURS_TERM; - } else if (strstr(name, "text")) { - which = CURS_TERM; - } else if (strstr(class, "text")) { - which = CURS_TERM; - } else if (strstr(name, "onsole")) { - which = CURS_TERM; - } else if (strstr(class, "onsole")) { - which = CURS_TERM; - } else if (strstr(name, "cmdtool")) { - which = CURS_TERM; - } else if (strstr(class, "cmdtool")) { - which = CURS_TERM; - } else if (strstr(name, "shelltool")) { - which = CURS_TERM; - } else if (strstr(class, "shelltool")) { - which = CURS_TERM; - } - } - } - } - return which; -} - -void set_cursor_was_changed(rfbScreenInfoPtr s) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - - if (! s) { - return; - } - iter = rfbGetClientIterator(s); - while( (cl = rfbClientIteratorNext(iter)) ) { - cl->cursorWasChanged = TRUE; - } - rfbReleaseClientIterator(iter); -} - -void set_cursor_was_moved(rfbScreenInfoPtr s) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - - if (! s) { - return; - } - iter = rfbGetClientIterator(s); - while( (cl = rfbClientIteratorNext(iter)) ) { - cl->cursorWasMoved = TRUE; - } - rfbReleaseClientIterator(iter); -} - -void restore_cursor_shape_updates(rfbScreenInfoPtr s) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int count = 0; - - if (! s || ! s->clientHead) { - return; - } - iter = rfbGetClientIterator(s); - while( (cl = rfbClientIteratorNext(iter)) ) { - int changed = 0; - ClientData *cd = (ClientData *) cl->clientData; - - if (cd->had_cursor_shape_updates) { - rfbLog("restoring enableCursorShapeUpdates for client" - " 0x%x\n", cl); - cl->enableCursorShapeUpdates = TRUE; - changed = 1; - } - if (cd->had_cursor_pos_updates) { - rfbLog("restoring enableCursorPosUpdates for client" - " 0x%x\n", cl); - cl->enableCursorPosUpdates = TRUE; - changed = 1; - } - if (changed) { - cl->cursorWasChanged = TRUE; - count++; - } - } - rfbReleaseClientIterator(iter); -} - -void disable_cursor_shape_updates(rfbScreenInfoPtr s) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - static int changed = 0; - int count = 0; - - if (! s || ! s->clientHead) { - return; - } - - iter = rfbGetClientIterator(s); - while( (cl = rfbClientIteratorNext(iter)) ) { - ClientData *cd; - cd = (ClientData *) cl->clientData; - - if (cl->enableCursorShapeUpdates) { - cd->had_cursor_shape_updates = 1; - count++; - if (debug_pointer) { - rfbLog("%s disable HCSU\n", cl->host); - } - } - if (cl->enableCursorPosUpdates) { - cd->had_cursor_pos_updates = 1; - count++; - if (debug_pointer) { - rfbLog("%s disable HCPU\n", cl->host); - } - } - - cl->enableCursorShapeUpdates = FALSE; - cl->enableCursorPosUpdates = FALSE; - cl->cursorWasChanged = FALSE; - } - rfbReleaseClientIterator(iter); - - if (count) { - changed = 1; - } -} - -int cursor_shape_updates_clients(rfbScreenInfoPtr s) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int count = 0; - - if (! s) { - return 0; - } - iter = rfbGetClientIterator(s); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->enableCursorShapeUpdates) { - count++; - } - } - rfbReleaseClientIterator(iter); - return count; -} - -int cursor_pos_updates_clients(rfbScreenInfoPtr s) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int count = 0; - - if (! s) { - return 0; - } - iter = rfbGetClientIterator(s); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (cl->enableCursorPosUpdates) { - count++; - } - } - rfbReleaseClientIterator(iter); - return count; -} - -/* - * Record rfb cursor position screen->cursorX, etc (a la defaultPtrAddEvent()) - * Then set up for sending rfbCursorPosUpdates back - * to clients that understand them. This seems to be TightVNC specific. - */ -void cursor_position(int x, int y) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int cnt = 0, nonCursorPosUpdates_clients = 0; - int x_in = x, y_in = y; - - /* x and y are current positions of X11 pointer on the X11 display */ - if (!screen) { - return; - } - - if (scaling) { - x = ((double) x / dpy_x) * scaled_x; - x = nfix(x, scaled_x); - y = ((double) y / dpy_y) * scaled_y; - y = nfix(y, scaled_y); - } - - if (x == screen->cursorX && y == screen->cursorY) { - return; - } - - LOCK(screen->cursorMutex); - screen->cursorX = x; - screen->cursorY = y; - UNLOCK(screen->cursorMutex); - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - if (! cl->enableCursorPosUpdates) { - nonCursorPosUpdates_clients++; - continue; - } - if (! cursor_pos_updates) { - continue; - } - if (cl == last_pointer_client) { - /* - * special case if this client was the last one to - * send a pointer position. - */ - if (x_in == cursor_x && y_in == cursor_y) { - cl->cursorWasMoved = FALSE; - } else { - /* an X11 app evidently warped the pointer */ - if (debug_pointer) { - rfbLog("cursor_position: warp " - "detected dx=%3d dy=%3d\n", - cursor_x - x, cursor_y - y); - } - cl->cursorWasMoved = TRUE; - cnt++; - } - } else { - cl->cursorWasMoved = TRUE; - cnt++; - } - } - rfbReleaseClientIterator(iter); - - if (debug_pointer && cnt) { - rfbLog("cursor_position: sent position x=%3d y=%3d to %d" - " clients\n", x, y, cnt); - } -} - -void set_rfb_cursor(int which) { - - if (! show_cursor) { - return; - } - if (! screen) { - return; - } - - if (!cursors[which] || !cursors[which]->rfb) { - rfbLog("non-existent cursor: which=%d\n", which); - return; - } else { - rfbSetCursor(screen, cursors[which]->rfb); - } -} - -void set_no_cursor(void) { - set_rfb_cursor(CURS_EMPTY); -} - -int set_cursor(int x, int y, int which) { - static int last = -1; - int changed_cursor = 0; - - if (x || y) {} /* unused vars warning: */ - - if (which < 0) { - which = last; - } - if (last < 0 || which != last) { - set_rfb_cursor(which); - changed_cursor = 1; - } - last = which; - - return changed_cursor; -} - -/* - * routine called periodically to update cursor aspects, this catches - * warps and cursor shape changes. - */ -int check_x11_pointer(void) { - Window root_w, child_w; - rfbBool ret; - int root_x, root_y, win_x, win_y; - int x, y; - unsigned int mask; - - if (raw_fb && ! dpy) return 0; /* raw_fb hack */ - - X_LOCK; - ret = XQueryPointer(dpy, rootwin, &root_w, &child_w, &root_x, &root_y, - &win_x, &win_y, &mask); - X_UNLOCK; - - if (! ret) { - return 0; - } - if (debug_pointer) { - static int last_x = -1, last_y = -1; - if (root_x != last_x || root_y != last_y) { - rfbLog("XQueryPointer: x:%4d, y:%4d)\n", - root_x, root_y); - } - last_x = root_x; - last_y = root_y; - } - - /* offset subtracted since XQueryPointer relative to rootwin */ - x = root_x - off_x - coff_x; - y = root_y - off_y - coff_y; - - /* record the cursor position in the rfb screen */ - cursor_position(x, y); - - /* change the cursor shape if necessary */ - return set_cursor(x, y, get_which_cursor()); -} - -/* -- screen.c -- */ -/* - * X11 and rfb display/screen related routines - */ - -/* - * Some handling of 8bpp PseudoColor colormaps. Called for initializing - * the clients and dynamically if -flashcmap is specified. - */ -#define NCOLOR 256 -void set_colormap(int reset) { - static int first = 1; - static XColor color[NCOLOR], prev[NCOLOR]; - Colormap cmap; - Visual *vis; - int i, ncells, diffs = 0; - - if (reset) { - first = 1; - if (screen->colourMap.data.shorts) { - free(screen->colourMap.data.shorts); - screen->colourMap.data.shorts = NULL; - } - } - - if (first) { - screen->colourMap.count = NCOLOR; - screen->serverFormat.trueColour = FALSE; - screen->colourMap.is16 = TRUE; - screen->colourMap.data.shorts = (unsigned short *) - malloc(3*sizeof(unsigned short) * NCOLOR); - } - - for (i=0; i < NCOLOR; i++) { - prev[i].red = color[i].red; - prev[i].green = color[i].green; - prev[i].blue = color[i].blue; - } - - X_LOCK; - - cmap = DefaultColormap(dpy, scr); - ncells = CellsOfScreen(ScreenOfDisplay(dpy, scr)); - vis = default_visual; - - if (subwin) { - XWindowAttributes attr; - - if (XGetWindowAttributes(dpy, window, &attr)) { - cmap = attr.colormap; - vis = attr.visual; - ncells = vis->map_entries; - } - } - - if (ncells != NCOLOR) { - if (first && ! quiet) { - rfbLog("set_colormap: number of cells is %d " - "instead of %d.\n", ncells, NCOLOR); - } - if (! shift_cmap) { - screen->colourMap.count = ncells; - } - } - - if (flash_cmap && ! first) { - XWindowAttributes attr; - Window c; - int tries = 0; - - c = window; - while (c && tries++ < 16) { - c = query_pointer(c); - if (valid_window(c, &attr, 0)) { - if (attr.colormap && attr.map_installed) { - cmap = attr.colormap; - vis = attr.visual; - ncells = vis->map_entries; - break; - } - } else { - break; - } - } - } - if (ncells > NCOLOR && ! quiet) { - rfbLog("set_colormap: big problem: ncells=%d > %d\n", - ncells, NCOLOR); - } - - if (vis->class == TrueColor || vis->class == DirectColor) { - /* - * Kludge to make 8bpp TrueColor & DirectColor be like - * the StaticColor map. The ncells = 8 is "8 per subfield" - * mentioned in xdpyinfo. Looks OK... perhaps fortuitously. - */ - if (ncells == 8 && ! shift_cmap) { - ncells = NCOLOR; - } - } - - for (i=0; i < ncells; i++) { - color[i].pixel = i; - color[i].pad = 0; - } - - XQueryColors(dpy, cmap, color, ncells); - - X_UNLOCK; - - for(i = ncells - 1; i >= 0; i--) { - int k = i + shift_cmap; - - screen->colourMap.data.shorts[i*3+0] = color[i].red; - screen->colourMap.data.shorts[i*3+1] = color[i].green; - screen->colourMap.data.shorts[i*3+2] = color[i].blue; - - if (prev[i].red != color[i].red || - prev[i].green != color[i].green || - prev[i].blue != color[i].blue ) { - diffs++; - } - - if (shift_cmap && k >= 0 && k < NCOLOR) { - /* kludge to copy the colors to higher pixel values */ - screen->colourMap.data.shorts[k*3+0] = color[i].red; - screen->colourMap.data.shorts[k*3+1] = color[i].green; - screen->colourMap.data.shorts[k*3+2] = color[i].blue; - } - } - - if (diffs && ! first) { - if (! all_clients_initialized()) { - rfbLog("set_colormap: warning: sending cmap " - "with uninitialized clients.\n"); - } - if (shift_cmap) { - rfbSetClientColourMaps(screen, 0, NCOLOR); - } else { - rfbSetClientColourMaps(screen, 0, ncells); - } - } - - first = 0; -} - -void debug_colormap(XImage *fb) { - static int debug_cmap = -1; - int i, k, histo[NCOLOR]; - - if (debug_cmap < 0) { - if (getenv("DEBUG_CMAP") != NULL) { - debug_cmap = 1; - } else { - debug_cmap = 0; - } - } - if (! debug_cmap) { - return; - } - if (! fb) { - return; - } - if (fb->bits_per_pixel > 8) { - return; - } - - for (i=0; i < NCOLOR; i++) { - histo[i] = 0; - } - for (k = 0; k < fb->width * fb->height; k++) { - unsigned char n; - char c = *(fb->data + k); - - n = (unsigned char) c; - histo[n]++; - } - fprintf(stderr, "\nColormap histogram for current screen contents:\n"); - for (i=0; i < NCOLOR; i++) { - unsigned short r = screen->colourMap.data.shorts[i*3+0]; - unsigned short g = screen->colourMap.data.shorts[i*3+1]; - unsigned short b = screen->colourMap.data.shorts[i*3+2]; - - fprintf(stderr, " %03d: %7d %04x/%04x/%04x", i, histo[i], - r, g, b); - if ((i+1) % 2 == 0) { - fprintf(stderr, "\n"); - } - } - fprintf(stderr, "\n"); -} - -/* - * Experimental mode to force the visual of the window instead of querying - * it. Used for testing, overriding some rare cases (win2vnc), and for - * -overlay . Input string can be a decimal or 0x hex or something like - * TrueColor or TrueColor:24 to force a depth as well. - * - * visual_id and possibly visual_depth are set. - */ -void set_visual(char *str) { - int vis, vdepth, defdepth = DefaultDepth(dpy, scr); - XVisualInfo vinfo; - char *p, *vstring = strdup(str); - - visual_id = (VisualID) 0; - visual_depth = 0; - - if (!strcmp(vstring, "ignore") || !strcmp(vstring, "default") - || !strcmp(vstring, "")) { - free(vstring); - return; - } - - /* set visual depth */ - if ((p = strchr(vstring, ':')) != NULL) { - visual_depth = atoi(p+1); - *p = '\0'; - vdepth = visual_depth; - } else { - vdepth = defdepth; - } - if (! quiet) { - fprintf(stderr, "\nVisual Info:\n"); - fprintf(stderr, " set_visual(\"%s\")\n", str); - fprintf(stderr, " visual_depth: %d\n", vdepth); - } - - /* set visual id number */ - if (strcmp(vstring, "StaticGray") == 0) { - vis = StaticGray; - } else if (strcmp(vstring, "GrayScale") == 0) { - vis = GrayScale; - } else if (strcmp(vstring, "StaticColor") == 0) { - vis = StaticColor; - } else if (strcmp(vstring, "PseudoColor") == 0) { - vis = PseudoColor; - } else if (strcmp(vstring, "TrueColor") == 0) { - vis = TrueColor; - } else if (strcmp(vstring, "DirectColor") == 0) { - vis = DirectColor; - } else { - unsigned int v_in; - if (sscanf(vstring, "0x%x", &v_in) != 1) { - if (sscanf(vstring, "%u", &v_in) == 1) { - visual_id = (VisualID) v_in; - return; - } - rfbLogEnable(1); - rfbLog("invalid -visual arg: %s\n", vstring); - X_UNLOCK; - clean_up_exit(1); - } - visual_id = (VisualID) v_in; - free(vstring); - return; - } - - if (! quiet) fprintf(stderr, " visual: %d\n", vis); - if (XMatchVisualInfo(dpy, scr, visual_depth, vis, &vinfo)) { - ; - } else if (XMatchVisualInfo(dpy, scr, defdepth, vis, &vinfo)) { - ; - } else { - rfbLogEnable(1); - rfbLog("could not find visual: %s\n", vstring); - X_UNLOCK; - clean_up_exit(1); - } - free(vstring); - - /* set numerical visual id. */ - visual_id = vinfo.visualid; -} - -void set_nofb_params(int restore) { - static int first = 1; - static int save[100]; - int i = 0; - - if (first) { - first = 0; - save[i++] = use_xfixes; - save[i++] = use_xdamage; - save[i++] = use_xrecord; - save[i++] = wireframe; - save[i++] = use_solid_bg; - save[i++] = overlay; - save[i++] = overlay_cursor; - save[i++] = using_shm; - save[i++] = single_copytile; - save[i++] = take_naps; - save[i++] = measure_speeds; - save[i++] = grab_buster; - save[i++] = show_cursor; - save[i++] = cursor_shape_updates; - save[i++] = cursor_pos_updates; - } - if (restore) { - i = 0; - use_xfixes = save[i++]; - use_xdamage = save[i++]; - use_xrecord = save[i++]; - wireframe = save[i++]; - use_solid_bg = save[i++]; - overlay = save[i++]; - overlay_cursor = save[i++]; - using_shm = save[i++]; - single_copytile = save[i++]; - take_naps = save[i++]; - measure_speeds = save[i++]; - grab_buster = save[i++]; - show_cursor = save[i++]; - cursor_shape_updates = save[i++]; - cursor_pos_updates = save[i++]; - - if (cursor_shape_updates) { - restore_cursor_shape_updates(screen); - } - initialize_cursors_mode(); - - return; - } - - use_xfixes = 0; - use_xdamage = 0; - use_xrecord = 0; - wireframe = 0; - - use_solid_bg = 0; - overlay = 0; - overlay_cursor = 0; - - using_shm = 0; - single_copytile = 1; - - take_naps = 0; - measure_speeds = 0; - - /* got_grab_buster? */ - grab_buster = 0; - - show_cursor = 0; - show_multiple_cursors = 0; - cursor_shape_updates = 0; - if (! got_cursorpos) { - cursor_pos_updates = 0; - } - - if (! quiet) { - rfbLog("disabling: xfixes, xdamage, solid, overlay, shm,\n"); - rfbLog(" wireframe, scrollcopyrect,\n"); - rfbLog(" noonetile, nap, cursor, %scursorshape\n", - got_cursorpos ? "" : "cursorpos, " ); - rfbLog(" in -nofb mode.\n"); - } -} - -char *raw_fb_orig_dpy = NULL; - -void set_raw_fb_params(int restore) { - static int first = 1; - static int vo0, us0, sm0, ws0, wp0, wb0, na0, tn0; - static int xr0, sb0; - static char *mc0; - - /* - * set turn off a bunch of parameters not compatible with - * -rawfb mode: 1) ignoring the X server 2) ignoring user input. - */ - - if (first) { - /* at least save the initial settings... */ - vo0 = view_only; - ws0 = watch_selection; - wp0 = watch_primary; - wb0 = watch_bell; - na0 = no_autorepeat; - sb0 = use_solid_bg; - - us0 = use_snapfb; - sm0 = using_shm; - tn0 = take_naps; - xr0 = xrandr; - mc0 = multiple_cursors_mode; - - first = 0; - } - - if (restore) { - view_only = vo0; - watch_selection = ws0; - watch_primary = wp0; - watch_bell = wb0; - no_autorepeat = na0; - use_solid_bg = sb0; - - use_snapfb = us0; - using_shm = sm0; - take_naps = tn0; - xrandr = xr0; - multiple_cursors_mode = mc0; - - if (! dpy && raw_fb_orig_dpy) { - dpy = XOpenDisplay(raw_fb_orig_dpy); - if (dpy) { - if (! quiet) rfbLog("reopened DISPLAY: %s\n", - raw_fb_orig_dpy); - } else { - if (! quiet) rfbLog("WARNING: failed to reopen " - "DISPLAY: %s\n", raw_fb_orig_dpy); - } - } - return; - } - - if (! quiet) { - rfbLog("set_raw_fb_params: modifying settings for " - "-rawfb mode.\n"); - } - - if (got_noviewonly) { - /* - * The user input parameters are not unset under - * -noviewonly... this usage should be very rare - * (i.e. rawfb but also send user input to the X - * display, most likely using /dev/fb0 for some reason...) - */ - if (! quiet) { - rfbLog("rawfb: -noviewonly mode: still sending mouse and\n"); - rfbLog("rawfb: keyboard input to the X DISPLAY!!\n"); - } - } else { - /* Normal case: */ - if (! view_only) { - if (! quiet) rfbLog("rawfb: setting view_only\n"); - view_only = 1; - } - if (watch_selection) { - if (! quiet) rfbLog("rawfb: turning off " - "watch_selection\n"); - watch_selection = 0; - } - if (watch_primary) { - if (! quiet) rfbLog("rawfb: turning off " - "watch_primary\n"); - watch_primary = 0; - } - if (watch_bell) { - if (! quiet) rfbLog("rawfb: turning off watch_bell\n"); - watch_bell = 0; - } - if (no_autorepeat) { - if (! quiet) rfbLog("rawfb: turning off " - "no_autorepeat\n"); - no_autorepeat = 0; - } - if (use_solid_bg) { - if (! quiet) rfbLog("rawfb: turning off " - "use_solid_bg\n"); - use_solid_bg = 0; - } - multiple_cursors_mode = strdup("arrow"); - } - if (use_snapfb) { - if (! quiet) rfbLog("rawfb: turning off use_snapfb\n"); - use_snapfb = 0; - } - if (using_shm) { - if (! quiet) rfbLog("rawfb: turning off using_shm\n"); - using_shm = 0; - } - if (take_naps) { - if (! quiet) rfbLog("rawfb: turning off take_naps\n"); - take_naps = 0; - } - if (xrandr) { - if (! quiet) rfbLog("rawfb: turning off xrandr\n"); - xrandr = 0; - } -} - -/* - * Presumably under -nofb the clients will never request the framebuffer. - * However, we have gotten such a request... so let's just give them - * the current view on the display. n.b. x2vnc and perhaps win2vnc - * requests a 1x1 pixel for some workaround so sadly this evidently - * nearly always happens. - */ -void nofb_hook(rfbClientPtr cl) { - XImage *fb; - rfbLog("framebuffer requested in -nofb mode by client %s\n", cl->host); - /* ignore xrandr */ - fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, ZPixmap); - main_fb = fb->data; - rfb_fb = main_fb; - screen->frameBuffer = rfb_fb; - screen->displayHook = NULL; -} - -void do_new_fb(int reset_mem) { - XImage *fb; - char *old_main = main_fb; - char *old_rfb = rfb_fb; - - /* for threaded we really should lock libvncserver out. */ - if (use_threads) { - rfbLog("warning: changing framebuffers while threaded may\n"); - rfbLog(" not work, do not use -threads if problems arise.\n"); - } - - if (reset_mem == 1) { - /* reset_mem == 2 is a hack for changing users... */ - clean_shm(0); - free_tiles(); - } - - fb = initialize_xdisplay_fb(); - - initialize_screen(NULL, NULL, fb); - - if (reset_mem) { - initialize_tiles(); - initialize_blackouts_and_xinerama(); - initialize_polling_images(); - } - - if (old_main != old_rfb && old_main) { - free(old_main); - } - if (old_rfb) { - free(old_rfb); - } - fb0 = fb; -} - -void remove_fake_fb(void) { - if (! screen) { - return; - } - rfbLog("removing fake fb: 0x%x\n", fake_fb); - - do_new_fb(1); - - /* - * fake_fb is freed in do_new_fb(), but we set to NULL here to - * indicate it is gone. - */ - fake_fb = NULL; -} - -void install_fake_fb(int w, int h, int bpp) { - int bpc; - if (! screen) { - return; - } - if (fake_fb) { - free(fake_fb); - } - fake_fb = (char *) calloc(w*h*bpp/8, 1); - if (! fake_fb) { - rfbLog("could not create fake fb: %dx%d %d\n", w, h, bpp); - return; - } - bpc = guess_bits_per_color(bpp); - rfbLog("installing fake fb: %dx%d %d\n", w, h, bpp); - rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n", - screen, fake_fb, w, h, bpc, 1, bpp/8); - - rfbNewFramebuffer(screen, fake_fb, w, h, bpc, 1, bpp/8); -} - -void check_padded_fb(void) { - if (! fake_fb) { - return; - } - if (time(0) > pad_geometry_time+1 && all_clients_initialized()) { - remove_fake_fb(); - } -} - -void install_padded_fb(char *geom) { - int w, h; - int ok = 1; - if (! geom || *geom == '\0') { - ok = 0; - } else if (sscanf(geom, "%dx%d", &w, &h) != 2) { - ok = 0; - } - w = nabs(w); - h = nabs(h); - - if (w < 5) w = 5; - if (h < 5) h = 5; - - if (!ok) { - rfbLog("skipping invalid pad geometry: '%s'\n", NONUL(geom)); - return; - } - install_fake_fb(w, h, bpp); - pad_geometry_time = time(0); -} - -void initialize_snap_fb(void) { - if (snap_fb) { - free(snap_fb); - } - snap = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, - ZPixmap); - snap_fb = snap->data; -} - - -XImage *initialize_raw_fb(void) { - char *str, *q; - int w, h, b, shmid = 0; - unsigned long rm = 0, gm = 0, bm = 0; - static XImage ximage_struct; /* n.b.: not (XImage *) */ - int closedpy = 1, i, m; - - if (raw_fb_addr || raw_fb_seek) { - if (raw_fb_shm) { - shmdt(raw_fb_addr); -#if LIBVNCSERVER_HAVE_MMAP - } else if (raw_fb_mmap) { - munmap(raw_fb_addr, raw_fb_mmap); - if (raw_fb_fd >= 0) { - close(raw_fb_fd); - } -#endif - } else if (raw_fb_seek) { - if (raw_fb_fd >= 0) { - close(raw_fb_fd); - } - } - raw_fb_addr = NULL; - } - if (! raw_fb_str) { - return NULL; - } - - - if ( (q = strstr(raw_fb_str, "setup:")) == raw_fb_str) { - FILE *pipe; - char line[1024], *t; - - set_child_info(); - q += strlen("setup:"); - if (no_external_cmds) { - rfbLogEnable(1); - rfbLog("cannot run external commands in -nocmds " - "mode:\n"); - rfbLog(" \"%s\"\n", q); - rfbLog(" exiting.\n"); - clean_up_exit(1); - } - rfbLog("running command to setup rawfb: %s\n", q); - pipe = popen(q, "r"); - if (! pipe) { - rfbLogEnable(1); - rfbLog("popen of setup command failed.\n"); - rfbLogPerror("popen"); - clean_up_exit(1); - } - line[0] = '\0'; - if (fgets(line, 1024, pipe) == NULL) { - rfbLogEnable(1); - rfbLog("read of setup command failed.\n"); - clean_up_exit(1); - } - pclose(pipe); - str = strdup(line); - t = str; - while (*t != '\0') { - if (*t == '\n') { - *t = '\0'; - } - t++; - } - rfbLog("setup command returned: %s\n", str); - - } else { - str = strdup(raw_fb_str); - } - - /* - * uppercase means do not close the display (e.g. for remote control) - */ - if (strstr(str, "SHM:") == str) { - closedpy = 0; - str[0] = 's'; str[1] = 'h'; str[2] = 'm'; - } else if (strstr(str, "MAP:") == str) { - closedpy = 0; - str[0] = 'm'; str[1] = 'a'; str[2] = 'p'; - } else if (strstr(str, "MMAP:") == str) { - closedpy = 0; - str[0] = 'm'; str[1] = 'm'; str[2] = 'a'; str[3] = 'p'; - } else if (strstr(str, "FILE:") == str) { - str[0] = 'f'; str[1] = 'i'; str[2] = 'l'; str[3] = 'e'; - closedpy = 0; - } - - if (closedpy && !view_only && got_noviewonly) { - rfbLog("not closing X DISPLAY under -noviewonly option.\n"); - closedpy = 0; - if (! window) { - window = rootwin; - } - } - - if (! raw_fb_orig_dpy && dpy) { - raw_fb_orig_dpy = strdup(DisplayString(dpy)); - } -#ifndef BOLDLY_CLOSE_DISPLAY -#define BOLDLY_CLOSE_DISPLAY 1 -#endif -#if BOLDLY_CLOSE_DISPLAY - if (closedpy) { - if (dpy) { - rfbLog("closing X DISPLAY: %s in rawfb mode.\n", - DisplayString(dpy)); - XCloseDisplay(dpy); /* yow! */ - } - dpy = NULL; - } -#endif - - /* - * -rawfb shm:163938442@640x480x32:ff/ff00/ff0000+3000 - * -rawfb map:/path/to/file@640x480x32:ff/ff00/ff0000 - * -rawfb file:/path/to/file@640x480x32:ff/ff00/ff0000 - */ - - raw_fb_offset = 0; - - /* +O offset */ - if ((q = strrchr(str, '+')) != NULL) { - if (sscanf(q, "+%d", &raw_fb_offset) == 1) { - *q = '\0'; - } else { - raw_fb_offset = 0; - } - } - /* :R/G/B masks */ - if ((q = strrchr(str, ':')) != NULL) { - if (sscanf(q, ":%lx/%lx/%lx", &rm, &gm, &bm) == 3) { - *q = '\0'; - } else if (sscanf(q, ":0x%lx/0x%lx/0x%lx", &rm, &gm, &bm)== 3) { - *q = '\0'; - } else if (sscanf(q, ":%lu/%lu/%lu", &rm, &gm, &bm) == 3) { - *q = '\0'; - } else { - rm = 0; - gm = 0; - bm = 0; - } - } - if ((q = strrchr(str, '@')) == NULL) { - rfbLogEnable(1); - rfbLog("invalid rawfb str: %s\n", str); - clean_up_exit(1); - } - /* @WxHxB */ - if (sscanf(q, "@%dx%dx%d", &w, &h, &b) != 3) { - rfbLogEnable(1); - rfbLog("invalid rawfb str: %s\n", str); - clean_up_exit(1); - } - *q = '\0'; - - if (strstr(str, "shm:") != str && strstr(str, "mmap:") != str && - strstr(str, "map:") != str && strstr(str, "file:") != str) { - /* hmmm, not following directions, see if map: applies */ - struct stat sbuf; - if (stat(str, &sbuf) == 0) { - char *new; - int len = strlen("map:") + strlen(str) + 1; - rfbLog("no type prefix: %s\n", raw_fb_str); - rfbLog(" but file exists, so assuming: map:%s\n", - raw_fb_str); - new = (char *) malloc(len); - strcpy(new, "map:"); - strcat(new, str); - free(str); - str = new; - } - } - - dpy_x = wdpy_x = w; - dpy_y = wdpy_y = h; - off_x = 0; - off_y = 0; - - raw_fb_shm = 0; - raw_fb_mmap = 0; - raw_fb_seek = 0; - raw_fb_fd = -1; - raw_fb_addr = NULL; - - if (sscanf(str, "shm:%d", &shmid) == 1) { - /* shm:N */ -#if LIBVNCSERVER_HAVE_XSHM - raw_fb_addr = (char *) shmat(shmid, 0, SHM_RDONLY); - if (! raw_fb_addr) { - rfbLogEnable(1); - rfbLog("failed to attach to shm: %d, %s\n", shmid, str); - rfbLogPerror("shmat"); - clean_up_exit(1); - } - raw_fb_shm = 1; - rfbLog("rawfb: shm: %d W: %d H: %d B: %d addr: %p\n", - shmid, w, h, b, raw_fb_addr); -#else - rfbLogEnable(1); - rfbLog("x11vnc was compiled without shm support.\n"); - rfbLogPerror("shmat"); - clean_up_exit(1); -#endif - } else if (strstr(str, "map:") == str || strstr(str, "mmap:") == str - || strstr(str, "file:") == str) { - /* map:/path/... or file:/path */ - int fd, do_mmap = 1, size; - struct stat sbuf; - - if (*str == 'f') { - do_mmap = 0; - } - q = strchr(str, ':'); - q++; - - fd = open(q, O_RDONLY); - if (fd < 0) { - rfbLogEnable(1); - rfbLog("failed to open file: %s, %s\n", q, str); - rfbLogPerror("open"); - clean_up_exit(1); - } - raw_fb_fd = fd; - - size = w*h*b/8 + raw_fb_offset; - if (fstat(fd, &sbuf) == 0) { - if (S_ISREG(sbuf.st_mode)) { - if (0) size = sbuf.st_size; - } else { - rfbLog("raw fb is non-regular file: %s\n", q); - } - } - - if (do_mmap) { -#if LIBVNCSERVER_HAVE_MMAP - raw_fb_addr = mmap(0, size, PROT_READ, MAP_SHARED, - fd, 0); - - if (raw_fb_addr == MAP_FAILED || raw_fb_addr == NULL) { - rfbLogEnable(1); - rfbLog("failed to mmap file: %s, %s\n", q, str); - rfbLog(" raw_fb_addr: %p\n", raw_fb_addr); - rfbLogPerror("mmap"); - clean_up_exit(1); - } - raw_fb_mmap = size; - - rfbLog("rawfb: mmap file: %s\n", q); - rfbLog(" w: %d h: %d b: %d addr: %p sz: %d\n", w, h, - b, raw_fb_addr, size); -#else - rfbLog("mmap(2) not supported on system, using" - " slower lseek(2)\n"); - raw_fb_seek = size; -#endif - } else { - raw_fb_seek = size; - - rfbLog("rawfb: seek file: %s\n", q); - rfbLog(" W: %d H: %d B: %d sz: %d\n", w, h, b, size); - } - } else { - rfbLogEnable(1); - rfbLog("invalid rawfb str: %s\n", str); - clean_up_exit(1); - } - - if (! raw_fb_image) { - raw_fb_image = &ximage_struct; - } - - initialize_clipshift(); - - raw_fb = (char *) malloc(dpy_x * dpy_y * b/8); - raw_fb_image->data = raw_fb; - raw_fb_image->format = ZPixmap; - raw_fb_image->width = dpy_x; - raw_fb_image->height = dpy_y; - raw_fb_image->bits_per_pixel = b; - raw_fb_image->bytes_per_line = dpy_x*b/8; - - if (rm == 0 && gm == 0 && bm == 0) { - /* guess masks... */ - if (b == 24 || b == 32) { - rm = 0xff0000; - gm = 0x00ff00; - bm = 0x0000ff; - } else if (b == 16) { - rm = 0xf800; - gm = 0x07e0; - bm = 0x001f; - } else if (b == 8) { - rm = 0x07; - gm = 0x38; - bm = 0xc0; - } - } - - raw_fb_image->red_mask = rm; - raw_fb_image->green_mask = gm; - raw_fb_image->blue_mask = bm; - - raw_fb_image->depth = 0; - m = 1; - for (i=0; i<32; i++) { - if (rm & m) { - raw_fb_image->depth++; - } - if (gm & m) { - raw_fb_image->depth++; - } - if (bm & m) { - raw_fb_image->depth++; - } - m = m << 1; - } - if (! raw_fb_image->depth) { - raw_fb_image->depth = (b == 32) ? 24 : b; - } - - if (clipshift) { - memset(raw_fb, 0xff, dpy_x * dpy_y * b/8); - } else if (raw_fb_addr) { - memcpy(raw_fb, raw_fb_addr + raw_fb_offset, dpy_x*dpy_y*b/8); - } else { - memset(raw_fb, 0xff, dpy_x * dpy_y * b/8); - } - - rfbLog("rawfb: raw_fb %p\n", raw_fb); - - free(str); - - return raw_fb_image; -} - -void initialize_clipshift(void) { - clipshift = 0; - cdpy_x = cdpy_y = coff_x = coff_y = 0; - if (clip_str) { - int w, h, x, y, bad = 0; - if (parse_geom(clip_str, &w, &h, &x, &y, wdpy_x, wdpy_y)) { - if (x < 0) { - x = 0; - } - if (y < 0) { - y = 0; - } - if (x + w > wdpy_x) { - w = wdpy_x - x; - } - if (y + h > wdpy_y) { - h = wdpy_y - y; - } - if (w <= 0 || h <= 0) { - bad = 1; - } - } else { - bad = 1; - } - if (bad) { - rfbLog("skipping invalid -clip WxH+X+Y: %s\n", - clip_str); - } else { - /* OK, change geom behind everyone's back... */ - cdpy_x = w; - cdpy_y = h; - coff_x = x; - coff_y = y; - - clipshift = 1; - - dpy_x = cdpy_x; - dpy_y = cdpy_y; - } - } -} - -/* - * initialize a fb for the X display - */ -XImage *initialize_xdisplay_fb(void) { - XImage *fb; - char *vis_str = visual_str; - int try = 0, subwin_tries = 3; - XErrorHandler old_handler; - int subwin_bs; - - if (raw_fb_str) { - return initialize_raw_fb(); - } - - X_LOCK; - if (subwin) { - if (subwin_wait_mapped) { - wait_until_mapped(subwin); - } - if (!valid_window((Window) subwin, NULL, 0)) { - rfbLogEnable(1); - rfbLog("invalid sub-window: 0x%lx\n", subwin); - X_UNLOCK; - clean_up_exit(1); - } - } - - if (overlay) { - /* - * ideally we'd like to not have to cook up the - * visual variables but rather let it all come out - * of XReadScreen(), however there is no way to get - * a default visual out of it, so we pretend -visual - * TrueColor:NN was supplied with NN usually 24. - */ - char str[32]; - Window twin = subwin ? subwin : rootwin; - XImage *xi; - - xi = xreadscreen(dpy, twin, 0, 0, 8, 8, False); - sprintf(str, "TrueColor:%d", xi->depth); - if (xi->depth != 24 && ! quiet) { - rfbLog("warning: overlay image has depth %d " - "instead of 24.\n", xi->depth); - } - XDestroyImage(xi); - if (visual_str != NULL && ! quiet) { - rfbLog("warning: replacing '-visual %s' by '%s' " - "for use with -overlay\n", visual_str, str); - } - vis_str = strdup(str); - } - - if (vis_str != NULL) { - set_visual(vis_str); - if (vis_str != visual_str) { - free(vis_str); - } - } - - /* set up parameters for subwin or non-subwin cases: */ - - if (! subwin) { - /* full screen */ - window = rootwin; - dpy_x = wdpy_x = DisplayWidth(dpy, scr); - dpy_y = wdpy_y = DisplayHeight(dpy, scr); - off_x = 0; - off_y = 0; - /* this may be overridden via visual_id below */ - default_visual = DefaultVisual(dpy, scr); - } else { - /* single window */ - XWindowAttributes attr; - - window = (Window) subwin; - if (! XGetWindowAttributes(dpy, window, &attr)) { - rfbLogEnable(1); - rfbLog("invalid window: 0x%lx\n", window); - X_UNLOCK; - clean_up_exit(1); - } - dpy_x = wdpy_x = attr.width; - dpy_y = wdpy_y = attr.height; - - subwin_bs = attr.backing_store; - - /* this may be overridden via visual_id below */ - default_visual = attr.visual; - - X_UNLOCK; - set_offset(); - X_LOCK; - } - - initialize_clipshift(); - - /* initialize depth to reasonable value, visual_id may override */ - depth = DefaultDepth(dpy, scr); - - if (visual_id) { - int n; - XVisualInfo vinfo_tmpl, *vinfo; - - /* - * we are in here from -visual or -overlay options - * visual_id and visual_depth were set in set_visual(). - */ - - vinfo_tmpl.visualid = visual_id; - vinfo = XGetVisualInfo(dpy, VisualIDMask, &vinfo_tmpl, &n); - if (vinfo == NULL || n == 0) { - rfbLogEnable(1); - rfbLog("could not match visual_id: 0x%x\n", - (int) visual_id); - X_UNLOCK; - clean_up_exit(1); - } - default_visual = vinfo->visual; - depth = vinfo->depth; - if (visual_depth) { - /* force it from -visual MooColor:NN */ - depth = visual_depth; - } - if (! quiet) { - fprintf(stderr, " initialize_xdisplay_fb()\n"); - fprintf(stderr, " Visual*: %p\n", - (void *) vinfo->visual); - fprintf(stderr, " visualid: 0x%x\n", - (int) vinfo->visualid); - fprintf(stderr, " screen: %d\n", vinfo->screen); - fprintf(stderr, " depth: %d\n", vinfo->depth); - fprintf(stderr, " class: %d\n", vinfo->class); - fprintf(stderr, " red_mask: 0x%08lx %s\n", - vinfo->red_mask, bitprint(vinfo->red_mask, 32)); - fprintf(stderr, " green_mask: 0x%08lx %s\n", - vinfo->green_mask, bitprint(vinfo->green_mask, 32)); - fprintf(stderr, " blue_mask: 0x%08lx %s\n", - vinfo->blue_mask, bitprint(vinfo->blue_mask, 32)); - fprintf(stderr, " cmap_size: %d\n", - vinfo->colormap_size); - fprintf(stderr, " bits b/rgb: %d\n", - vinfo->bits_per_rgb); - fprintf(stderr, "\n"); - } - XFree(vinfo); - } - - if (! quiet) { - rfbLog("Default visual ID: 0x%x\n", - (int) XVisualIDFromVisual(default_visual)); - } - - again: - if (subwin) { - int shift = 0; - int subwin_x, subwin_y; - int disp_x = DisplayWidth(dpy, scr); - int disp_y = DisplayHeight(dpy, scr); - Window twin; - /* subwins can be a dicey if they are changing size... */ - trapped_xerror = 0; - old_handler = XSetErrorHandler(trap_xerror); - XTranslateCoordinates(dpy, window, rootwin, 0, 0, &subwin_x, - &subwin_y, &twin); - - if (subwin_x + wdpy_x > disp_x) { - shift = 1; - subwin_x = disp_x - wdpy_x - 3; - } - if (subwin_y + wdpy_y > disp_y) { - shift = 1; - subwin_y = disp_y - wdpy_y - 3; - } - if (subwin_x < 0) { - shift = 1; - subwin_x = 1; - } - if (subwin_y < 0) { - shift = 1; - subwin_y = 1; - } - - if (shift) { - XMoveWindow(dpy, window, subwin_x, subwin_y); - } - XMapRaised(dpy, window); - XRaiseWindow(dpy, window); - XFlush(dpy); - } - try++; - - if (nofb) { - /* - * For -nofb we do not allocate the framebuffer, so we - * can save a few MB of memory. - */ - fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, - 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); - - } else if (visual_id) { - /* - * we need to call XCreateImage to supply the visual - */ - fb = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, - 0, NULL, dpy_x, dpy_y, BitmapPad(dpy), 0); - fb->data = (char *) malloc(fb->bytes_per_line * fb->height); - - } else { - fb = XGetImage_wr(dpy, window, 0, 0, dpy_x, dpy_y, AllPlanes, - ZPixmap); - if (! quiet) { - rfbLog("Read initial data from X display into" - " framebuffer.\n"); - } - } - - if (subwin) { - XSetErrorHandler(old_handler); - if (trapped_xerror) { - rfbLog("trapped GetImage at SUBWIN creation.\n"); - if (try < subwin_tries) { - usleep(250 * 1000); - if (!get_window_size(window, &wdpy_x, &wdpy_y)) { - rfbLogEnable(1); - rfbLog("could not get size of subwin " - "0x%lx\n", subwin); - X_UNLOCK; - clean_up_exit(1); - } - goto again; - } - } - trapped_xerror = 0; - - } else if (! fb && try == 1) { - /* try once more */ - usleep(250 * 1000); - goto again; - } - if (use_snapfb) { - initialize_snap_fb(); - } - X_UNLOCK; - - if (fb->bits_per_pixel == 24 && ! quiet) { - rfbLog("warning: 24 bpp may have poor performance.\n"); - } - return fb; -} - -void parse_scale_string(char *str, double *factor, int *scaling, int *blend, - int *nomult4, int *pad, int *interpolate, int *numer, int *denom) { - - int m, n; - char *p, *tstr; - double f; - - *factor = 1.0; - *scaling = 0; - *blend = 1; - *nomult4 = 0; - *pad = 0; - *interpolate = 0; - *numer = 0, *denom = 0; - - if (str == NULL || str[0] == '\0') { - return; - } - tstr = strdup(str); - - if ( (p = strchr(tstr, ':')) != NULL) { - /* options */ - if (strstr(p+1, "nb") != NULL) { - *blend = 0; - } - if (strstr(p+1, "fb") != NULL) { - *blend = 2; - } - if (strstr(p+1, "n4") != NULL) { - *nomult4 = 1; - } - if (strstr(p+1, "in") != NULL) { - *interpolate = 1; - } - if (strstr(p+1, "pad") != NULL) { - *pad = 1; - } - if (strstr(p+1, "nocr") != NULL) { - /* global */ - scaling_copyrect = 0; - } else if (strstr(p+1, "cr") != NULL) { - /* global */ - scaling_copyrect = 1; - } - *p = '\0'; - } - if (strchr(tstr, '.') != NULL) { - double test, diff, eps = 1.0e-7; - if (sscanf(tstr, "%lf", &f) != 1) { - rfbLogEnable(1); - rfbLog("invalid -scale arg: %s\n", tstr); - clean_up_exit(1); - } - *factor = (double) f; - /* look for common fractions from small ints: */ - for (n=2; n<=10; n++) { - for (m=1; m<n; m++) { - test = ((double) m)/ n; - diff = *factor - test; - if (-eps < diff && diff < eps) { - *numer = m; - *denom = n; - break; - - } - } - if (*denom) { - break; - } - } - if (*factor < 0.01) { - rfbLogEnable(1); - rfbLog("-scale factor too small: %f\n", scale_fac); - clean_up_exit(1); - } - } else { - if (sscanf(tstr, "%d/%d", &m, &n) != 2) { - if (sscanf(tstr, "%d", &m) != 1) { - rfbLogEnable(1); - rfbLog("invalid -scale arg: %s\n", tstr); - clean_up_exit(1); - } else { - /* e.g. -scale 1 or -scale 2 */ - n = 1; - } - } - if (n <= 0 || m <=0) { - rfbLogEnable(1); - rfbLog("invalid -scale arg: %s\n", tstr); - clean_up_exit(1); - } - *factor = ((double) m)/ n; - if (*factor < 0.01) { - rfbLogEnable(1); - rfbLog("-scale factor too small: %f\n", *factor); - clean_up_exit(1); - } - *numer = m; - *denom = n; - } - if (*factor == 1.0) { - if (! quiet) { - rfbLog("scaling disabled for factor %f\n", *factor); - } - } else { - *scaling = 1; - } - free(tstr); -} - -int scale_round(int len, double fac) { - double eps = 0.000001; - - len = (int) (len * fac + eps); - if (len < 1) { - len = 1; - } - return len; -} - -void setup_scaling(int *width_in, int *height_in) { - int width = *width_in; - int height = *height_in; - - parse_scale_string(scale_str, &scale_fac, &scaling, &scaling_blend, - &scaling_nomult4, &scaling_pad, &scaling_interpolate, - &scale_numer, &scale_denom); - - if (scaling) { - width = scale_round(width, scale_fac); - height = scale_round(height, scale_fac); - if (scale_denom && scaling_pad) { - /* it is not clear this padding is useful anymore */ - rfbLog("width %% denom: %d %% %d = %d\n", width, - scale_denom, width % scale_denom); - rfbLog("height %% denom: %d %% %d = %d\n", height, - scale_denom, height % scale_denom); - if (width % scale_denom != 0) { - int w = width; - w += scale_denom - (w % scale_denom); - if (!scaling_nomult4 && w % 4 != 0) { - /* need to make mult of 4 as well */ - int c = 0; - while (w % 4 != 0 && c++ <= 5) { - w += scale_denom; - } - } - width = w; - rfbLog("padded width to: %d (mult of %d%s\n", - width, scale_denom, !scaling_nomult4 ? - " and 4)" : ")"); - } - if (height % scale_denom != 0) { - height += scale_denom - (height % scale_denom); - rfbLog("padded height to: %d (mult of %d)\n", - height, scale_denom); - } - } - if (!scaling_nomult4 && width % 4 != 0 && width > 2) { - /* reset width to be multiple of 4 */ - int width0 = width; - if ((width+1) % 4 == 0) { - width = width+1; - } else if ((width-1) % 4 == 0) { - width = width-1; - } else if ((width+2) % 4 == 0) { - width = width+2; - } - rfbLog("reset scaled width %d -> %d to be a multiple of" - " 4 (to\n", width0, width); - rfbLog("make vncviewers happy). use -scale m/n:n4 to " - "disable.\n"); - } - scaled_x = width; - scaled_y = height; - - *width_in = width; - *height_in = height; - } -} - -/* - * initialize the rfb framebuffer/screen - */ -void initialize_screen(int *argc, char **argv, XImage *fb) { - int have_masks = 0; - int width = fb->width; - int height = fb->height; - int create_screen = screen ? 0 : 1; - int bits_per_color; - - main_bytes_per_line = fb->bytes_per_line; - - setup_scaling(&width, &height); - - - if (scaling) { - rfbLog("scaling screen: %dx%d -> %dx%d scale_fac=%.5f\n", - fb->width, fb->height, scaled_x, scaled_y, scale_fac); - - rfb_bytes_per_line = (main_bytes_per_line / fb->width) * width; - } else { - rfb_bytes_per_line = main_bytes_per_line; - } - - /* - * These are just hints wrt pixel format just to let - * rfbGetScreen/rfbNewFramebuffer proceed with reasonable - * defaults. We manually set them in painful detail below. - */ - bits_per_color = guess_bits_per_color(fb->bits_per_pixel); - - /* n.b. samplesPerPixel (set = 1 here) seems to be unused. */ - if (create_screen) { - screen = rfbGetScreen(argc, argv, width, height, - bits_per_color, 1, (int) fb->bits_per_pixel/8); - if (screen && http_dir) { - http_connections(1); - } - } else { - /* set set frameBuffer member below. */ - rfbLog("rfbNewFramebuffer(0x%x, 0x%x, %d, %d, %d, %d, %d)\n", - screen, NULL, width, height, - bits_per_color, 1, fb->bits_per_pixel/8); - - /* these are probably overwritten, but just to be safe: */ - screen->bitsPerPixel = fb->bits_per_pixel; - screen->depth = fb->depth; - - rfbNewFramebuffer(screen, NULL, width, height, - bits_per_color, 1, (int) fb->bits_per_pixel/8); - } - if (! screen) { - int i; - rfbLogEnable(1); - rfbLog("\n"); - rfbLog("failed to create rfb screen.\n"); - for (i=0; i< *argc; i++) { - rfbLog("\t[%d] %s\n", i, argv[i]); - } - clean_up_exit(1); - } - - if (create_screen && *argc != 1) { - int i; - rfbLogEnable(1); - rfbLog("*** unrecognized option(s) ***\n"); - for (i=1; i< *argc; i++) { - rfbLog("\t[%d] %s\n", i, argv[i]); - } - rfbLog("For a list of options run: x11vnc -opts\n"); - rfbLog("or for the full help: x11vnc -help\n"); - rfbLog("\n"); - rfbLog("Here is a list of removed or obsolete" - " options:\n"); - rfbLog("\n"); - rfbLog("removed: -hints, -nohints\n"); - rfbLog("removed: -cursorposall\n"); - rfbLog("\n"); - rfbLog("renamed: -old_copytile, use -onetile\n"); - rfbLog("renamed: -mouse, use -cursor\n"); - rfbLog("renamed: -mouseX, use -cursor X\n"); - rfbLog("renamed: -X, use -cursor X\n"); - rfbLog("renamed: -nomouse, use -nocursor\n"); - rfbLog("renamed: -old_pointer, use -pointer_mode 1\n"); - - clean_up_exit(1); - } - - /* set up format from scratch: */ - screen->paddedWidthInBytes = rfb_bytes_per_line; - screen->serverFormat.bitsPerPixel = fb->bits_per_pixel; - screen->serverFormat.depth = fb->depth; - screen->serverFormat.trueColour = TRUE; - - screen->serverFormat.redShift = 0; - screen->serverFormat.greenShift = 0; - screen->serverFormat.blueShift = 0; - screen->serverFormat.redMax = 0; - screen->serverFormat.greenMax = 0; - screen->serverFormat.blueMax = 0; - - /* these main_* formats are used generally. */ - main_red_shift = 0; - main_green_shift = 0; - main_blue_shift = 0; - main_red_max = 0; - main_green_max = 0; - main_blue_max = 0; - main_red_mask = fb->red_mask; - main_green_mask = fb->green_mask; - main_blue_mask = fb->blue_mask; - - - have_masks = ((fb->red_mask|fb->green_mask|fb->blue_mask) != 0); - if (force_indexed_color) { - have_masks = 0; - } - - if (! have_masks && screen->serverFormat.bitsPerPixel == 8 - && dpy && CellsOfScreen(ScreenOfDisplay(dpy, scr))) { - /* indexed color */ - if (!quiet) { - rfbLog("X display %s is 8bpp indexed color\n", - DisplayString(dpy)); - if (! flash_cmap && ! overlay) { - rfbLog("\n"); - rfbLog("In 8bpp PseudoColor mode if you " - "experience color\n"); - rfbLog("problems you may want to enable " - "following the\n"); - rfbLog("changing colormap by using the " - "-flashcmap option.\n"); - rfbLog("\n"); - } - } - screen->serverFormat.trueColour = FALSE; - indexed_color = 1; - set_colormap(1); - debug_colormap(fb); - } else { - /* - * general case, we call it truecolor, but could be direct - * color, static color, etc.... - */ - if (! quiet) { - if (raw_fb) { - rfbLog("Raw fb at addr %p is %dbpp depth=%d " - "true color\n", raw_fb_addr, - fb->bits_per_pixel, fb->depth); - } else { - rfbLog("X display %s is %dbpp depth=%d true " - "color\n", DisplayString(dpy), - fb->bits_per_pixel, fb->depth); - } - } - - indexed_color = 0; - - /* convert masks to bit shifts and max # colors */ - if (fb->red_mask) { - while (! (fb->red_mask - & (1 << screen->serverFormat.redShift))) { - screen->serverFormat.redShift++; - } - } - if (fb->green_mask) { - while (! (fb->green_mask - & (1 << screen->serverFormat.greenShift))) { - screen->serverFormat.greenShift++; - } - } - if (fb->blue_mask) { - while (! (fb->blue_mask - & (1 << screen->serverFormat.blueShift))) { - screen->serverFormat.blueShift++; - } - } - screen->serverFormat.redMax - = fb->red_mask >> screen->serverFormat.redShift; - screen->serverFormat.greenMax - = fb->green_mask >> screen->serverFormat.greenShift; - screen->serverFormat.blueMax - = fb->blue_mask >> screen->serverFormat.blueShift; - - main_red_max = screen->serverFormat.redMax; - main_green_max = screen->serverFormat.greenMax; - main_blue_max = screen->serverFormat.blueMax; - - main_red_shift = screen->serverFormat.redShift; - main_green_shift = screen->serverFormat.greenShift; - main_blue_shift = screen->serverFormat.blueShift; - } - -#if !SMALL_FOOTPRINT - if (!quiet) { - fprintf(stderr, "\n"); - fprintf(stderr, "FrameBuffer Info:\n"); - fprintf(stderr, " width: %d\n", fb->width); - fprintf(stderr, " height: %d\n", fb->height); - fprintf(stderr, " scaled_width: %d\n", width); - fprintf(stderr, " scaled_height: %d\n", height); - fprintf(stderr, " indexed_color: %d\n", indexed_color); - fprintf(stderr, " bits_per_pixel: %d\n", fb->bits_per_pixel); - fprintf(stderr, " depth: %d\n", fb->depth); - fprintf(stderr, " red_mask: 0x%08lx %s\n", fb->red_mask, - bitprint(fb->red_mask, 32)); - fprintf(stderr, " green_mask: 0x%08lx %s\n", fb->green_mask, - bitprint(fb->green_mask, 32)); - fprintf(stderr, " blue_mask: 0x%08lx %s\n", fb->blue_mask, - bitprint(fb->blue_mask, 32)); - fprintf(stderr, " red: max: %3d shift: %2d\n", - main_red_max, main_red_shift); - fprintf(stderr, " green: max: %3d shift: %2d\n", - main_green_max, main_green_shift); - fprintf(stderr, " blue: max: %3d shift: %2d\n", - main_blue_max, main_blue_shift); - fprintf(stderr, " mainfb_bytes_per_line: %d\n", - main_bytes_per_line); - fprintf(stderr, " rfb_fb_bytes_per_line: %d\n", - rfb_bytes_per_line); - switch(fb->format) { - case XYBitmap: - fprintf(stderr, " format: XYBitmap\n"); break; - case XYPixmap: - fprintf(stderr, " format: XYPixmap\n"); break; - case ZPixmap: - fprintf(stderr, " format: ZPixmap\n"); break; - default: - fprintf(stderr, " format: %d\n", fb->format); break; - } - switch(fb->byte_order) { - case LSBFirst: - fprintf(stderr, " byte_order: LSBFirst\n"); break; - case MSBFirst: - fprintf(stderr, " byte_order: MSBFirst\n"); break; - default: - fprintf(stderr, " byte_order: %d\n", fb->byte_order); - break; - } - fprintf(stderr, " bitmap_pad: %d\n", fb->bitmap_pad); - fprintf(stderr, " bitmap_unit: %d\n", fb->bitmap_unit); - switch(fb->bitmap_bit_order) { - case LSBFirst: - fprintf(stderr, " bitmap_bit_order: LSBFirst\n"); break; - case MSBFirst: - fprintf(stderr, " bitmap_bit_order: MSBFirst\n"); break; - default: - fprintf(stderr, " bitmap_bit_order: %d\n", - fb->bitmap_bit_order); break; - } - } - if (overlay && ! quiet) { - rfbLog("\n"); - rfbLog("Overlay mode enabled: If you experience color\n"); - rfbLog("problems when popup menus are on the screen, try\n"); - rfbLog("disabling SaveUnders in your X server, one way is\n"); - rfbLog("to start the X server with the '-su' option, e.g.:\n"); - rfbLog("Xsun -su ... see Xserver(1), xinit(1) for more info.\n"); - rfbLog("\n"); - } -#endif - /* nofb is for pointer/keyboard only handling. */ - if (nofb) { - main_fb = NULL; - rfb_fb = main_fb; - screen->displayHook = nofb_hook; - } else { - main_fb = fb->data; - if (scaling) { - rfb_fb = (char *) malloc(rfb_bytes_per_line * height); - memset(rfb_fb, 0, rfb_bytes_per_line * height); - } else { - rfb_fb = main_fb; - } - } - screen->frameBuffer = rfb_fb; - if (!quiet) { - fprintf(stderr, " main_fb: %p\n", main_fb); - fprintf(stderr, " rfb_fb: %p\n", rfb_fb); - fprintf(stderr, "\n"); - } - - bpp = screen->serverFormat.bitsPerPixel; - depth = screen->serverFormat.depth; - - /* may need, bpp, main_red_max, etc. */ - parse_wireframe(); - parse_scroll_copyrect(); - - setup_cursors_and_push(); - - if (scaling) { - mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); - } - - if (! create_screen) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - - /* - * since bits_per_color above may have been approximate, - * try to reset the individual translation tables... - * we do not seem to need this with rfbGetScreen()... - */ - if (!quiet) rfbLog("calling setTranslateFunction()...\n"); - iter = rfbGetClientIterator(screen); - while ((cl = rfbClientIteratorNext(iter)) != NULL) { - screen->setTranslateFunction(cl); - } - rfbReleaseClientIterator(iter); - if (!quiet) rfbLog(" done.\n"); - do_copy_screen = 1; - - /* done for framebuffer change case */ - return; - } - - /* - * the rest is screen server initialization, etc, only needed - * at screen creation time. - */ - - /* called from inetd, we need to treat stdio as our socket */ - if (inetd) { - int fd = dup(0); - if (fd < 0) { - rfbLogEnable(1); - rfbErr("dup(0) = %d failed.\n", fd); - rfbLogPerror("dup"); - clean_up_exit(1); - } - fclose(stdin); - fclose(stdout); - /* we keep stderr for logging */ - screen->inetdSock = fd; - screen->port = 0; - - } else if (! got_rfbport) { - screen->autoPort = TRUE; - } - - if (! got_nevershared && ! got_alwaysshared) { - if (shared) { - screen->alwaysShared = TRUE; - } else { - screen->neverShared = TRUE; - } - screen->dontDisconnect = TRUE; - } - if (! got_deferupdate) { - screen->deferUpdateTime = defer_update; - } - - /* event callbacks: */ - screen->newClientHook = new_client; - screen->kbdAddEvent = keyboard; - screen->ptrAddEvent = pointer; - screen->setXCutText = xcut_receive; - - rfbInitServer(screen); - - if (viewonly_passwd) { - /* append the view only passwd after the normal passwd */ - char **passwds_new = (char **) malloc(3*sizeof(char *)); - char **passwds_old = (char **) screen->authPasswdData; - passwds_new[0] = passwds_old[0]; - passwds_new[1] = viewonly_passwd; - passwds_new[2] = NULL; - screen->authPasswdData = (void*) passwds_new; - } else if (passwd_list) { - int i = 0; - while(passwd_list[i] != NULL) { - i++; - } - if (begin_viewonly < 0) { - begin_viewonly = i+1; - } - screen->authPasswdData = (void*) passwd_list; - screen->authPasswdFirstViewOnly = begin_viewonly; - } -} - -/* -- solid.c -- */ - -void usr_bin_path(int restore) { - static char *oldpath = NULL; - char *newpath; - char addpath[] = "/usr/bin:/bin:"; - - if (restore) { - if (oldpath) { - set_env("PATH", oldpath); - free(oldpath); - oldpath = NULL; - } - return; - } - - if (getenv("PATH")) { - oldpath = strdup(getenv("PATH")); - } else { - oldpath = strdup("/usr/bin"); - } - newpath = (char *) malloc(strlen(oldpath) + strlen(addpath) + 1); - newpath[0] = '\0'; - strcat(newpath, addpath); - strcat(newpath, oldpath); - set_env("PATH", newpath); - free(newpath); -} - -int dt_cmd(char *cmd) { - int rc; - - if (!cmd || *cmd == '\0') { - return 0; - } - - if (no_external_cmds) { - rfbLog("cannot run external commands in -nocmds mode:\n"); - rfbLog(" \"%s\"\n", cmd); - rfbLog(" dt_cmd: returning 1\n"); - return 1; - } - - if (getenv("DISPLAY") == NULL) { - set_env("DISPLAY", DisplayString(dpy)); - } - - rfbLog("running command:\n %s\n", cmd); - usr_bin_path(0); - rc = system(cmd); - usr_bin_path(1); - - if (rc >= 256) { - rc = rc/256; - } - return rc; -} - -char *cmd_output(char *cmd) { - FILE *p; - static char output[50000]; - char line[1024]; - int rc; - - if (!cmd || *cmd == '\0') { - return ""; - } - - if (no_external_cmds) { - rfbLog("cannot run external commands in -nocmds mode:\n"); - rfbLog(" \"%s\"\n", cmd); - rfbLog(" cmd_output: null string.\n"); - return ""; - } - - rfbLog("running pipe:\n %s\n", cmd); - usr_bin_path(0); - p = popen(cmd, "r"); - usr_bin_path(1); - - output[0] = '\0'; - - while (fgets(line, 1024, p) != NULL) { - if (strlen(output) + strlen(line) + 1 < 50000) { - strcat(output, line); - } - } - rc = pclose(p); - return(output); -} - -void solid_root(char *color) { - Window expose; - static XImage *image = NULL; - Pixmap pixmap; - XGCValues gcv; - GC gc; - XSetWindowAttributes swa; - Visual visual; - unsigned long mask, pixel; - XColor cdef; - Colormap cmap; - - if (subwin || window != rootwin) { - rfbLog("cannot set subwin to solid color, must be rootwin\n"); - return; - } - - /* create the "clear" window just for generating exposures */ - swa.override_redirect = True; - swa.backing_store = NotUseful; - swa.save_under = False; - swa.background_pixmap = None; - visual.visualid = CopyFromParent; - mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap); - expose = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, depth, - InputOutput, &visual, mask, &swa); - - if (! color) { - /* restore the root window from the XImage snapshot */ - pixmap = XCreatePixmap(dpy, window, wdpy_x, wdpy_y, depth); - - if (! image) { - /* whoops */ - XDestroyWindow(dpy, expose); - rfbLog("no root snapshot available.\n"); - return; - } - - - /* draw the image to a pixmap: */ - gcv.function = GXcopy; - gcv.plane_mask = AllPlanes; - gc = XCreateGC(dpy, window, GCFunction|GCPlaneMask, &gcv); - - XPutImage(dpy, pixmap, gc, image, 0, 0, 0, 0, wdpy_x, wdpy_y); - - gcv.foreground = gcv.background = BlackPixel(dpy, scr); - gc = XCreateGC(dpy, window, GCForeground|GCBackground, &gcv); - - rfbLog("restoring root snapshot...\n"); - /* set the pixmap as the bg: */ - XSetWindowBackgroundPixmap(dpy, window, pixmap); - XFreePixmap(dpy, pixmap); - XClearWindow(dpy, window); - XFlush(dpy); - - /* generate exposures */ - XMapWindow(dpy, expose); - XSync(dpy, False); - XDestroyWindow(dpy, expose); - return; - } - - if (! image) { - /* need to retrieve a snapshot of the root background: */ - Window iwin; - XSetWindowAttributes iswa; - - /* create image window: */ - iswa.override_redirect = True; - iswa.backing_store = NotUseful; - iswa.save_under = False; - iswa.background_pixmap = ParentRelative; - - iwin = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, - depth, InputOutput, &visual, mask, &iswa); - - rfbLog("snapshotting background...\n"); - - XMapWindow(dpy, iwin); - XSync(dpy, False); - image = XGetImage(dpy, iwin, 0, 0, wdpy_x, wdpy_y, AllPlanes, - ZPixmap); - XSync(dpy, False); - XDestroyWindow(dpy, iwin); - } - - /* use black for low colors or failure */ - pixel = BlackPixel(dpy, scr); - if (depth > 8 || strcmp(color, solid_default)) { - cmap = DefaultColormap (dpy, scr); - if (XParseColor(dpy, cmap, color, &cdef) && - XAllocColor(dpy, cmap, &cdef)) { - pixel = cdef.pixel; - } else { - rfbLog("error parsing/allocing color: %s\n", color); - } - } - - rfbLog("setting solid background...\n"); - XSetWindowBackground(dpy, window, pixel); - XMapWindow(dpy, expose); - XSync(dpy, False); - XDestroyWindow(dpy, expose); -} - -void solid_cde(char *color) { - int wsmax = 16; - static XImage *image[16]; - static Window ws_wins[16]; - static int nws = -1; - - Window expose; - Pixmap pixmap; - XGCValues gcv; - GC gc; - XSetWindowAttributes swa; - Visual visual; - unsigned long mask, pixel; - XColor cdef; - Colormap cmap; - int n; - - if (subwin || window != rootwin) { - rfbLog("cannot set subwin to solid color, must be rootwin\n"); - return; - } - - /* create the "clear" window just for generating exposures */ - swa.override_redirect = True; - swa.backing_store = NotUseful; - swa.save_under = False; - swa.background_pixmap = None; - visual.visualid = CopyFromParent; - mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap); - expose = XCreateWindow(dpy, window, 0, 0, wdpy_x, wdpy_y, 0, depth, - InputOutput, &visual, mask, &swa); - - if (! color) { - /* restore the backdrop windows from the XImage snapshots */ - - for (n=0; n < nws; n++) { - Window twin; - - if (! image[n]) { - continue; - } - - twin = ws_wins[n]; - if (! twin) { - twin = rootwin; - } - if (! valid_window(twin, NULL, 0)) { - continue; - } - - pixmap = XCreatePixmap(dpy, twin, wdpy_x, wdpy_y, - depth); - - /* draw the image to a pixmap: */ - gcv.function = GXcopy; - gcv.plane_mask = AllPlanes; - gc = XCreateGC(dpy, twin, GCFunction|GCPlaneMask, &gcv); - - XPutImage(dpy, pixmap, gc, image[n], 0, 0, 0, 0, - wdpy_x, wdpy_y); - - gcv.foreground = gcv.background = BlackPixel(dpy, scr); - gc = XCreateGC(dpy, twin, GCForeground|GCBackground, - &gcv); - - rfbLog("restoring CDE ws%d snapshot to 0x%lx\n", - n, twin); - /* set the pixmap as the bg: */ - XSetWindowBackgroundPixmap(dpy, twin, pixmap); - XFreePixmap(dpy, pixmap); - XClearWindow(dpy, twin); - XFlush(dpy); - } - - /* generate exposures */ - XMapWindow(dpy, expose); - XSync(dpy, False); - XDestroyWindow(dpy, expose); - return; - } - - if (nws < 0) { - /* need to retrieve snapshots of the ws backgrounds: */ - Window iwin, wm_win; - XSetWindowAttributes iswa; - Atom dt_list, wm_info, type; - int format; - unsigned long length, after; - unsigned char *data; - unsigned int * dp; - - nws = 0; - - /* extract the hidden wm properties about backdrops: */ - - wm_info = XInternAtom(dpy, "_MOTIF_WM_INFO", True); - if (wm_info == None) { - return; - } - - XGetWindowProperty(dpy, rootwin, wm_info, 0L, 10L, False, - AnyPropertyType, &type, &format, &length, &after, &data); - - /* - * xprop -notype -root _MOTIF_WM_INFO - * _MOTIF_WM_INFO = 0x2, 0x580028 - */ - - if (length < 2 || format != 32 || after != 0) { - return; - } - - dp = (unsigned int *) data; - wm_win = (Window) *(dp+1); /* 2nd item. */ - - - dt_list = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True); - if (dt_list == None) { - return; - } - - XGetWindowProperty(dpy, wm_win, dt_list, 0L, 10L, False, - AnyPropertyType, &type, &format, &length, &after, &data); - - nws = length; - - if (nws > wsmax) { - nws = wsmax; - } - if (nws < 0) { - nws = 0; - } - - rfbLog("special CDE win: 0x%lx, %d workspaces\n", wm_win, nws); - if (nws == 0) { - return; - } - - for (n=0; n<nws; n++) { - Atom ws_atom; - char tmp[32]; - Window twin; - XWindowAttributes attr; - int i, cnt; - - image[n] = NULL; - ws_wins[n] = 0x0; - - sprintf(tmp, "_DT_WORKSPACE_INFO_ws%d", n); - ws_atom = XInternAtom(dpy, tmp, False); - if (ws_atom == None) { - continue; - } - XGetWindowProperty(dpy, wm_win, ws_atom, 0L, 100L, - False, AnyPropertyType, &type, &format, &length, - &after, &data); - - if (format != 8 || after != 0) { - continue; - } - /* - * xprop -notype -id wm_win - * _DT_WORKSPACE_INFO_ws0 = "One", "3", "0x2f2f4a", - * "0x63639c", "0x103", "1", "0x58044e" - */ - - cnt = 0; - twin = 0x0; - for (i=0; i< (int) length; i++) { - if (*(data+i) != '\0') { - continue; - } - cnt++; /* count nulls to indicate field */ - if (cnt == 6) { - /* one past the null: */ - char *q = (char *) (data+i+1); - unsigned long in; - if (sscanf(q, "0x%lx", &in) == 1) { - twin = (Window) in; - break; - } - } - } - ws_wins[n] = twin; - - if (! twin) { - twin = rootwin; - } - - XGetWindowAttributes(dpy, twin, &attr); - if (twin != rootwin) { - if (attr.map_state != IsViewable) { - XMapWindow(dpy, twin); - } - XRaiseWindow(dpy, twin); - } - XSync(dpy, False); - - /* create image window: */ - iswa.override_redirect = True; - iswa.backing_store = NotUseful; - iswa.save_under = False; - iswa.background_pixmap = ParentRelative; - visual.visualid = CopyFromParent; - - iwin = XCreateWindow(dpy, twin, 0, 0, wdpy_x, wdpy_y, - 0, depth, InputOutput, &visual, mask, &iswa); - - rfbLog("snapshotting CDE backdrop ws%d 0x%lx -> " - "0x%lx ...\n", n, twin, iwin); - XMapWindow(dpy, iwin); - XSync(dpy, False); - - image[n] = XGetImage(dpy, iwin, 0, 0, wdpy_x, wdpy_y, - AllPlanes, ZPixmap); - XSync(dpy, False); - XDestroyWindow(dpy, iwin); - if (twin != rootwin) { - XLowerWindow(dpy, twin); - if (attr.map_state != IsViewable) { - XUnmapWindow(dpy, twin); - } - } - } - } - if (nws == 0) { - return; - } - - /* use black for low colors or failure */ - pixel = BlackPixel(dpy, scr); - if (depth > 8 || strcmp(color, solid_default)) { - cmap = DefaultColormap (dpy, scr); - if (XParseColor(dpy, cmap, color, &cdef) && - XAllocColor(dpy, cmap, &cdef)) { - pixel = cdef.pixel; - } else { - rfbLog("error parsing/allocing color: %s\n", color); - } - } - - rfbLog("setting solid backgrounds...\n"); - - for (n=0; n < nws; n++) { - Window twin = ws_wins[n]; - if (image[n] == NULL) { - continue; - } - if (! twin) { - twin = rootwin; - } - XSetWindowBackground(dpy, twin, pixel); - } - XMapWindow(dpy, expose); - XSync(dpy, False); - XDestroyWindow(dpy, expose); -} - -void solid_gnome(char *color) { - char get_color[] = "gconftool-2 --get " - "/desktop/gnome/background/primary_color"; - char set_color[] = "gconftool-2 --set " - "/desktop/gnome/background/primary_color --type string '%s'"; - char get_option[] = "gconftool-2 --get " - "/desktop/gnome/background/picture_options"; - char set_option[] = "gconftool-2 --set " - "/desktop/gnome/background/picture_options --type string '%s'"; - static char *orig_color = NULL; - static char *orig_option = NULL; - char *cmd; - - if (! color) { - if (! orig_color) { - orig_color = strdup("#FFFFFF"); - } - if (! orig_option) { - orig_option = strdup("stretched"); - } - if (strstr(orig_color, "'") != NULL) { - rfbLog("invalid color: %s\n", orig_color); - return; - } - if (strstr(orig_option, "'") != NULL) { - rfbLog("invalid option: %s\n", orig_option); - return; - } - cmd = (char *) malloc(strlen(set_option) - 2 + - strlen(orig_option) + 1); - sprintf(cmd, set_option, orig_option); - dt_cmd(cmd); - free(cmd); - cmd = (char *) malloc(strlen(set_color) - 2 + - strlen(orig_color) + 1); - sprintf(cmd, set_color, orig_color); - dt_cmd(cmd); - free(cmd); - return; - } - - if (! orig_color) { - char *q; - orig_color = strdup(cmd_output(get_color)); - if (*orig_color == '\0') { - orig_color = strdup("#FFFFFF"); - } - if ((q = strchr(orig_color, '\n')) != NULL) { - *q = '\0'; - } - } - if (! orig_option) { - char *q; - orig_option = strdup(cmd_output(get_option)); - if (*orig_option == '\0') { - orig_option = strdup("stretched"); - } - if ((q = strchr(orig_option, '\n')) != NULL) { - *q = '\0'; - } - } - if (strstr(color, "'") != NULL) { - rfbLog("invalid color: %s\n", color); - return; - } - cmd = (char *) malloc(strlen(set_color) + strlen(color) + 1); - sprintf(cmd, set_color, color); - dt_cmd(cmd); - free(cmd); - - cmd = (char *) malloc(strlen(set_option) + strlen("none") + 1); - sprintf(cmd, set_option, "none"); - dt_cmd(cmd); - free(cmd); -} - -void solid_kde(char *color) { - char set_color[] = - "dcop --user '%s' kdesktop KBackgroundIface setColor '%s' 1"; - char bg_off[] = - "dcop --user '%s' kdesktop KBackgroundIface setBackgroundEnabled 0"; - char bg_on[] = - "dcop --user '%s' kdesktop KBackgroundIface setBackgroundEnabled 1"; - char *cmd, *user = NULL; - int len; - - user = get_user_name(); - if (strstr(user, "'") != NULL) { - rfbLog("invalid user: %s\n", user); - free(user); - return; - } - - if (! color) { - len = strlen(bg_on) + strlen(user) + 1; - cmd = (char *) malloc(len); - sprintf(cmd, bg_on, user); - dt_cmd(cmd); - free(cmd); - free(user); - - return; - } - - if (strstr(color, "'") != NULL) { - rfbLog("invalid color: %s\n", color); - return; - } - - len = strlen(set_color) + strlen(user) + strlen(color) + 1; - cmd = (char *) malloc(len); - sprintf(cmd, set_color, user, color); - dt_cmd(cmd); - free(cmd); - - len = strlen(bg_off) + strlen(user) + 1; - cmd = (char *) malloc(len); - sprintf(cmd, bg_off, user); - dt_cmd(cmd); - free(cmd); - free(user); -} - -char *guess_desktop() { - Atom prop; - - if (wmdt_str && *wmdt_str != '\0') { - char *s = wmdt_str; - lowercase(s); - if (strstr(s, "xfce")) { - return "xfce"; - } - if (strstr(s, "gnome") || strstr(s, "metacity")) { - return "gnome"; - } - if (strstr(s, "kde") || strstr(s, "kwin")) { - return "kde"; - } - if (strstr(s, "cde")) { - return "cde"; - } - return "root"; - } - - if (! dpy) { - return ""; - } - - prop = XInternAtom(dpy, "XFCE_DESKTOP_WINDOW", True); - if (prop != None) return "xfce"; - - /* special case windowmaker */ - prop = XInternAtom(dpy, "_WINDOWMAKER_WM_PROTOCOLS", True); - if (prop != None) return "root"; - - prop = XInternAtom(dpy, "_WINDOWMAKER_COMMAND", True); - if (prop != None) return "root"; - - prop = XInternAtom(dpy, "NAUTILUS_DESKTOP_WINDOW_ID", True); - if (prop != None) return "gnome"; - - prop = XInternAtom(dpy, "KWIN_RUNNING", True); - if (prop != None) return "kde"; - - prop = XInternAtom(dpy, "_MOTIF_WM_INFO", True); - if (prop != None) { - prop = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True); - if (prop != None) return "cde"; - } - return "root"; -} - -void solid_bg(int restore) { - static int desktop = -1; - static int solid_on = 0; - static char *prev_str; - char *dtname, *color; - - if (started_as_root == 1 && users_list) { - /* we are still root, don't try. */ - return; - } - - if (restore) { - if (! solid_on) { - return; - } - if (desktop == 0) { - solid_root(NULL); - } else if (desktop == 1) { - solid_gnome(NULL); - } else if (desktop == 2) { - solid_kde(NULL); - } else if (desktop == 3) { - solid_cde(NULL); - } - solid_on = 0; - return; - } - if (! solid_str) { - return; - } - if (solid_on && !strcmp(prev_str, solid_str)) { - return; - } - if (strstr(solid_str, "guess:") == solid_str - || !strchr(solid_str, ':')) { - dtname = guess_desktop(); - rfbLog("guessed desktop: %s\n", dtname); - } else { - if (strstr(solid_str, "gnome:") == solid_str) { - dtname = "gnome"; - } else if (strstr(solid_str, "kde:") == solid_str) { - dtname = "kde"; - } else if (strstr(solid_str, "cde:") == solid_str) { - dtname = "cde"; - } else { - dtname = "root"; - } - } - - color = strchr(solid_str, ':'); - if (! color) { - color = solid_str; - } else { - color++; - if (*color == '\0') { - color = solid_default; - } - } - if (!strcmp(dtname, "gnome")) { - desktop = 1; - solid_gnome(color); - } else if (!strcmp(dtname, "kde")) { - desktop = 2; - solid_kde(color); - } else if (!strcmp(dtname, "cde")) { - desktop = 3; - solid_cde(color); - } else { - desktop = 0; - solid_root(color); - } - if (prev_str) { - free(prev_str); - } - prev_str = strdup(solid_str); - solid_on = 1; -} - -/* -- xinerama.c -- */ -/* - * routines related to xinerama and blacking out rectangles - */ - -/* blacked-out region (-blackout, -xinerama) */ -#define BO_MAX 32 -typedef struct tbout { - blackout_t bo[BO_MAX]; /* hardwired max rectangles. */ - int cover; - int count; -} tile_blackout_t; - -#define BLACKR_MAX 100 -blackout_t blackr[BLACKR_MAX]; /* hardwired max blackouts */ -tile_blackout_t *tile_blackout; -int blackouts = 0; - -/* - * Take a comma separated list of geometries: WxH+X+Y and register them as - * rectangles to black out from the screen. - */ -void initialize_blackouts(char *list) { - char *p, *blist = strdup(list); - int x, y, X, Y, h, w, t; - - p = strtok(blist, ", \t"); - while (p) { - if (!strcmp("noptr", p)) { - blackout_ptr = 1; - rfbLog("pointer will be blocked from blackout " - "regions\n"); - p = strtok(NULL, ", \t"); - continue; - } - if (! parse_geom(p, &w, &h, &x, &y, dpy_x, dpy_y)) { - if (*p != '\0') { - rfbLog("skipping invalid geometry: %s\n", p); - } - p = strtok(NULL, ", \t"); - continue; - } - w = nabs(w); - h = nabs(h); - x = nfix(x, dpy_x); - y = nfix(y, dpy_y); - X = x + w; - Y = y + h; - X = nfix(X, dpy_x+1); - Y = nfix(Y, dpy_y+1); - if (x > X) { - t = X; X = x; x = t; - } - if (y > Y) { - t = Y; Y = y; y = t; - } - if (x < 0 || x > dpy_x || y < 0 || y > dpy_y || - X < 0 || X > dpy_x || Y < 0 || Y > dpy_y || - x == X || y == Y) { - rfbLog("skipping invalid blackout geometry: %s x=" - "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h); - } else { - rfbLog("blackout rect: %s: x=%d-%d y=%d-%d\n", p, - x, X, y, Y); - - /* - * note that the black out is x1 <= x but x < x2 - * for the region. i.e. the x2, y2 are outside - * by 1 pixel. - */ - blackr[blackouts].x1 = x; - blackr[blackouts].y1 = y; - blackr[blackouts].x2 = X; - blackr[blackouts].y2 = Y; - blackouts++; - if (blackouts >= BLACKR_MAX) { - rfbLog("too many blackouts: %d\n", blackouts); - break; - } - } - p = strtok(NULL, ", \t"); - } - free(blist); -} - -/* - * Now that all blackout rectangles have been constructed, see what overlap - * they have with the tiles in the system. If a tile is touched by a - * blackout, record information. - */ -void blackout_tiles(void) { - int tx, ty; - int debug_bo = 0; - if (! blackouts) { - return; - } - if (getenv("DEBUG_BLACKOUT") != NULL) { - debug_bo = 1; - } - - /* - * to simplify things drop down to single copy mode, etc... - */ - single_copytile = 1; - /* loop over all tiles. */ - for (ty=0; ty < ntiles_y; ty++) { - for (tx=0; tx < ntiles_x; tx++) { - sraRegionPtr tile_reg, black_reg; - sraRect rect; - sraRectangleIterator *iter; - int n, b, x1, y1, x2, y2, cnt; - - /* tile number and coordinates: */ - n = tx + ty * ntiles_x; - x1 = tx * tile_x; - y1 = ty * tile_y; - x2 = x1 + tile_x; - y2 = y1 + tile_y; - if (x2 > dpy_x) { - x2 = dpy_x; - } - if (y2 > dpy_y) { - y2 = dpy_y; - } - - /* make regions for the tile and the blackouts: */ - black_reg = (sraRegionPtr) sraRgnCreate(); - tile_reg = (sraRegionPtr) sraRgnCreateRect(x1, y1, - x2, y2); - - tile_blackout[n].cover = 0; - tile_blackout[n].count = 0; - - /* union of blackouts */ - for (b=0; b < blackouts; b++) { - sraRegionPtr tmp_reg = (sraRegionPtr) - sraRgnCreateRect(blackr[b].x1, - blackr[b].y1, blackr[b].x2, blackr[b].y2); - - sraRgnOr(black_reg, tmp_reg); - sraRgnDestroy(tmp_reg); - } - - if (! sraRgnAnd(black_reg, tile_reg)) { - /* - * no intersection for this tile, so we - * are done. - */ - sraRgnDestroy(black_reg); - sraRgnDestroy(tile_reg); - continue; - } - - /* - * loop over rectangles that make up the blackout - * region: - */ - cnt = 0; - iter = sraRgnGetIterator(black_reg); - while (sraRgnIteratorNext(iter, &rect)) { - - /* make sure x1 < x2 and y1 < y2 */ - if (rect.x1 > rect.x2) { - int tmp = rect.x2; - rect.x2 = rect.x1; - rect.x1 = tmp; - } - if (rect.y1 > rect.y2) { - int tmp = rect.y2; - rect.y2 = rect.y1; - rect.y1 = tmp; - } - - /* store coordinates */ - tile_blackout[n].bo[cnt].x1 = rect.x1; - tile_blackout[n].bo[cnt].y1 = rect.y1; - tile_blackout[n].bo[cnt].x2 = rect.x2; - tile_blackout[n].bo[cnt].y2 = rect.y2; - - /* note if the tile is completely obscured */ - if (rect.x1 == x1 && rect.y1 == y1 && - rect.x2 == x2 && rect.y2 == y2) { - tile_blackout[n].cover = 2; - if (debug_bo) { - fprintf(stderr, "full: %d=%d,%d" - " (%d-%d) (%d-%d)\n", - n, tx, ty, x1, x2, y1, y2); - } - } else { - tile_blackout[n].cover = 1; - if (debug_bo) { - fprintf(stderr, "part: %d=%d,%d" - " (%d-%d) (%d-%d)\n", - n, tx, ty, x1, x2, y1, y2); - } - } - - if (++cnt >= BO_MAX) { - rfbLog("too many blackout rectangles " - "for tile %d=%d,%d.\n", n, tx, ty); - break; - } - } - - sraRgnReleaseIterator(iter); - sraRgnDestroy(black_reg); - sraRgnDestroy(tile_reg); - - tile_blackout[n].count = cnt; - if (debug_bo && cnt > 1) { - rfbLog("warning: multiple region overlaps[%d] " - "for tile %d=%d,%d.\n", cnt, n, tx, ty); - } - } - } -} - -void initialize_xinerama (void) { -#if !LIBVNCSERVER_HAVE_LIBXINERAMA - rfbLog("Xinerama: Library libXinerama is not available to determine\n"); - rfbLog("Xinerama: the head geometries, consider using -blackout\n"); - rfbLog("Xinerama: if the screen is non-rectangular.\n"); -#else - XineramaScreenInfo *sc, *xineramas; - sraRegionPtr black_region, tmp_region; - sraRectangleIterator *iter; - sraRect rect; - char *bstr, *tstr; - int ev, er, i, n, rcnt; - - if (raw_fb && ! dpy) return; /* raw_fb hack */ - - if (! XineramaQueryExtension(dpy, &ev, &er)) { - rfbLog("Xinerama: disabling: display does not support it.\n"); - xinerama = 0; - xinerama_present = 0; - return; - } - if (! XineramaIsActive(dpy)) { - /* n.b. change to XineramaActive(dpy, window) someday */ - rfbLog("Xinerama: disabling: not active on display.\n"); - xinerama = 0; - xinerama_present = 0; - return; - } - xinerama_present = 1; - - /* n.b. change to XineramaGetData() someday */ - xineramas = XineramaQueryScreens(dpy, &n); - rfbLog("Xinerama: number of sub-screens: %d\n", n); - - if (n == 1) { - rfbLog("Xinerama: no blackouts needed (only one" - " sub-screen)\n"); - XFree(xineramas); - return; /* must be OK w/o change */ - } - - black_region = sraRgnCreateRect(0, 0, dpy_x, dpy_y); - - sc = xineramas; - for (i=0; i<n; i++) { - int x, y, w, h; - - x = sc->x_org; - y = sc->y_org; - w = sc->width; - h = sc->height; - - tmp_region = sraRgnCreateRect(x, y, x + w, y + h); - - sraRgnSubtract(black_region, tmp_region); - sraRgnDestroy(tmp_region); - sc++; - } - XFree(xineramas); - - if (sraRgnEmpty(black_region)) { - rfbLog("Xinerama: no blackouts needed (screen fills" - " rectangle)\n"); - sraRgnDestroy(black_region); - return; - } - - /* max len is 10000x10000+10000+10000 (23 chars) per geometry */ - rcnt = (int) sraRgnCountRects(black_region); - bstr = (char *) malloc(30 * (rcnt+1)); - tstr = (char *) malloc(30); - bstr[0] = '\0'; - - iter = sraRgnGetIterator(black_region); - while (sraRgnIteratorNext(iter, &rect)) { - int x, y, w, h; - - /* make sure x1 < x2 and y1 < y2 */ - if (rect.x1 > rect.x2) { - int tmp = rect.x2; - rect.x2 = rect.x1; - rect.x1 = tmp; - } - if (rect.y1 > rect.y2) { - int tmp = rect.y2; - rect.y2 = rect.y1; - rect.y1 = tmp; - } - x = rect.x1; - y = rect.y1; - w = rect.x2 - x; - h = rect.y2 - y; - sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y); - strcat(bstr, tstr); - } - initialize_blackouts(bstr); - - free(bstr); - free(tstr); -#endif -} - -void initialize_blackouts_and_xinerama(void) { - - blackouts = 0; - blackout_ptr = 0; - - if (blackout_str != NULL) { - initialize_blackouts(blackout_str); - } - if (xinerama) { - initialize_xinerama(); - } - if (blackouts) { - blackout_tiles(); - /* schedule a copy_screen(), now is too early. */ - do_copy_screen = 1; - } -} - -void push_sleep(int n) { - int i; - for (i=0; i<n; i++) { - rfbPE(-1); - if (i != n-1 && defer_update) { - usleep(defer_update * 1000); - } - } -} - -/* - * try to forcefully push a black screen to all connected clients - */ -void push_black_screen(int n) { - if (!screen) { - return; - } - zero_fb(0, 0, dpy_x, dpy_y); - mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); - push_sleep(n); -} - -void refresh_screen(int push) { - int i; - if (!screen) { - return; - } - mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); - for (i=0; i<push; i++) { - rfbPE(-1); - } -} - -/* - * Fill the framebuffer with zero for the prescribed rectangle - */ -void zero_fb(int x1, int y1, int x2, int y2) { - int pixelsize = bpp/8; - int line, fill = 0; - char *dst; - - if (x1 < 0 || x2 <= x1 || x2 > dpy_x) { - return; - } - if (y1 < 0 || y2 <= y1 || y2 > dpy_y) { - return; - } - if (! main_fb) { - return; - } - - dst = main_fb + y1 * main_bytes_per_line + x1 * pixelsize; - line = y1; - while (line++ < y2) { - memset(dst, fill, (size_t) (x2 - x1) * pixelsize); - dst += main_bytes_per_line; - } -} - -/* -- scan.c -- */ -/* - * routines for scanning and reading the X11 display for changes, and - * for doing all the tile work (shm, etc). - */ - -/* array to hold the hints: */ -static hint_t *hint_list; - -/* nap state */ -static int nap_ok = 0, nap_diff_count = 0; - -static int scan_count = 0; /* indicates which scan pattern we are on */ -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; -} region_t; - -/* array to hold the tiles region_t-s. */ -static region_t *tile_region; - - -/* - * setup tile numbers and allocate the tile and hint arrays: - */ -void initialize_tiles(void) { - - ntiles_x = (dpy_x - 1)/tile_x + 1; - ntiles_y = (dpy_y - 1)/tile_y + 1; - ntiles = ntiles_x * ntiles_y; - - tile_has_diff = (unsigned char *) - malloc((size_t) (ntiles * sizeof(unsigned char))); - tile_has_xdamage_diff = (unsigned char *) - malloc((size_t) (ntiles * sizeof(unsigned char))); - tile_row_has_xdamage_diff = (unsigned char *) - malloc((size_t) (ntiles_y * sizeof(unsigned char))); - tile_tried = (unsigned char *) - malloc((size_t) (ntiles * sizeof(unsigned char))); - tile_copied = (unsigned char *) - malloc((size_t) (ntiles * sizeof(unsigned char))); - tile_blackout = (tile_blackout_t *) - malloc((size_t) (ntiles * sizeof(tile_blackout_t))); - tile_region = (region_t *) malloc((size_t) (ntiles * sizeof(region_t))); - - tile_row = (XImage **) - malloc((size_t) ((ntiles_x + 1) * sizeof(XImage *))); - tile_row_shm = (XShmSegmentInfo *) - malloc((size_t) ((ntiles_x + 1) * sizeof(XShmSegmentInfo))); - - /* there will never be more hints than tiles: */ - hint_list = (hint_t *) malloc((size_t) (ntiles * sizeof(hint_t))); -} - -void free_tiles(void) { - if (tile_has_diff) { - free(tile_has_diff); - tile_has_diff = NULL; - } - if (tile_has_xdamage_diff) { - free(tile_has_xdamage_diff); - tile_has_xdamage_diff = NULL; - } - if (tile_row_has_xdamage_diff) { - free(tile_row_has_xdamage_diff); - tile_row_has_xdamage_diff = NULL; - } - if (tile_tried) { - free(tile_tried); - tile_tried = NULL; - } - if (tile_copied) { - free(tile_copied); - tile_copied = NULL; - } - if (tile_blackout) { - free(tile_blackout); - tile_blackout = NULL; - } - if (tile_region) { - free(tile_region); - tile_region = NULL; - } - if (tile_row) { - free(tile_row); - tile_row = NULL; - } - if (tile_row_shm) { - free(tile_row_shm); - tile_row_shm = NULL; - } - if (hint_list) { - free(hint_list); - hint_list = NULL; - } -} - -/* - * silly function to factor dpy_y until fullscreen shm is not bigger than max. - * should always work unless dpy_y is a large prime or something... under - * failure fs_factor remains 0 and no fullscreen updates will be tried. - */ -static int fs_factor = 0; - -static void set_fs_factor(int max) { - int f, fac = 1, n = dpy_y; - - fs_factor = 0; - if ((bpp/8) * dpy_x * dpy_y <= max) { - fs_factor = 1; - return; - } - for (f=2; f <= 101; f++) { - while (n % f == 0) { - n = n / f; - fac = fac * f; - if ( (bpp/8) * dpy_x * (dpy_y/fac) <= max ) { - fs_factor = fac; - return; - } - } - } -} - -char *flip_ximage_byte_order(XImage *xim) { - char *order; - if (xim->byte_order == LSBFirst) { - order = "MSBFirst"; - xim->byte_order = MSBFirst; - xim->bitmap_bit_order = MSBFirst; - } else { - order = "LSBFirst"; - xim->byte_order = LSBFirst; - xim->bitmap_bit_order = LSBFirst; - } - return order; -} - -/* - * set up an XShm image, or if not using shm just create the XImage. - */ -static int shm_create(XShmSegmentInfo *shm, XImage **ximg_ptr, int w, int h, - char *name) { - - XImage *xim; - static int reported_flip = 0; - - shm->shmid = -1; - shm->shmaddr = (char *) -1; - *ximg_ptr = NULL; - - if (nofb) { - return 1; - } - - X_LOCK; - - if (! using_shm) { - /* we only need the XImage created */ - xim = XCreateImage_wr(dpy, default_visual, depth, ZPixmap, - 0, NULL, w, h, raw_fb ? 32 : BitmapPad(dpy), 0); - - X_UNLOCK; - - if (xim == NULL) { - rfbErr("XCreateImage(%s) failed.\n", name); - if (quiet) { - fprintf(stderr, "XCreateImage(%s) failed.\n", - name); - } - return 0; - } - xim->data = (char *) malloc(xim->bytes_per_line * xim->height); - if (xim->data == NULL) { - rfbErr("XCreateImage(%s) data malloc failed.\n", name); - if (quiet) { - fprintf(stderr, "XCreateImage(%s) data malloc" - " failed.\n", name); - } - return 0; - } - if (flip_byte_order) { - char *order = flip_ximage_byte_order(xim); - if (! reported_flip && ! quiet) { - rfbLog("Changing XImage byte order" - " to %s\n", order); - reported_flip = 1; - } - } - - *ximg_ptr = xim; - return 1; - } - - xim = XShmCreateImage_wr(dpy, default_visual, depth, ZPixmap, NULL, - shm, w, h); - - if (xim == NULL) { - rfbErr("XShmCreateImage(%s) failed.\n", name); - if (quiet) { - fprintf(stderr, "XShmCreateImage(%s) failed.\n", name); - } - X_UNLOCK; - return 0; - } - - *ximg_ptr = xim; - -#if LIBVNCSERVER_HAVE_XSHM - shm->shmid = shmget(IPC_PRIVATE, - xim->bytes_per_line * xim->height, IPC_CREAT | 0777); - - if (shm->shmid == -1) { - rfbErr("shmget(%s) failed.\n", name); - rfbLogPerror("shmget"); - - XDestroyImage(xim); - *ximg_ptr = NULL; - - X_UNLOCK; - return 0; - } - - shm->shmaddr = xim->data = (char *) shmat(shm->shmid, 0, 0); - - if (shm->shmaddr == (char *)-1) { - rfbErr("shmat(%s) failed.\n", name); - rfbLogPerror("shmat"); - - XDestroyImage(xim); - *ximg_ptr = NULL; - - shmctl(shm->shmid, IPC_RMID, 0); - shm->shmid = -1; - - X_UNLOCK; - return 0; - } - - shm->readOnly = False; - - if (! XShmAttach_wr(dpy, shm)) { - rfbErr("XShmAttach(%s) failed.\n", name); - XDestroyImage(xim); - *ximg_ptr = NULL; - - shmdt(shm->shmaddr); - shm->shmaddr = (char *) -1; - - shmctl(shm->shmid, IPC_RMID, 0); - shm->shmid = -1; - - X_UNLOCK; - return 0; - } -#endif - - X_UNLOCK; - return 1; -} - -void shm_delete(XShmSegmentInfo *shm) { -#if LIBVNCSERVER_HAVE_XSHM - if (shm != NULL && shm->shmaddr != (char *) -1) { - shmdt(shm->shmaddr); - } - if (shm != NULL && shm->shmid != -1) { - shmctl(shm->shmid, IPC_RMID, 0); - } -#endif -} - -void shm_clean(XShmSegmentInfo *shm, XImage *xim) { - - X_LOCK; -#if LIBVNCSERVER_HAVE_XSHM - if (shm != NULL && shm->shmid != -1 && dpy) { /* raw_fb hack */ - XShmDetach_wr(dpy, shm); - } -#endif - if (xim != NULL) { - XDestroyImage(xim); - xim = NULL; - } - X_UNLOCK; - - shm_delete(shm); -} - -void initialize_polling_images(void) { - int i, MB = 1024 * 1024; - - /* set all shm areas to "none" before trying to create any */ - scanline_shm.shmid = -1; - scanline_shm.shmaddr = (char *) -1; - scanline = NULL; - fullscreen_shm.shmid = -1; - fullscreen_shm.shmaddr = (char *) -1; - fullscreen = NULL; - snaprect_shm.shmid = -1; - snaprect_shm.shmaddr = (char *) -1; - snaprect = NULL; - for (i=1; i<=ntiles_x; i++) { - tile_row_shm[i].shmid = -1; - tile_row_shm[i].shmaddr = (char *) -1; - tile_row[i] = NULL; - } - - /* the scanline (e.g. 1280x1) shared memory area image: */ - - if (! shm_create(&scanline_shm, &scanline, dpy_x, 1, "scanline")) { - clean_up_exit(1); - } - - /* - * the fullscreen (e.g. 1280x1024/fs_factor) shared memory area image: - * (we cut down the size of the shm area to try avoid and shm segment - * limits, e.g. the default 1MB on Solaris) - */ - if (UT.sysname && strstr(UT.sysname, "Linux")) { - set_fs_factor(10 * MB); - } else { - set_fs_factor(1 * MB); - } - if (fs_frac >= 1.0) { - fs_frac = 1.1; - fs_factor = 0; - } - if (! fs_factor) { - rfbLog("warning: fullscreen updates are disabled.\n"); - } else { - if (! shm_create(&fullscreen_shm, &fullscreen, dpy_x, - dpy_y/fs_factor, "fullscreen")) { - clean_up_exit(1); - } - } - if (use_snapfb) { - if (! fs_factor) { - rfbLog("warning: disabling -snapfb mode.\n"); - use_snapfb = 0; - } else if (! shm_create(&snaprect_shm, &snaprect, dpy_x, - dpy_y/fs_factor, "snaprect")) { - clean_up_exit(1); - } - } - - /* - * for copy_tiles we need a lot of shared memory areas, one for - * each possible run length of changed tiles. 32 for 1024x768 - * and 40 for 1280x1024, etc. - */ - - tile_shm_count = 0; - for (i=1; i<=ntiles_x; i++) { - if (! shm_create(&tile_row_shm[i], &tile_row[i], tile_x * i, - tile_y, "tile_row")) { - if (i == 1) { - clean_up_exit(1); - } - rfbLog("shm: Error creating shared memory tile-row for" - " len=%d,\n", i); - rfbLog("shm: reverting to -onetile mode. If this" - " problem persists\n"); - rfbLog("shm: try using the -onetile or -noshm options" - " to limit\n"); - rfbLog("shm: shared memory usage, or run ipcrm(1)" - " to manually\n"); - rfbLog("shm: delete unattached shm segments.\n"); - single_copytile_count = i; - single_copytile = 1; - } - tile_shm_count++; - if (single_copytile && i >= 1) { - /* only need 1x1 tiles */ - break; - } - } - if (!quiet) { - if (using_shm) { - rfbLog("created %d tile_row shm polling images.\n", - tile_shm_count); - } else { - rfbLog("created %d tile_row polling images.\n", - tile_shm_count); - } - } -} - -/* - * A hint is a rectangular region built from 1 or more adjacent tiles - * 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 tw, int th, hint_t *hint) { - int w = dpy_x - x; - int h = dpy_y - y; - - if (w > tw) { - w = tw; - } - if (h > th) { - h = th; - } - - hint->x = x; - hint->y = y; - hint->w = w; - hint->h = h; -} - -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 > tw) { - w = tw; - } - if (h > th) { - h = th; - } - - if (hint->x > x) { /* extend to the left */ - hint->w += hint->x - x; - hint->x = x; - } - if (hint->y > y) { /* extend upward */ - hint->h += hint->y - y; - hint->y = y; - } - - if (hint->x + hint->w < x + w) { /* extend to the right */ - hint->w = x + w - hint->x; - } - if (hint->y + hint->h < y + h) { /* extend downward */ - hint->h = y + h - hint->y; - } -} - -static void save_hint(hint_t hint, int loc) { - /* simply copy it to the global array for later use. */ - hint_list[loc].x = hint.x; - hint_list[loc].y = hint.y; - hint_list[loc].w = hint.w; - hint_list[loc].h = hint.h; -} - -/* - * Glue together horizontal "runs" of adjacent changed tiles into one big - * rectangle change "hint" to be passed to the vnc machinery. - */ -static void hint_updates(void) { - hint_t hint; - int x, y, i, n, ty, th, tx, tw; - int hint_count = 0, in_run = 0; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - - 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 + tx, - y * tile_y + ty, tw, th, &hint); - in_run = 1; - } else { - extend_tile_hint( x * tile_x + tx, - y * tile_y + ty, tw, th, &hint); - } - } else { - if (in_run) { - /* end of a row run of altered tiles: */ - save_hint(hint, hint_count++); - in_run = 0; - } - } - } - if (in_run) { /* save the last row run */ - save_hint(hint, hint_count++); - in_run = 0; - } - } - - for (i=0; i < hint_count; i++) { - /* pass update info to vnc: */ - mark_hint(hint_list[i]); - } -} - -/* - * kludge, simple ceil+floor for non-negative doubles: - */ -#define CEIL(x) ( (double) ((int) (x)) == (x) ? \ - (double) ((int) (x)) : (double) ((int) (x) + 1) ) -#define FLOOR(x) ( (double) ((int) (x)) ) - -/* - * Scaling: - * - * For shrinking, a destination (scaled) pixel will correspond to more - * than one source (i.e. main fb) pixel. Think of an x-y plane made with - * graph paper. Each unit square in the graph paper (i.e. collection of - * points (x,y) such that N < x < N+1 and M < y < M+1, N and M integers) - * corresponds to one pixel in the unscaled fb. There is a solid - * color filling the inside of such a square. A scaled pixel has width - * 1/scale_fac, e.g. for "-scale 3/4" the width of the scaled pixel - * is 1.333. The area of this scaled pixel is 1.333 * 1.333 (so it - * obviously overlaps more than one source pixel, each which have area 1). - * - * We take the weight an unscaled pixel (source) contributes to a - * scaled pixel (destination) as simply proportional to the overlap area - * between the two pixels. One can then think of the value of the scaled - * pixel as an integral over the portion of the graph paper it covers. - * The thing being integrated is the color value of the unscaled source. - * That color value is constant over a graph paper square (source pixel), - * and changes discontinuously from one unit square to the next. - * - -Here is an example for -scale 3/4, the solid lines are the source pixels -(graph paper unit squares), while the dotted lines denote the scaled -pixels (destination pixels): - - 0 1 4/3 2 8/3 3 4=12/3 - |---------|--.------|------.--|---------|. - | | . | . | |. - | A | . B | . | |. - | | . | . | |. - | | . | . | |. - 1 |---------|--.------|------.--|---------|. - 4/3|.........|.........|.........|.........|. - | | . | . | |. - | C | . D | . | |. - | | . | . | |. - 2 |---------|--.------|------.--|---------|. - | | . | . | |. - | | . | . | |. - 8/3|.........|.........|.........|.........|. - | | . | . | |. - 3 |---------|--.------|------.--|---------|. - -So we see the first scaled pixel (0 < x < 4/3 and 0 < y < 4/3) mostly -overlaps with unscaled source pixel "A". The integration (averaging) -weights for this scaled pixel are: - - A 1 - B 1/3 - C 1/3 - D 1/9 - - * - * The Red, Green, and Blue color values must be averaged over separately - * otherwise you can get a complete mess (except in solid regions), - * because high order bits are averaged differently from the low order bits. - * - * So the algorithm is roughly: - * - * - Given as input a rectangle in the unscaled source fb with changes, - * find the rectangle of pixels this affects in the scaled destination fb. - * - * - For each of the affected scaled (dest) pixels, determine all of the - * unscaled (source) pixels it overlaps with. - * - * - Average those unscaled source values together, weighted by the area - * overlap with the destination pixel. Average R, G, B separately. - * - * - Take this average value and convert to a valid pixel value if - * necessary (e.g. rounding, shifting), and then insert it into the - * destination framebuffer as the pixel value. - * - * - On to the next destination pixel... - * - * ======================================================================== - * - * For expanding, e.g. -scale 1.1 (which we don't think people will do - * very often... or at least so we hope, the framebuffer can become huge) - * the situation is reversed and the destination pixel is smaller than a - * "graph paper" unit square (source pixel). Some destination pixels - * will be completely within a single unscaled source pixel. - * - * What we do here is a simple 4 point interpolation scheme: - * - * Let P00 be the source pixel closest to the destination pixel but with - * x and y values less than or equal to those of the destination pixel. - * (for simplicity, think of the upper left corner of a pixel defining the - * x,y location of the pixel, the center would work just as well). So it - * is the source pixel immediately to the upper left of the destination - * pixel. Let P10 be the source pixel one to the right of P00. Let P01 - * be one down from P00. And let P11 be one down and one to the right - * of P00. They form a 2x2 square we will interpolate inside of. - * - * Let V00, V10, V01, and V11 be the color values of those 4 source - * pixels. Let dx be the displacement along x the destination pixel is - * from P00. Note: 0 <= dx < 1 by definition of P00. Similarly let - * dy be the displacement along y. The weighted average for the - * interpolation is: - * - * V_ave = V00 * (1 - dx) * (1 - dy) - * + V10 * dx * (1 - dy) - * + V01 * (1 - dx) * dy - * + V11 * dx * dy - * - * Note that the weights (1-dx)*(1-dy) + dx*(1-dy) + (1-dx)*dy + dx*dy - * automatically add up to 1. It is also nice that all the weights are - * positive (unsigned char stays unsigned char). The above formula can - * be motivated by doing two 1D interpolations along x: - * - * VA = V00 * (1 - dx) + V10 * dx - * VB = V01 * (1 - dx) + V11 * dx - * - * and then interpolating VA and VB along y: - * - * V_ave = VA * (1 - dy) + VB * dy - * - * VA - * v |<-dx->| - * -- V00 ------ V10 - * dy | | - * -- | o...|... "o" denotes the position of the desired - * ^ | . | . destination pixel relative to the P00 - * | . | . source pixel. - * V10 ----.- V11 . - * ........ - * | - * VB - * - * - * Of course R, G, B averages are done separately as in the shrinking - * case. This gives reasonable results, and the implementation for - * shrinking can simply be used with different choices for weights for - * the loop over the 4 pixels. - */ - -void scale_rect(double factor, int blend, int interpolate, int Bpp, - char *src_fb, int src_bytes_per_line, char *dst_fb, int dst_bytes_per_line, - int Nx, int Ny, int nx, int ny, int X1, int Y1, int X2, int Y2, int mark) { -/* - * Notation: - * "i" an x pixel index in the destination (scaled) framebuffer - * "j" a y pixel index in the destination (scaled) framebuffer - * "I" an x pixel index in the source (un-scaled, i.e. main) framebuffer - * "J" a y pixel index in the source (un-scaled, i.e. main) framebuffer - * - * Similarly for nx, ny, Nx, Ny, etc. Lowercase: dest, Uppercase: source. - */ - int i, j, i1, i2, j1, j2; /* indices for scaled fb (dest) */ - int I, J, I1, I2, J1, J2; /* indices for main fb (source) */ - - double w, wx, wy, wtot; /* pixel weights */ - - double x1, y1, x2, y2; /* x-y coords for destination pixels edges */ - double dx, dy; /* size of destination pixel */ - double ddx, ddy; /* for interpolation expansion */ - - char *src, *dest; /* pointers to the two framebuffers */ - - - unsigned short us; - unsigned char uc; - unsigned int ui; - - int use_noblend_shortcut = 1; - int shrink; /* whether shrinking or expanding */ - static int constant_weights = -1, mag_int = -1; - static int last_Nx = -1, last_Ny = -1, cnt = 0; - static double last_factor = -1.0; - int b, k; - double pixave[4]; /* for averaging pixel values */ - - if (factor <= 1.0) { - shrink = 1; - } else { - shrink = 0; - } - - /* - * N.B. width and height (real numbers) of a scaled pixel. - * both are > 1 (e.g. 1.333 for -scale 3/4) - * they should also be equal but we don't assume it. - * - * This new way is probably the best we can do, take the inverse - * of the scaling factor to double precision. - */ - dx = 1.0/factor; - dy = 1.0/factor; - - /* - * There is some speedup if the pixel weights are constant, so - * let's special case these. - * - * If scale = 1/n and n divides Nx and Ny, the pixel weights - * are constant (e.g. 1/2 => equal on 2x2 square). - */ - if (factor != last_factor || Nx != last_Nx || Ny != last_Ny) { - constant_weights = -1; - mag_int = -1; - last_Nx = Nx; - last_Ny = Ny; - last_factor = factor; - } - - if (constant_weights < 0) { - int n = 0; - - constant_weights = 0; - mag_int = 0; - - for (i = 2; i<=128; i++) { - double test = ((double) 1)/ i; - double diff, eps = 1.0e-7; - diff = factor - test; - if (-eps < diff && diff < eps) { - n = i; - break; - } - } - if (! blend || ! shrink || interpolate) { - ; - } else if (n != 0) { - if (Nx % n == 0 && Ny % n == 0) { - static int didmsg = 0; - if (mark && ! didmsg) { - didmsg = 1; - rfbLog("scale_and_mark_rect: using " - "constant pixel weight speedup " - "for 1/%d\n", n); - } - constant_weights = 1; - } - } - - n = 0; - for (i = 2; i<=32; i++) { - double test = (double) i; - double diff, eps = 1.0e-7; - diff = factor - test; - if (-eps < diff && diff < eps) { - n = i; - break; - } - } - if (! blend && factor > 1.0 && n) { - mag_int = n; - } - } - - if (mark && factor > 1.0 && blend) { - /* - * kludge: correct for interpolating blurring leaking - * up or left 1 destination pixel. - */ - if (X1 > 0) X1--; - if (Y1 > 0) Y1--; - } - - /* - * find the extent of the change the input rectangle induces in - * the scaled framebuffer. - */ - - /* Left edges: find largest i such that i * dx <= X1 */ - i1 = FLOOR(X1/dx); - - /* Right edges: find smallest i such that (i+1) * dx >= X2+1 */ - i2 = CEIL( (X2+1)/dx ) - 1; - - /* To be safe, correct any overflows: */ - i1 = nfix(i1, nx); - i2 = nfix(i2, nx) + 1; /* add 1 to make a rectangle upper boundary */ - - /* Repeat above for y direction: */ - j1 = FLOOR(Y1/dy); - j2 = CEIL( (Y2+1)/dy ) - 1; - - j1 = nfix(j1, ny); - j2 = nfix(j2, ny) + 1; - - /* special case integer magnification with no blending */ - if (mark && ! blend && mag_int && Bpp != 3) { - int jmin, jmax, imin, imax; - - /* outer loop over *source* pixels */ - for (J=Y1; J < Y2; J++) { - jmin = J * mag_int; - jmax = jmin + mag_int; - for (I=X1; I < X2; I++) { - /* extract value */ - src = src_fb + J*src_bytes_per_line + I*Bpp; - if (Bpp == 4) { - ui = *((unsigned int *)src); - } else if (Bpp == 2) { - us = *((unsigned short *)src); - } else if (Bpp == 1) { - uc = *((unsigned char *)src); - } - imin = I * mag_int; - imax = imin + mag_int; - /* inner loop over *dest* pixels */ - for (j=jmin; j<jmax; j++) { - dest = dst_fb + j*dst_bytes_per_line + imin*Bpp; - for (i=imin; i<imax; i++) { - if (Bpp == 4) { - *((unsigned int *)dest) = ui; - } else if (Bpp == 2) { - *((unsigned short *)dest) = us; - } else if (Bpp == 1) { - *((unsigned char *)dest) = uc; - } - dest += Bpp; - } - } - } - } - goto markit; - } - - /* set these all to 1.0 to begin with */ - wx = 1.0; - wy = 1.0; - w = 1.0; - - /* - * Loop over destination pixels in scaled fb: - */ - for (j=j1; j<j2; j++) { - y1 = j * dy; /* top edge */ - if (y1 > Ny - 1) { - /* can go over with dy = 1/scale_fac */ - y1 = Ny - 1; - } - y2 = y1 + dy; /* bottom edge */ - - /* Find main fb indices covered by this dest pixel: */ - J1 = (int) FLOOR(y1); - J1 = nfix(J1, Ny); - - if (shrink && ! interpolate) { - J2 = (int) CEIL(y2) - 1; - J2 = nfix(J2, Ny); - } else { - J2 = J1 + 1; /* simple interpolation */ - ddy = y1 - J1; - } - - /* destination char* pointer: */ - dest = dst_fb + j*dst_bytes_per_line + i1*Bpp; - - for (i=i1; i<i2; i++) { - - x1 = i * dx; /* left edge */ - if (x1 > Nx - 1) { - /* can go over with dx = 1/scale_fac */ - x1 = Nx - 1; - } - x2 = x1 + dx; /* right edge */ - - cnt++; - - /* Find main fb indices covered by this dest pixel: */ - I1 = (int) FLOOR(x1); - if (I1 >= Nx) I1 = Nx - 1; - - if (! blend && use_noblend_shortcut) { - /* - * The noblend case involves no weights, - * and 1 pixel, so just copy the value - * directly. - */ - src = src_fb + J1*src_bytes_per_line + I1*Bpp; - if (Bpp == 4) { - *((unsigned int *)dest) - = *((unsigned int *)src); - } else if (Bpp == 2) { - *((unsigned short *)dest) - = *((unsigned short *)src); - } else if (Bpp == 1) { - *(dest) = *(src); - } else if (Bpp == 3) { - /* rare case */ - for (k=0; k<=2; k++) { - *(dest+k) = *(src+k); - } - } - dest += Bpp; - continue; - } - - if (shrink && ! interpolate) { - I2 = (int) CEIL(x2) - 1; - if (I2 >= Nx) I2 = Nx - 1; - } else { - I2 = I1 + 1; /* simple interpolation */ - ddx = x1 - I1; - } - - /* Zero out accumulators for next pixel average: */ - for (b=0; b<4; b++) { - pixave[b] = 0.0; /* for RGB weighted sums */ - } - - /* - * wtot is for accumulating the total weight. - * It should always sum to 1/(scale_fac * scale_fac). - */ - wtot = 0.0; - - /* - * Loop over source pixels covered by this dest pixel. - * - * These "extra" loops over "J" and "I" make - * the cache/cacheline performance unclear. - * For example, will the data brought in from - * src for j, i, and J=0 still be in the cache - * after the J > 0 data have been accessed and - * we are at j, i+1, J=0? The stride in J is - * main_bytes_per_line, and so ~4 KB. - * - * Typical case when shrinking are 2x2 loop, so - * just two lines to worry about. - */ - for (J=J1; J<=J2; J++) { - /* see comments for I, x1, x2, etc. below */ - if (constant_weights) { - ; - } else if (! blend) { - if (J != J1) { - continue; - } - wy = 1.0; - - /* interpolation scheme: */ - } else if (! shrink || interpolate) { - if (J >= Ny) { - continue; - } else if (J == J1) { - wy = 1.0 - ddy; - } else if (J != J1) { - wy = ddy; - } - - /* integration scheme: */ - } else if (J < y1) { - wy = J+1 - y1; - } else if (J+1 > y2) { - wy = y2 - J; - } else { - wy = 1.0; - } - - src = src_fb + J*src_bytes_per_line + I1*Bpp; - - for (I=I1; I<=I2; I++) { - - /* Work out the weight: */ - - if (constant_weights) { - ; - } else if (! blend) { - /* - * Ugh, PseudoColor colormap is - * bad news, to avoid random - * colors just take the first - * pixel. Or user may have - * specified :nb to fraction. - * The :fb will force blending - * for this case. - */ - if (I != I1) { - continue; - } - wx = 1.0; - - /* interpolation scheme: */ - } else if (! shrink || interpolate) { - if (I >= Nx) { - continue; /* off edge */ - } else if (I == I1) { - wx = 1.0 - ddx; - } else if (I != I1) { - wx = ddx; - } - - /* integration scheme: */ - } else if (I < x1) { - /* - * source left edge (I) to the - * left of dest left edge (x1): - * fractional weight - */ - wx = I+1 - x1; - } else if (I+1 > x2) { - /* - * source right edge (I+1) to the - * right of dest right edge (x2): - * fractional weight - */ - wx = x2 - I; - } else { - /* - * source edges (I and I+1) completely - * inside dest edges (x1 and x2): - * full weight - */ - wx = 1.0; - } - - w = wx * wy; - wtot += w; - - /* - * We average the unsigned char value - * instead of char value: otherwise - * the minimum (char 0) is right next - * to the maximum (char -1)! This way - * they are spread between 0 and 255. - */ - if (Bpp == 4) { - /* unroll the loops, can give 20% */ - pixave[0] += w * - ((unsigned char) *(src )); - pixave[1] += w * - ((unsigned char) *(src+1)); - pixave[2] += w * - ((unsigned char) *(src+2)); - pixave[3] += w * - ((unsigned char) *(src+3)); - } else if (Bpp == 2) { - /* - * 16bpp: trickier with green - * split over two bytes, so we - * use the masks: - */ - us = *((unsigned short *) src); - pixave[0] += w*(us & main_red_mask); - pixave[1] += w*(us & main_green_mask); - pixave[2] += w*(us & main_blue_mask); - } else if (Bpp == 1) { - pixave[0] += w * - ((unsigned char) *(src)); - } else { - for (b=0; b<Bpp; b++) { - pixave[b] += w * - ((unsigned char) *(src+b)); - } - } - src += Bpp; - } - } - - if (wtot <= 0.0) { - wtot = 1.0; - } - wtot = 1.0/wtot; /* normalization factor */ - - /* place weighted average pixel in the scaled fb: */ - if (Bpp == 4) { - *(dest ) = (char) (wtot * pixave[0]); - *(dest+1) = (char) (wtot * pixave[1]); - *(dest+2) = (char) (wtot * pixave[2]); - *(dest+3) = (char) (wtot * pixave[3]); - } else if (Bpp == 2) { - /* 16bpp / 565 case: */ - pixave[0] *= wtot; - pixave[1] *= wtot; - pixave[2] *= wtot; - us = (main_red_mask & (int) pixave[0]) - | (main_green_mask & (int) pixave[1]) - | (main_blue_mask & (int) pixave[2]); - *( (unsigned short *) dest ) = us; - } else if (Bpp == 1) { - *(dest) = (char) (wtot * pixave[0]); - } else { - for (b=0; b<Bpp; b++) { - *(dest+b) = (char) (wtot * pixave[b]); - } - } - dest += Bpp; - } - } - markit: - if (mark) { - mark_rect_as_modified(i1, j1, i2, j2, 1); - } -} - -static void scale_and_mark_rect(int X1, int Y1, int X2, int Y2) { - - if (!screen || !rfb_fb || !main_fb) { - return; - } - if (! screen->serverFormat.trueColour) { - /* - * PseudoColor colormap... blending leads to random colors. - * User can override with ":fb" - */ - if (scaling_blend == 1) { - /* :fb option sets it to 2 */ - if (default_visual->class == StaticGray) { - /* - * StaticGray can be blended OK, otherwise - * user can disable with :nb - */ - ; - } else { - scaling_blend = 0; - } - } - } - - scale_rect(scale_fac, scaling_blend, scaling_interpolate, bpp/8, - main_fb, main_bytes_per_line, rfb_fb, rfb_bytes_per_line, - dpy_x, dpy_y, scaled_x, scaled_y, X1, Y1, X2, Y2, 1); -} - -void mark_rect_as_modified(int x1, int y1, int x2, int y2, int force) { - - if (damage_time != 0) { - /* - * This is not XDAMAGE, rather a hack for testing - * where we allow the framebuffer to be corrupted for - * damage_delay seconds. - */ - int debug = 0; - if (time(0) > damage_time + damage_delay) { - if (! quiet) { - rfbLog("damaging turned off.\n"); - } - damage_time = 0; - damage_delay = 0; - } else { - if (debug) { - rfbLog("damaging viewer fb by not marking " - "rect: %d,%d,%d,%d\n", x1, y1, x2, y2); - } - return; - } - } - - if (rfb_fb == main_fb || force) { - rfbMarkRectAsModified(screen, x1, y1, x2, y2); - } else if (scaling) { - scale_and_mark_rect(x1, y1, x2, y2); - } -} - -/* - * Notifies libvncserver of a changed hint rectangle. - */ -void mark_hint(hint_t hint) { - int x = hint.x; - int y = hint.y; - int w = hint.w; - int h = hint.h; - - mark_rect_as_modified(x, y, x + w, y + h, 0); -} - -/* - * copy_tiles() gives a slight improvement over copy_tile() since - * adjacent runs of tiles are done all at once there is some savings - * due to contiguous memory access. Not a great speedup, but in some - * cases it can be up to 2X. Even more on a SunRay or ShadowFB where - * no graphics hardware is involved in the read. Generally, graphics - * devices are optimized for write, not read, so we are limited by the - * read bandwidth, sometimes only 5 MB/sec on otherwise fast hardware. - */ -static int *first_line = NULL, *last_line; -static unsigned short *left_diff, *right_diff; - -static int copy_tiles(int tx, int ty, int nt) { - int x, y, line; - int size_x, size_y, width1, width2; - int off, len, n, dw, dx, t; - 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; - if (! first_line) { - /* allocate arrays first time in. */ - int n = ntiles_x + 1; - first_line = (int *) malloc((size_t) (n * sizeof(int))); - last_line = (int *) malloc((size_t) (n * sizeof(int))); - left_diff = (unsigned short *) - malloc((size_t) (n * sizeof(unsigned short))); - right_diff = (unsigned short *) - malloc((size_t) (n * sizeof(unsigned short))); - } - - x = tx * tile_x; - y = ty * tile_y; - - size_x = dpy_x - x; - if ( size_x > tile_x * nt ) { - size_x = tile_x * nt; - width1 = tile_x; - width2 = tile_x; - } else { - /* short tile */ - width1 = tile_x; /* internal tile */ - width2 = size_x - (nt - 1) * tile_x; /* right hand tile */ - } - - size_y = dpy_y - y; - if ( size_y > tile_y ) { - size_y = tile_y; - } - - n = tx + ty * ntiles_x; /* number of the first tile */ - - if (blackouts && tile_blackout[n].cover == 2) { - /* - * If there are blackouts and this tile is completely covered - * no need to poll screen or do anything else.. - * n.b. we are in single copy_tile mode: nt=1 - */ - tile_has_diff[n] = 0; - return(0); - } - - X_LOCK; - XRANDR_SET_TRAP_RET(-1, "copy_tile-set"); - /* read in the whole tile run at once: */ - copy_image(tile_row[nt], x, y, size_x, size_y); - XRANDR_CHK_TRAP_RET(-1, "copy_tile-chk"); - - X_UNLOCK; - - if (blackouts && tile_blackout[n].cover == 1) { - /* - * If there are blackouts and this tile is partially covered - * we should re-black-out the portion. - * n.b. we are in single copy_tile mode: nt=1 - */ - int x1, x2, y1, y2, b; - int w, s, fill = 0; - - for (b=0; b < tile_blackout[n].count; b++) { - char *b_dst = tile_row[nt]->data; - - x1 = tile_blackout[n].bo[b].x1 - x; - y1 = tile_blackout[n].bo[b].y1 - y; - x2 = tile_blackout[n].bo[b].x2 - x; - y2 = tile_blackout[n].bo[b].y2 - y; - - w = (x2 - x1) * pixelsize; - s = x1 * pixelsize; - - for (line = 0; line < size_y; line++) { - if (y1 <= line && line < y2) { - memset(b_dst + s, fill, (size_t) w); - } - b_dst += tile_row[nt]->bytes_per_line; - } - } - } - - src = tile_row[nt]->data; - dst = main_fb + y * main_bytes_per_line + x * pixelsize; - - s_src = src; - s_dst = dst; - - for (t=1; t <= nt; t++) { - first_line[t] = -1; - } - - /* find the first line with difference: */ - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - /* foreach line: */ - for (line = 0; line < size_y; line++) { - /* foreach horizontal tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] != -1) { - continue; - } - - off = (t-1) * w1; - if (t == nt) { - len = w2; /* possible short tile */ - } else { - len = w1; - } - - if (memcmp(s_dst + off, s_src + off, len)) { - first_line[t] = line; - } - } - s_src += tile_row[nt]->bytes_per_line; - s_dst += main_bytes_per_line; - } - - /* see if there were any differences for any tile: */ - first_min = -1; - for (t=1; t <= nt; t++) { - tile_tried[n+(t-1)] = 1; - if (first_line[t] != -1) { - if (first_min == -1 || first_line[t] < first_min) { - first_min = first_line[t]; - } - } - } - if (first_min == -1) { - /* no tile has a difference, note this and get out: */ - for (t=1; t <= nt; t++) { - tile_has_diff[n+(t-1)] = 0; - } - return(0); - } else { - /* - * at least one tile has a difference. make sure info - * is recorded (e.g. sometimes we guess tiles and they - * came in with tile_has_diff 0) - */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - tile_has_diff[n+(t-1)] = 0; - } else { - tile_has_diff[n+(t-1)] = 1; - } - } - } - - m_src = src + (tile_row[nt]->bytes_per_line * size_y); - m_dst = dst + (main_bytes_per_line * size_y); - - for (t=1; t <= nt; t++) { - last_line[t] = first_line[t]; - } - - /* find the last line with difference: */ - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - /* foreach line: */ - for (line = size_y - 1; line > first_min; line--) { - - m_src -= tile_row[nt]->bytes_per_line; - m_dst -= main_bytes_per_line; - - /* foreach tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1 - || last_line[t] != first_line[t]) { - /* tile has no changes or already done */ - continue; - } - - off = (t-1) * w1; - if (t == nt) { - len = w2; /* possible short tile */ - } else { - len = w1; - } - if (memcmp(m_dst + off, m_src + off, len)) { - last_line[t] = line; - } - } - } - - /* - * determine the farthest down last changed line - * will be used below to limit our memcpy() to the framebuffer. - */ - last_max = -1; - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - continue; - } - if (last_max == -1 || last_line[t] > last_max) { - last_max = last_line[t]; - } - } - - /* look for differences on left and right hand edges: */ - for (t=1; t <= nt; t++) { - left_diff[t] = 0; - right_diff[t] = 0; - } - - h_src = src; - h_dst = dst; - - w1 = width1 * pixelsize; - w2 = width2 * pixelsize; - - dx1 = (width1 - tile_fuzz) * pixelsize; - dx2 = (width2 - tile_fuzz) * pixelsize; - dw = tile_fuzz * pixelsize; - - /* foreach line: */ - for (line = 0; line < size_y; line++) { - /* foreach tile: */ - for (t=1; t <= nt; t++) { - if (first_line[t] == -1) { - /* tile has no changes at all */ - continue; - } - - off = (t-1) * w1; - if (t == nt) { - dx = dx2; /* possible short tile */ - if (dx <= 0) { - break; - } - } else { - dx = dx1; - } - - if (! left_diff[t] && memcmp(h_dst + off, - h_src + off, dw)) { - left_diff[t] = 1; - } - if (! right_diff[t] && memcmp(h_dst + off + dx, - h_src + off + dx, dw) ) { - right_diff[t] = 1; - } - } - h_src += tile_row[nt]->bytes_per_line; - h_dst += main_bytes_per_line; - } - - /* now finally copy the difference to the rfb framebuffer: */ - s_src = src + tile_row[nt]->bytes_per_line * first_min; - s_dst = dst + main_bytes_per_line * first_min; - - 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 to limit - * the size of the hint. could help for a slow - * link. Unfortunately we spent a lot of time - * reading in the many tiles. - * - * BTW, 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) - * so this inner loop is not as bad as it seems. - */ - 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; - } - - /* record all the info in the region array for this tile: */ - for (t=1; t <= nt; t++) { - int s = t - 1; - - if (first_line[t] == -1) { - /* tile unchanged */ - continue; - } - 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 ) { - tile_region[n+s].top_diff = 1; - } - if ( last_line[t] > (size_y - 1) - tile_fuzz ) { - tile_region[n+s].bot_diff = 1; - } - - tile_region[n+s].left_diff = left_diff[t]; - tile_region[n+s].right_diff = right_diff[t]; - - tile_copied[n+s] = 1; - } - - return(1); -} - -/* - * The copy_tile() call in the loop below copies the changed tile into - * the rfb framebuffer. Note that copy_tile() sets the tile_region - * struct to have info about the y-range of the changed region and also - * whether the tile edges contain diffs (within distance tile_fuzz). - * - * We use this tile_region info to try to guess if the downward and right - * tiles will have diffs. These tiles will be checked later in the loop - * (since y+1 > y and x+1 > x). - * - * See copy_tiles_backward_pass() for analogous checking upward and - * left tiles. - */ -static int copy_all_tiles(void) { - int x, y, n, m; - int diffs = 0, ct; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - - if (tile_has_diff[n]) { - ct = copy_tiles(x, y, 1); - if (ct < 0) return ct; /* fatal */ - } - if (! tile_has_diff[n]) { - /* - * n.b. copy_tiles() may have detected - * no change and reset tile_has_diff to 0. - */ - continue; - } - diffs++; - - /* neighboring tile downward: */ - if ( (y+1) < ntiles_y && tile_region[n].bot_diff) { - m = x + (y+1) * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - } - /* neighboring tile to right: */ - if ( (x+1) < ntiles_x && tile_region[n].right_diff) { - m = (x+1) + y * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - } - } - } - return diffs; -} - -/* - * Routine analogous to copy_all_tiles() above, but for horizontal runs - * of adjacent changed tiles. - */ -static int copy_all_tile_runs(void) { - int x, y, n, m, i; - int diffs = 0, ct; - int in_run = 0, run = 0; - int ntave = 0, ntcnt = 0; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x + 1; x++) { - n = x + y * ntiles_x; - - if (x != ntiles_x && tile_has_diff[n]) { - in_run = 1; - run++; - } else { - if (! in_run) { - in_run = 0; - run = 0; - continue; - } - ct = copy_tiles(x - run, y, run); - if (ct < 0) return ct; /* fatal */ - - ntcnt++; - ntave += run; - diffs += run; - - /* neighboring tile downward: */ - for (i=1; i <= run; i++) { - if ((y+1) < ntiles_y - && tile_region[n-i].bot_diff) { - m = (x-i) + (y+1) * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - } - } - - /* neighboring tile to right: */ - if (((x-1)+1) < ntiles_x - && tile_region[n-1].right_diff) { - m = ((x-1)+1) + y * ntiles_x; - if (! tile_has_diff[m]) { - tile_has_diff[m] = 2; - } - - /* note that this starts a new run */ - in_run = 1; - run = 1; - } else { - in_run = 0; - run = 0; - } - } - } - /* - * Could some activity go here, to emulate threaded - * behavior by servicing some libvncserver tasks? - */ - } - return diffs; -} - -/* - * Here starts a bunch of heuristics to guess/detect changed tiles. - * They are: - * copy_tiles_backward_pass, fill_tile_gaps/gap_try, grow_islands/island_try - */ - -/* - * Try to predict whether the upward and/or leftward tile has been modified. - * copy_all_tiles() has already done downward and rightward tiles. - */ -static int copy_tiles_backward_pass(void) { - int x, y, n, m; - int diffs = 0, ct; - - for (y = ntiles_y - 1; y >= 0; y--) { - for (x = ntiles_x - 1; x >= 0; x--) { - n = x + y * ntiles_x; /* number of this tile */ - - if (! tile_has_diff[n]) { - continue; - } - - m = x + (y-1) * ntiles_x; /* neighboring tile upward */ - - if (y >= 1 && ! tile_has_diff[m] && tile_region[n].top_diff) { - if (! tile_tried[m]) { - tile_has_diff[m] = 2; - ct = copy_tiles(x, y-1, 1); - if (ct < 0) return ct; /* fatal */ - } - } - - m = (x-1) + y * ntiles_x; /* neighboring tile to left */ - - if (x >= 1 && ! tile_has_diff[m] && tile_region[n].left_diff) { - if (! tile_tried[m]) { - tile_has_diff[m] = 2; - ct = copy_tiles(x-1, y, 1); - if (ct < 0) return ct; /* fatal */ - } - } - } - } - for (n=0; n < ntiles; n++) { - if (tile_has_diff[n]) { - diffs++; - } - } - return diffs; -} - -static int copy_tiles_additional_pass(void) { - int x, y, n; - int diffs = 0, ct; - - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; /* number of this tile */ - - if (! tile_has_diff[n]) { - continue; - } - if (tile_copied[n]) { - continue; - } - - ct = copy_tiles(x, y, 1); - if (ct < 0) return ct; /* fatal */ - } - } - for (n=0; n < ntiles; n++) { - if (tile_has_diff[n]) { - diffs++; - } - } - return diffs; -} - -static int gap_try(int x, int y, int *run, int *saw, int along_x) { - int n, m, i, xt, yt, ct; - - n = x + y * ntiles_x; - - if (! tile_has_diff[n]) { - if (*saw) { - (*run)++; /* extend the gap run. */ - } - return 0; - } - if (! *saw || *run == 0 || *run > gaps_fill) { - *run = 0; /* unacceptable run. */ - *saw = 1; - return 0; - } - - for (i=1; i <= *run; i++) { /* iterate thru the run. */ - if (along_x) { - xt = x - i; - yt = y; - } else { - xt = x; - yt = y - i; - } - - m = xt + yt * ntiles_x; - if (tile_tried[m]) { /* do not repeat tiles */ - continue; - } - - ct = copy_tiles(xt, yt, 1); - if (ct < 0) return ct; /* fatal */ - } - *run = 0; - *saw = 1; - return 1; -} - -/* - * Look for small gaps of unchanged tiles that may actually contain changes. - * E.g. when paging up and down in a web broswer or terminal there can - * be a distracting delayed filling in of such gaps. gaps_fill is the - * tweak parameter that sets the width of the gaps that are checked. - * - * BTW, grow_islands() is actually pretty successful at doing this too... - */ -static int fill_tile_gaps(void) { - int x, y, run, saw; - int n, diffs = 0, ct; - - /* horizontal: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - saw = 0; - for (x=0; x < ntiles_x; x++) { - ct = gap_try(x, y, &run, &saw, 1); - if (ct < 0) return ct; /* fatal */ - } - } - - /* vertical: */ - for (x=0; x < ntiles_x; x++) { - run = 0; - saw = 0; - for (y=0; y < ntiles_y; y++) { - ct = gap_try(x, y, &run, &saw, 0); - if (ct < 0) return ct; /* fatal */ - } - } - - for (n=0; n < ntiles; n++) { - if (tile_has_diff[n]) { - diffs++; - } - } - return diffs; -} - -static int island_try(int x, int y, int u, int v, int *run) { - int n, m, ct; - - n = x + y * ntiles_x; - m = u + v * ntiles_x; - - if (tile_has_diff[n]) { - (*run)++; - } else { - *run = 0; - } - - if (tile_has_diff[n] && ! tile_has_diff[m]) { - /* found a discontinuity */ - - if (tile_tried[m]) { - return 0; - } else if (*run < grow_fill) { - return 0; - } - - ct = copy_tiles(u, v, 1); - if (ct < 0) return ct; /* fatal */ - } - return 1; -} - -/* - * Scan looking for discontinuities in tile_has_diff[]. Try to extend - * the boundary of the discontinuity (i.e. make the island larger). - * Vertical scans are skipped since they do not seem to yield much... - */ -static int grow_islands(void) { - int x, y, n, run; - int diffs = 0, ct; - - /* - * n.b. the way we scan here should keep an extension going, - * and so also fill in gaps effectively... - */ - - /* left to right: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - for (x=0; x <= ntiles_x - 2; x++) { - ct = island_try(x, y, x+1, y, &run); - if (ct < 0) return ct; /* fatal */ - } - } - /* right to left: */ - for (y=0; y < ntiles_y; y++) { - run = 0; - for (x = ntiles_x - 1; x >= 1; x--) { - ct = island_try(x, y, x-1, y, &run); - if (ct < 0) return ct; /* fatal */ - } - } - for (n=0; n < ntiles; n++) { - if (tile_has_diff[n]) { - diffs++; - } - } - return diffs; -} - -/* - * Fill the framebuffer with zeros for each blackout region - */ -static void blackout_regions(void) { - int i; - for (i=0; i < blackouts; i++) { - zero_fb(blackr[i].x1, blackr[i].y1, blackr[i].x2, blackr[i].y2); - } -} - -/* - * copy the whole X screen to the rfb framebuffer. For a large enough - * number of changed tiles, this is faster than tiles scheme at retrieving - * the info from the X server. Bandwidth to client and compression time - * are other issues... use -fs 1.0 to disable. - */ -int copy_screen(void) { - int pixelsize = bpp/8; - char *fbp; - int i, y, block_size; - - if (! fs_factor) { - return 0; - } - - block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); - - if (! main_fb) { - return 0; - } - fbp = main_fb; - y = 0; - - X_LOCK; - - /* screen may be too big for 1 shm area, so broken into fs_factor */ - for (i=0; i < fs_factor; i++) { - XRANDR_SET_TRAP_RET(-1, "copy_screen-set"); - copy_image(fullscreen, 0, y, 0, 0); - XRANDR_CHK_TRAP_RET(-1, "copy_screen-chk"); - - memcpy(fbp, fullscreen->data, (size_t) block_size); - - y += dpy_y / fs_factor; - fbp += block_size; - } - - X_UNLOCK; - - if (blackouts) { - blackout_regions(); - } - - mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); - return 0; -} - -int copy_snap(void) { - int pixelsize = bpp/8; - char *fbp; - int i, y, block_size; - double dt; - static int first = 1; - - if (! fs_factor) { - return 0; - } - - block_size = (dpy_x * (dpy_y/fs_factor) * pixelsize); - - if (! snap_fb || ! snap || ! snaprect) { - return 0; - } - fbp = snap_fb; - y = 0; - - dtime0(&dt); - X_LOCK; - - /* screen may be too big for 1 shm area, so broken into fs_factor */ - for (i=0; i < fs_factor; i++) { - XRANDR_SET_TRAP_RET(-1, "copy_snap-set"); - copy_image(snaprect, 0, y, 0, 0); - XRANDR_CHK_TRAP_RET(-1, "copy_snap-chk"); - - memcpy(fbp, snaprect->data, (size_t) block_size); - - y += dpy_y / fs_factor; - fbp += block_size; - } - - X_UNLOCK; - dt = dtime(&dt); - if (first) { - rfbLog("copy_snap: time for -snapfb snapshot: %.3f sec\n", dt); - first = 0; - } - - return 0; -} - - -/* - * Utilities for managing the "naps" to cut down on amount of polling. - */ -static void nap_set(int tile_cnt) { - int nap_in = nap_ok; - time_t now = time(0); - - if (scan_count == 0) { - /* roll up check for all NSCAN scans */ - nap_ok = 0; - if (naptile && nap_diff_count < 2 * NSCAN * naptile) { - /* "2" is a fudge to permit a bit of bg drawing */ - nap_ok = 1; - } - nap_diff_count = 0; - } - if (nap_ok && ! nap_in && use_xdamage) { - if (XD_skip > 0.8 * XD_tot) { - /* X DAMAGE is keeping load low, so skip nap */ - nap_ok = 0; - } - } - if (! nap_ok && client_count) { - if(now > last_fb_bytes_sent + no_fbu_blank) { - if (debug_tiles > 1) { - printf("nap_set: nap_ok=1: now: %d last: %d\n", - (int) now, (int) last_fb_bytes_sent); - } - nap_ok = 1; - } - } - - if (show_cursor) { - /* kludge for the up to 4 tiles the mouse patch could occupy */ - if ( tile_cnt > 4) { - last_event = now; - } - } else if (tile_cnt != 0) { - last_event = now; - } -} - -/* - * split up a long nap to improve the wakeup time - */ -static void nap_sleep(int ms, int split) { - int i, input = got_user_input; - - for (i=0; i<split; i++) { - usleep(ms * 1000 / split); - if (! use_threads && i != split - 1) { - rfbPE(-1); - } - if (input != got_user_input) { - break; - } - } -} - -/* - * see if we should take a nap of some sort between polls - */ -static void nap_check(int tile_cnt) { - time_t now; - - nap_diff_count += tile_cnt; - - if (! take_naps) { - return; - } - - now = time(0); - - if (screen_blank > 0) { - int dt_ev, dt_fbu, ms = 2000; - - /* if no activity, pause here for a second or so. */ - dt_ev = (int) (now - last_event); - dt_fbu = (int) (now - last_fb_bytes_sent); - if (dt_fbu > screen_blank) { - /* sleep longer for no fb requests */ - nap_sleep(2 * ms, 16); - return; - } - if (dt_ev > screen_blank) { - nap_sleep(ms, 8); - return; - } - } - if (naptile && nap_ok && tile_cnt < naptile) { - int ms = napfac * waitms; - ms = ms > napmax ? napmax : ms; - if (now - last_input <= 2) { - nap_ok = 0; - } else { - nap_sleep(ms, 1); - } - } -} - -/* - * This is called to avoid a ~20 second timeout in libvncserver. - * May no longer be needed. - */ -static void ping_clients(int tile_cnt) { - static time_t last_send = 0; - time_t now = time(0); - - if (rfbMaxClientWait < 20000) { - rfbMaxClientWait = 20000; - rfbLog("reset rfbMaxClientWait to %d msec.\n", - rfbMaxClientWait); - } - if (tile_cnt) { - last_send = now; - } else if (now - last_send > 1) { - /* Send small heartbeat to client */ - mark_rect_as_modified(0, 0, 1, 1, 1); - last_send = now; - } -} - -/* - * scan_display() wants to know if this tile can be skipped due to - * blackout regions: (no data compare is done, just a quick geometric test) - */ -static int blackout_line_skip(int n, int x, int y, int rescan, - int *tile_count) { - - if (tile_blackout[n].cover == 2) { - tile_has_diff[n] = 0; - return 1; /* skip it */ - - } else if (tile_blackout[n].cover == 1) { - int w, x1, y1, x2, y2, b, hit = 0; - if (x + NSCAN > dpy_x) { - w = dpy_x - x; - } else { - w = NSCAN; - } - - for (b=0; b < tile_blackout[n].count; b++) { - - /* n.b. these coords are in full display space: */ - x1 = tile_blackout[n].bo[b].x1; - x2 = tile_blackout[n].bo[b].x2; - y1 = tile_blackout[n].bo[b].y1; - y2 = tile_blackout[n].bo[b].y2; - - if (x2 - x1 < w) { - /* need to cover full width */ - continue; - } - if (y1 <= y && y < y2) { - hit = 1; - break; - } - } - if (hit) { - if (! rescan) { - tile_has_diff[n] = 0; - } else { - *tile_count += tile_has_diff[n]; - } - return 1; /* skip */ - } - } - return 0; /* do not skip */ -} - -static int blackout_line_cmpskip(int n, int x, int y, char *dst, char *src, - int w, int pixelsize) { - - int i, x1, y1, x2, y2, b, hit = 0; - int beg = -1, end = -1; - - if (tile_blackout[n].cover == 0) { - return 0; /* 0 means do not skip it. */ - } else if (tile_blackout[n].cover == 2) { - return 1; /* 1 means skip it. */ - } - - /* tile has partial coverage: */ - - for (i=0; i < w * pixelsize; i++) { - if (*(dst+i) != *(src+i)) { - beg = i/pixelsize; /* beginning difference */ - break; - } - } - for (i = w * pixelsize - 1; i >= 0; i--) { - if (*(dst+i) != *(src+i)) { - end = i/pixelsize; /* ending difference */ - break; - } - } - if (beg < 0 || end < 0) { - /* problem finding range... */ - return 0; - } - - /* loop over blackout rectangles: */ - for (b=0; b < tile_blackout[n].count; b++) { - - /* y in full display space: */ - y1 = tile_blackout[n].bo[b].y1; - y2 = tile_blackout[n].bo[b].y2; - - /* x relative to tile origin: */ - x1 = tile_blackout[n].bo[b].x1 - x; - x2 = tile_blackout[n].bo[b].x2 - x; - - if (y1 > y || y >= y2) { - continue; - } - if (x1 <= beg && end <= x2) { - hit = 1; - break; - } - } - if (hit) { - return 1; - } else { - return 0; - } -} - -/* - * For the subwin case follows the window if it is moved. - */ -void set_offset(void) { - Window w; - if (! subwin) { - return; - } - X_LOCK; - xtranslate(window, rootwin, 0, 0, &off_x, &off_y, &w, 0); - X_UNLOCK; -} - -/* - * Loop over 1-pixel tall horizontal scanlines looking for changes. - * Record the changes in tile_has_diff[]. Scanlines in the loop are - * equally spaced along y by NSCAN pixels, but have a slightly random - * starting offset ystart ( < NSCAN ) from scanlines[]. - */ -static int scan_display(int ystart, int rescan) { - char *src, *dst; - int pixelsize = bpp/8; - int x, y, w, n; - int tile_count = 0; - int nodiffs = 0, diff_hint; - - y = ystart; - - if (! main_fb) { - rfbLog("scan_display: no main_fb!\n"); - return 0; - } - - while (y < dpy_y) { - - if (use_xdamage) { - XD_tot++; - if (xdamage_hint_skip(y)) { - XD_skip++; - y += NSCAN; - continue; - } - } - - /* grab the horizontal scanline from the display: */ - X_LOCK; - XRANDR_SET_TRAP_RET(-1, "scan_display-set"); - copy_image(scanline, 0, y, 0, 0); - XRANDR_CHK_TRAP_RET(-1, "scan_display-chk"); - X_UNLOCK; - - /* for better memory i/o try the whole line at once */ - src = scanline->data; - dst = main_fb + y * main_bytes_per_line; - - if (! memcmp(dst, src, main_bytes_per_line)) { - /* no changes anywhere in scan line */ - nodiffs = 1; - if (! rescan) { - y += NSCAN; - continue; - } - } - - x = 0; - while (x < dpy_x) { - n = (x/tile_x) + (y/tile_y) * ntiles_x; - diff_hint = 0; - - if (blackouts) { - if (blackout_line_skip(n, x, y, rescan, - &tile_count)) { - x += NSCAN; - continue; - } - } - - if (rescan) { - if (nodiffs || tile_has_diff[n]) { - tile_count += tile_has_diff[n]; - x += NSCAN; - continue; - } - } else if (xdamage_tile_count && - tile_has_xdamage_diff[n]) { - tile_has_xdamage_diff[n] = 2; - diff_hint = 1; - } - - /* set ptrs to correspond to the x offset: */ - src = scanline->data + x * pixelsize; - dst = main_fb + y * main_bytes_per_line + x * pixelsize; - - /* compute the width of data to be compared: */ - if (x + NSCAN > dpy_x) { - w = dpy_x - x; - } else { - w = NSCAN; - } - - if (diff_hint || memcmp(dst, src, w * pixelsize)) { - /* found a difference, record it: */ - if (! blackouts) { - tile_has_diff[n] = 1; - tile_count++; - } else { - if (blackout_line_cmpskip(n, x, y, - dst, src, w, pixelsize)) { - tile_has_diff[n] = 0; - } else { - tile_has_diff[n] = 1; - tile_count++; - } - } - } - x += NSCAN; - } - y += NSCAN; - } - return tile_count; -} - - -/* - * toplevel for the scanning, rescanning, and applying the heuristics. - * returns number of changed tiles. - */ -int scan_for_updates(int count_only) { - int i, tile_count, tile_diffs; - int old_copy_tile; - double frac1 = 0.1; /* tweak parameter to try a 2nd scan_display() */ - double frac2 = 0.35; /* or 3rd */ - double frac3 = 0.02; /* do scan_display() again after copy_tiles() */ - static double last_poll = 0.0; - - if (slow_fb > 0.0) { - double now = dnow(); - if (now < last_poll + slow_fb) { - return 0; - } - last_poll = now; - } - - for (i=0; i < ntiles; i++) { - tile_has_diff[i] = 0; - tile_has_xdamage_diff[i] = 0; - tile_tried[i] = 0; - tile_copied[i] = 0; - } - for (i=0; i < ntiles_y; i++) { - /* could be useful, currently not used */ - tile_row_has_xdamage_diff[i] = 0; - } - xdamage_tile_count = 0; - - /* - * n.b. this program has only been tested so far with - * tile_x = tile_y = NSCAN = 32! - */ - - if (!count_only) { - scan_count++; - scan_count %= NSCAN; - - /* some periodic maintenance */ - if (subwin) { - set_offset(); /* follow the subwindow */ - } - if (indexed_color && scan_count % 4 == 0) { - /* check for changed colormap */ - set_colormap(0); - } - if (use_xdamage) { - /* first pass collecting DAMAGE events: */ - collect_xdamage(scan_count, 0); - } - } - -#define SCAN_FATAL(x) \ - if (x < 0) { \ - scan_in_progress = 0; \ - fb_copy_in_progress = 0; \ - return 0; \ - } - - /* scan with the initial y to the jitter value from scanlines: */ - scan_in_progress = 1; - tile_count = scan_display(scanlines[scan_count], 0); - SCAN_FATAL(tile_count); - - /* - * we do the XDAMAGE here too since after scan_display() - * there is a better chance we have received the events from - * the X server (otherwise the DAMAGE events will be processed - * in the *next* call, usually too late and wasteful since - * the unchanged tiles are read in again). - */ - if (use_xdamage) { - collect_xdamage(scan_count, 1); - } - if (count_only) { - scan_in_progress = 0; - fb_copy_in_progress = 0; - return tile_count; - } - - if (xdamage_tile_count) { - /* pick up "known" damaged tiles we missed in scan_display() */ - for (i=0; i < ntiles; i++) { - if (tile_has_diff[i]) { - continue; - } - if (tile_has_xdamage_diff[i]) { - tile_has_diff[i] = 1; - if (tile_has_xdamage_diff[i] == 1) { - tile_has_xdamage_diff[i] = 2; - tile_count++; - } - } - } - } - - nap_set(tile_count); - - if (fs_factor && frac1 >= fs_frac) { - /* make frac1 < fs_frac if fullscreen updates are enabled */ - frac1 = fs_frac/2.0; - } - - if (tile_count > frac1 * ntiles) { - /* - * many tiles have changed, so try a rescan (since it should - * be short compared to the many upcoming copy_tiles() calls) - */ - - /* this check is done to skip the extra scan_display() call */ - if (! fs_factor || tile_count <= fs_frac * ntiles) { - int cp, tile_count_old = tile_count; - - /* choose a different y shift for the 2nd scan: */ - cp = (NSCAN - scan_count) % NSCAN; - - tile_count = scan_display(scanlines[cp], 1); - SCAN_FATAL(tile_count); - - if (tile_count >= (1 + frac2) * tile_count_old) { - /* on a roll... do a 3rd scan */ - cp = (NSCAN - scan_count + 7) % NSCAN; - tile_count = scan_display(scanlines[cp], 1); - SCAN_FATAL(tile_count); - } - } - scan_in_progress = 0; - - /* - * At some number of changed tiles it is better to just - * copy the full screen at once. I.e. time = c1 + m * r1 - * where m is number of tiles, r1 is the copy_tiles() - * time, and c1 is the scan_display() time: for some m - * it crosses the full screen update time. - * - * We try to predict that crossover with the fs_frac - * fudge factor... seems to be about 1/2 the total number - * of tiles. n.b. this ignores network bandwidth, - * compression time etc... - * - * Use -fs 1.0 to disable on slow links. - */ - if (fs_factor && tile_count > fs_frac * ntiles) { - int cs; - fb_copy_in_progress = 1; - cs = copy_screen(); - fb_copy_in_progress = 0; - SCAN_FATAL(cs); - if (use_threads && pointer_mode != 1) { - pointer(-1, 0, 0, NULL); - } - nap_check(tile_count); - return tile_count; - } - } - scan_in_progress = 0; - - /* copy all tiles with differences from display to rfb framebuffer: */ - fb_copy_in_progress = 1; - - if (single_copytile || tile_shm_count < ntiles_x) { - /* - * Old way, copy I/O one tile at a time. - */ - old_copy_tile = 1; - } else { - /* - * New way, does runs of horizontal tiles at once. - * Note that below, for simplicity, the extra tile finding - * (e.g. copy_tiles_backward_pass) is done the old way. - */ - old_copy_tile = 0; - } - if (old_copy_tile) { - tile_diffs = copy_all_tiles(); - } else { - tile_diffs = copy_all_tile_runs(); - } - SCAN_FATAL(tile_diffs); - - /* - * This backward pass for upward and left tiles complements what - * was done in copy_all_tiles() for downward and right tiles. - */ - tile_diffs = copy_tiles_backward_pass(); - SCAN_FATAL(tile_diffs); - - if (tile_diffs > frac3 * ntiles) { - /* - * we spent a lot of time in those copy_tiles, run - * another scan, maybe more of the screen changed. - */ - int cp = (NSCAN - scan_count + 13) % NSCAN; - - scan_in_progress = 1; - tile_count = scan_display(scanlines[cp], 1); - SCAN_FATAL(tile_count); - scan_in_progress = 0; - - tile_diffs = copy_tiles_additional_pass(); - SCAN_FATAL(tile_diffs); - } - - /* Given enough tile diffs, try the islands: */ - if (grow_fill && tile_diffs > 4) { - tile_diffs = grow_islands(); - } - SCAN_FATAL(tile_diffs); - - /* Given enough tile diffs, try the gaps: */ - if (gaps_fill && tile_diffs > 4) { - tile_diffs = fill_tile_gaps(); - } - SCAN_FATAL(tile_diffs); - - fb_copy_in_progress = 0; - if (use_threads && pointer_mode != 1) { - /* - * tell the pointer handler it can process any queued - * pointer events: - */ - pointer(-1, 0, 0, NULL); - } - - if (blackouts) { - /* ignore any diffs in completely covered tiles */ - int x, y, n; - for (y=0; y < ntiles_y; y++) { - for (x=0; x < ntiles_x; x++) { - n = x + y * ntiles_x; - if (tile_blackout[n].cover == 2) { - tile_has_diff[n] = 0; - } - } - } - } - - hint_updates(); /* use x0rfbserver hints algorithm */ - - /* Work around threaded rfbProcessClientMessage() calls timeouts */ - if (use_threads) { - ping_clients(tile_diffs); - } - - - nap_check(tile_diffs); - return tile_diffs; -} - -Window tweak_tk_window_id(Window win) { - char *name = NULL; - Window parent, new; - - /* hack for tk, does not report outermost window */ - new = win; - parent = parent_window(win, &name); - if (parent && name != NULL) { - lowercase(name); - if (strstr(name, "wish") || strstr(name, "x11vnc")) { - new = parent; - rfbLog("tray_embed: using parent: %s\n", name); - } - } - if (name != NULL) { - XFree(name); - } - return new; -} - -#define SYSTEM_TRAY_REQUEST_DOCK 0 -#define SYSTEM_TRAY_BEGIN_MESSAGE 1 -#define SYSTEM_TRAY_CANCEL_MESSAGE 2 -#define XEMBED_VERSION 0 -#define XEMBED_MAPPED (1 << 0) - -int tray_embed(Window iconwin, int remove) { - XEvent ev; - XErrorHandler old_handler; - Window manager; - Atom xembed_info; - Atom tatom; - XWindowAttributes attr; - long info[2] = {XEMBED_VERSION, XEMBED_MAPPED}; - long data = 0; - - if (remove) { - if (!valid_window(iconwin, &attr, 1)) { - return 0; - } - iconwin = tweak_tk_window_id(iconwin); - trapped_xerror = 0; - old_handler = XSetErrorHandler(trap_xerror); - - /* - * unfortunately no desktops seem to obey this - * part of the XEMBED spec yet... - */ - XReparentWindow(dpy, iconwin, rootwin, 0, 0); - - XSetErrorHandler(old_handler); - if (trapped_xerror) { - trapped_xerror = 0; - return 0; - } - trapped_xerror = 0; - return 1; - } - - xembed_info = XInternAtom(dpy, "_XEMBED_INFO", False); - if (xembed_info == None) { - return 0; - } - - if (!tray_manager_running(dpy, &manager)) { - return 0; - } - - memset(&ev, 0, sizeof(ev)); - ev.xclient.type = ClientMessage; - ev.xclient.window = manager; - ev.xclient.message_type = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", - False); - ev.xclient.format = 32; - ev.xclient.data.l[0] = CurrentTime; - ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK; - ev.xclient.data.l[2] = iconwin; - ev.xclient.data.l[3] = 0; - ev.xclient.data.l[4] = 0; - - if (!valid_window(iconwin, &attr, 1)) { - return 0; - } - - iconwin = tweak_tk_window_id(iconwin); - ev.xclient.data.l[2] = iconwin; - - XUnmapWindow(dpy, iconwin); - - trapped_xerror = 0; - old_handler = XSetErrorHandler(trap_xerror); - - XSendEvent(dpy, manager, False, NoEventMask, &ev); - XSync(dpy, False); - - if (trapped_xerror) { - XSetErrorHandler(old_handler); - trapped_xerror = 0; - return 0; - } - - XChangeProperty(dpy, iconwin, xembed_info, xembed_info, 32, - PropModeReplace, (unsigned char *)&info, 2); - - /* kludge for KDE evidently needed... */ - tatom = XInternAtom(dpy, "KWM_DOCKWINDOW", False); - XChangeProperty(dpy, iconwin, tatom, tatom, 32, PropModeReplace, - (unsigned char *)&data, 1); - tatom = XInternAtom(dpy, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); - XChangeProperty(dpy, iconwin, tatom, XA_WINDOW, 32, PropModeReplace, - (unsigned char *)&data, 1); - - XSetErrorHandler(old_handler); - trapped_xerror = 0; - return 1; -} - -int tray_manager_running(Display *d, Window *manager) { - char tray_string[100]; - Atom tray_manager; - Window tray_win; - - if (manager) { - *manager = None; - } - sprintf(tray_string, "_NET_SYSTEM_TRAY_S%d", scr); - - tray_manager = XInternAtom(d, tray_string, True); - if (tray_manager == None) { - return 0; - } - - tray_win = XGetSelectionOwner(d, tray_manager); - if (manager) { - *manager = tray_win; - } - - if (tray_win == None) { - return 0; - } else { - return 1; - } -} - -/* -- gui.c -- */ -#ifdef NOGUI -char gui_code[] = ""; -#else -#include "tkx11vnc.h" -#endif - -void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int start_x11vnc, - int simple_gui, pid_t parent, char *gui_opts) { - char *x11vnc_xdisplay = NULL; - char extra_path[] = ":/usr/local/bin:/usr/bin/X11:/usr/sfw/bin" - ":/usr/X11R6/bin:/usr/openwin/bin:/usr/dt/bin"; - char cmd[100]; - char *wish = NULL, *orig_path, *full_path, *tpath, *p; - char *old_xauth = NULL; - int try_max = 4, sleep = 300; - pid_t mypid = getpid(); - FILE *pipe, *tmpf; - - if (*gui_code == '\0') { - rfbLog("gui: gui not compiled into this program.\n"); - exit(0); - } - if (getenv("DISPLAY") != NULL) { - /* worst case */ - x11vnc_xdisplay = strdup(getenv("DISPLAY")); - } - if (use_dpy) { - /* better */ - x11vnc_xdisplay = strdup(use_dpy); - } - if (connect_to_x11vnc) { - int rc, i; - rfbLogEnable(1); - if (! client_connect_file) { - if (getenv("XAUTHORITY") != NULL) { - old_xauth = strdup(getenv("XAUTHORITY")); - } else { - old_xauth = strdup(""); - } - dpy = XOpenDisplay(x11vnc_xdisplay); - if (! dpy && auth_file) { - set_env("XAUTHORITY", auth_file); - dpy = XOpenDisplay(x11vnc_xdisplay); - } - if (! dpy && ! x11vnc_xdisplay) { - /* worstest case */ - x11vnc_xdisplay = strdup(":0"); - dpy = XOpenDisplay(x11vnc_xdisplay); - } - if (! dpy) { - rfbLog("gui: could not open x11vnc " - "display: %s\n", NONUL(x11vnc_xdisplay)); - exit(1); - } - scr = DefaultScreen(dpy); - rootwin = RootWindow(dpy, scr); - initialize_vnc_connect_prop(); - } - usleep(2200*1000); - fprintf(stderr, "\n"); - if (!quiet) { - rfbLog("gui: trying to contact a x11vnc server at X" - " display %s ...\n", NONUL(x11vnc_xdisplay)); - } - for (i=0; i<try_max; i++) { - usleep(sleep*1000); - if (!quiet) { - rfbLog("gui: pinging %s try=%d ...\n", - NONUL(x11vnc_xdisplay), i+1); - } - rc = send_remote_cmd("qry=ping", 1, 1); - if (rc == 0) { - break; - } - if (parent && mypid != parent && kill(parent, 0) != 0) { - rfbLog("gui: parent process %d has gone" - " away: bailing out.\n", parent); - rc = 1; - break; - } - } - set_env("X11VNC_XDISPLAY", x11vnc_xdisplay); - if (getenv("XAUTHORITY") != NULL) { - set_env("X11VNC_AUTH_FILE", getenv("XAUTHORITY")); - } - if (rc == 0) { - rfbLog("gui: ping succeeded.\n"); - set_env("X11VNC_CONNECT", "1"); - } else { - rfbLog("gui: could not connect to: '%s', try" - " again manually.\n", x11vnc_xdisplay); - } - if (client_connect_file) { - set_env("X11VNC_CONNECT_FILE", client_connect_file); - } - if (dpy) { - XCloseDisplay(dpy); - dpy = NULL; - } - if (old_xauth) { - if (*old_xauth == '\0') { - /* wasn't set, hack it out if it is now */ - char *xauth = getenv("XAUTHORITY"); - if (xauth) { - *(xauth-2) = '_'; /* yow */ - } - } else { - set_env("XAUTHORITY", old_xauth); - } - free(old_xauth); - } - rfbLogEnable(0); - } - - orig_path = getenv("PATH"); - if (! orig_path) { - orig_path = strdup("/bin:/usr/bin:/usr/bin/X11"); - } - full_path = (char *) malloc(strlen(orig_path)+strlen(extra_path)+1); - strcpy(full_path, orig_path); - strcat(full_path, extra_path); - - tpath = strdup(full_path); - p = strtok(tpath, ":"); - - while (p) { - char *try; - struct stat sbuf; - char *wishes[] = {"wish", "wish8.3", "wish8.4", "wish8.5", - "wish8.0"}; - int nwishes = 3, i; - - try = (char *) malloc(strlen(p) + 1 + strlen("wish8.4") + 1); - for (i=0; i<nwishes; i++) { - sprintf(try, "%s/%s", p, wishes[i]); - if (stat(try, &sbuf) == 0) { - /* assume executable, should check mode */ - wish = wishes[i]; - } - } - free(try); - if (wish) { - break; - } - p = strtok(NULL, ":"); - } - free(tpath); - if (!wish) { - wish = strdup("wish"); - } - set_env("PATH", full_path); - set_env("DISPLAY", gui_xdisplay); - set_env("X11VNC_PROG", program_name); - set_env("X11VNC_CMDLINE", program_cmdline); - set_env("X11VNC_WISHCMD", wish); - if (simple_gui) { - set_env("X11VNC_SIMPLE_GUI", "1"); - } - if (gui_opts) { - set_env("X11VNC_GUI_PARAMS", gui_opts); - } - if (gui_geometry) { - set_env("X11VNC_GUI_GEOM", gui_geometry); - } - if (start_x11vnc) { - set_env("X11VNC_STARTED", "1"); - } - if (icon_mode) { - set_env("X11VNC_ICON_MODE", "1"); - if (icon_mode_file) { - set_env("X11VNC_CLIENT_FILE", icon_mode_file); - } - if (icon_in_tray) { - if (tray_manager_ok) { - set_env("X11VNC_ICON_MODE", "TRAY:RUNNING"); - } else { - set_env("X11VNC_ICON_MODE", "TRAY"); - } - } else { - set_env("X11VNC_ICON_MODE", "ICON"); - } - if (icon_mode_params) { - char *p, *str = strdup(icon_mode_params); - p = strtok(str, ":-/,.+"); - while (p) { - if(strstr(p, "setp") == p) { - set_env("X11VNC_ICON_SETPASS", "1"); - } else if(strstr(p, "noadvanced") == p) { - set_env("X11VNC_ICON_NOADVANCED", "1"); - } else if(strstr(p, "minimal") == p) { - set_env("X11VNC_ICON_MINIMAL", "1"); - } else if (strstr(p, "0x") == p) { - set_env("X11VNC_ICON_EMBED_ID", p); - icon_mode_embed_id = strdup(p); - } - p = strtok(NULL, ":-/,.+"); - } - free(str); - } - } - if (icon_mode_font) { - set_env("X11VNC_ICON_FONT", icon_mode_font); - } - - if (no_external_cmds) { - fprintf(stderr, "cannot run external commands in -nocmds " - "mode:\n"); - fprintf(stderr, " \"%s\"\n", "gui + wish"); - fprintf(stderr, " exiting.\n"); - fflush(stderr); - exit(1); - } - - tmpf = tmpfile(); - if (tmpf == NULL) { - /* if no tmpfile, use a pipe */ - if (icon_mode_embed_id) { - if (strlen(icon_mode_embed_id) < 20) { - strcat(cmd, " -use "); - strcat(cmd, icon_mode_embed_id); - } - } - pipe = popen(cmd, "w"); - if (! pipe) { - fprintf(stderr, "could not run: %s\n", cmd); - perror("popen"); - } - fprintf(pipe, "%s", gui_code); - pclose(pipe); - } else { - /* - * we prefer a tmpfile since then this x11vnc process - * will then be gone, otherwise the x11vnc program text - * will still be in use. - */ - int n = fileno(tmpf); - fprintf(tmpf, "%s", gui_code); - fflush(tmpf); - rewind(tmpf); - dup2(n, 0); - close(n); - if (icon_mode_embed_id) { - execlp(wish, wish, "-", "-use", icon_mode_embed_id, - (char *) NULL); - } else { - execlp(wish, wish, "-", (char *) NULL); - } - fprintf(stderr, "could not exec wish: %s -\n", wish); - perror("execlp"); - } - exit(0); -} - -void do_gui(char *opts, int sleep) { - char *s, *p; - char *old_xauth = NULL; - char *gui_xdisplay = NULL; - int got_gui_xdisplay = 0; - int start_x11vnc = 1; - int connect_to_x11vnc = 0; - int simple_gui = 0, none_gui = 0; - Display *test_dpy; - - if (opts) { - s = strdup(opts); - } else { - s = strdup(""); - } - - if (use_dpy) { - /* worst case */ - gui_xdisplay = strdup(use_dpy); - - } - if (getenv("DISPLAY") != NULL) { - /* better */ - gui_xdisplay = strdup(getenv("DISPLAY")); - } - - p = strtok(s, ","); - - while(p) { - if (*p == '\0') { - ; - } else if (strchr(p, ':') != NULL) { - /* best */ - if (gui_xdisplay) { - free(gui_xdisplay); - } - gui_xdisplay = strdup(p); - got_gui_xdisplay = 1; - } else if (!strcmp(p, "wait")) { - start_x11vnc = 0; - connect_to_x11vnc = 0; - } else if (!strcmp(p, "none")) { - none_gui = 1; - } else if (!strcmp(p, "conn") || !strcmp(p, "connect")) { - start_x11vnc = 0; - connect_to_x11vnc = 1; - } else if (!strcmp(p, "ez") || !strcmp(p, "simple")) { - simple_gui = 1; - } else if (strstr(p, "iconfont") == p) { - char *q; - if ((q = strchr(p, '=')) != NULL) { - icon_mode_font = strdup(q+1); - } - } else if (!strcmp(p, "full")) { - ; - } else if (strstr(p, "tray") == p || strstr(p, "icon") == p) { - char *q; - icon_mode = 1; - if ((q = strchr(p, '=')) != NULL) { - icon_mode_params = strdup(q+1); - if (strstr(icon_mode_params, "setp")) { - deny_all = 1; - } - } - if (strstr(p, "tray") == p) { - icon_in_tray = 1; - } - } else if (strstr(p, "geom") == p) { - char *q; - if ((q = strchr(p, '=')) != NULL) { - gui_geometry = strdup(q+1); - } - } else { - fprintf(stderr, "unrecognized gui opt: %s\n", p); - } - - p = strtok(NULL, ","); - } - free(s); - - if (none_gui) { - if (!start_x11vnc) { - exit(0); - } - return; - } - if (start_x11vnc) { - connect_to_x11vnc = 1; - } - - if (icon_mode && !got_gui_xdisplay) { - /* for tray mode, prefer the polled DISPLAY */ - if (use_dpy) { - if (gui_xdisplay) { - free(gui_xdisplay); - } - gui_xdisplay = strdup(use_dpy); - } - } - - if (! gui_xdisplay) { - fprintf(stderr, "error: cannot determine X DISPLAY for gui" - " to display on.\n"); - exit(1); - } - if (!quiet) { - fprintf(stderr, "starting gui, trying display: %s\n", - gui_xdisplay); - } - test_dpy = XOpenDisplay(gui_xdisplay); - if (! test_dpy && auth_file) { - if (getenv("XAUTHORITY") != NULL) { - old_xauth = strdup(getenv("XAUTHORITY")); - } - set_env("XAUTHORITY", auth_file); - test_dpy = XOpenDisplay(gui_xdisplay); - } - if (! test_dpy) { - if (! old_xauth && getenv("XAUTHORITY") != NULL) { - old_xauth = strdup(getenv("XAUTHORITY")); - } - set_env("XAUTHORITY", ""); - test_dpy = XOpenDisplay(gui_xdisplay); - } - if (! test_dpy) { - fprintf(stderr, "error: cannot connect to gui X DISPLAY: %s\n", - gui_xdisplay); - exit(1); - } - if (icon_mode && icon_in_tray) { - if (tray_manager_running(test_dpy, NULL)) { - tray_manager_ok = 1; - } else { - tray_manager_ok = 0; - } - } - XCloseDisplay(test_dpy); - - if (start_x11vnc) { - -#if LIBVNCSERVER_HAVE_FORK - /* fork into the background now */ - int p; - pid_t parent = getpid(); - - if (icon_mode) { - char tf[100]; - double dn = dnow(); - struct stat sbuf; - /* FIXME */ - dn = dn - ((int) dn); - sprintf(tf, "/tmp/x11vnc.tray%d%d", (int) (1000000*dn), - (int) getpid()); - unlink(tf); - /* race begins.. */ - if (stat(tf, &sbuf) == 0) { - icon_mode = 0; - } else { - icon_mode_fh = fopen(tf, "w"); - if (! icon_mode_fh) { - icon_mode = 0; - } else { - chmod(tf, 0400); - icon_mode_file = strdup(tf); - fprintf(icon_mode_fh, "none\n"); - fprintf(icon_mode_fh, "none\n"); - fflush(icon_mode_fh); - if (! got_connect_once) { - /* want -forever for tray */ - connect_once = 0; - } - } - } - } - - if ((p = fork()) > 0) { - ; /* parent */ - } else if (p == -1) { - fprintf(stderr, "could not fork\n"); - perror("fork"); - clean_up_exit(1); - } else { - if (sleep > 0) { - usleep(sleep * 1000 * 1000); - } - run_gui(gui_xdisplay, connect_to_x11vnc, start_x11vnc, - simple_gui, parent, opts); - exit(1); - } -#else - fprintf(stderr, "system does not support fork: start " - "x11vnc in the gui.\n"); - start_x11vnc = 0; -#endif - } - if (!start_x11vnc) { - run_gui(gui_xdisplay, connect_to_x11vnc, start_x11vnc, - simple_gui, 0, opts); - exit(1); - } - if (old_xauth) { - set_env("XAUTHORITY", old_xauth); - } -} - -/* -- userinput.c -- */ -/* - * 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) && name != NULL) { - int l = strlen(name); - if (written + l+2 < len) { - strcat(store, "^^"); - written += 2; - strcat(store, name); - written += l; - } else { - filled = 1; - } - XFree(name); - } - } - if (store && classhint && ! filled) { - classhint->res_name = NULL; - classhint->res_class = NULL; - if (XGetClassHint(dpy, clast, classhint)) { - 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, "##"); - if (classhint->res_class) { - strcat(store, - classhint->res_class); - } - strcat(store, "++"); - if (classhint->res_name) { - strcat(store, - classhint->res_name); - } - written += l+4; - } else { - filled = 1; - } - if (classhint->res_class) { - XFree(classhint->res_class); - } - if (classhint->res_name) { - XFree(classhint->res_name); - } - } - } - 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 *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); - - *frame = c; - - /* current pointer position is returned too */ - *px = rootx; - *py = rooty; - - if (!ret || ! c || c == rootwin) { - /* no immediate child */ - return 0; - } - - /* child window position and size */ - if (! valid_window(c, &attr, 1)) { - return 0; - } - - *x = attr.x; - *y = attr.y; - *w = attr.width; - *h = attr.height; - - if (win != NULL) { - *win = descend_pointer(5, c, NULL, 0); - } - - return 1; -} - -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; -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; - int i; - char *part[10]; - - for (i=0; i<10; i++) { - part[i] = NULL; - } - - if (scr == NULL || *scr == '\0') { - return; - } - - str = strdup(scr); - - p = strtok(str, ","); - i = 0; - while (p) { - part[i++] = strdup(p); - p = strtok(NULL, ","); - } - free(str); - - - /* - * Top, Bottom, Left, Right tolerances for scrollbar locations. - */ - if ((str = part[0]) != NULL) { - int t, b, l, r; - if (sscanf(str, "%d+%d+%d+%d", &t, &b, &l, &r) == 4) { - scrollcopyrect_top = t; - scrollcopyrect_bot = b; - scrollcopyrect_left = l; - scrollcopyrect_right = r; - } - free(str); - } - - /* key scrolling timing heuristics. */ - if ((str = part[1]) != NULL) { - 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, 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); - } -} - -void parse_scroll_copyrect(void) { - parse_scroll_copyrect_str(SCROLL_COPYRECT_PARMS); - if (! scroll_copyrect_str) { - scroll_copyrect_str = strdup(SCROLL_COPYRECT_PARMS); - } - parse_scroll_copyrect_str(scroll_copyrect_str); -} - -void parse_fixscreen(void) { - char *str, *p; - - screen_fixup_V = 0.0; - screen_fixup_C = 0.0; - screen_fixup_X = 0.0; - - if (! screen_fixup_str) { - return; - } - - str = strdup(screen_fixup_str); - - p = strtok(str, ","); - while (p) { - double t; - if (*p == 'V' && sscanf(p, "V=%lf", &t) == 1) { - screen_fixup_V = t; - } else if (*p == 'C' && sscanf(p, "C=%lf", &t) == 1) { - screen_fixup_C = t; - } else if (*p == 'X' && sscanf(p, "X=%lf", &t) == 1) { - screen_fixup_X = t; - } - p = strtok(NULL, ","); - } - free(str); - - if (screen_fixup_V < 0.0) screen_fixup_V = 0.0; - if (screen_fixup_C < 0.0) screen_fixup_C = 0.0; - if (screen_fixup_X < 0.0) screen_fixup_X = 0.0; -} - -/* -WIREFRAME_PARMS "0xff,2,0,30+6+6+6,Alt,0.05+0.3+2.0,8" -shade,linewidth,percent,T+B+L+R,mods,t1+t2+t3+t4 - */ -#define LW_MAX 8 -unsigned long wireframe_shade; -int wireframe_lw; -double wireframe_frac; -int wireframe_top, wireframe_bot, wireframe_left, wireframe_right; -double wireframe_t1, wireframe_t2, wireframe_t3, wireframe_t4; -char *wireframe_mods = NULL; - -/* - * Parse the gory -wireframe string for parameters. - */ -void parse_wireframe_str(char *wf) { - char *p, *str; - int i; - char *part[10]; - - for (i=0; i<10; i++) { - part[i] = NULL; - } - - if (wf == NULL || *wf == '\0') { - return; - } - - str = strdup(wf); - - /* leading ",", make it start with ignorable string "z" */ - if (*str == ',') { - char *tmp = (char *) malloc(strlen(str)+2); - strcpy(tmp, "z"); - strcat(tmp, str); - free(str); - str = tmp; - } - - p = strtok(str, ","); - i = 0; - while (p) { - part[i++] = strdup(p); - p = strtok(NULL, ","); - } - free(str); - - - /* Wireframe shade, color, RGB: */ - if ((str = part[0]) != NULL) { - unsigned long n; - int r, g, b, ok = 0; - XColor cdef; - Colormap cmap; - if (dpy && (bpp == 32 || bpp == 16)) { - cmap = DefaultColormap (dpy, scr); - if (XParseColor(dpy, cmap, str, &cdef) && - XAllocColor(dpy, cmap, &cdef)) { - r = cdef.red >> 8; - g = cdef.green >> 8; - b = cdef.blue >> 8; - if (r == 0 && g == 0) { - g = 1; /* need to be > 255 */ - } - n = 0; - n |= (r << main_red_shift); - n |= (g << main_green_shift); - n |= (b << main_blue_shift); - wireframe_shade = n; - ok = 1; - } - } - if (ok) { - ; - } else if (sscanf(str, "0x%lx", &n) == 1) { - wireframe_shade = n; - } else if (sscanf(str, "%lu", &n) == 1) { - wireframe_shade = n; - } else if (sscanf(str, "%lx", &n) == 1) { - wireframe_shade = n; - } - free(str); - } - - /* linewidth: # of pixels wide for the wireframe lines */ - if ((str = part[1]) != NULL) { - int n; - if (sscanf(str, "%d", &n) == 1) { - wireframe_lw = n; - if (wireframe_lw < 1) { - wireframe_lw = 1; - } - if (wireframe_lw > LW_MAX) { - wireframe_lw = LW_MAX; - } - } - free(str); - } - - /* percentage cutoff for opaque move/resize (like WM's) */ - if ((str = part[2]) != NULL) { - if (*str == '\0') { - ; - } else if (strchr(str, '.')) { - wireframe_frac = atof(str); - } else { - wireframe_frac = ((double) atoi(str))/100.0; - } - free(str); - } - - /* - * Top, Bottom, Left, Right tolerances to guess the wm frame is - * being grabbed (Top is traditionally bigger, i.e. titlebar): - */ - if ((str = part[3]) != NULL) { - int t, b, l, r; - if (sscanf(str, "%d+%d+%d+%d", &t, &b, &l, &r) == 4) { - wireframe_top = t; - wireframe_bot = b; - wireframe_left = l; - wireframe_right = r; - } - free(str); - } - - /* - * wireframe in interior with Modifier down. - * 0 => no mods - * 1 => all mods - * Shift,Alt,Control,Meta,Super,Hyper - */ - if (wireframe_mods) { - free(wireframe_mods); - } - wireframe_mods = NULL; - if ((str = part[4]) != NULL) { - if (*str == '0' || !strcmp(str, "none")) { - ; - } else if (*str == '1' || !strcmp(str, "all")) { - wireframe_mods = strdup("all"); - } else if (!strcmp(str, "Alt") || !strcmp(str, "Shift") - || !strcmp(str, "Control") || !strcmp(str, "Meta") - || !strcmp(str, "Super") || !strcmp(str, "Hyper")) { - wireframe_mods = strdup(str); - } - } - - /* check_wireframe() timing heuristics. */ - if ((str = part[5]) != NULL) { - double t1, t2, t3, t4; - if (sscanf(str, "%lf+%lf+%lf+%lf", &t1, &t2, &t3, &t4) == 4) { - wireframe_t1 = t1; - wireframe_t2 = t2; - wireframe_t3 = t3; - wireframe_t4 = t4; - } - free(str); - } -} - -/* - * First parse the defaults and apply any user supplied ones (may be a subset) - */ -void parse_wireframe(void) { - parse_wireframe_str(WIREFRAME_PARMS); - if (! wireframe_str) { - wireframe_str = strdup(WIREFRAME_PARMS); - } - parse_wireframe_str(wireframe_str); -} - -/* - * Set wireframe_copyrect based on desired mode. - */ -void set_wirecopyrect_mode(char *str) { - char *orig = wireframe_copyrect; - if (str == NULL || *str == '\0') { - wireframe_copyrect = strdup(wireframe_copyrect_default); - } else if (!strcmp(str, "always") || !strcmp(str, "all")) { - wireframe_copyrect = strdup("always"); - } else if (!strcmp(str, "top")) { - wireframe_copyrect = strdup("top"); - } else if (!strcmp(str, "never") || !strcmp(str, "none")) { - wireframe_copyrect = strdup("never"); - } else { - if (! wireframe_copyrect) { - wireframe_copyrect = strdup(wireframe_copyrect_default); - } else { - orig = NULL; - } - rfbLog("unknown -wirecopyrect mode: %s, using: %s\n", str, - wireframe_copyrect); - } - if (orig) { - free(orig); - } -} - -/* - * Set scroll_copyrect based on desired mode. - */ -void set_scrollcopyrect_mode(char *str) { - char *orig = scroll_copyrect; - if (str == NULL || *str == '\0') { - scroll_copyrect = strdup(scroll_copyrect_default); - } else if (!strcmp(str, "always") || !strcmp(str, "all") || - !strcmp(str, "both")) { - scroll_copyrect = strdup("always"); - } else if (!strcmp(str, "keys") || !strcmp(str, "keyboard")) { - scroll_copyrect = strdup("keys"); - } else if (!strcmp(str, "mouse") || !strcmp(str, "pointer")) { - scroll_copyrect = strdup("mouse"); - } else if (!strcmp(str, "never") || !strcmp(str, "none")) { - scroll_copyrect = strdup("never"); - } else { - if (! scroll_copyrect) { - scroll_copyrect = strdup(scroll_copyrect_default); - } else { - orig = NULL; - } - rfbLog("unknown -scrollcopyrect mode: %s, using: %s\n", str, - scroll_copyrect); - } - if (orig) { - free(orig); - } -} - -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 { - unsigned int in; - if (sscanf(p, "%u", &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); - } - } -} - -void initialize_scroll_term(void) { - char *str; - int n; - - destroy_str_list(scroll_term); - scroll_term = NULL; - - if (scroll_term_str != NULL && *scroll_term_str != '\0') { - str = scroll_term_str; - } else { - str = scroll_term_str0; - } - if (!strcmp(str, "none")) { - return; - } - scroll_term = create_str_list(str); - - n = 0; - while (scroll_term[n] != NULL) { - char *s = scroll_good_all[n++]; - /* pull parameters out at some point */ - s = NULL; - } -} - -void initialize_max_keyrepeat(void) { - char *str; - int lo, hi; - - if (max_keyrepeat_str != NULL && *max_keyrepeat_str != '\0') { - str = max_keyrepeat_str; - } else { - str = max_keyrepeat_str0; - } - - if (sscanf(str, "%d-%d", &lo, &hi) != 2) { - rfbLog("skipping invalid -scr_keyrepeat string: %s\n", str); - sscanf(max_keyrepeat_str0, "%d-%d", &lo, &hi); - } - max_keyrepeat_lo = lo; - max_keyrepeat_hi = hi; - if (max_keyrepeat_lo < 1) { - max_keyrepeat_lo = 1; - } - if (max_keyrepeat_hi > 40) { - max_keyrepeat_hi = 40; - } -} - -typedef struct saveline { - int x0, y0, x1, y1; - int shift; - int vert; - int saved; - char *data; -} saveline_t; - -/* - * Draw the wireframe box onto the framebuffer. Saves the real - * framebuffer data to some storage lines. Restores previous lines. - * use restore = 1 to clean up (done with animation). - * This works with -scale. - */ -void draw_box(int x, int y, int w, int h, int restore) { - int x0, y0, x1, y1, i, pixelsize = bpp/8; - char *dst, *src; - static saveline_t *save[4]; - static int first = 1, len = 0; - int max = dpy_x > dpy_y ? dpy_x : dpy_y; - int sz, lw = wireframe_lw; - unsigned long shade = wireframe_shade; - int color = 0; - unsigned short us; - unsigned long ul; - - if (clipshift) { - x -= coff_x; - y -= coff_y; - } - /* no subwin for wireframe */ - - if (max > len) { - /* create/resize storage lines: */ - for (i=0; i<4; i++) { - len = max; - if (! first && save[i]) { - if (save[i]->data) { - free(save[i]->data); - save[i]->data = NULL; - } - free(save[i]); - } - save[i] = (saveline_t *) malloc(sizeof(saveline_t)); - save[i]->saved = 0; - sz = (LW_MAX+1)*len*pixelsize; - save[i]->data = (char *) malloc(sz); - - /* - * Four types of lines: - * 0) top horizontal - * 1) bottom horizontal - * 2) left vertical - * 3) right vertical - * - * shift means shifted by width or height. - */ - if (i == 0) { - save[i]->vert = 0; - save[i]->shift = 0; - } else if (i == 1) { - save[i]->vert = 0; - save[i]->shift = 1; - } else if (i == 2) { - save[i]->vert = 1; - save[i]->shift = 0; - } else if (i == 3) { - save[i]->vert = 1; - save[i]->shift = 1; - } - } - } - first = 0; - - /* - * restore any saved lines. see below for algorithm and - * how x0, etc. are used. we just reverse those steps. - */ - for (i=0; i<4; i++) { - int s = save[i]->shift; - int yu, y_min = -1, y_max = -1; - int y_start, y_stop, y_step; - - if (! save[i]->saved) { - continue; - } - x0 = save[i]->x0; - y0 = save[i]->y0; - x1 = save[i]->x1; - y1 = save[i]->y1; - if (save[i]->vert) { - y_start = y0+lw; - y_stop = y1-lw; - y_step = lw*pixelsize; - } else { - y_start = y0 - s*lw; - y_stop = y_start + lw; - y_step = max*pixelsize; - } - for (yu = y_start; yu < y_stop; yu++) { - if (x0 == x1) { - continue; - } - if (yu < 0 || yu >= dpy_y) { - continue; - } - if (y_min < 0 || yu < y_min) { - y_min = yu; - } - if (y_max < 0 || yu > y_max) { - y_max = yu; - } - src = save[i]->data + (yu-y_start)*y_step; - dst = main_fb + yu*main_bytes_per_line + - x0*pixelsize; - memcpy(dst, src, (x1-x0)*pixelsize); - } - if (y_min >= 0) { - mark_rect_as_modified(x0, y_min, x1, y_max+1, 0); - } - save[i]->saved = 0; - } - - if (restore) { - return; - } - -if (0) fprintf(stderr, " DrawBox: %dx%d+%d+%d\n", w, h, x, y); - - /* - * work out shade/color for the wireframe line, could be a color - * for 16bpp or 24bpp. - */ - if (shade > 255) { - if (pixelsize == 2) { - us = (unsigned short) (shade & 0xffff); - color = 1; - } else if (pixelsize == 4) { - ul = (unsigned long) shade; - color = 1; - } else { - shade = shade % 256; - } - } - - for (i=0; i<4; i++) { - int s = save[i]->shift; - int yu, y_min = -1, y_max = -1; - int yblack = -1, xblack1 = -1, xblack2 = -1; - int y_start, y_stop, y_step; - - if (save[i]->vert) { - /* - * make the narrow x's be on the screen, let - * the y's hang off (not drawn). - */ - save[i]->x0 = x0 = nfix(x + s*w - s*lw, dpy_x); - save[i]->y0 = y0 = y; - save[i]->x1 = x1 = nfix(x + s*w - s*lw + lw, dpy_x); - save[i]->y1 = y1 = y + h; - - /* - * start and stop a linewidth away from true edge, - * to avoid interfering with horizontal lines. - */ - y_start = y0+lw; - y_stop = y1-lw; - y_step = lw*pixelsize; - - /* draw a black pixel for the border if lw > 1 */ - if (s) { - xblack1 = x1-1; - } else { - xblack1 = x0; - } - } else { - /* - * make the wide x's be on the screen, let the y's - * hang off (not drawn). - */ - save[i]->x0 = x0 = nfix(x, dpy_x); - save[i]->y0 = y0 = y + s*h; - save[i]->x1 = x1 = nfix(x + w, dpy_x); - save[i]->y1 = y1 = y0 + lw; - y_start = y0 - s*lw; - y_stop = y_start + lw; - y_step = max*pixelsize; - - /* draw a black pixels for the border if lw > 1 */ - if (s) { - yblack = y_stop - 1; - } else { - yblack = y_start; - } - xblack1 = x0; - xblack2 = x1-1; - } - - /* now loop over the allowed y's for either case */ - for (yu = y_start; yu < y_stop; yu++) { - if (x0 == x1) { - continue; - } - if (yu < 0 || yu >= dpy_y) { - continue; - } - - /* record min and max y's for marking rectangle: */ - if (y_min < 0 || yu < y_min) { - y_min = yu; - } - if (y_max < 0 || yu > y_max) { - y_max = yu; - } - - /* save fb data for this line: */ - save[i]->saved = 1; - src = main_fb + yu*main_bytes_per_line + - x0*pixelsize; - dst = save[i]->data + (yu-y_start)*y_step; - memcpy(dst, src, (x1-x0)*pixelsize); - - /* apply the shade/color to make the wireframe line: */ - if (! color) { - memset(src, shade, (x1-x0)*pixelsize); - } else { - char *csrc = src; - unsigned short *usp; - unsigned long *ulp; - int k; - for (k=0; k < x1 - x0; k++) { - if (pixelsize == 4) { - ulp = (unsigned long *)csrc; - *ulp = ul; - } else if (pixelsize == 2) { - usp = (unsigned short *)csrc; - *usp = us; - } - csrc += pixelsize; - } - } - - /* apply black border for lw >= 2 */ - if (lw > 1) { - if (yu == yblack) { - memset(src, 0, (x1-x0)*pixelsize); - } - if (xblack1 >= 0) { - src = src + (xblack1 - x0)*pixelsize; - memset(src, 0, pixelsize); - } - if (xblack2 >= 0) { - src = src + (xblack2 - x0)*pixelsize; - memset(src, 0, pixelsize); - } - } - } - /* mark it for sending: */ - if (save[i]->saved) { - mark_rect_as_modified(x0, y_min, x1, y_max+1, 0); - } - } -} - -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 = 2; - double tm; - int db = 0; - -if (db) dtime0(&tm); - - x1 = nfix(x1, dpy_x); - y1 = nfix(y1, dpy_y); - x2 = nfix(x2, dpy_x+1); - y2 = nfix(y2, dpy_y+1); - - if (x1 == x2) { - return 1; - } - if (y1 == y2) { - return 1; - } - - X_LOCK; - for (y = y1; y < y2; y++) { - XRANDR_SET_TRAP_RET(0, "direct_fb_copy-set"); - copy_image(scanline, x1, y, x2 - x1, 1); - XRANDR_CHK_TRAP_RET(0, "direct_fb_copy-chk"); - - src = scanline->data; - dst = main_fb + y * main_bytes_per_line + x1 * pixelsize; - - if (do_cmp == 0 || !mark) { - memcpy(dst, src, (x2 - x1)*pixelsize); - - } else if (do_cmp == 1) { - if (memcmp(dst, src, (x2 - x1)*pixelsize)) { - if (ymin == -1 || y < ymin) { - ymin = y; - } - if (ymax == -1 || y > ymax) { - ymax = y; - } - memcpy(dst, src, (x2 - x1)*pixelsize); - } - - } else if (do_cmp == 2) { - int n, shift, xlo, xhi, k, block = 32; - char *dst2, *src2; - - for (k=0; k*block < (x2 - x1); k++) { - shift = k*block; - xlo = x1 + shift; - xhi = xlo + block; - if (xhi > x2) { - xhi = x2; - } - n = xhi - xlo; - if (n < 1) { - continue; - } - src2 = src + shift*pixelsize; - dst2 = dst + shift*pixelsize; - if (memcmp(dst2, src2, n*pixelsize)) { - if (ymin == -1 || y < ymin) { - ymin = y; - } - if (ymax == -1 || y > ymax) { - ymax = y; - } - if (xmin == -1 || xlo < xmin) { - xmin = xlo; - } - if (xmax == -1 || xhi > xmax) { - xmax = xhi; - } - memcpy(dst2, src2, n*pixelsize); - } - } - } - } - X_UNLOCK; - - if (do_cmp == 0) { - xmin = x1; - ymin = y1; - xmax = x2; - ymax = y2; - } else if (do_cmp == 1) { - xmin = x1; - xmax = x2; - } - - if (xmin < 0 || ymin < 0 || xmax < 0 || xmin < 0) { - /* no diffs */ - return 1; - } - - if (xmax < x2) { - xmax++; - } - if (ymax < y2) { - ymax++; - } - - if (mark) { - mark_rect_as_modified(xmin, ymin, xmax, ymax, 0); - } - -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); - if (clipshift) { - sraRgnOffset(whole, coff_x, coff_y); - } - if (subwin) { - sraRgnOffset(whole, off_x, off_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; - } - } - if (clipshift) { - tx1 -= coff_x; - ty1 -= coff_y; - tx2 -= coff_x; - ty2 -= coff_y; - } - if (subwin) { - tx1 -= off_x; - ty1 -= off_y; - tx2 -= off_x; - ty2 -= off_y; - } - - 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; -} - -int set_ypad(void) { - int ev, ev_tot = scr_ev_cnt; - static Window last_win = None; - static double last_time = 0.0; - static int y_accum = 0, last_sign = 0; - double now, cut = 0.1; - int dy_sum = 0, ys = 0, sign; - int font_size = 15; - int win_y, scr_y, loc_cut = 4*font_size, y_cut = 10*font_size; - - if (!xrecord_set_by_keys || !xrecord_name_info) { - return 0; - } - if (xrecord_name_info[0] == '\0') { - return 0; - } - if (! ev_tot) { - return 0; - } - if (xrecord_keysym == NoSymbol) { - return 0; - } - if (!xrecord_scroll_keysym(xrecord_keysym)) { - return 0; - } - if (!scroll_term) { - return 0; - } - if (!match_str_list(xrecord_name_info, scroll_term)) { - return 0; - } - - for (ev=0; ev < ev_tot; ev++) { - dy_sum += nabs(scr_ev[ev].dy); - if (scr_ev[ev].dy < 0) { - ys--; - } else if (scr_ev[ev].dy > 0) { - ys++; - } else { - ys = 0; - break; - } - if (scr_ev[ev].win != scr_ev[0].win) { - ys = 0; - break; - } - if (scr_ev[ev].dx != 0) { - ys = 0; - break; - } - } - if (ys != ev_tot && ys != -ev_tot) { - return 0; - } - if (ys < 0) { - sign = -1; - } else { - sign = 1; - } - - if (sign > 0) { - /* - * this case is not as useful as scrolling near the - * bottom of a terminal. But there are problems for it too. - */ - return 0; - } - - win_y = scr_ev[0].win_y + scr_ev[0].win_h; - scr_y = scr_ev[0].y + scr_ev[0].h; - if (nabs(scr_y - win_y) > loc_cut) { - /* require it to be near the bottom. */ - return 0; - } - - now = dnow(); - - if (now < last_time + cut) { - int ok = 1; - if (last_win && scr_ev[0].win != last_win) { - ok = 0; - } - if (last_sign && sign != last_sign) { - ok = 0; - } - if (! ok) { - last_win = None; - last_sign = 0; - y_accum = 0; - last_time = 0.0; - return 0; - } - } else { - last_win = None; - last_sign = 0; - last_time = 0.0; - y_accum = 0; - } - - y_accum += sign * dy_sum; - - if (4 * nabs(y_accum) > scr_ev[0].h && y_cut) { - ; /* TBD */ - } - - last_sign = sign; - last_win = scr_ev[0].win; - last_time = now; - - return y_accum; -} - -void scale_mark(int x1, int y1, int x2, int y2) { - int s = 2; - x1 = nfix(x1 - s, dpy_x); - y1 = nfix(y1 - s, dpy_y); - x2 = nfix(x2 + s, dpy_x+1); - y2 = nfix(y2 + s, dpy_y+1); - scale_and_mark_rect(x1, y1, x2, y2); -} - -#define PUSH_TEST(n) \ -if (n) { \ - 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 *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, do_fb_push = 0, obscured; - int ev, ev_tot = scr_ev_cnt; - double tm, dt, st, waittime = 0.125; - double max_age = *age; - int db = debug_scroll, rrate = get_read_rate(); - sraRegionPtr backfill, whole, tmpregion, tmpregion2; - int link, latency, netrate; - int ypad = 0; - double last_scroll_event_save = last_scroll_event; - - /* we return the oldest one. */ - *age = 0.0; - - 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; - } - - backfill = sraRgnCreate(); - whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); - if (clipshift) { - sraRgnOffset(whole, coff_x, coff_y); - } - if (subwin) { - sraRgnOffset(whole, off_x, off_y); - } - - win0 = scr_ev[0].win; - x0 = scr_ev[0].win_x; - y0 = scr_ev[0].win_y; - w0 = scr_ev[0].win_w; - h0 = scr_ev[0].win_h; - - ypad = set_ypad(); - -if (db) fprintf(stderr, "ypad: %d dy[0]: %d\n", ypad, scr_ev[0].dy); - - for (ev=0; ev < ev_tot; ev++) { - double ag; - - x = scr_ev[ev].x; - y = scr_ev[ev].y; - w = scr_ev[ev].w; - h = scr_ev[ev].h; - dx = scr_ev[ev].dx; - dy = scr_ev[ev].dy; - win = scr_ev[ev].win; - wx = scr_ev[ev].win_x; - wy = scr_ev[ev].win_y; - ww = scr_ev[ev].win_w; - wh = scr_ev[ev].win_h; - nx = scr_ev[ev].new_x; - ny = scr_ev[ev].new_y; - nw = scr_ev[ev].new_w; - nh = scr_ev[ev].new_h; - st = scr_ev[ev].t; - - ag = (dnow() - servertime_diff) - st; - if (ag > *age) { - *age = ag; - } - - if (dabs(ag) > max_age) { -if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f :: (%.4f - %.4f) " - "- %.4f \n", ag, dnow(), servertime_diff, st); - dret = 0; - break; - } else { -if (db) fprintf(stderr, "push_scr_ev: AGE: %.4f\n", ag); - } - if (win != win0) { -if (db) fprintf(stderr, "push_scr_ev: DIFF WIN: 0x%lx != 0x%lx\n", win, win0); - dret = 0; - break; - } - if (wx != x0 || wy != y0) { -if (db) fprintf(stderr, "push_scr_ev: WIN SHIFT: %d %d, %d %d", wx, x0, wy, y0); - dret = 0; - break; - } - if (ww != w0 || wh != h0) { -if (db) fprintf(stderr, "push_scr_ev: WIN RESIZE: %d %d, %d %d", ww, w0, wh, h0); - dret = 0; - break; - } - if (w < 1 || h < 1 || ww < 1 || wh < 1) { -if (db) fprintf(stderr, "push_scr_ev: NEGATIVE h/w: %d %d %d %d\n", w, h, ww, wh); - dret = 0; - break; - } - -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 > 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 > 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) { - frame = xrecord_wm_window; - } - if (! frame) { - X_LOCK; - frame = query_pointer(rootwin); - X_UNLOCK; - } - if (! frame) { - frame = win; - } - - dtime0(&tm); - - tmpregion = sraRgnCreateRect(0, 0, dpy_x, dpy_y); - if (clipshift) { - sraRgnOffset(tmpregion, coff_x, coff_y); - } - if (subwin) { - sraRgnOffset(tmpregion, off_x, off_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 (ypad) { - if (ypad < 0) { - if (h > -ypad) { - h += ypad; - } else { - ypad = 0; - } - } else { - if (h > ypad) { - y += ypad; - } else { - ypad = 0; - } - } - } - - 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); - sraRgnAnd(backfill, whole); - } - - if (ypad) { - if (ypad < 0) { - ny += ypad; - nh -= ypad; - } else { - ; - } - } - - tmpregion = sraRgnCreateRect(nx, ny, nx + nw, ny + nh); - sraRgnAnd(tmpregion, whole); - sraRgnOr(backfill, tmpregion); - 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; - - tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0); - sraRgnAnd(tmpregion, whole); - - sraRgnAnd(backfill, tmpregion); - - 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); - sraRgnDestroy(tmpregion); - - - iter = sraRgnGetIterator(backfill); - while (sraRgnIteratorNext(iter, &rect)) { - tx1 = rect.x1; - ty1 = rect.y1; - tx2 = rect.x2; - ty2 = rect.y2; - - area += (tx2 - tx1)*(ty2 - ty1); - } - sraRgnReleaseIterator(iter); - - 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.90 * win_area) { -if (db) fprintf(stderr, " AREA_TOO_MUCH"); - dret = 0; - } else if (est > 0.6) { -if (db) fprintf(stderr, " EST_TOO_LARGE"); - dret = 0; - } else if (area <= 0.0) { - ; - } else { - dtime0(&tm); - iter = sraRgnGetIterator(backfill); - while (sraRgnIteratorNext(iter, &rect)) { - tx1 = rect.x1; - ty1 = rect.y1; - tx2 = rect.x2; - ty2 = rect.y2; - - if (clipshift) { - tx1 -= coff_x; - ty1 -= coff_y; - tx2 -= coff_x; - ty2 -= coff_y; - } - if (subwin) { - tx1 -= off_x; - ty1 -= off_y; - tx2 -= off_x; - ty2 -= off_y; - } - tx1 = nfix(tx1, dpy_x); - ty1 = nfix(ty1, dpy_y); - tx2 = nfix(tx2, dpy_x+1); - ty2 = nfix(ty2, dpy_y+1); - - dtime(&tm); -if (db) fprintf(stderr, " DFC(%d,%d-%d,%d)", tx1, ty1, tx2, ty2); - direct_fb_copy(tx1, ty1, tx2, ty2, 1); - do_fb_push++; -PUSH_TEST(0); - } - sraRgnReleaseIterator(iter); - dt = dtime(&tm); -if (db) fprintf(stderr, " dfc---- dt: %.4f", dt); - - } -if (db && dret) fprintf(stderr, " **** dret=%d", dret); -if (db && !dret) fprintf(stderr, " ---- dret=%d", dret); -if (db) fprintf(stderr, "\n"); - } - -if (db && bdpush) fprintf(stderr, "BDPUSH-TIME: 0x%lx\n", xrecord_wm_window); - - 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 (do_fb_push) { - dtime0(&tm); - fb_push(); - dt = dtime(&tm); -if (0) fprintf(stderr, " fb_push dt: %.4f", dt); - if (scaling) { - static double last_time = 0.0; - double now = dnow(), delay = 0.4, first_wait = 3.0; - double trate; - int repeating, lat, rate; - int link = link_rate(&lat, &rate); - int skip_first = 0; - - if (link == LR_DIALUP || rate < 35) { - delay *= 4; - } else if (link != LR_LAN || rate < 100) { - delay *= 2; - } - - trate = typing_rate(0.0, &repeating); - - if (xrecord_set_by_mouse || repeating >= 3) { - if (now > last_scroll_event_save + first_wait) { - skip_first = 1; - } - } - - if (skip_first) { - /* - * try not to send the first one, but a - * single keystroke scroll would be OK. - */ - } else if (now > last_time + delay) { - - scale_mark(x0, y0, x0 + w0, y0 + h0); - last_copyrect_fix = now; - } - last_time = now; - } - } - - 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 - * only approximate. - */ -void do_copyregion(sraRegionPtr region, int dx, int dy) { - sraRectangleIterator *iter; - sraRect rect; - 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; - - last_copyrect = dnow(); - - 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 = sraRgnGetReverseIterator(region, dx < 0, dy < 0); - while(sraRgnIteratorNext(iter, &rect)) { - int j; - - x1 = rect.x1; - y1 = rect.y1; - x2 = rect.x2; - y2 = rect.y2; - - w = (x2 - x1)*Bpp; - dst = main_fb + y1*stride + x1*Bpp; - src = main_fb + (y1-dy)*stride + (x1-dx)*Bpp; - - if (dy < 0) { - for (j=y1; j<y2; j++) { - memmove(dst, src, w); - dst += stride; - src += stride; - } - } else { - dst += (y2 - y1 - 1)*stride; - src += (y2 - y1 - 1)*stride; - for (j=y2-1; j>=y1; j--) { - memmove(dst, src, w); - dst -= stride; - src -= stride; - } - } - - sx1 = ((double) x1 / dpy_x) * scaled_x; - sy1 = ((double) y1 / dpy_y) * scaled_y; - sx2 = ((double) x2 / dpy_x) * scaled_x; - sy2 = ((double) y2 / dpy_y) * scaled_y; - sdx = ((double) dx / dpy_x) * scaled_x; - sdy = ((double) dy / dpy_y) * scaled_y; - - rfbDoCopyRect(screen, sx1, sy1, sx2, sy2, sdx, sdy); - } - sraRgnReleaseIterator(iter); -} - -void fb_push_wait(double max_wait, int flags) { - double tm, dt = 0.0; - int req, mod, cpy, ncli; - - dtime0(&tm); - while (dt < max_wait) { - int done = 1; - rfbCFD(0); - get_client_regions(&req, &mod, &cpy, &ncli); - if (flags & FB_COPY && cpy) { - done = 0; - } - if (flags & FB_MOD && mod) { - done = 0; - } - if (flags & FB_REQ && req) { - done = 0; - } - if (done) { - break; - } - - usleep(1000); - fb_push(); - dt += dtime(&tm); - } -} - -void fb_push(void) { - char *httpdir = screen->httpDir; - int defer = screen->deferUpdateTime; - int req0, mod0, cpy0, req1, mod1, cpy1, ncli; - int db = (debug_scroll || debug_wireframe); - - screen->httpDir = NULL; - screen->deferUpdateTime = 0; - -if (db) get_client_regions(&req0, &mod0, &cpy0, &ncli); - - rfbPE(0); - - screen->httpDir = httpdir; - screen->deferUpdateTime = defer; - -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); -} - -} - -/* - * utility routine for CopyRect of the window (but not CopyRegion) - */ -int crfix(int x, int dx, int Lx) { - /* adjust x so that copy source is on screen */ - if (dx > 0) { - if (x-dx < 0) { - /* off on the left */ - x = dx; - } - } else { - if (x-dx >= Lx) { - /* off on the right */ - x = Lx + dx - 1; - } - } - return x; -} - -typedef struct scroll_result { - Window win; - double time; - int result; -} scroll_result_t; - -#define SCR_RESULTS_MAX 256 -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; - static int first = 1; - - if (first) { - 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 (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; - } - if (scroll_results[i].win == None) { - break; - } - } - 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 (next_index >= 0 && scroll_results[i].win == None) { - 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].result = set; - } - scroll_results[index].time = dnow(); - } - - return set; -} - -void eat_viewonly_input(int max_eat, int keep) { - int i, gp, gk; - - for (i=0; i<max_eat; i++) { - int cont = 0; - gp = got_pointer_calls; - gk = got_keyboard_calls; - rfbCFD(0); - if (got_pointer_calls > gp) { - if (debug_pointer) { - rfbLog("eat_viewonly_input: pointer: %d\n", i); - } - cont++; - } - if (got_keyboard_calls > gk) { - if (debug_keyboard) { - rfbLog("eat_viewonly_input: keyboard: %d\n", i); - } - cont++; - } - if (i >= keep - 1 && ! cont) { - break; - } - } -} - -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+1); - ty2 = nfix(y + h, dpy_y+1); - - tmpregion = sraRgnCreateRect(tx1, ty1, tx2, ty2); - add_region_xdamage(tmpregion); - sraRgnDestroy(tmpregion); -} - -void mark_region_for_xdamage(sraRegionPtr region) { - sraRectangleIterator *iter; - sraRect rect; - iter = sraRgnGetIterator(region); - while (sraRgnIteratorNext(iter, &rect)) { - int x1 = rect.x1; - int y1 = rect.y1; - int x2 = rect.x2; - int y2 = rect.y2; - mark_for_xdamage(x1, y1, x2 - x1, y2 - y1); - } - sraRgnReleaseIterator(iter); -} - -void set_xdamage_mark(int x, int y, int w, int h) { - sraRegionPtr region; - - if (! use_xdamage) { - return; - } - mark_for_xdamage(x, y, w, h); - - if (xdamage_scheduled_mark == 0.0) { - xdamage_scheduled_mark = dnow() + 2.0; - } - - if (xdamage_scheduled_mark_region == NULL) { - xdamage_scheduled_mark_region = sraRgnCreate(); - } - region = sraRgnCreateRect(x, y, x + w, y + w); - sraRgnOr(xdamage_scheduled_mark_region, region); - sraRgnDestroy(region); -} - -int repeat_check(double last_key_scroll) { - int repeating; - double rate = typing_rate(0.0, &repeating); - double now = dnow(), delay = 0.5; - if (rate > 2.0 && repeating && now > last_key_scroll + delay) { - return 0; - } else { - return 1; - } -} - -int check_xrecord_keys(void) { - 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; - static double last_bdpush = 0.0; - static int persist_count = 0; - int scroll_keysym = 0; - double last_scroll, scroll_persist = scr_key_persist; - double spin_fac = 1.0, scroll_fac = 2.0, noscroll_fac = 0.75; - double max_spin, max_long_spin = 0.3; - double set_repeat_in; - static double set_repeat = 0.0; - - set_repeat_in = set_repeat; - set_repeat = 0.0; - - get_out = 1; - if (got_keyboard_input) { - get_out = 0; - } - - dtime0(&tnow); - if (tnow < last_key_scroll + scroll_persist) { - get_out = 0; - } - - if (set_repeat_in > 0.0 && tnow < last_key_scroll + set_repeat_in) { - get_out = 0; - } - - if (get_out) { - persist_start = 0.0; - persist_count = 0; - last_bdpush = 0.0; - if (xrecording) { - xrecord_watch(0, SCR_KEY); - } - return 0; - } - -#if 0 - /* not used for keyboard yet */ - scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1; - if (scroll_rep == 1) { - scroll_rep = 2; /* if no info, assume the best. */ - } -#endif - - scroll_keysym = xrecord_scroll_keysym(last_rfb_keysym); - - max_spin = scr_key_time; - - if (set_repeat_in > 0.0 && tnow < last_key_scroll + 2*set_repeat_in) { - max_spin = 2 * set_repeat_in; - } else if (tnow < last_key_scroll + scroll_persist) { - max_spin = 1.25*(tnow - last_key_scroll); - } else if (tnow < last_key_to_button_remap_time + 1.5*scroll_persist) { - /* mostly a hack I use for testing -remap key -> btn4/btn5 */ - max_spin = scroll_persist; - } else if (scroll_keysym) { - if (repeat_check(last_key_scroll)) { - spin_fac = scroll_fac; - } else { - spin_fac = noscroll_fac; - } - } - if (max_spin > max_long_spin) { - max_spin = max_long_spin; - } - - /* XXX use this somehow */ -if (0) link = link_rate(&latency, &netrate); - - gk = gk0 = got_keyboard_input; - dtime0(&tm); - -if (db) fprintf(stderr, "check_xrecord_keys: BEGIN LOOP: scr_ev_cnt: " - "%d max: %.3f %.4f\n", scr_ev_cnt, max_spin, tm - x11vnc_start); - - while (1) { - - if (scr_ev_cnt) { - got_one = 1; - - scrollability(xrecord_ptr_window, SCR_SUCCESS); - scroll_rep = 2; - - dtime0(&last_scroll); - last_key_scroll = last_scroll; - scr_cnt++; - break; - } - - X_LOCK; - flush1 = 1; - XFlush(dpy); - X_UNLOCK; - - if (set_repeat_in > 0.0) { - max_keyrepeat_time = set_repeat_in; - } - - if (use_threads) { - usleep(1000); - } else { - rfbCFD(1000); - } - spin += dtime(&tm); - - X_LOCK; - if (got_keyboard_input > gk) { - gk = got_keyboard_input; - input++; - if (set_repeat_in) { - ; - } else if (xrecord_scroll_keysym(last_rfb_keysym)) { - if (repeat_check(last_key_scroll)) { - spin_fac = scroll_fac; - } else { - spin_fac = noscroll_fac; - } - } -if (0 || db) fprintf(stderr, "check_xrecord: more keys: %.3f 0x%x " - " %.4f %s %s\n", spin, last_rfb_keysym, last_rfb_keytime - x11vnc_start, - last_rfb_down ? "down":"up ", last_rfb_key_accepted ? "accept":"skip"); - flush2 = 1; - XFlush(dpy); - } -#if LIBVNCSERVER_HAVE_RECORD - SCR_LOCK; - XRecordProcessReplies(rdpy_data); - SCR_UNLOCK; -#endif - X_UNLOCK; - - if (spin >= max_spin * spin_fac) { -if (0 || db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin, - max_spin * spin_fac); - fail = 1; - break; - } - } - - max_keyrepeat_time = 0.0; - - if (scr_ev_cnt) { - int dret, ev = scr_ev_cnt - 1; - int bdx, bdy, bdskinny, bdpush = 0; - double max_age = 0.25, age, tm, dt; - static double last_scr_ev = 0.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); - } - - dtime0(&tm); - age = max_age; - dret = push_scr_ev(&age, SCR_KEY, bdpush, bdx, bdy, bdskinny); - dt = dtime(&tm); - - ret = 1 + dret; - scr_ev_cnt = 0; - - if (ret == 2 && xrecord_scroll_keysym(last_rfb_keysym)) { - int repeating; - double time_lo = 1.0/max_keyrepeat_lo; - double time_hi = 1.0/max_keyrepeat_hi; - double rate = typing_rate(0.0, &repeating); -if (0 || db) fprintf(stderr, "Typing: dt: %.4f rate: %.1f\n", dt, rate); - if (repeating) { - /* n.b. the "quantum" is about 1/30 sec. */ - max_keyrepeat_time = 1.0*dt; - if (max_keyrepeat_time > time_lo || - max_keyrepeat_time < time_hi) { - max_keyrepeat_time = 0.0; - } else { - set_repeat = max_keyrepeat_time; -if (0 || db) fprintf(stderr, "set max_keyrepeat_time: %.2f\n", max_keyrepeat_time); - } - } - } - - last_scr_ev = dnow(); - } - - if ((got_one && ret < 2) || persist_count) { - set_xdamage_mark(last_wx, last_wy, last_ww, last_wh); - } - - if (fail) { - scrollability(xrecord_ptr_window, SCR_FAIL); - } - - if (xrecording) { - if (ret < 2) { - xrecord_watch(0, SCR_KEY); - } - } - - 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) { - 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; - 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; - - int scroll_wheel = 0; - int btn4 = (1<<3); - int btn5 = (1<<4); - - get_out = 1; - if (button_mask) { - get_out = 0; - } - if (want_back_in) { - get_out = 0; - } - dtime0(&tnow); -if (0) fprintf(stderr, "check_xrecord_mouse: IN xrecording: %d\n", xrecording); - - if (get_out) { - if (xrecording) { - xrecord_watch(0, SCR_MOUSE); - } - return 0; - } - - scroll_rep = scrollability(xrecord_ptr_window, SCR_NONE) + 1; - if (scroll_rep == 1) { - scroll_rep = 2; /* if no info, assume the best. */ - } - - 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 (button_mask & (btn4|btn5)) { - scroll_wheel = 1; - } - - /* - * 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; - gk = gk0 = got_keyboard_input; - dtime0(&tm); - - /* - * 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; - -if (db) fprintf(stderr, "check_xrecord_mouse: BEGIN LOOP: scr_ev_cnt: " - "%d max: %.3f %.4f\n", scr_ev_cnt, max_spin[scroll_rep], tm - x11vnc_start); - - 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, age = 0.35; - - got_one = 1; - 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; - if (clipshift) { - bdx += coff_x; - bdy += coff_y; - } - if (subwin) { - bdx += off_x; - bdy += off_y; - } - bdskinny = 32; - - set_bdpush(SCR_MOUSE, &last_bdpush, &bdpush); - - dtime0(&tm); - - dret = push_scr_ev(&age, 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; - } - } - - - 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 (got_pointer_input > gp) { - flush2 = 1; - input += eat_pointer(max_ptr_eat, 1); - gp = got_pointer_input; - } - if (got_keyboard_input > gk) { - gk = got_keyboard_input; - input++; - } - X_LOCK; -#if LIBVNCSERVER_HAVE_RECORD - SCR_LOCK; - XRecordProcessReplies(rdpy_data); - SCR_UNLOCK; -#endif - X_UNLOCK; - - if (! input) { - spin_check = 1.5 * max_spin[scroll_rep]; - } else { - spin_check = max_spin[scroll_rep]; - } - - 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 (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 (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; - } - - if (! button_mask) { - int doflush = 0; - if (button_up_time > 0.0) { - ; - } else if (came_back_in) { - dtime0(&button_up_time); - doflush = 1; - } else if (scroll_wheel) { -if (db) fprintf(stderr, "check_xrecord: SCROLL-WHEEL-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); - } 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); - } - } - - last_x = cursor_x; - last_y = cursor_y; - } - - if (got_one) { - set_xdamage_mark(last_wx, last_wy, last_ww, last_wh); - } - - if (fail) { - scrollability(xrecord_ptr_window, SCR_FAIL); - } - - /* 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 { - if (ret) { - ret = 1; - } else { - ret = 0; - } - if (xrecording) { - xrecord_watch(0, SCR_MOUSE); - } - } - - 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, consider_mouse; - static int mouse_wants_back_in = 0; - - if (! use_xrecord) { - return 0; - } - if (skip_cr_when_scaling("scroll")) { - return 0; - } - -if (0) fprintf(stderr, "check_xrecord: IN xrecording: %d\n", xrecording); - - if (! xrecording) { - return 0; - } - - if (!strcmp(scroll_copyrect, "always")) { - watch_keys = 1; - watch_mouse = 1; - } else if (!strcmp(scroll_copyrect, "keys")) { - watch_keys = 1; - } else if (!strcmp(scroll_copyrect, "mouse")) { - watch_mouse = 1; - } - - 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; - } -} - -#define DB_SET \ - int db = 0; \ - int db2 = 0; \ - if (debug_wireframe == 1) { \ - db = 1; \ - } \ - if (debug_wireframe == 2) { \ - db2 = 1; \ - } \ - if (debug_wireframe == 3) { \ - db = 1; \ - db2 = 1; \ - } - -int try_copyrect(Window frame, int x, int y, int w, int h, int dx, int dy, - 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, FB_COPY); - - get_client_regions(&req, &mod, &cpy, &ncli); - } - if (cpy) { - return 0; - } - - *obscured = 0; - /* - * XXX KDE and xfce do some weird things with the - * stacking, it does not match XQueryTree. Work around - * it for now by CopyRect-ing the *whole* on-screen - * rectangle (whether obscured or not!) - */ - if (time(0) > dt_bad_check + 5) { - char *dt = guess_desktop(); - if (!strcmp(dt, "kde")) { - dt_bad = 1; - } else if (!strcmp(dt, "xfce")) { - dt_bad = 1; - } else { - dt_bad = 0; - } - dt_bad_check = time(0); - } - - if (clipshift) { - x -= coff_x; - y -= coff_y; - } - if (subwin) { - x -= off_x; - y -= off_y; - } - - if (dt_bad && wireframe_in_progress) { - sraRegionPtr rect; - /* send the whole thing... */ - x1 = crfix(nfix(x, dpy_x), dx, dpy_x); - y1 = crfix(nfix(y, dpy_y), dy, dpy_y); - x2 = crfix(nfix(x+w, dpy_x+1), dx, dpy_x+1); - y2 = crfix(nfix(y+h, dpy_y+1), dy, dpy_y+1); - - rect = sraRgnCreateRect(x1, y1, x2, y2); - - if (blackouts) { - int i; - sraRegionPtr bo_rect; - for (i=0; i<blackouts; i++) { - bo_rect = sraRgnCreateRect(blackr[i].x1, - blackr[i].y1, blackr[i].x2, blackr[i].y2); - sraRgnSubtract(rect, bo_rect); - sraRgnDestroy(bo_rect); - } - } - do_copyregion(rect, dx, dy); - sraRgnDestroy(rect); - - sent_copyrect = 1; - *obscured = 1; /* set to avoid an aggressive push */ - - } else if (stack_list_num || dt_bad) { - int k, tx1, tx2, ty1, ty2; - sraRegionPtr moved_win, tmp_win, whole; - sraRectangleIterator *iter; - sraRect rect; - int saw_me = 0; - int orig_x, orig_y; - XWindowAttributes attr; - - orig_x = x - dx; - orig_y = y - dy; - - tx1 = nfix(orig_x, dpy_x); - ty1 = nfix(orig_y, dpy_y); - tx2 = nfix(orig_x+w, dpy_x+1); - ty2 = nfix(orig_y+h, dpy_y+1); - -if (db2) fprintf(stderr, "moved_win: %4d %3d, %4d %3d 0x%lx ---\n", - tx1, ty1, tx2, ty2, frame); - - 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_list_num - 1; k >= 0; k--) { - Window swin; - - if (0 && dt_bad) { - break; - } - - swin = stack_list[k].win; - if (swin == frame) { -if (db2) { -saw_me = 1; fprintf(stderr, " ----------\n"); -} else { - break; -} - } -#if 0 -fprintf(stderr, "bo: %d/%lx\n", k, swin); -#endif - - /* skip some unwanted cases: */ - if (swin == None) { - continue; - } - if (swin < 10) { - ; /* blackouts */ - } else 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; - } - - if (clipshift) { - attr.x -= coff_x; - attr.y -= coff_y; - } - if (subwin) { - attr.x -= off_x; - attr.y -= off_y; - } - - /* - * first subtract any overlap from the initial - * window rectangle - */ - - /* clip the window to the visible screen: */ - tx1 = nfix(attr.x, dpy_x); - ty1 = nfix(attr.y, dpy_y); - tx2 = nfix(attr.x + attr.width, dpy_x+1); - ty2 = nfix(attr.y + attr.height, dpy_y+1); - -if (db2) fprintf(stderr, " tmp_win-1: %4d %3d, %4d %3d 0x%lx\n", - tx1, ty1, tx2, ty2, swin); -if (db2 && saw_me) continue; - - /* see if window clips us: */ - tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); - if (sraRgnAnd(tmp_win, moved_win)) { - *obscured = 1; -if (db2) fprintf(stderr, " : clips it.\n"); - } - 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. - */ - - /* clip the window to the visible screen: */ - tx1 = nfix(attr.x - dx, dpy_x); - ty1 = nfix(attr.y - dy, dpy_y); - tx2 = nfix(attr.x - dx + attr.width, dpy_x+1); - ty2 = nfix(attr.y - dy + attr.height, dpy_y+1); - -if (db2) fprintf(stderr, " tmp_win-2: %4d %3d, %4d %3d 0x%lx\n", - tx1, ty1, tx2, ty2, swin); -if (db2 && saw_me) continue; - - /* subtract it from our region: */ - tmp_win = sraRgnCreateRect(tx1, ty1, tx2, ty2); - sraRgnSubtract(moved_win, tmp_win); - sraRgnDestroy(tmp_win); - } - X_UNLOCK; - - if (extra_clip && ! sraRgnEmpty(extra_clip)) { - whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); - - if (clipshift) { - sraRgnOffset(extra_clip, -coff_x, -coff_y); - } - if (subwin) { - sraRgnOffset(extra_clip, -off_x, -off_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)) { - sraRegionPtr whole, shifted_region; - - whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y); - shifted_region = sraRgnCreateRgn(moved_win); - sraRgnOffset(shifted_region, dx, dy); - sraRgnAnd(shifted_region, whole); - - sraRgnDestroy(whole); - - /* now send the CopyRegion: */ - if (! sraRgnEmpty(shifted_region)) { - dtime0(&tm); - do_copyregion(shifted_region, dx, dy); - dt = dtime(&tm); -if (0 || 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); - } - sraRgnDestroy(moved_win); - } - return sent_copyrect; -} - -int near_wm_edge(int x, int y, int w, int h, int px, int py) { - /* heuristics: */ - int wf_t = wireframe_top; - int wf_b = wireframe_bot; - int wf_l = wireframe_left; - int wf_r = wireframe_right; - - int near_edge = 0; - - if (wf_t || wf_b || wf_l || wf_r) { - if (nabs(y - py) < wf_t) { - near_edge = 1; - } - if (nabs(y + h - py) < wf_b) { - near_edge = 1; - } - if (nabs(x - px) < wf_l) { - near_edge = 1; - } - if (nabs(x + w - px) < wf_r) { - near_edge = 1; - } - } else { - /* all zero; always "near" edge: */ - near_edge = 1; - } - return near_edge; -} - -int near_scrollbar_edge(int x, int y, int w, int h, int px, int py) { - /* heuristics: */ - int sb_t = scrollcopyrect_top; - int sb_b = scrollcopyrect_bot; - int sb_l = scrollcopyrect_left; - int sb_r = scrollcopyrect_right; - - int near_edge = 0; - - if (sb_t || sb_b || sb_l || sb_r) { - if (nabs(y - py) < sb_t) { - near_edge = 1; - } - if (nabs(y + h - py) < sb_b) { - near_edge = 1; - } - if (nabs(x - px) < sb_l) { - near_edge = 1; - } - if (nabs(x + w - px) < sb_r) { - near_edge = 1; - } - } else { - /* all zero; always "near" edge: */ - near_edge = 1; - } - return near_edge; -} - -void check_fixscreen(void) { - double now = dnow(); - int didfull = 0, db = 0; - - if (!client_count) { - return; - } - - if (screen_fixup_X > 0.0) { - static double last = 0.0; - if (now > last + screen_fixup_X) { - if (db) rfbLog("doing screen_fixup_X\n"); - do_copy_screen = 1; - last = now; - didfull = 1; - } - - } - if (screen_fixup_V > 0.0) { - static double last = 0.0; - if (now > last + screen_fixup_V) { - if (! didfull) { - refresh_screen(0); - if (db) rfbLog("doing screen_fixup_V\n"); - } - last = now; - didfull = 1; - } - } - if (screen_fixup_C > 0.0) { - static double last = 0.0; - if (last_copyrect_fix < last_copyrect && - now > last_copyrect + screen_fixup_C) { - if (! didfull) { - refresh_screen(0); - if (db) rfbLog("doing screen_fixup_C\n"); - } - last_copyrect_fix = now; - last = now; - didfull = 1; - } - } - if (scaling && last_copyrect_fix < last_copyrect) { - static double last = 0.0; - double delay = 3.0; - if (now > last + delay) { - if (! didfull) { - scale_and_mark_rect(0, 0, dpy_x, dpy_y); - if (db) rfbLog("doing scale screen_fixup\n"); - } - last_copyrect_fix = now; - last = now; - didfull = 1; - } - } -} - -int wireframe_mod_state() { - if (! wireframe_mods) { - return 0; - } - if (!strcmp(wireframe_mods, "all")) { - if (track_mod_state(NoSymbol, FALSE, FALSE)) { - return 1; - } else { - return 0; - } - - } else if (!strcmp(wireframe_mods, "Alt")) { - if (track_mod_state(XK_Alt_L, FALSE, FALSE) == 1) { - return 1; - } else if (track_mod_state(XK_Alt_R, FALSE, FALSE) == 1) { - return 1; - } - } else if (!strcmp(wireframe_mods, "Shift")) { - if (track_mod_state(XK_Shift_L, FALSE, FALSE) == 1) { - return 1; - } else if (track_mod_state(XK_Shift_R, FALSE, FALSE) == 1) { - return 1; - } - } else if (!strcmp(wireframe_mods, "Control")) { - if (track_mod_state(XK_Control_L, FALSE, FALSE) == 1) { - return 1; - } else if (track_mod_state(XK_Control_R, FALSE, FALSE) == 1) { - return 1; - } - } else if (!strcmp(wireframe_mods, "Meta")) { - if (track_mod_state(XK_Meta_L, FALSE, FALSE) == 1) { - return 1; - } else if (track_mod_state(XK_Meta_R, FALSE, FALSE) == 1) { - return 1; - } - } else if (!strcmp(wireframe_mods, "Super")) { - if (track_mod_state(XK_Super_L, FALSE, FALSE) == 1) { - return 1; - } else if (track_mod_state(XK_Super_R, FALSE, FALSE) == 1) { - return 1; - } - } else if (!strcmp(wireframe_mods, "Hyper")) { - if (track_mod_state(XK_Hyper_L, FALSE, FALSE) == 1) { - return 1; - } else if (track_mod_state(XK_Hyper_R, FALSE, FALSE) == 1) { - return 1; - } - } - return 0; -} - -/* - * Applied just before any check_user_input() modes. Look for a - * ButtonPress; find window it happened in; find the wm frame window - * for it; watch for that window moving or resizing. If it does, do the - * wireframe animation. Do this until ButtonRelease or timeouts occur. - * Remove wireframe. - * - * Under -nowirecopyrect, return control to base scheme - * (check_user_input() ...) that will repaint the screen with the window - * in the new postion or size. Under -wirecopyrect, apply rfbDoCopyRect - * or rfbDoCopyRegion: this "pollutes" our framebuffer, but the normal - * polling will quickly repair it. Under happy circumstances, this - * reduces actual XShmGetImage work (i.e. if we correctly predicted how - * the X fb has changed. - * - * -scale doesn't always work under -wirecopyrect, but the wireframe does. - * - * testing of this mode under -threads is incomplete. - * - * returns 1 if it did an animation, 0 if no move/resize triggers - * went off. - * - * TBD: see if we can select StructureNotify ConfigureNotify events for - * the toplevel windows to get better info on moves and resizes. - */ -int check_wireframe(void) { - Window frame, orig_frame; - XWindowAttributes attr; - int dx, dy; - - int orig_px, orig_py, orig_x, orig_y, orig_w, orig_h; - int px, py, x, y, w, h; - 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, 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; - static int first_dt_cnt = 0; - static time_t last_save_stacklist = 0; - - /* heuristics: */ - double first_event_spin = wireframe_t1; - double frame_changed_spin = wireframe_t2; - double max_spin = wireframe_t3; - double min_draw = wireframe_t4; - int try_it = 0; - DB_SET - - if (nofb) { - return 0; - } - if (subwin) { - return 0; /* don't even bother for -id case */ - } - if (! button_mask) { - return 0; /* no button pressed down */ - } - if (!use_threads && !got_pointer_input) { - return 0; /* need ptr input, e.g. button down, motion */ - } - -if (db) fprintf(stderr, "\n*** button down!! x: %d y: %d\n", cursor_x, cursor_y); - - /* - * Query where the pointer is and which child of the root - * window. We will assume this is the frame the window manager - * makes when it reparents the toplevel window. - */ - X_LOCK; - 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; - } - X_UNLOCK; -if (db) fprintf(stderr, "a: %d wf: %.3f A: %d\n", w*h, wireframe_frac, (dpy_x*dpy_y)); - - /* - * apply the percentage size criterion (allow opaque moves for - * small windows) - */ - if ((double) w*h < wireframe_frac * (dpy_x * dpy_y)) { -if (db) fprintf(stderr, "small window %.3f\n", ((double) w*h)/(dpy_x * dpy_y)); - return 0; - } -if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr: 0x%lx\n", x, y, w, h, px, py, orig_frame); - - /* - * see if the pointer is within range of the assumed wm frame - * decorations on the edge of the window. - */ - - try_it = near_wm_edge(x, y, w, h, px, py); - - /* Often Alt+ButtonDown starts a window move: */ - if (! try_it && wireframe_mod_state()) { - try_it = 1; - } - if (! try_it) { -if (db) fprintf(stderr, "INTERIOR\n"); - return 0; - } - - wireframe_in_progress = 1; - - if (button_mask_prev) { - already_down = 1; - } - - if (! wireframe_str || !strcmp(wireframe_str, WIREFRAME_PARMS)) { - int link, latency, netrate; - static int didmsg = 0; - - 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; - max_spin *= 2.0; - min_draw *= 1.5; - if (! didmsg) { - rfbLog("increased wireframe timeouts for " - "slow network connection.\n"); - rfbLog("netrate: %d KB/sec, latency: %d ms\n", - netrate, latency); - didmsg = 1; - } - } - } - - /* - * pointer() should have snapped the stacking list for us, if - * not, do it now (if the XFakeButtonEvent has been flushed by - * now the stacking order may be incorrect). - */ - if (strcmp(wireframe_copyrect, "never")) { - if (already_down) { - double age = 0.0; - /* - * see if we can reuse the stack list (pause - * with button down) - */ - if (stack_list_num) { - int k, got_me = 0; - for (k = stack_list_num -1; k >=0; k--) { - if (frame == stack_list[k].win) { - got_me = 1; - break; - } - } - if (got_me) { - age = 1.0; - } - snapshot_stack_list(0, age); - } - } - if (! stack_list_num) { - snapshot_stack_list(0, 0.0); - } - } - - - /* store initial parameters, we look for changes in them */ - orig_frame = frame; - orig_px = px; /* pointer position */ - orig_py = py; - orig_x = x; /* frame position */ - orig_y = y; - orig_w = w; /* frame size */ - orig_h = h; - - orig_cursor_x = cursor_x; - orig_cursor_y = cursor_y; - - /* this is the box frame we would draw */ - box_x = x; - box_y = y; - box_w = w; - box_h = h; - - dtime0(&tm); - - last_draw = spin; - - /* -threads support for check_wireframe() is rough... crash? */ - if (use_threads) { - /* purge any stored up pointer events: */ - pointer(-1, 0, 0, NULL); - } - - g = got_pointer_input; - - while (1) { - - X_LOCK; - XFlush(dpy); - X_UNLOCK; - - /* try do induce/waitfor some more user input */ - if (use_threads) { - usleep(1000); - } else if (drew_box) { - rfbPE(1000); - } else { - rfbCFD(1000); - } - - spin += dtime(&tm); - -if (0) fprintf(stderr, "wf-spin: %.3f\n", spin); - - /* check for any timeouts: */ - if (frame_changed) { - double delay; - /* max time we play this game: */ - if (spin > max_spin) { -if (db || db2) fprintf(stderr, " SPIN-OUT-MAX: %.3f\n", spin); - break_reason = 1; - break; - } - /* watch for pointer events slowing down: */ - if (special_t1) { - delay = max_spin; - } else { - delay = 2.0* frame_changed_spin; - if (spin > 3.0 * frame_changed_spin) { - delay = 1.5 * delay; - } - } - if (spin > last_ptr + delay) { -if (db || db2) fprintf(stderr, " SPIN-OUT-NOT-FAST: %.3f\n", spin); - break_reason = 2; - break; - } - } else if (got_2nd_pointer) { - /* - * pointer is moving, max time we wait for wm - * move or resize to be detected - */ - if (spin > frame_changed_spin) { -if (db || db2) fprintf(stderr, " SPIN-OUT-NOFRAME-SPIN: %.3f\n", spin); - break_reason = 3; - break; - } - } else { - /* max time we wait for any pointer input */ - if (spin > first_event_spin) { -if (db || db2) fprintf(stderr, " SPIN-OUT-NO2ND_PTR: %.3f\n", spin); - break_reason = 4; - break; - } - } - - /* see if some pointer input occurred: */ - if (got_pointer_input > g) { -if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mask: %d\n", got_2nd_pointer+1, spin, cursor_x, cursor_y, button_mask); - - g = got_pointer_input; - - X_LOCK; - XFlush(dpy); - X_UNLOCK; - - /* periodically try to let the wm get moving: */ - if (!frame_changed && got_2nd_pointer % 4 == 0) { - if (got_2nd_pointer == 0) { - usleep(50 * 1000); - } else { - usleep(25 * 1000); - } - } - got_2nd_pointer++; - last_ptr = spin; - - /* - * see where the pointer currently is. It may - * not be our starting frame (i.e. mouse now - * outside of the moving window). - */ - frame = 0x0; - X_LOCK; - - if (! get_wm_frame_pos(&px, &py, &x, &y, &w, &h, - &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, 1)) { - X_UNLOCK; - /* our window frame went away! */ - win_gone = 1; -if (db) fprintf(stderr, "FRAME-GONE: 0x%lx\n", orig_frame); - break_reason = 5; - break; - } - if (attr.map_state == IsUnmapped) { - X_UNLOCK; - /* our window frame is now unmapped! */ - win_unmapped = 1; -if (db) fprintf(stderr, "FRAME-UNMAPPED: 0x%lx\n", orig_frame); - break_reason = 5; - break; - } - -if (db) fprintf(stderr, "OUT-OF-FRAME: old: x: %d y: %d px: %d py: %d 0x%lx\n", x, y, px, py, frame); - - /* new parameters for our frame */ - x = attr.x; /* n.b. rootwin is parent */ - y = attr.y; - w = attr.width; - h = attr.height; - } - X_UNLOCK; - -if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr: 0x%lx\n", x, y, w, h, px, py, frame); -if (db) fprintf(stderr, " MO,PT,FR: %d/%d %d/%d %d/%d\n", cursor_x - orig_cursor_x, cursor_y - orig_cursor_y, px - orig_px, py - orig_py, x - orig_x, y - orig_y); - - if (frame_changed && frame != orig_frame) { -if (db) fprintf(stderr, "CHANGED and window switch: 0x%lx\n", frame); - } - if (frame_changed && px - orig_px != x - orig_x) { -if (db) fprintf(stderr, "MOVED and diff DX\n"); - } - if (frame_changed && py - orig_py != y - orig_y) { -if (db) fprintf(stderr, "MOVED and diff DY\n"); - } - - /* check and see if our frame has been resized: */ - if (!frame_changed && (w != orig_w || h != orig_h)) { - int n; - if (!already_down) { - first_dt_ave += spin; - first_dt_cnt++; - } - n = first_dt_cnt ? first_dt_cnt : 1; - frame_changed = 2; - -if (db) fprintf(stderr, "WIN RESIZE 1st-dt: %.3f\n", first_dt_ave/n); - } - - /* check and see if our frame has been moved: */ - if (!frame_changed && (x != orig_x || y != orig_y)) { - int n; - if (!already_down) { - first_dt_ave += spin; - first_dt_cnt++; - } - n = first_dt_cnt ? first_dt_cnt : 1; - frame_changed = 1; -if (db) fprintf(stderr, "FRAME MOVE 1st-dt: %.3f\n", first_dt_ave/n); - } - - /* - * see if it is time to draw any or a new wireframe box - */ - if (frame_changed) { - int drawit = 0; - if (x != box_x || y != box_y) { - /* moved since last */ - drawit = 1; - } else if (w != box_w || h != box_h) { - /* resize since last */ - drawit = 1; - } - if (drawit) { - /* - * check time (to avoid too much - * animations on slow machines - * or links). - */ - if (spin > last_draw + min_draw || - ! drew_box) { - draw_box(x, y, w, h, 0); - drew_box = 1; - rfbPE(1000); - last_draw = spin; - } - } - box_x = x; - box_y = y; - box_w = w; - box_h = h; - } - } - - /* - * Now (not earlier) check if the button has come back up. - * we check here to get a better location and size of - * the final window. - */ - if (! button_mask) { -if (db || db2) fprintf(stderr, "NO button_mask\n"); - break_reason = 6; - break; - } - } - - if (! drew_box) { - /* nice try, but no move or resize detected. cleanup. */ - if (stack_list_num) { - stack_list_num = 0; - } - wireframe_in_progress = 0; - return 0; - } - - /* remove the wireframe */ - draw_box(0, 0, 0, 0, 1); - - dx = x - orig_x; - dy = y - orig_y; - - /* - * see if we can apply CopyRect or CopyRegion to the change: - */ - if (!strcmp(wireframe_copyrect, "never")) { - ; - } else if (win_gone || win_unmapped) { - ; - } else if (skip_cr_when_scaling("wireframe")) { - ; - } else if (w != orig_w || h != orig_h) { - ; - } else if (dx == 0 && dy == 0) { - ; - } else { - int spin_ms = (int) (spin * 1000 * 1000); - int obscured, sent_copyrect = 0; - - /* - * set a timescale comparable to the spin time, - * but not too short or too long. - */ - if (spin_ms < 30) { - spin_ms = 30; - } else if (spin_ms > 400) { - spin_ms = 400; - } - - /* try to flush the wireframe removal: */ - fb_push_wait(0.1, FB_COPY|FB_MOD); - - /* try to send a clipped copyrect of translation: */ - sent_copyrect = try_copyrect(frame, x, y, w, h, dx, dy, - &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_wait(0.1, FB_COPY); - } else { - /* no diff for now... */ - fb_push_wait(0.1, FB_COPY); - } - if (scaling) { - static double last_time = 0.0; - double now = dnow(), delay = 0.35; - - fb_push_wait(0.1, FB_COPY); - - if (now > last_time + delay) { - int xt = x, yt = y; - - if (clipshift) { - xt -= coff_x; - yt -= coff_y; - } - if (subwin) { - xt -= off_x; - yt -= off_y; - } - - scale_mark(xt, yt, xt+w, yt+h); - last_time = now; - last_copyrect_fix = now; - } - } - } - } - - if (stack_list_num) { - /* clean up stack_list for next time: */ - if (break_reason == 1 || break_reason == 2) { - /* - * save the stack list, perhaps the user has - * paused with button down. - */ - last_save_stacklist = time(0); - } else { - 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); - xdamage_scheduled_mark = dnow() + 2.0; - } - - return 1; -} - -/* - * We need to handle user input, particularly pointer input, carefully. - * This function is only called when non-threaded. Note that - * rfbProcessEvents() only processes *one* pointer event per call, - * so if we interlace it with scan_for_updates(), we can get swamped - * with queued up pointer inputs. And if the pointer inputs are inducing - * large changes on the screen (e.g. window drags), the whole thing - * bogs down miserably and only comes back to life at some time after - * one stops moving the mouse. So, to first approximation, we are trying - * to eat as much user input here as we can using some hints from the - * duration of the previous scan_for_updates() call (in dt). - * - * note: we do this even under -nofb - * - * return of 1 means watch_loop should short-circuit and reloop, - * return of 0 means watch_loop should proceed to scan_for_updates(). - * (this is for pointer_mode == 1 mode, the others do it all internally, - * cnt is also only for that mode). - */ - -static void check_user_input2(double dt) { - - int eaten = 0, miss = 0, max_eat = 50, do_flush = 1; - int g, g_in; - double spin = 0.0, tm; - double quick_spin_fac = 0.40; - double grind_spin_time = 0.175; - - dtime0(&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) { - if (show_multiple_cursors) { - rfbPE(1000); - } else { - rfbCFD(1000); - } - rfbCFD(0); - - spin += dtime(&tm); - - if (spin > quick_spin_fac * dt) { - /* get out if spin time comparable to last scan time */ - break; - } - if (got_pointer_input > g) { - int i, max_extra = max_eat / 2; - g = got_pointer_input; - 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 { - miss++; - } - if (miss > 1) { /* 1 means out on 2nd miss */ - break; - } - } - if (do_flush) { - X_LOCK; -if (0) fprintf(stderr, "check_user_input2-B: XFlush %.4f\n", tm); - XFlush(dpy); - X_UNLOCK; - } - - - /* - * 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; - dtime0(&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) { - 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; - } else { - miss++; - } - g = got_pointer_input; - if (miss > 2) { - break; - } - if (1000 * spin > ms * split) { - break; - } - } - } -} - -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; - double spin, spin_max, tm, to, dtm; - int rfb_wait_ms = 2; - static double dt_cut = 0.075; - int gcnt, ginput; - static int first = 1; - - if (dtr || tile_diffs) {} /* unused vars warning: */ - - if (first) { - char *p = getenv("SPIN"); - if (p) { - double junk; - sscanf(p, "%lf,%lf", &dt_cut, &junk); - } - first = 0; - } - - if (!got_pointer_input) { - return; - } - - - if (dt < dt_cut) { - dt = dt_cut; /* this is to try to avoid early exit */ - } - spin_max = 0.5; - - spin = 0.0; /* amount of time spinning */ - allowed_misses = 10; /* number of ptr inputs we can miss */ - miss_tweak = 8; - last_was_miss = 0; - consecutive_misses = 1; - gcnt = 0; - ginput = 0; - - dtime0(&tm); - to = tm; /* last time we did rfbPE() */ - - g = g_in = got_pointer_input; - - while (1) { - int got_input = 0; - - gcnt++; - - if (button_mask) { - drag_in_progress = 1; - } - - rfbCFD(rfb_wait_ms * 1000); - - dtm = dtime(&tm); - spin += dtm; - - if (got_pointer_input == g) { - if (last_was_miss) { - consecutive_misses++; - } - last_was_miss = 1; - } else { - ginput++; - if (ginput % miss_tweak == 0) { - allowed_misses++; - } - consecutive_misses = 1; - last_was_miss = 0; - } - - if (spin > spin_max) { - /* get out if spin time over limit */ - break; - - } else if (got_pointer_input > g) { - /* received some input, flush to display. */ - got_input = 1; - g = got_pointer_input; - X_LOCK; - XFlush(dpy); - X_UNLOCK; - } else if (--allowed_misses <= 0) { - /* too many misses */ - break; - } else if (consecutive_misses >=3) { - /* too many misses */ - break; - } else { - /* these are misses */ - int wms = 0; - if (gcnt == 1 && button_mask) { - /* - * missed our first input, wait - * for a defer time. (e.g. on - * slow link) hopefully client - * will batch them. - */ - wms = 50; - } else if (button_mask) { - wms = 10; - } else { - } - if (wms) { - usleep(wms * 1000); - } - } - } - - if (ginput >= 2) { - /* try for a couple more quick ones */ - for (i=0; i<2; i++) { - rfbCFD(rfb_wait_ms * 1000); - } - } - - drag_in_progress = 0; -} - -int fb_update_sent(int *count) { - static int last_count = 0; - int sent = 0, rc = 0; - rfbClientIteratorPtr i; - rfbClientPtr cl; - - if (nofb) { - return 0; - } - - i = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(i)) ) { - sent += cl->framebufferUpdateMessagesSent; - } - rfbReleaseClientIterator(i); - if (sent != last_count) { - rc = 1; - } - if (count != NULL) { - *count = sent; - } - last_count = sent; - return rc; -} - -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; - int min_frame_size = 10; /* 10 tiles */ - double spin, tm, to, tc, dtm, rpe_last; - int rfb_wait_ms = 2; - static double dt_cut = 0.050; - static int first = 1; - - int Btile = tile_x * tile_y * bpp/8; /* Bytes per tile */ - double Ttile, dt_use; - double screen_rate = 6000000.; /* 5 MB/sec */ - double vnccpu_rate = 80 * 100000.; /* 20 KB/sec @ 80X compression */ - double net_rate = 50000.; - static double Tfac_r = 1.0, Tfac_v = 1.0, Tfac_n = 1.0, Tdelay = 0.001; - static double dt_min = -1.0, dt_max = -1.0; - double dt_min_fallback = 0.050; - static int ssec = 0, total_calls = 0; - static int push_frame = 0, update_count = 0; - - if (first) { - char *p = getenv("SPIN"); - if (p) { - sscanf(p, "%lf,%lf,%lf,%lf", &dt_cut, &Tfac_r, &Tfac_v, &Tfac_n); - } - first = 0; - ssec = time(0); - - if (dtr) {} /* unused vars warning: */ - } - - total_calls++; - - if (dt_min < 0.0 || dt < dt_min) { - if (dt > 0.0) { - dt_min = dt; - } - } - if (dt_min < 0.0) { - /* sensible value for the very 1st call if dt = 0.0 */ - dt_min = dt_min_fallback; - } - if (dt_max < 0.0 || dt > dt_max) { - dt_max = dt; - } - - if (total_calls > 30 && dt_min > 0.0) { - static int first = 1; - /* - * dt_min will soon be the quickest time to do - * one scan_for_updates with no tiles copied. - * use this (instead of copy_tiles) to estimate - * screen read rate. - */ - screen_rate = (main_bytes_per_line * ntiles_y) / dt_min; - if (first) { - rfbLog("measured screen read rate: %.2f Bytes/sec\n", - screen_rate); - } - first = 0; - } - - dtime0(&tm); - - if (dt < dt_cut) { - dt_use = dt_cut; - } else { - dt_use = dt; - } - - if (push_frame) { - int cnt, iter = 0; - double tp, push_spin = 0.0; - dtime0(&tp); - while (push_spin < dt_use * 0.5) { - fb_update_sent(&cnt); - if (cnt != update_count) { - break; - } - /* damn, they didn't push our frame! */ - iter++; - rfbPE(rfb_wait_ms * 1000); - - push_spin += dtime(&tp); - } - if (iter) { - X_LOCK; - XFlush(dpy); - X_UNLOCK; - } - push_frame = 0; - update_count = 0; - } - - /* - * when we first enter we require some pointer input - */ - if (!got_pointer_input) { - return; - } - - vnccpu_rate = get_raw_rate(); - - if ((tmp = get_read_rate()) != 0) { - screen_rate = (double) tmp; - } - if ((tmp = get_net_rate()) != 0) { - net_rate = (double) tmp; - } - net_rate = (vnccpu_rate/get_cmp_rate()) * net_rate; - - if ((tmp = get_net_latency()) != 0) { - Tdelay = 0.5 * ((double) tmp)/1000.; - } - - Ttile = Btile * (Tfac_r/screen_rate + Tfac_v/vnccpu_rate + Tfac_n/net_rate); - - spin = 0.0; /* amount of time spinning */ - last_was_miss = 0; - consecutive_misses = 1; - gcnt = 0; - ginput = 0; - - rpe_last = to = tc = tm; /* last time we did rfbPE() */ - g = g_in = got_pointer_input; - - tile_diffs = 0; /* reset our knowlegde of tile_diffs to zero */ - - while (1) { - int got_input = 0; - - gcnt++; - - if (button_mask) { - /* this varible is used by our pointer handler */ - drag_in_progress = 1; - } - - /* turn libvncserver crank to process events: */ - rfbCFD(rfb_wait_ms * 1000); - - dtm = dtime(&tm); - spin += dtm; - - if ( (gcnt == 1 && got_pointer_input > g) || tm-tc > 2*dt_min) { - tile_diffs = scan_for_updates(1); - tc = tm; - } - - if (got_pointer_input == g) { - if (last_was_miss) { - consecutive_misses++; - } - last_was_miss = 1; - } else { - ginput++; - consecutive_misses = 1; - last_was_miss = 0; - } - - if (tile_diffs > min_frame_size && spin > Ttile * tile_diffs + Tdelay) { - /* we think we can push the frame */ - push_frame = 1; - fb_update_sent(&update_count); - break; - - } else if (got_pointer_input > g) { - /* received some input, flush it to display. */ - got_input = 1; - g = got_pointer_input; - X_LOCK; - XFlush(dpy); - X_UNLOCK; - - } else if (consecutive_misses >= 2) { - /* too many misses in a row */ - break; - - } else { - /* these are pointer input misses */ - int wms; - if (gcnt == 1 && button_mask) { - /* - * missed our first input, wait for - * a defer time. (e.g. on slow link) - * hopefully client will batch many - * of them for the next read. - */ - wms = 50; - - } else if (button_mask) { - wms = 10; - } else { - wms = 0; - } - if (wms) { - usleep(wms * 1000); - } - } - } - if (ginput >= 2) { - /* try for a couple more quick ones */ - for (i=0; i<2; i++) { - rfbCFD(rfb_wait_ms * 1000); - } - } - drag_in_progress = 0; -} - -static int check_user_input(double dt, double dtr, int tile_diffs, int *cnt) { - - if (raw_fb && ! dpy) return 0; /* raw_fb hack */ - - if (use_xrecord) { - int rc = check_xrecord(); - /* - * 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; - } - } - - if (wireframe) { - if (check_wireframe()) { - return 0; - } - } - - if (pointer_mode == 1) { - if ((got_user_input || ui_skip < 0) && *cnt % ui_skip != 0) { - /* every ui_skip-th drops thru to scan */ - *cnt++; - X_LOCK; - XFlush(dpy); - X_UNLOCK; - return 1; /* short circuit watch_loop */ - } else { - return 0; - } - } - if (pointer_mode >= 2 && pointer_mode <= 4) { - if (got_keyboard_input) { - /* - * for these modes, short circuit watch_loop on - * *keyboard* input. - */ - if (*cnt % ui_skip != 0) { - *cnt++; - return 1; - } - } - /* otherwise continue below with pointer input method */ - } +/* -- x11vnc.c -- */ - if (pointer_mode == 2) { - check_user_input2(dt); - } else if (pointer_mode == 3) { - check_user_input3(dt, dtr, tile_diffs); - } else if (pointer_mode == 4) { - check_user_input4(dt, dtr, tile_diffs); - } - return 0; -} +#include "x11vnc.h" +#include "xwrappers.h" +#include "xdamage.h" +#include "xrecord.h" +#include "xevents.h" +#include "xinerama.h" +#include "xrandr.h" +#include "xkb_bell.h" +#include "win_utils.h" +#include "remote.h" +#include "scan.h" +#include "gui.h" +#include "help.h" +#include "user.h" +#include "cleanup.h" +#include "keyboard.h" +#include "pointer.h" +#include "cursor.h" +#include "userinput.h" +#include "screen.h" +#include "connections.h" +#include "rates.h" -/* -- x11vnc.c -- */ /* * main routine for the x11vnc program */ -/* - * simple function for measuring sub-second time differences, using - * a double to hold the value. - */ -double dtime(double *t_old) { - /* - * usage: call with 0.0 to initialize, subsequent calls give - * the time difference since last call. - */ - double t_now, dt; - struct timeval now; - - gettimeofday(&now, NULL); - t_now = now.tv_sec + ( (double) now.tv_usec/1000000. ); - if (*t_old == 0.0) { - *t_old = t_now; - return t_now; - } - dt = t_now - *t_old; - *t_old = t_now; - 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); -} - -double dnowx(void) { - return dnow() - x11vnc_start; -} - -double rnow(void) { - double t = dnowx(); - t = t - ((int) t); - if (t > 1.0) { - t = 1.0; - } else if (t < 0.0) { - t = 0.0; - } - return t; -} - -double rfac(void) { - double f = (double) rand(); - f = f / ((double) RAND_MAX); - return f; -} - -void measure_display_hook(rfbClientPtr cl) { - ClientData *cd = (ClientData *) cl->clientData; - dtime0(&cd->timer); -} -int get_rate(int which) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int irate, irate_min = 1; /* 1 KB/sec */ - int irate_max = 100000; /* 100 MB/sec */ - int count = 0; - double slowest = -1.0, rate; - static double save_rate = 1000 * NETRATE0; - - if (!screen) { - return 0; - } - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - ClientData *cd = (ClientData *) cl->clientData; - - if (cl->state != RFB_NORMAL) { - continue; - } - if (cd->send_cmp_rate == 0.0 || cd->send_raw_rate == 0.0) { - continue; - } - count++; - - if (which == 0) { - rate = cd->send_cmp_rate; - } else { - rate = cd->send_raw_rate; - } - if (slowest == -1.0 || rate < slowest) { - slowest = rate; - } - - } - rfbReleaseClientIterator(iter); - - if (! count) { - return NETRATE0; - } - - if (slowest == -1.0) { - slowest = save_rate; - } else { - save_rate = slowest; - } +static void check_cursor_changes(void); +static void record_last_fb_update(void); +static int choose_delay(double dt); +static void watch_loop(void); +static char *choose_title(char *display); +static int limit_shm(void); +static void check_rcfile(int argc, char **argv); +static void immediate_switch_user(int argc, char* argv[]); +static void print_settings(int try_http, int bg, char *gui_str); +static void check_loop_mode(int argc, char* argv[]); - irate = (int) (slowest/1000.0); - if (irate < irate_min) { - irate = irate_min; - } - if (irate > irate_max) { - irate = irate_max; - } -if (0) fprintf(stderr, "get_rate(%d) %d %.3f/%.3f\n", which, irate, save_rate, slowest); - - return irate; -} - -int get_latency(void) { - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int ilat, ilat_min = 1; /* 1 ms */ - int ilat_max = 2000; /* 2 sec */ - double slowest = -1.0, lat; - static double save_lat = ((double) LATENCY0)/1000.0; - int count = 0; - - if (!screen) { - return 0; - } - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - ClientData *cd = (ClientData *) cl->clientData; - - if (cl->state != RFB_NORMAL) { - continue; - } - if (cd->latency == 0.0) { - continue; - } - count++; - - lat = cd->latency; - if (slowest == -1.0 || lat > slowest) { - slowest = lat; - } - } - rfbReleaseClientIterator(iter); - - if (! count) { - return LATENCY0; - } - - if (slowest == -1.0) { - slowest = save_lat; - } else { - save_lat = slowest; - } - - ilat = (int) (slowest * 1000.0); - if (ilat < ilat_min) { - ilat = ilat_min; - } - if (ilat > ilat_max) { - ilat = ilat_max; - } - - return ilat; -} - -int get_cmp_rate(void) { - return get_rate(0); -} - -int get_raw_rate(void) { - return get_rate(1); -} - -void initialize_speeds(void) { - char *s, *s_in, *p; - int i; - speeds_read_rate = 0; - speeds_net_rate = 0; - speeds_net_latency = 0; - if (! speeds_str || *speeds_str == '\0') { - s_in = strdup(""); - } else { - s_in = strdup(speeds_str); - } - - if (!strcmp(s_in, "modem")) { - s = strdup("6,4,200"); - } else if (!strcmp(s_in, "dsl")) { - s = strdup("6,100,50"); - } else if (!strcmp(s_in, "lan")) { - s = strdup("6,5000,1"); - } else { - s = strdup(s_in); - } - - p = strtok(s, ","); - i = 0; - while (p) { - double val; - if (*p != '\0') { - val = atof(p); - if (i==0) { - speeds_read_rate = (int) 1000000 * val; - } else if (i==1) { - speeds_net_rate = (int) 1000 * val; - } else if (i==2) { - speeds_net_latency = (int) val; - } - } - i++; - p = strtok(NULL, ","); - } - free(s); - free(s_in); - - if (! speeds_read_rate) { - int n = 0; - double dt, timer; - dtime0(&timer); - if (fullscreen) { - copy_image(fullscreen, 0, 0, 0, 0); - n = fullscreen->bytes_per_line * fullscreen->height; - } else if (scanline) { - copy_image(scanline, 0, 0, 0, 0); - n = scanline->bytes_per_line * scanline->height; - } - dt = dtime(&timer); - if (n && dt > 0.0) { - double rate = ((double) n) / dt; - speeds_read_rate_measured = (int) (rate/1000000.0); - if (speeds_read_rate_measured < 1) { - speeds_read_rate_measured = 1; - } else { - rfbLog("fb read rate: %d MB/sec\n", - speeds_read_rate_measured); - } - } - } -} - -int get_read_rate(void) { - if (speeds_read_rate) { - return speeds_read_rate; - } - if (speeds_read_rate_measured) { - return speeds_read_rate_measured; - } - 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 (! spm || spm == NETRATE0) { - speeds_net_rate_measured = get_cmp_rate(); - } - if (speeds_net_rate_measured) { - return speeds_net_rate_measured; - } - return 0; -} - -int get_net_latency(void) { - int spm = speeds_net_latency_measured; - if (speeds_net_latency) { - return speeds_net_latency; - } - if (! spm || spm == LATENCY0) { - speeds_net_latency_measured = get_latency(); - } - if (speeds_net_latency_measured) { - return speeds_net_latency_measured; - } - return 0; -} - -void measure_send_rates(int init) { - double cmp_rate, raw_rate; - static double now, start = 0.0; - static rfbDisplayHookPtr orig_display_hook = NULL; - double cmp_max = 1.0e+08; /* 100 MB/sec */ - double cmp_min = 1000.0; /* 9600baud */ - double lat_max = 5.0; /* 5 sec */ - double lat_min = .0005; /* 0.5 ms */ - int min_cmp = 10000, nclients; - rfbClientIteratorPtr iter; - rfbClientPtr cl; - int db = 0, msg = 0; - -db = 0; - - if (! measure_speeds) { - return; - } - if (speeds_net_rate && speeds_net_latency) { - return; - } - - if (! orig_display_hook) { - orig_display_hook = screen->displayHook; - } - - if (start == 0.0) { - dtime(&start); - } - dtime0(&now); - now = now - start; - - nclients = 0; - - if (!screen) { - return; - } - - iter = rfbGetClientIterator(screen); - while( (cl = rfbClientIteratorNext(iter)) ) { - int defer, i, cbs, rbs; - char *httpdir; - double dt, dt1 = 0.0, dt2, dt3; - double tm, spin_max = 15.0, spin_lat_max = 1.5; - int got_t2 = 0, got_t3 = 0; - ClientData *cd = (ClientData *) cl->clientData; - - if (cd->send_cmp_rate > 0.0) { - continue; - } - nclients++; - - cbs = 0; - for (i=0; i<MAX_ENCODINGS; i++) { - cbs += cl->bytesSent[i]; - } - rbs = cl->rawBytesEquivalent; - - if (init) { - -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); - - cd->timer = dnow(); - cd->cmp_bytes_sent = cbs; - cd->raw_bytes_sent = rbs; - continue; - } - - /* first part of the bulk transfer of initial screen */ - dt1 = dtime(&cd->timer); - -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); - - if (dt1 <= 0.0) { - continue; - } - - cbs = cbs - cd->cmp_bytes_sent; - rbs = rbs - cd->raw_bytes_sent; - - if (cbs < min_cmp) { - continue; - } - - rfbPE(1000); - if (sraRgnCountRects(cl->modifiedRegion)) { - rfbPE(1000); - } - - defer = screen->deferUpdateTime; - httpdir = screen->httpDir; - screen->deferUpdateTime = 0; - screen->httpDir = NULL; - - /* mark a small rectangle: */ - mark_rect_as_modified(0, 0, 16, 16, 1); - - dtime0(&tm); - - dt2 = 0.0; - dt3 = 0.0; - - if (dt1 < 0.25) { - /* try to cut it down to avoid long pauses. */ - spin_max = 5.0; - } - - /* when req1 = 1 mod1 == 0, end of 2nd part of bulk transfer */ - while (1) { - int req0, req1, mod0, mod1; - req0 = sraRgnCountRects(cl->requestedRegion); - mod0 = sraRgnCountRects(cl->modifiedRegion); - if (use_threads) { - usleep(1000); - } else { - if (mod0) { - rfbPE(1000); - } else { - rfbCFD(1000); - } - } - dt = dtime(&tm); - dt2 += dt; - if (dt2 > spin_max) { - break; - } - req1 = sraRgnCountRects(cl->requestedRegion); - mod1 = sraRgnCountRects(cl->modifiedRegion); - -if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d " - "fbu-sent: %d dt: %.4f dt2: %.4f tm: %.4f\n", - req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt2, tm); - if (req1 != 0 && mod1 == 0) { - got_t2 = 1; - break; - } - } - - if (! got_t2) { - dt2 = 0.0; - } else { - int tr, trm = 3; - double dts[10]; - - /* - * Note: since often select(2) cannot sleep - * less than 1/HZ (e.g. 10ms), the resolution - * of the latency may be messed up by something - * of this order. Effect may occur on both ends, - * i.e. the viewer may not respond immediately. - */ - - for (tr = 0; tr < trm; tr++) { - usleep(5000); - - /* mark a 2nd small rectangle: */ - mark_rect_as_modified(0, 0, 16, 16, 1); - i = 0; - dtime0(&tm); - dt3 = 0.0; - - /* - * when req1 > 0 and mod1 == 0, we say - * that is the "ping" time. - */ - while (1) { - int req0, req1, mod0, mod1; - - req0 = sraRgnCountRects( - cl->requestedRegion); - mod0 = sraRgnCountRects( - cl->modifiedRegion); - - if (i == 0) { - rfbPE(0); - } else { - if (use_threads) { - usleep(1000); - } else { - /* try to get it all */ - rfbCFD(1000*1000); - } - } - dt = dtime(&tm); - i++; - - dt3 += dt; - if (dt3 > spin_lat_max) { - break; - } - req1 = sraRgnCountRects( - cl->requestedRegion); - - mod1 = sraRgnCountRects( - cl->modifiedRegion); - -if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d " - "fbu-sent: %d dt: %.4f dt3: %.4f tm: %.4f\n", - req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt3, tm); - - if (req1 != 0 && mod1 == 0) { - dts[got_t3++] = dt3; - break; - } - } - } - - if (! got_t3) { - dt3 = 0.0; - } else { - if (got_t3 == 1) { - dt3 = dts[0]; - } else if (got_t3 == 2) { - dt3 = dts[1]; - } else { - if (dts[2] >= 0.0) { - double rat = dts[1]/dts[2]; - if (rat > 0.5 && rat < 2.0) { - dt3 = dts[1]+dts[2]; - dt3 *= 0.5; - } else { - dt3 = dts[1]; - } - } else { - dt3 = dts[1]; - } - } - } - } - - screen->deferUpdateTime = defer; - screen->httpDir = httpdir; - - dt = dt1 + dt2; - - - if (dt3 <= dt2/2.0) { - /* guess only 1/2 a ping for reply... */ - dt = dt - dt3/2.0; - } - - cmp_rate = cbs/dt; - raw_rate = rbs/dt; - - if (cmp_rate > cmp_max) { - cmp_rate = cmp_max; - } - if (cmp_rate <= cmp_min) { - cmp_rate = cmp_min; - } - - cd->send_cmp_rate = cmp_rate; - cd->send_raw_rate = raw_rate; - - if (dt3 > lat_max) { - dt3 = lat_max; - } - if (dt3 <= lat_min) { - dt3 = lat_min; - } - - cd->latency = dt3; - - rfbLog("client %d network rate %.1f KB/sec (%.1f eff KB/sec)\n", - cd->uid, cmp_rate/1000.0, raw_rate/1000.0); - 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; - } - } else { - screen->displayHook = orig_display_hook; - } -} - -void check_cursor_changes(void) { +static void check_cursor_changes(void) { static double last_push = 0.0; cursor_changes += check_x11_pointer(); @@ -29766,25 +203,7 @@ void check_cursor_changes(void) { cursor_changes = 0; } -/* - * 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); - } -} - -void record_last_fb_update(void) { +static void record_last_fb_update(void) { static int rbs0 = -1; static time_t last_call = 0; time_t now = time(0); @@ -29823,19 +242,7 @@ void record_last_fb_update(void) { } } -void rfbCFD(long usec) { - if (! screen) { - return; - } - if (usec > USEC_MAX) { - usec = USEC_MAX; - } - if (! use_threads) { - rfbCheckFds(screen, usec); - } -} - -int choose_delay(double dt) { +static int choose_delay(double dt) { static double t0 = 0.0, t1 = 0.0, t2 = 0.0, now; static int x0, y0, x1, y1, x2, y2, first = 1; int dx0, dy0, dx1, dy1, dm, i, msec = waitms; @@ -30060,6 +467,7 @@ if (debug_scroll) fprintf(stderr, "watch_loop: LOOP-BACK: %d\n", ret); check_xdamage_state(); check_xrecord_reset(0); check_add_keysyms(); + check_new_passwds(); if (started_as_root) { check_switched_user(); } @@ -30130,13 +538,13 @@ if (debug_scroll) fprintf(stderr, "watch_loop: LOOP-BACK: %d\n", ret); last_dt = dt; } -if ((debug_tiles || debug_scroll > 1 || debug_wireframe > 1) + 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 nap_ok: %d\n", tile_diffs, dt, tm - x11vnc_start, rate/1000000.0, nap_ok); -} + } } @@ -30155,2112 +563,6 @@ if ((debug_tiles || debug_scroll > 1 || debug_wireframe > 1) } /* - * text printed out under -help option - */ -static void print_help(int mode) { -#if !SMALL_FOOTPRINT - char help[] = -"\n" -"x11vnc: allow VNC connections to real X11 displays. %s\n" -"\n" -"Typical usage is:\n" -"\n" -" Run this command in a shell on the remote machine \"far-host\"\n" -" with X session you wish to view:\n" -"\n" -" x11vnc -display :0\n" -"\n" -" Then run this in another window on the machine you are sitting at:\n" -"\n" -" vncviewer far-host:0\n" -"\n" -"Once x11vnc establishes connections with the X11 server and starts listening\n" -"as a VNC server it will print out a string: PORT=XXXX where XXXX is typically\n" -"5900 (the default VNC server port). One would next run something like\n" -"this on the local machine: \"vncviewer hostname:N\" where \"hostname\" is\n" -"the name of the machine running x11vnc and N is XXXX - 5900, i.e. usually\n" -"\"vncviewer hostname:0\".\n" -"\n" -"By default x11vnc will not allow the screen to be shared and it will exit\n" -"as soon as the client disconnects. See -shared and -forever below to override\n" -"these protections. See the FAQ for details how to tunnel the VNC connection\n" -"through an encrypted channel such as ssh(1). In brief:\n" -"\n" -" ssh -L 5900:localhost:5900 far-host 'x11vnc -localhost -display :0'\n" -"\n" -" vncviewer -encodings 'copyrect tight zrle hextile' localhost:0\n" -"\n" -"Also, use of a VNC password (-rfbauth or -passwdfile) is strongly recommend.\n" -"\n" -"For additional info see: http://www.karlrunge.com/x11vnc/\n" -" and http://www.karlrunge.com/x11vnc/#faq\n" -"\n" -"\n" -"Rudimentary config file support: if the file $HOME/.x11vncrc exists then each\n" -"line in it is treated as a single command line option. Disable with -norc.\n" -"For each option name, the leading character \"-\" is not required. E.g. a\n" -"line that is either \"forever\" or \"-forever\" may be used and are equivalent.\n" -"Likewise \"wait 100\" or \"-wait 100\" are acceptable and equivalent lines.\n" -"The \"#\" character comments out to the end of the line in the usual way\n" -"(backslash it for a literal). Leading and trailing whitespace is trimmed off.\n" -"Lines may be continued with a \"\\\" as the last character of a line (it\n" -"becomes a space character).\n" -"\n" -"Options:\n" -"\n" -"-display disp X11 server display to connect to, usually :0. The X\n" -" server process must be running on same machine and\n" -" support MIT-SHM. Equivalent to setting the DISPLAY\n" -" environment variable to \"disp\".\n" -"-auth file Set the X authority file to be \"file\", equivalent to\n" -" setting the XAUTHORITY environment variable to \"file\"\n" -" before startup. Same as -xauth file. See Xsecurity(7),\n" -" xauth(1) man pages for more info.\n" -"\n" -"-id windowid Show the window corresponding to \"windowid\" not\n" -" the entire display. New windows like popup menus,\n" -" transient toplevels, etc, may not be seen or may be\n" -" clipped. Disabling SaveUnders or BackingStore in the\n" -" X server may help show them. x11vnc may crash if the\n" -" window is initially partially obscured, changes size,\n" -" is iconified, etc. Some steps are taken to avoid this\n" -" and the -xrandr mechanism is used to track resizes. Use\n" -" xwininfo(1) to get the window id, or use \"-id pick\"\n" -" to have x11vnc run xwininfo(1) for you and extract\n" -" the id. The -id option is useful for exporting very\n" -" simple applications (e.g. the current view on a webcam).\n" -"-sid windowid As -id, but instead of using the window directly it\n" -" shifts a root view to it: this shows SaveUnders menus,\n" -" etc, although they will be clipped if they extend beyond\n" -" the window.\n" -"-clip WxH+X+Y Only show the sub-region of the full display that\n" -" corresponds to the rectangle with size WxH and offset\n" -" +X+Y. The VNC display has size WxH (i.e. smaller than\n" -" the full display). This also works for -id/-sid mode\n" -" where the offset is relative to the upper left corner\n" -" of the selected window.\n" -"\n" -"-flashcmap In 8bpp indexed color, let the installed colormap flash\n" -" as the pointer moves from window to window (slow).\n" -"-shiftcmap n Rare problem, but some 8bpp displays use less than 256\n" -" colorcells (e.g. 16-color grayscale, perhaps the other\n" -" bits are used for double buffering) *and* also need to\n" -" shift the pixels values away from 0, .., ncells. \"n\"\n" -" indicates the shift to be applied to the pixel values.\n" -" To see the pixel values set DEBUG_CMAP=1 to print out\n" -" a colormap histogram. Example: -shiftcmap 240\n" -"-notruecolor For 8bpp displays, force indexed color (i.e. a colormap)\n" -" even if it looks like 8bpp TrueColor (rare problem).\n" -"-visual n Experimental option: probably does not do what you\n" -" think. It simply *forces* the visual used for the\n" -" framebuffer; this may be a bad thing... (e.g. messes\n" -" up colors or cause a crash). It is useful for testing\n" -" and for some workarounds. n may be a decimal number,\n" -" or 0x hex. Run xdpyinfo(1) for the values. One may\n" -" also use \"TrueColor\", etc. see <X11/X.h> for a list.\n" -" If the string ends in \":m\" then for better or for\n" -" worse the visual depth is forced to be m.\n" -"\n" -"-overlay Handle multiple depth visuals on one screen, e.g. 8+24\n" -" and 24+8 overlay visuals (the 32 bits per pixel are\n" -" packed with 8 for PseudoColor and 24 for TrueColor).\n" -"\n" -" Currently -overlay only works on Solaris via\n" -" XReadScreen(3X11) and IRIX using XReadDisplay(3).\n" -" On Solaris there is a problem with image \"bleeding\"\n" -" around transient popup menus (but not for the menu\n" -" itself): a workaround is to disable SaveUnders\n" -" by passing the \"-su\" argument to Xsun (in\n" -" /etc/dt/config/Xservers).\n" -"\n" -" Use -overlay as a workaround for situations like these:\n" -" Some legacy applications require the default visual to\n" -" be 8bpp (8+24), or they will use 8bpp PseudoColor even\n" -" when the default visual is depth 24 TrueColor (24+8).\n" -" In these cases colors in some windows will be incorrect\n" -" in x11vnc unless -overlay is used. Another use of\n" -" -overlay is to enable showing the exact mouse cursor\n" -" shape (details below).\n" -"\n" -" Under -overlay, performance will be somewhat slower\n" -" due to the extra image transformations required.\n" -" For optimal performance do not use -overlay, but rather\n" -" configure the X server so that the default visual is\n" -" depth 24 TrueColor and try to have all apps use that\n" -" visual (e.g. some apps have -use24 or -visual options).\n" -"-overlay_nocursor Sets -overlay, but does not try to draw the exact mouse\n" -" cursor shape using the overlay mechanism.\n" -"\n" -"-scale fraction Scale the framebuffer by factor \"fraction\". Values\n" -" less than 1 shrink the fb, larger ones expand it. Note:\n" -" image may not be sharp and response may be slower.\n" -" If \"fraction\" contains a decimal point \".\" it\n" -" is taken as a floating point number, alternatively\n" -" the notation \"m/n\" may be used to denote fractions\n" -" exactly, e.g. -scale 2/3\n" -"\n" -" Scaling Options: can be added after \"fraction\" via\n" -" \":\", to supply multiple \":\" options use commas.\n" -" If you just want a quick, rough scaling without\n" -" blending, append \":nb\" to \"fraction\" (e.g. -scale\n" -" 1/3:nb). No blending is the default for 8bpp indexed\n" -" color, to force blending for this case use \":fb\".\n" -"\n" -" To disable -scrollcopyrect and -wirecopyrect under\n" -" -scale use \":nocr\". If you need to to enable them use\n" -" \":cr\" or specify them explicitly on the command line.\n" -" If a slow link is detected, \":nocr\" may be applied\n" -" automatically. Default: %s\n" -"\n" -" More esoteric options: for compatibility with vncviewers\n" -" the scaled width is adjusted to be a multiple of 4:\n" -" to disable this use \":n4\". \":in\" use interpolation\n" -" scheme even when shrinking, \":pad\" pad scaled width\n" -" and height to be multiples of scaling denominator\n" -" (e.g. 3 for 2/3).\n" -"\n" -"-scale_cursor frac By default if -scale is supplied the cursor shape is\n" -" scaled by the same factor. Depending on your usage,\n" -" you may want to scale the cursor independently of the\n" -" screen or not at all. If you specify -scale_cursor\n" -" the cursor will be scaled by that factor. When using\n" -" -scale mode to keep the cursor at its \"natural\" size\n" -" use \"-scale_cursor 1\". Most of the \":\" scaling\n" -" options apply here as well.\n" -"\n" -"-viewonly All VNC clients can only watch (default %s).\n" -"-shared VNC display is shared, i.e. more than one viewer can\n" -" connect at the same time (default %s).\n" -"-once Exit after the first successfully connected viewer\n" -" disconnects, opposite of -forever. This is the Default.\n" -"-forever Keep listening for more connections rather than exiting\n" -" as soon as the first client(s) disconnect. Same as -many\n" -"-loop Create an outer loop restarting the x11vnc process\n" -" whenever it terminates. -bg and -inetd are ignored in\n" -" this mode. Useful for continuing even if the X server\n" -" terminates and restarts (you will need permission to\n" -" reconnect of course). Use, e.g., -loop100 to sleep\n" -" 100 millisecs between restarts, etc. Default is 2000ms\n" -" (i.e. 2 secs) Use, e.g. -loop300,5 to sleep 300 ms\n" -" and only loop 5 times.\n" -"-timeout n Exit unless a client connects within the first n seconds\n" -" after startup.\n" -"-inetd Launched by inetd(1): stdio instead of listening socket.\n" -" Note: if you are not redirecting stderr to a log file\n" -" (via shell 2> or -o option) you MUST also specify the -q\n" -" option, otherwise the stderr goes to the viewer which\n" -" will cause it to abort. Specifying both -inetd and -q\n" -" and no -o will automatically close the stderr.\n" -"-nofilexfer Disable the TightVNC file transfer extension. (same as\n" -" -disablefiletransfer). Note that when the -viewonly\n" -" option is supplied all file transfers are disabled.\n" -" Also clients that log in viewonly cannot transfer files.\n" -" However, if the remote control mechanism is used to\n" -" change the global or per-client viewonly state the\n" -" filetransfer permissions will NOT change.\n" -"-http Instead of using -httpdir (see below) to specify\n" -" where the Java vncviewer applet is, have x11vnc try\n" -" to *guess* where the directory is by looking relative\n" -" to the program location and in standard locations\n" -" (/usr/local/share/x11vnc/classes, etc).\n" -"-connect string For use with \"vncviewer -listen\" reverse connections.\n" -" If \"string\" has the form \"host\" or \"host:port\"\n" -" the connection is made once at startup. Use commas\n" -" for a list of host's and host:port's.\n" -"\n" -" If \"string\" contains \"/\" it is instead interpreted\n" -" as a file to periodically check for new hosts.\n" -" The first line is read and then the file is truncated.\n" -" Be careful for this usage mode if x11vnc is running as\n" -" root (e.g. via gdm(1), etc).\n" -"-vncconnect Monitor the VNC_CONNECT X property set by the standard\n" -"-novncconnect VNC program vncconnect(1). When the property is\n" -" set to \"host\" or \"host:port\" establish a reverse\n" -" connection. Using xprop(1) instead of vncconnect may\n" -" work (see the FAQ). The -remote control mechanism also\n" -" uses this VNC_CONNECT channel. Default: %s\n" -"\n" -"-allow host1[,host2..] Only allow client connections from hosts matching\n" -" the comma separated list of hostnames or IP addresses.\n" -" Can also be a numerical IP prefix, e.g. \"192.168.100.\"\n" -" to match a simple subnet, for more control build\n" -" libvncserver with libwrap support (See the FAQ). If the\n" -" list contains a \"/\" it instead is a interpreted as a\n" -" file containing addresses or prefixes that is re-read\n" -" each time a new client connects. Lines can be commented\n" -" out with the \"#\" character in the usual way.\n" -"-localhost Same as \"-allow 127.0.0.1\".\n" -"\n" -" Note: if you want to restrict which network interface\n" -" x11vnc listens on, see the -listen option below.\n" -" E.g. \"-listen localhost\" or \"-listen 192.168.3.21\".\n" -" As a special case, the option \"-localhost\" implies\n" -" \"-listen localhost\".\n" -"\n" -" For non-localhost -listen usage, if you use the remote\n" -" control mechanism (-R) to change the -listen interface\n" -" you may need to manually adjust the -allow list (and\n" -" vice versa) to avoid situations where no connections\n" -" (or too many) are allowed.\n" -"\n" -"-nolookup Do not use gethostbyname() or gethostbyaddr() to look up\n" -" host names or IP numbers. Use this if name resolution\n" -" is incorrectly set up and leads to long pauses as name\n" -" lookups time out, etc.\n" -"\n" -"-input string Fine tuning of allowed user input. If \"string\" does\n" -" not contain a comma \",\" the tuning applies only to\n" -" normal clients. Otherwise the part before \",\" is\n" -" for normal clients and the part after for view-only\n" -" clients. \"K\" is for Keystroke input, \"M\" for\n" -" Mouse-motion input, and \"B\" for Button-click input.\n" -" Their presence in the string enables that type of input.\n" -" E.g. \"-input M\" means normal users can only move\n" -" the mouse and \"-input KMB,M\" lets normal users do\n" -" anything and enables view-only users to move the mouse.\n" -" This option is ignored when a global -viewonly is in\n" -" effect (all input is discarded in that case).\n" -"\n" -"-viewpasswd string Supply a 2nd password for view-only logins. The -passwd\n" -" (full-access) password must also be supplied.\n" -"\n" -"-passwdfile filename Specify the libvncserver password via the first line\n" -" of the file \"filename\" (instead of via -passwd on\n" -" the command line where others might see it via ps(1)).\n" -"\n" -" If the filename is prefixed with \"rm:\" it will be\n" -" removed after being read. In general, the password file\n" -" should not be readable by untrusted users (BTW: neither\n" -" should the VNC -rfbauth file: it is NOT encrypted).\n" -"\n" -" Note that only the first 8 characters of a password\n" -" are used.\n" -"\n" -" If multiple non-blank lines exist in the file they are\n" -" all taken as valid passwords. Blank lines are ignored.\n" -" Password lines may be \"commented out\" (ignored) if\n" -" they begin with the charactor \"#\" or the line contains\n" -" the string \"__SKIP__\". Lines may be annotated by use\n" -" of the \"__COMM__\" string: from it to the end of the\n" -" line is ignored. An empty password may be specified\n" -" via the \"__EMPTY__\" string on a line by itself (note\n" -" your viewer might not accept empty passwords).\n" -"\n" -" If the string \"__BEGIN_VIEWONLY__\" appears on a\n" -" line by itself, the remaining passwords are used for\n" -" viewonly access. For compatibility, as a special case\n" -" if the file contains only two password lines the 2nd\n" -" one is automatically taken as the viewonly password.\n" -" Otherwise the \"__BEGIN_VIEWONLY__\" token must be used\n" -" to have viewonly passwords. (tip: make it the 3rd and\n" -" last line to have 2 full-access passwords)\n" - -"-nopw Disable the big warning message when you use x11vnc\n" -" without some sort of password.\n" -"-storepasswd pass file Store password \"pass\" as the VNC password in the\n" -" file \"file\". Once the password is stored the\n" -" program exits. Use the password via \"-rfbauth file\"\n" -"\n" -"-accept string Run a command (possibly to prompt the user at the\n" -" X11 display) to decide whether an incoming client\n" -" should be allowed to connect or not. \"string\" is\n" -" an external command run via system(3) or some special\n" -" cases described below. Be sure to quote \"string\"\n" -" if it contains spaces, shell characters, etc. If the\n" -" external command returns 0 the client is accepted,\n" -" otherwise the client is rejected. See below for an\n" -" extension to accept a client view-only.\n" -"\n" -" If x11vnc is running as root (say from inetd(1) or from\n" -" display managers xdm(1), gdm(1), etc), think about the\n" -" security implications carefully before supplying this\n" -" option (likewise for the -gone option).\n" -"\n" -" Environment: The RFB_CLIENT_IP environment variable will\n" -" be set to the incoming client IP number and the port\n" -" in RFB_CLIENT_PORT (or -1 if unavailable). Similarly,\n" -" RFB_SERVER_IP and RFB_SERVER_PORT (the x11vnc side\n" -" of the connection), are set to allow identification\n" -" of the tcp virtual circuit. The x11vnc process\n" -" id will be in RFB_X11VNC_PID, a client id number in\n" -" RFB_CLIENT_ID, and the number of other connected clients\n" -" in RFB_CLIENT_COUNT. RFB_MODE will be \"accept\"\n" -"\n" -" If \"string\" is \"popup\" then a builtin popup window\n" -" is used. The popup will time out after 120 seconds,\n" -" use \"popup:N\" to modify the timeout to N seconds\n" -" (use 0 for no timeout).\n" -"\n" -" If \"string\" is \"xmessage\" then an xmessage(1)\n" -" invocation is used for the command. xmessage must be\n" -" installed on the machine for this to work.\n" -"\n" -" Both \"popup\" and \"xmessage\" will present an option\n" -" for accepting the client \"View-Only\" (the client\n" -" can only watch). This option will not be presented if\n" -" -viewonly has been specified, in which case the entire\n" -" display is view only.\n" -"\n" -" If the user supplied command is prefixed with something\n" -" like \"yes:0,no:*,view:3 mycommand ...\" then this\n" -" associates the numerical command return code with\n" -" the actions: accept, reject, and accept-view-only,\n" -" respectively. Use \"*\" instead of a number to indicate\n" -" the default action (in case the command returns an\n" -" unexpected value). E.g. \"no:*\" is a good choice.\n" -"\n" -" Note that x11vnc blocks while the external command\n" -" or popup is running (other clients may see no updates\n" -" during this period). So a person sitting a the physical\n" -" display is needed to respond to an popup prompt. (use\n" -" a 2nd x11vnc if you lock yourself out).\n" -"\n" -" More -accept tricks: use \"popupmouse\" to only allow\n" -" mouse clicks in the builtin popup to be recognized.\n" -" Similarly use \"popupkey\" to only recognize\n" -" keystroke responses. These are to help avoid the\n" -" user accidentally accepting a client by typing or\n" -" clicking. All 3 of the popup keywords can be followed\n" -" by +N+M to supply a position for the popup window.\n" -" The default is to center the popup window.\n" -"-gone string As -accept, except to run a user supplied command when\n" -" a client goes away (disconnects). RFB_MODE will be\n" -" set to \"gone\" and the other RFB_* variables are as\n" -" in -accept. Unlike -accept, the command return code\n" -" is not interpreted by x11vnc. Example: -gone 'xlock &'\n" -"\n" -"-users list If x11vnc is started as root (say from inetd(1) or from\n" -" display managers xdm(1), gdm(1), etc), then as soon\n" -" as possible after connections to the X display are\n" -" established try to switch to one of the users in the\n" -" comma separated \"list\". If x11vnc is not running as\n" -" root this option is ignored.\n" -" \n" -" Why use this option? In general it is not needed since\n" -" x11vnc is already connected to the X display and can\n" -" perform its primary functions. The option was added\n" -" to make some of the *external* utility commands x11vnc\n" -" occasionally runs work properly. In particular under\n" -" GNOME and KDE to implement the \"-solid color\" feature\n" -" external commands (gconftool-2 and dcop) must be run\n" -" as the user owning the desktop session. Since this\n" -" option switches userid it also affects the userid used\n" -" to run the processes for the -accept and -gone options.\n" -" It also affects the ability to read files for options\n" -" such as -connect, -allow, and -remap. Note that the\n" -" -connect file is also sometimes written to.\n" -" \n" -" So be careful with this option since in many situations\n" -" its use can decrease security.\n" -" \n" -" The switch to a user will only take place if the\n" -" display can still be successfully opened as that user\n" -" (this is primarily to try to guess the actual owner\n" -" of the session). Example: \"-users fred,wilma,betty\".\n" -" Note that a malicious user \"barney\" by quickly using\n" -" \"xhost +\" when logging in may get x11vnc to switch\n" -" to user \"fred\". What happens next?\n" -" \n" -" Under display managers it may be a long time before\n" -" the switch succeeds (i.e. a user logs in). To make\n" -" it switch immediately regardless if the display\n" -" can be reopened prefix the username with the \"+\"\n" -" character. E.g. \"-users +bob\" or \"-users +nobody\".\n" -" The latter (i.e. switching immediately to user\n" -" \"nobody\") is probably the only use of this option\n" -" that increases security.\n" -" \n" -" To immediately switch to a user *before* connections\n" -" to the X display are made or any files opened use the\n" -" \"=\" character: \"-users =bob\". That user needs to\n" -" be able to open the X display of course.\n" -" \n" -" The special user \"guess=\" means to examine the utmpx\n" -" database (see who(1)) looking for a user attached to\n" -" the display number (from DISPLAY or -display option)\n" -" and try him/her. To limit the list of guesses, use:\n" -" \"-users guess=bob,betty\".\n" -" \n" -" Even more sinister is the special user \"lurk=\" that\n" -" means to try to guess the DISPLAY from the utmpx login\n" -" database as well. So it \"lurks\" waiting for anyone\n" -" to log into an X session and then connects to it.\n" -" Specify a list of users after the = to limit which\n" -" users will be tried. To enable a different searching\n" -" mode, if the first user in the list is something like\n" -" \":0\" or \":0-2\" that indicates a range of DISPLAY\n" -" numbers that will be tried (regardless of whether\n" -" they are in the utmpx database) for all users that\n" -" are logged in. Examples: \"-users lurk=\" and also\n" -" \"-users lurk=:0-1,bob,mary\"\n" -" \n" -" Be especially careful using the \"guess=\" and \"lurk=\"\n" -" modes. They are not recommended for use on machines\n" -" with untrustworthy local users.\n" -" \n" -"-noshm Do not use the MIT-SHM extension for the polling.\n" -" Remote displays can be polled this way: be careful this\n" -" can use large amounts of network bandwidth. This is\n" -" also of use if the local machine has a limited number\n" -" of shm segments and -onetile is not sufficient.\n" -"-flipbyteorder Sometimes needed if remotely polled host has different\n" -" endianness. Ignored unless -noshm is set.\n" -"-onetile Do not use the new copy_tiles() framebuffer mechanism,\n" -" just use 1 shm tile for polling. Limits shm segments\n" -" used to 3.\n" -"\n" -"-solid [color] To improve performance, when VNC clients are connected\n" -" try to change the desktop background to a solid color.\n" -" The [color] is optional: the default color is \"cyan4\".\n" -" For a different one specify the X color (rgb.txt name,\n" -" e.g. \"darkblue\" or numerical \"#RRGGBB\").\n" -"\n" -" Currently this option only works on GNOME, KDE, CDE,\n" -" and classic X (i.e. with the background image on the\n" -" root window). The \"gconftool-2\" and \"dcop\" external\n" -" commands are run for GNOME and KDE respectively.\n" -" Other desktops won't work, e.g. Xfce (send us the\n" -" corresponding commands if you find them). If x11vnc is\n" -" running as root (inetd(1) or gdm(1)), the -users option\n" -" may be needed for GNOME and KDE. If x11vnc guesses\n" -" your desktop incorrectly, you can force it by prefixing\n" -" color with \"gnome:\", \"kde:\", \"cde:\" or \"root:\".\n" -"-blackout string Black out rectangles on the screen. \"string\" is a\n" -" comma separated list of WxH+X+Y type geometries for\n" -" each rectangle. If one of the items on the list is the\n" -" string \"noptr\" the mouse pointer will not be allowed\n" -" to go into a blacked out region.\n" -"-xinerama If your screen is composed of multiple monitors\n" -" glued together via XINERAMA, and that screen is\n" -" not a rectangle this option will try to guess the\n" -" areas to black out (if your system has libXinerama).\n" -"\n" -" In general, we have noticed on XINERAMA displays you\n" -" may need to use the \"-xwarppointer\" option if the mouse\n" -" pointer misbehaves.\n" -"\n" -"-xtrap Use the DEC-XTRAP extension for keystroke and mouse\n" -" input insertion. For use on legacy systems, e.g. X11R5,\n" -" running an incomplete or missing XTEST extension.\n" -" By default DEC-XTRAP will be used if XTEST server grab\n" -" control is missing, use -xtrap to do the keystroke and\n" -" mouse insertion via DEC-XTRAP as well.\n" -"\n" -"-xrandr [mode] If the display supports the XRANDR (X Resize, Rotate\n" -" and Reflection) extension, and you expect XRANDR events\n" -" to occur to the display while x11vnc is running, this\n" -" options indicates x11vnc should try to respond to\n" -" them (as opposed to simply crashing by assuming the\n" -" old screen size). See the xrandr(1) manpage and run\n" -" 'xrandr -q' for more info. [mode] is optional and\n" -" described below.\n" -"\n" -" Since watching for XRANDR events and trapping errors\n" -" increases polling overhead, only use this option if\n" -" XRANDR changes are expected. For example on a rotatable\n" -" screen PDA or laptop, or using a XRANDR-aware Desktop\n" -" where you resize often. It is best to be viewing with a\n" -" vncviewer that supports the NewFBSize encoding, since it\n" -" knows how to react to screen size changes. Otherwise,\n" -" libvncserver tries to do so something reasonable for\n" -" viewers that cannot do this (portions of the screen\n" -" may be clipped, unused, etc).\n" -"\n" -" \"mode\" defaults to \"resize\", which means create a\n" -" new, resized, framebuffer and hope all viewers can cope\n" -" with the change. \"newfbsize\" means first disconnect\n" -" all viewers that do not support the NewFBSize VNC\n" -" encoding, and then resize the framebuffer. \"exit\"\n" -" means disconnect all viewer clients, and then terminate\n" -" x11vnc.\n" -"-padgeom WxH Whenever a new vncviewer connects, the framebuffer is\n" -" replaced with a fake, solid black one of geometry WxH.\n" -" Shortly afterwards the framebuffer is replaced with the\n" -" real one. This is intended for use with vncviewers\n" -" that do not support NewFBSize and one wants to make\n" -" sure the initial viewer geometry will be big enough\n" -" to handle all subsequent resizes (e.g. under -xrandr,\n" -" -remote id:windowid, rescaling, etc.)\n" -"\n" -"-o logfile Write stderr messages to file \"logfile\" instead of\n" -" to the terminal. Same as \"-logfile file\". To append\n" -" to the file use \"-oa file\" or \"-logappend file\".\n" -"-flag file Write the \"PORT=NNNN\" (e.g. PORT=5900) string to\n" -" \"file\" in addition to stdout. This option could be\n" -" useful by wrapper script to detect when x11vnc is ready.\n" -"\n" -"-rc filename Use \"filename\" instead of $HOME/.x11vncrc for rc file.\n" -"-norc Do not process any .x11vncrc file for options.\n" -"\n" -"-h, -help Print this help text.\n" -"-?, -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" -" stderr are lost unless -o logfile is used. Something\n" -" like this could be useful in a script:\n" -" port=`ssh $host \"x11vnc -display :0 -bg\" | grep PORT`\n" -" port=`echo \"$port\" | sed -e 's/PORT=//'`\n" -" port=`expr $port - 5900`\n" -" vncviewer $host:$port\n" -"\n" -"-modtweak Option -modtweak automatically tries to adjust the AltGr\n" -"-nomodtweak and Shift modifiers for differing language keyboards\n" -" between client and host. Otherwise, only a single key\n" -" press/release of a Keycode is simulated (i.e. ignoring\n" -" the state of the modifiers: this usually works for\n" -" identical keyboards). Also useful in resolving cases\n" -" where a Keysym is bound to multiple keys (e.g. \"<\" + \">\"\n" -" and \",\" + \"<\" keys). Default: %s\n" -"-xkb When in modtweak mode, use the XKEYBOARD extension (if\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" -" to -xkb mode. Use this option to help x11vnc in the\n" -" reverse problem it tries to solve: Keysym -> Keycode(s)\n" -" when ambiguities exist (more than one Keycode per\n" -" Keysym). Run 'xmodmap -pk' to see your keymapping.\n" -" Example: \"-skip_keycodes 94,114\"\n" -"-sloppy_keys Experimental option that tries to correct some\n" -" \"sloppy\" key behavior. E.g. if at the viewer you\n" -" press Shift+Key but then release the Shift before\n" -" Key that could give rise to extra unwanted characters\n" -" (usually only between keyboards of different languages).\n" -" Only use this option if you observe problems with\n" -" some keystrokes.\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 same\n" -" key, or 20 downs in a row for the same modifier key!\n" -" Setting -skip_dups means to skip these duplicates and\n" -" just process the first event. Note: some VNC viewers\n" -" assume they can send down's without the corresponding\n" -" up's and so you should not set this option for\n" -" these viewers (symptom: some keys do not autorepeat)\n" -" Default: %s\n" -"-add_keysyms If a Keysym is received from a VNC viewer and that\n" -"-noadd_keysyms Keysym does not exist in the X server, then add the\n" -" Keysym to the X server's keyboard mapping on an unused\n" -" key. Added Keysyms will be removed periodically and\n" -" also 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" -" an international keyboard (AltGr or ISO_Level3_Shift),\n" -" and the OS or keyboard where the VNC viewer is run\n" -" is not identical to that of the X server, and you are\n" -" having problems typing some keys. Implies -nobell.\n" -#endif -"-clear_mods At startup and exit clear the modifier keys by sending\n" -" KeyRelease for each one. The Lock modifiers are skipped.\n" -" Used to clear the state if the display was accidentally\n" -" left with any pressed down.\n" -"-clear_keys As -clear_mods, except try to release any pressed key.\n" -" Note that this option and -clear_mods can interfere\n" -" with a person typing at the physical keyboard.\n" -"-remap string Read Keysym remappings from file named \"string\".\n" -" Format is one pair of Keysyms per line (can be name\n" -" or hex value) separated by a space. If no file named\n" -" \"string\" exists, it is instead interpreted as this\n" -" form: key1-key2,key3-key4,... See <X11/keysymdef.h>\n" -" header file for a list of Keysym names, or use xev(1).\n" -" 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" -"\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: 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.\n" -"\n" -" Use \"-norepeat N\" to set how many times norepeat will\n" -" be reset if something else (e.g. X session manager)\n" -" undoes it. The default is 2. Use a negative value\n" -" for unlimited resets.\n" -"\n" -"-nofb Ignore video framebuffer: only process keyboard and\n" -" pointer. Intended for use with Win2VNC and x2vnc\n" -" dual-monitor setups.\n" -"-nobell Do not watch for XBell events. (no beeps will be heard)\n" -" Note: XBell monitoring requires the XKEYBOARD extension.\n" -"-nosel Do not manage exchange of X selection/cutbuffer between\n" -" VNC viewers and the X server.\n" -"-noprimary Do not poll the PRIMARY selection for changes to send\n" -" back to clients. (PRIMARY is still set on received\n" -" changes, however).\n" -"-seldir string If direction string is \"send\", only send the selection\n" -" to viewers, and if it is \"recv\" only receive it from\n" -" viewers. To work around apps setting the selection\n" -" too frequently and messing up the other end. You can\n" -" actually supply a comma separated list of directions,\n" -" including \"debug\" to turn on debugging output.\n" -"\n" -"-cursor [mode] Sets how the pointer cursor shape (little icon at the\n" -"-nocursor mouse pointer) should be handled. The \"mode\" string\n" -" is optional and is described below. The default\n" -" is to show some sort of cursor shape(s). How this\n" -" is done depends on the VNC viewer and the X server.\n" -" Use -nocursor to disable cursor shapes completely.\n" -"\n" -" Some VNC viewers support the TightVNC CursorPosUpdates\n" -" and CursorShapeUpdates extensions (cuts down on\n" -" network traffic by not having to send the cursor image\n" -" every time the pointer is moved), in which case these\n" -" extensions are used (see -nocursorshape and -nocursorpos\n" -" below to disable). For other viewers the cursor shape\n" -" is written directly to the framebuffer every time the\n" -" pointer is moved or changed and gets sent along with\n" -" the other framebuffer updates. In this case, there\n" -" will be some lag between the vnc viewer pointer and\n" -" the remote cursor position.\n" -"\n" -" If the X display supports retrieving the cursor shape\n" -" information from the X server, then the default is\n" -" to use that mode. On Solaris this can be done with\n" -" the SUN_OVL extension using -overlay (see also the\n" -" -overlay_nocursor option). A similar overlay scheme\n" -" is used on IRIX. Xorg (e.g. Linux) and recent Solaris\n" -" Xsun servers support the XFIXES extension to retrieve\n" -" the exact cursor shape from the X server. If XFIXES\n" -" is present it is preferred over Overlay and is used by\n" -" default (see -noxfixes below). This can be disabled\n" -" with -nocursor, and also some values of the \"mode\"\n" -" option below.\n" -" \n" -" Note that under XFIXES cursors with transparency (alpha\n" -" channel) will usually not be exactly represented and one\n" -" may find Overlay preferable. See also the -alphacut\n" -" and -alphafrac options below as fudge factors to try\n" -" to improve the situation for cursors with transparency\n" -" for a given theme.\n" -"\n" -" The \"mode\" string can be used to fine-tune the\n" -" displaying of cursor shapes. It can be used the\n" -" following ways:\n" -"\n" -" \"-cursor arrow\" - just show the standard arrow\n" -" nothing more or nothing less.\n" -"\n" -" \"-cursor none\" - same as \"-nocursor\"\n" -"\n" -" \"-cursor X\" - when the cursor appears to be on the\n" -" root window, draw the familiar X shape. Some desktops\n" -" such as GNOME cover up the root window completely,\n" -" and so this will not work, try \"X1\", etc, to try to\n" -" shift the tree depth. On high latency links or slow\n" -" machines there will be a time lag between expected and\n" -" the actual cursor shape.\n" -"\n" -" \"-cursor some\" - like \"X\" but use additional\n" -" heuristics to try to guess if the window should have\n" -" a windowmanager-like resizer cursor or a text input\n" -" I-beam cursor. This is a complete hack, but may be\n" -" useful in some situations because it provides a little\n" -" more feedback about the cursor shape.\n" -"\n" -" \"-cursor most\" - try to show as many cursors as\n" -" possible. Often this will only be the same as \"some\"\n" -" unless the display has overlay visuals or XFIXES\n" -" extensions available. On Solaris and IRIX if XFIXES\n" -" is not available, -overlay mode will be attempted.\n" -"\n" -"-arrow n Choose an alternate \"arrow\" cursor from a set of\n" -" some common ones. n can be 1 to %d. Default is: %d\n" -" Ignored when in XFIXES cursor-grabbing mode.\n" -"\n" -"-noxfixes Do not use the XFIXES extension to draw the exact cursor\n" -" shape even if it is available.\n" -"-alphacut n When using the XFIXES extension for the cursor shape,\n" -" cursors with transparency will not usually be displayed\n" -" exactly (but opaque ones will). This option sets n as\n" -" a cutoff for cursors that have transparency (\"alpha\n" -" channel\" with values ranging from 0 to 255) Any cursor\n" -" pixel with alpha value less than n becomes completely\n" -" transparent. Otherwise the pixel is completely opaque.\n" -" Default %d\n" -" \n" -"-alphafrac fraction With the threshold in -alphacut some cursors will become\n" -" almost completely transparent because their alpha values\n" -" are not high enough. For those cursors adjust the\n" -" alpha threshold until fraction of the non-zero alpha\n" -" channel pixels become opaque. Default %.2f\n" -"-alpharemove By default, XFIXES cursors pixels with transparency have\n" -" the alpha factor multiplied into the RGB color values\n" -" (i.e. that corresponding to blending the cursor with a\n" -" black background). Specify this option to remove the\n" -" alpha factor. (useful for light colored semi-transparent\n" -" cursors).\n" -"-noalphablend In XFIXES mode do not send cursor alpha channel data\n" -" to libvncserver. The default is to send it. The\n" -" alphablend effect will only be visible in -nocursorshape\n" -" mode or for clients with cursorshapeupdates turned\n" -" off. (However there is a hack for 32bpp with depth 24,\n" -" it uses the extra 8 bits to store cursor transparency\n" -" for use with a hacked vncviewer that applies the\n" -" transparency locally. See the FAQ for more info).\n" -"\n" -"-nocursorshape Do not use the TightVNC CursorShapeUpdates extension\n" -" even if clients support it. See -cursor above.\n" -"-cursorpos Option -cursorpos enables sending the X cursor position\n" -"-nocursorpos back to all vnc clients that support the TightVNC\n" -" CursorPosUpdates extension. Other clients will be able\n" -" to see the pointer motions. Default: %s\n" -"-xwarppointer Move the pointer with XWarpPointer(3X) instead of\n" -" the XTEST extension. Use this as a workaround\n" -" if the pointer motion behaves incorrectly, e.g.\n" -" on touchscreens or other non-standard setups.\n" -" Also sometimes needed on XINERAMA displays.\n" -"\n" -"-buttonmap string String to remap mouse buttons. Format: IJK-LMN, this\n" -" maps buttons I -> L, etc., e.g. -buttonmap 13-31\n" -"\n" -" Button presses can also be mapped to keystrokes: replace\n" -" a button digit on the right of the dash with :<sym>:\n" -" or :<sym1>+<sym2>: etc. for multiple keys. For example,\n" -" if the viewing machine has a mouse-wheel (buttons 4 5)\n" -" but the x11vnc side does not, these will do scrolls:\n" -" -buttonmap 12345-123:Prior::Next:\n" -" -buttonmap 12345-123:Up+Up+Up::Down+Down+Down:\n" -"\n" -" See <X11/keysymdef.h> header file for a list of Keysyms,\n" -" or use the xev(1) program. Note: mapping of button\n" -" clicks to Keysyms may not work if -modtweak or -xkb is\n" -" needed for the Keysym.\n" -"\n" -" If you include a modifier like \"Shift_L\" the\n" -" modifier's up/down state is toggled, e.g. to send\n" -" \"The\" use :Shift_L+t+Shift_L+h+e: (the 1st one is\n" -" shift down and the 2nd one is shift up). (note: the\n" -" initial state of the modifier is ignored and not reset)\n" -" To include button events use \"Button1\", ... etc.\n" -"\n" -"-nodragging Do not update the display during mouse dragging events\n" -" (mouse button held down). Greatly improves response on\n" -" slow setups, but you lose all visual feedback for drags,\n" -" text selection, and some menu traversals. It overrides\n" -" any -pointer_mode setting.\n" -"\n" -"-wireframe [str] Try to detect window moves or resizes when a mouse\n" -"-nowireframe button is held down and show a wireframe instead of\n" -" the full opaque window. This is based completely on\n" -" heuristics and may not always work: it depends on your\n" -" window manager and even how you move things around.\n" -" See -pointer_mode below for discussion of the \"bogging\n" -" down\" problem this tries to avoid.\n" -" Default: %s\n" -"\n" -" Shorter aliases: -wf [str] and -nowf\n" -"\n" -" The value \"str\" is optional and, of course, is\n" -" packed with many tunable parameters for this scheme:\n" -"\n" -" Format: shade,linewidth,percent,T+B+L+R,mod,t1+t2+t3+t4\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" -" \"shade\" indicate the \"color\" for the wireframe,\n" -" usually a greyscale: 0-255, however for 16 and 32bpp you\n" -" can specify an rgb.txt X color (e.g. \"dodgerblue\") or\n" -" a value > 255 is treated as RGB (e.g. red is 0xff0000).\n" -" \"linewidth\" sets the width of the wireframe in pixels.\n" -" \"percent\" indicates to not apply the wireframe scheme\n" -" to windows with area less than this percent of the\n" -" full screen.\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 wireframing.\n" -" This is a speedup to quickly exclude a window from being\n" -" wireframed: set them all to zero to not try the speedup\n" -" (scrolling and selecting text will likely be slower).\n" -"\n" -" \"mod\" specifies if a button down event in the\n" -" interior of the window with a modifier key (Alt, Shift,\n" -" etc.) down should indicate a wireframe opportunity.\n" -" It can be \"0\" or \"none\" to skip it, \"1\" or \"all\"\n" -" to apply it to any modifier, or \"Shift\", \"Alt\",\n" -" \"Control\", \"Meta\", \"Super\", or \"Hyper\" to only\n" -" apply for that type of modifier key.\n" -"\n" -" \"t1+t2+t3+t4\" specify four floating point times in\n" -" seconds: t1 is how long to wait for the pointer to move,\n" -" t2 is how long to wait for the window to start moving\n" -" or being resized (for some window managers this can be\n" -" rather long), t3 is how long to keep a wireframe moving\n" -" before repainting the window. t4 is the minimum time\n" -" between sending wireframe \"animations\". If a slow\n" -" link is detected, these values may be automatically\n" -" changed to something better for a slow link.\n" -"\n" -"-wirecopyrect mode Since the -wireframe mechanism evidently tracks moving\n" -"-nowirecopyrect windows accurately, a speedup can be obtained by\n" -" telling the VNC viewers to locally copy the translated\n" -" window region. This is the VNC CopyRect encoding:\n" -" the framebuffer update doesn't need to send the actual\n" -" new image data.\n" -"\n" -" Shorter aliases: -wcr [mode] and -nowcr\n" -"\n" -" \"mode\" can be \"never\" (same as -nowirecopyrect)\n" -" to never try the copyrect, \"top\" means only do it if\n" -" the window was not covered by any other windows, and\n" -" \"always\" means to translate the orginally unobscured\n" -" region (this may look odd as the remaining pieces come\n" -" in, but helps on a slow link). Default: \"%s\"\n" -"\n" -" Note: there can be painting errors or slow response\n" -" when using -scale so you may want to disable CopyRect\n" -" in this case \"-wirecopyrect never\" on the command\n" -" line or by remote-control. Or you can also use the\n" -" \"-scale xxx:nocr\" scale option.\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" -" to \"snoop\" on X applications (currently for certain\n" -" XCopyArea and XConfigureWindow X protocol requests).\n" -" Examples: Hitting <Return> in a terminal window when the\n" -" cursor was at the bottom, the text scrolls up one line.\n" -" Hitting <Down> arrow in a web browser window, the web\n" -" page scrolls up a small amount. Or scrolling with a\n" -" scrollbar or mouse wheel.\n" -"\n" -" Shorter aliases: -scr [mode] and -noscr\n" -"\n" -" This scheme will not always detect scrolls, but when\n" -" 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, 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" -" Screen clearing kludges: for testing at least, there\n" -" are some \"magic key sequences\" (must be done in less\n" -" than 1 second) to aid repairing painting errors that\n" -" may be seen when using this mode:\n" -"\n" -" 3 Alt_L's in a row: resend whole screen,\n" -" 4 Alt_L's in a row: reread and resend whole screen,\n" -" 3 Super_L's in a row: mark whole screen for polling,\n" -" 4 Super_L's in a row: reset RECORD context,\n" -" 5 Super_L's in a row: try to push a black screen\n" -"\n" -" note: Alt_L is the Left \"Alt\" key (a single key)\n" -" Super_L is the Left \"Super\" key (Windows flag).\n" -" Both of these are modifier keys, and so should not\n" -" generate characters when pressed by themselves. Also,\n" -" your VNC viewer may have its own refresh hot-key\n" -" or button.\n" -"\n" -" \"mode\" can be \"never\" (same as -noscrollcopyrect)\n" -" to never try the copyrect, \"keys\" means to try it\n" -" in response to keystrokes only, \"mouse\" means to\n" -" try it in response to mouse events only, \"always\"\n" -" means to do both. Default: \"%s\"\n" -"\n" -" Note: there can be painting errors or slow response\n" -" when using -scale so you may want to disable CopyRect\n" -" in this case \"-scrollcopyrect never\" on the command\n" -" line or by remote-control. Or you can also use the\n" -" \"-scale xxx:nocr\" scale option.\n" -"\n" -"-scr_area n Set the minimum area in pixels for a rectangle\n" -" to be considered for the -scrollcopyrect detection\n" -" scheme. This is to avoid wasting the effort on small\n" -" rectangles that would be quickly updated the normal way.\n" -" E.g. suppose an app updated the position of its skinny\n" -" scrollbar first and then shifted the large panel\n" -" 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_term list Yet another cosmetic kludge. Apply shell/terminal\n" -" heuristics to applications matching comma separated\n" -" list (same as for -scr_skip/-scr_inc). For example an\n" -" annoying transient under scroll detection is if you\n" -" hit Enter in a terminal shell with full text window,\n" -" the solid text cursor block will be scrolled up.\n" -" So for a short time there are two (or more) block\n" -" cursors on the screen. There are similar scenarios,\n" -" (e.g. an output line is duplicated).\n" -" \n" -" These transients are induced by the approximation of\n" -" scroll detection (e.g. it detects the scroll, but not\n" -" the fact that the block cursor was cleared just before\n" -" the scroll). In nearly all cases these transient errors\n" -" are repaired when the true X framebuffer is consulted\n" -" by the normal polling. But they are distracting, so\n" -" what this option provides is extra \"padding\" near the\n" -" bottom of the terminal window: a few extra lines near\n" -" the bottom will not be scrolled, but rather updated\n" -" from the actual X framebuffer. This usually reduces\n" -" the annoying artifacts. Use \"none\" to disable.\n" -" Default: \"%s\"\n" -"\n" -"-scr_keyrepeat lo-hi If a key is held down (or otherwise repeats rapidly) and\n" -" this induces a rapid sequence of scrolls (e.g. holding\n" -" down an Arrow key) the \"scrollcopyrect\" detection\n" -" and overhead may not be able to keep up. A time per\n" -" single scroll estimate is performed and if that estimate\n" -" predicts a sustainable scrollrate of keys per second\n" -" between \"lo\" and \"hi\" then repeated keys will be\n" -" DISCARDED to maintain the scrollrate. For example your\n" -" key autorepeat may be 25 keys/sec, but for a large\n" -" window or slow link only 8 scrolls per second can be\n" -" sustained, then roughly 2 out of every 3 repeated keys\n" -" will be discarded during this period. Default: \"%s\"\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" -"-fixscreen string Periodically \"repair\" the screen based on settings\n" -" in \"string\". Hopefully you won't need this option,\n" -" it is intended for cases when the -scrollcopyrect or\n" -" -wirecopyrect features leave too many painting errors,\n" -" but it can be used for any scenario. This option\n" -" periodically performs costly operations and so\n" -" interactive response may be reduced when it is on.\n" -" You can use 3 Alt_L's (the Left \"Alt\" key) taps in a\n" -" row described under -scrollcopyrect instead to manually\n" -" request a screen repaint when it is needed.\n" -"\n" -" \"string\" is a comma separated list of one or more of\n" -" the following: \"V=t\", \"C=t\", and \"X=t\". In these\n" -" \"t\" stands for a time in seconds (it is a floating\n" -" point even though one should usually use values > 2 to\n" -" avoid wasting resources). V sets how frequently the\n" -" entire screen should be sent to viewers (it is like the\n" -" 3 Alt_L's). C sets how long to wait after a CopyRect\n" -" to repaint the full screen. X sets how frequently\n" -" to reread the full X11 framebuffer from the X server\n" -" and push it out to connected viewers. Use of X should\n" -" be rare, please report a bug if you find you need it.\n" -" Examples: -fixscreen V=10 -fixscreen C=10\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" -"-noxrecord Disable any use of the RECORD extension. This is\n" -" currently used by the -scrollcopyrect scheme and to\n" -" monitor X server grabs.\n" -"\n" -"-grab_buster Some of the use of the RECORD extension can leave a\n" -"-nograb_buster tiny window for XGrabServer deadlock. This is only if\n" -" the whole-server grabbing application expects mouse or\n" -" keyboard input before releasing the grab. It is usually\n" -" a window manager that does this. x11vnc takes care to\n" -" avoid the the problem, but if caught x11vnc will freeze.\n" -" Without -grab_buster, the only solution is to go the\n" -" physical display and give it some input to satisfy the\n" -" grabbing app. Or manually kill and restart the window\n" -" manager if that is feasible. With -grab_buster, x11vnc\n" -" will fork a helper thread and if x11vnc appears to be\n" -" stuck in a grab after a period of time (20-30 sec) then\n" -" it will inject some user input: button clicks, Escape,\n" -" mouse motion, etc to try to break the grab. If you\n" -" experience a lot of grab deadlock, please report a bug.\n" -"\n" -"-debug_grabs Turn on debugging info printout with respect to\n" -" XGrabServer() deadlock for -scrollcopyrect mode.\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\n" -" changes when you drag a large window around opaquely.\n" -" Neither x11vnc's screen polling and vnc compression\n" -" routines nor the bandwidth to the vncviewers can keep\n" -" up these rapid screen changes: everything will bog down\n" -" when dragging or scrolling. So a scheme has to be used\n" -" to \"eat\" much of that pointer input before re-polling\n" -" the screen and sending out framebuffer updates. The\n" -" mode number \"n\" can be 0 to %d and selects one of\n" -" the schemes desribed below.\n" -"\n" -" Note that the -wireframe and -scrollcopyrect modes\n" -" complement -pointer_mode by detecting (and improving)\n" -" certain periods of \"rapid screen change\".\n" -"\n" -" n=0: does the same as -nodragging. (all screen polling\n" -" is suspended if a mouse button is pressed.)\n" -"\n" -" n=1: was the original scheme used to about Jan 2004:\n" -" it basically just skips -input_skip keyboard or pointer\n" -" events before repolling the screen.\n" -"\n" -" n=2 is an improved scheme: by watching the current rate\n" -" 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 a dynamic -nodragging mode: it detects\n" -" when the mouse motion has paused and then refreshes\n" -" the display.\n" -"\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 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" -"\n" -" To try out the different pointer modes to see which\n" -" one gives the best response for your usage, it is\n" -" convenient to use the remote control function, for\n" -" example \"x11vnc -R pm:4\" or the tcl/tk gui (Tuning ->\n" -" pointer_mode -> n).\n" -"\n" -"-input_skip n For the pointer handling when non-threaded: try to\n" -" read n user input events before scanning display. n < 0\n" -" means to act as though there is always user input.\n" -" Default: %d\n" -"\n" -"-speeds rd,bw,lat x11vnc tries to estimate some speed parameters that\n" -" are used to optimize scheduling (e.g. -pointer_mode\n" -" 4, -wireframe, -scrollcopyrect) and other things.\n" -" Use the -speeds option to set these manually.\n" -" The triple \"rd,bw,lat\" corresponds to video h/w\n" -" read rate in MB/sec, network bandwidth to clients in\n" -" KB/sec, and network latency to clients in milliseconds,\n" -" respectively. If a value is left blank, e.g. \"-speeds\n" -" ,100,15\", then the internal scheme is used to estimate\n" -" the empty value(s).\n" -"\n" -" Typical PC video cards have read rates of 5-10 MB/sec.\n" -" If the framebuffer is in main memory instead of video\n" -" h/w (e.g. SunRay, shadowfb, dummy driver, Xvfb), the\n" -" read rate may be much faster. \"x11perf -getimage500\"\n" -" can be used to get a lower bound (remember to factor\n" -" in the bytes per pixel). It is up to you to estimate\n" -" the network bandwith and latency to clients. For the\n" -" latency the ping(1) command can be used.\n" -"\n" -" For convenience there are some aliases provided,\n" -" e.g. \"-speeds modem\". The aliases are: \"modem\" for\n" -" 6,4,200; \"dsl\" for 6,100,50; and \"lan\" for 6,5000,1\n" -"\n" -"-wmdt string For some features, e.g. -wireframe and -scrollcopyrect,\n" -" x11vnc has to work around issues for certain window\n" -" managers or desktops (currently kde and xfce).\n" -" By default it tries to guess which one, but it can\n" -" guess incorrectly. Use this option to indicate which\n" -" wm/dt. \"string\" can be \"gnome\", \"kde\", \"cde\",\n" -" \"xfce\", or \"root\" (classic X wm). Anything else\n" -" is interpreted as \"root\".\n" -"\n" -"-debug_pointer Print debugging output for every pointer event.\n" -"-debug_keyboard Print debugging output for every keyboard event.\n" -" Same as -dp and -dk, respectively. Use multiple\n" -" times for more output.\n" -"\n" -"-defer time Time in ms to wait for updates before sending to client\n" -" (deferUpdateTime) Default: %d\n" -"-wait time Time in ms to pause between screen polls. Used to cut\n" -" down on load. Default: %d\n" -"-wait_ui factor Factor by which to cut the -wait time if there\n" -" has been recent user input (pointer or keyboard).\n" -" Improves response, but increases the load whenever you\n" -" are moving the mouse or typing. Default: %.2f\n" -"-nowait_bog Do not detect if the screen polling is \"bogging down\"\n" -" and sleep more. Some activities with no user input can\n" -" slow things down a lot: consider a large terminal window\n" -" with a long build running in it continously streaming\n" -" text output. By default x11vnc will try to detect this\n" -" (3 screen polls in a row each longer than 0.25 sec with\n" -" no user input), and sleep up to 1.5 secs to let things\n" -" \"catch up\". Use this option to disable that detection.\n" -"-slow_fb time Floating point time in seconds delay all screen polling.\n" -" For special purpose usage where a low frame rate is\n" -" acceptable and desirable, but you want the user input\n" -" processed at the normal rate so you cannot use -wait.\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" -"-sb time Time in seconds after NO activity (e.g. screen blank)\n" -" to really throttle down the screen polls (i.e. sleep\n" -" for about 1.5 secs). Use 0 to disable. Default: %d\n" -"\n" -"-noxdamage Do not use the X DAMAGE extension to detect framebuffer\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" -" and 2) detects changed areas (small ones by default)\n" -" more quickly.\n" -"\n" -" Currently the DAMAGE extension is overly conservative\n" -" and often reports large areas (e.g. a whole terminal\n" -" or browser window) as damaged even though the actual\n" -" changed region is much smaller (sometimes just a few\n" -" pixels). So heuristics were introduced to skip large\n" -" areas and use the damage rectangles only as \"hints\"\n" -" for the traditional scanline polling. The following\n" -" tuning parameters are introduced to adjust this\n" -" behavior:\n" -"\n" -"-xd_area A Set the largest DAMAGE rectangle area \"A\" (in\n" -" pixels: width * height) to trust as truly damaged:\n" -" the rectangle will be copied from the framebuffer\n" -" (slow) no matter what. Set to zero to trust *all*\n" -" rectangles. Default: %d\n" -"-xd_mem f Set how long DAMAGE rectangles should be \"remembered\",\n" -" \"f\" is a floating point number and is in units of the\n" -" scanline repeat cycle time (%d iterations). The default\n" -" (%.1f) should give no painting problems. Increase it if\n" -" there are problems or decrease it to live on the edge\n" -" (perhaps useful on a slow machine).\n" -"\n" -"-sigpipe string Broken pipe (SIGPIPE) handling. \"string\" can be\n" -" \"ignore\" or \"exit\". For \"ignore\" libvncserver\n" -" will handle the abrupt loss of a client and continue,\n" -" for \"exit\" x11vnc will cleanup and exit at the 1st\n" -" broken connection. Default: \"ignore\". This option\n" -" is obsolete.\n" -"-threads Whether or not to use the threaded libvncserver\n" -"-nothreads algorithm [rfbRunEventLoop] if libpthread is available\n" -" Default: %s\n" -"\n" -"-fs f If the fraction of changed tiles in a poll is greater\n" -" than f, the whole screen is updated. Default: %.2f\n" -"-gaps n Heuristic to fill in gaps in rows or cols of n or\n" -" less tiles. Used to improve text paging. Default: %d\n" -"-grow n Heuristic to grow islands of changed tiles n or wider\n" -" 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" -" memory and examine that copy for changes. Under some\n" -" circumstances this will improve interactive response,\n" -" or at least make things look smoother, but in others\n" -" (most!) it will make the response worse. If the video\n" -" h/w fb is such that reading small tiles is very slow\n" -" this mode could help. To keep the \"framerate\" up\n" -" the screen size x bpp cannot be too large. Note that\n" -" this mode is very wasteful of memory I/O resources\n" -" (it makes full screen copies even if nothing changes).\n" -" It may be of use in video capture-like applications,\n" -" or where window tearing is a problem.\n" -"\n" -"-rawfb string Experimental option, instead of polling X, poll the\n" -" memory object specified in \"string\". For shared\n" -" memory segments it is of the form: \"shm:N@WxHxB\"\n" -" which specifies a shmid N and framebuffer Width, Height,\n" -" and Bits per pixel. To memory map mmap(2) a file use:\n" -" \"map:/path/to/a/file@WxHxB\". If there is trouble\n" -" with mmap, use \"file:/...\" for slower lseek(2)\n" -" based reading. If you do not supply a type \"map\"\n" -" is assumed if the file exists.\n" -"\n" -" If string is \"setup:cmd\", then the command \"cmd\"\n" -" is run and the first line from it is read and used\n" -" as \"string\". This allows initializing the device,\n" -" determining WxHxB, etc. These are often done as root\n" -" so take care.\n" -"\n" -" Optional suffixes are \":R/G/B\" and \"+O\" to specify\n" -" red, green, and blue masks and an offset into the\n" -" memory object. If the masks are not provided x11vnc\n" -" guesses them based on the bpp.\n" -"\n" -" Examples:\n" -" -rawfb shm:210337933@800x600x32:ff/ff00/ff0000\n" -" -rawfb map:/dev/fb0@1024x768x32\n" -" -rawfb map:/tmp/Xvfb_screen0@640x480x8+3232\n" -" -rawfb file:/tmp/my.pnm@250x200x24+37\n" -"\n" -" (see ipcs(1) and fbset(1) for the first two examples)\n" -"\n" -" All user input is discarded by default (but see the\n" -" -pipeinput option). Most of the X11 (screen, keyboard,\n" -" mouse) options do not make sense and many will cause\n" -" this mode to crash, so please think twice before\n" -" setting/changing them.\n" -"\n" -" If you don't want x11vnc to close the X DISPLAY in\n" -" rawfb mode, then capitalize the prefix, SHM:, MAP:,\n" -" FILE: Keeping the display open enables the default\n" -" remote-control channel, which could be useful. Also,\n" -" if you also specify -noviewonly, then the mouse and\n" -" keyboard input are STILL sent to the X display, this\n" -" usage should be very rare, i.e. doing something strange\n" -" with /dev/fb0.\n" -"\n" -"-pipeinput cmd Another experimental option: it lets you supply an\n" -" external command in \"cmd\" that x11vnc will pipe\n" -" all of the user input events to in a simple format.\n" -" In -pipeinput mode by default x11vnc will not process\n" -" any of the user input events. If you prefix \"cmd\"\n" -" with \"tee:\" it will both send them to the pipe\n" -" command and process them. For a description of the\n" -" format run \"-pipeinput tee:/bin/cat\". Another prefix\n" -" is \"reopen\" which means to reopen pipe if it exits.\n" -" Separate multiple prefixes with commas.\n" -"\n" -" In combination with -rawfb one might be able to\n" -" do amusing things (e.g. control non-X devices).\n" -" To facilitate this, if -rawfb is in effect then the\n" -" value is stored in X11VNC_RAWFB_STR for the pipe command\n" -" to use if it wants. Do 'env | grep X11VNC' for more.\n" -"\n" -"-gui [gui-opts] Start up a simple tcl/tk gui based on the the remote\n" -" control options -remote/-query described below.\n" -" Requires the \"wish\" program to be installed on the\n" -" machine. \"gui-opts\" is not required: the default\n" -" is to start up both the full gui and x11vnc with the\n" -" gui showing up on the X display in the environment\n" -" variable DISPLAY.\n" -"\n" -" \"gui-opts\" can be a comma separated list of items.\n" -" Currently there are these types of items: 1) a gui\n" -" mode, a 2) gui \"simplicity\", 3) the X display the\n" -" gui should display on, 4) a \"tray\" or \"icon\" mode,\n" -" and 5) a gui geometry.\n" -"\n" -" 1) The gui mode can be \"start\", \"conn\", or \"wait\"\n" -" \"start\" is the default mode above and is not required.\n" -" \"conn\" means do not automatically start up x11vnc,\n" -" but instead just try to connect to an existing x11vnc\n" -" process. \"wait\" means just start the gui and nothing\n" -" else (you will later instruct the gui to start x11vnc\n" -" or connect to an existing one.)\n" -"\n" -" 2) The gui simplicity is off by default (a power-user\n" -" gui with all options is presented) To start with\n" -" something less daunting supply the string \"simple\"\n" -" (\"ez\" is an alias for this). Once the gui is\n" -" started you can toggle between the two with \"Misc ->\n" -" simple_gui\".\n" -"\n" -" 3) Note the possible confusion regarding the potentially\n" -" two different X displays: x11vnc polls one, but you\n" -" may want the gui to appear on another. For example, if\n" -" you ssh in and x11vnc is not running yet you may want\n" -" the gui to come back to you via your ssh redirected X\n" -" display (e.g. localhost:10).\n" -"\n" -" If you do not specify a gui X display in \"gui-opts\"\n" -" then the DISPLAY environment variable and -display\n" -" option are tried (in that order). Regarding the x11vnc\n" -" X display the gui will try to communication with, it\n" -" first tries -display and then DISPLAY. For example,\n" -" \"x11vnc -display :0 -gui otherhost:0\", will remote\n" -" control an x11vnc polling :0 and display the gui on\n" -" otherhost:0 The \"tray/icon\" mode below reverses this\n" -" preference, preferring to display on the x11vnc display.\n" -"\n" -" 4) When \"tray\" or \"icon\" is specified, the gui\n" -" presents itself as a small icon with behavior typical\n" -" of a \"system tray\" or \"dock applet\". The color\n" -" of the icon indicates status (connected clients) and\n" -" there is also a balloon status. Clicking on the icon\n" -" gives a menu from which properties, etc, can be set and\n" -" the full gui is available under \"Advanced\". To be\n" -" fully functional, the gui mode should be \"start\"\n" -" (the default).\n" -"\n" -" For \"icon\" the gui just a small standalone window.\n" -" For \"tray\" it will attempt to embed itself in the\n" -" \"system tray\" if possible. If \"=setpass\" is appended then\n" -" at startup the X11 user will be prompted to set the\n" -" VNC session password. If =<hexnumber> is appended\n" -" that icon will attempt to embed itself in the window\n" -" given by hexnumber. Use =noadvanced to disable the\n" -" full gui. (To supply more than one, use \"+\" sign).\n" -" E.g. -gui tray=setpass and -gui icon=0x3600028\n" -"\n" -" Other modes: \"full\", the default and need not be\n" -" specified. \"-gui none\", do not show a gui, useful\n" -" to override a ~/.x11vncrc setting, etc.\n" -"\n" -" 5) When \"geom=+X+Y\" is specified, that geometry\n" -" is passed to the gui toplevel. This is the icon in\n" -" icon/tray mode, or the full gui otherwise. You can\n" -" also specify width and height, i.e. WxH+X+Y, but it\n" -" is not recommended. In \"tray\" mode the geometry is\n" -" ignored unless the system tray manager does not seem\n" -" to be running. One could imagine using something like\n" -" \"-gui tray,geom=+4000+4000\" with a display manager\n" -" to keep the gui invisible until someone logs in...\n" -"\n" -" More icon tricks, \"icon=minimal\" gives an icon just\n" -" with the VNC display number. You can also set the font\n" -" with \"iconfont=...\". The following could be useful:\n" -" \"-gui icon=minimal,iconfont=5x8,geom=24x10+0-0\"\n" -"\n" -" General examples of the -gui option: \"x11vnc -gui\",\n" -" \"x11vnc -gui ez\" \"x11vnc -gui localhost:10\",\n" -" \"x11vnc -gui conn,host:0\", \"x11vnc -gui tray,ez\"\n" -" \"x11vnc -gui tray=setpass\"\n" -"\n" -" If you do not intend to start x11vnc from the gui\n" -" (i.e. just remote control an existing one), then the\n" -" gui process can run on a different machine from the\n" -" x11vnc server as long as X permissions, etc. permit\n" -" communication between the two.\n" -"\n" -"-remote command Remotely control some aspects of an already running\n" -" x11vnc server. \"-R\" and \"-r\" are aliases for\n" -" \"-remote\". After the remote control command is\n" -" sent to the running server the 'x11vnc -remote ...'\n" -" command exits. You can often use the -query command\n" -" (see below) to see if the x11vnc server processed your\n" -" -remote command.\n" -"\n" -" The default communication channel is that of X\n" -" properties (specifically VNC_CONNECT), and so this\n" -" command must be run with correct settings for DISPLAY\n" -" and possibly XAUTHORITY to connect to the X server\n" -" and set the property. Alternatively, use the -display\n" -" and -auth options to set them to the correct values.\n" -" The running server cannot use the -novncconnect option\n" -" because that disables the communication channel.\n" -" See below for alternate channels.\n" -"\n" -" For example: 'x11vnc -remote stop' (which is the same as\n" -" 'x11vnc -R stop') will close down the x11vnc server.\n" -" 'x11vnc -R shared' will enable shared connections, and\n" -" 'x11vnc -R scale:3/4' will rescale the desktop.\n" -"\n" -" The following -remote/-R commands are supported:\n" -"\n" -" stop terminate the server, same as \"quit\"\n" -" \"exit\" or \"shutdown\".\n" -" ping see if the x11vnc server responds.\n" -" Return is: ans=ping:<xdisplay>\n" -" blacken try to push a black fb update to all\n" -" clients (due to timings a client\n" -" could miss it). Same as \"zero\", also\n" -" \"zero:x1,y1,x2,y2\" for a rectangle.\n" -" refresh send the entire fb to all clients.\n" -" reset recreate the fb, polling memory, etc.\n" -/* ext. cmd. */ -" id:windowid set -id window to \"windowid\". empty\n" -" or \"root\" to go back to root window\n" -" sid:windowid set -sid window to \"windowid\"\n" -" waitmapped wait until subwin is mapped.\n" -" nowaitmapped do not wait until subwin is mapped.\n" -" clip:WxH+X+Y set -clip mode to \"WxH+X+Y\"\n" -" flashcmap enable -flashcmap mode.\n" -" noflashcmap disable -flashcmap mode.\n" -" shiftcmap:n set -shiftcmap to n.\n" -" notruecolor enable -notruecolor mode.\n" -" truecolor disable -notruecolor mode.\n" -" overlay enable -overlay mode (if applicable).\n" -" nooverlay disable -overlay mode.\n" -" overlay_cursor in -overlay mode, enable cursor drawing.\n" -" overlay_nocursor disable cursor drawing. same as\n" -" nooverlay_cursor.\n" -" visual:vis set -visual to \"vis\"\n" -" scale:frac set -scale to \"frac\"\n" -" scale_cursor:f set -scale_cursor to \"f\"\n" -" viewonly enable -viewonly mode.\n" -/* access view,share,forever */ -" noviewonly disable -viewonly mode.\n" -" shared enable -shared mode.\n" -" noshared disable -shared mode.\n" -" forever enable -forever mode.\n" -" noforever disable -forever mode.\n" -" timeout:n reset -timeout to n, if there are\n" -" currently no clients, exit unless one\n" -" connects in the next n secs.\n" -/* access */ -" http enable http client connections.\n" -" nohttp disable http client connections.\n" -" deny deny any new connections, same as \"lock\"\n" -" nodeny allow new connections, same as \"unlock\"\n" -/* access, filename */ -" connect:host do reverse connection to host, \"host\"\n" -" may be a comma separated list of hosts\n" -" or host:ports. See -connect.\n" -" disconnect:host disconnect any clients from \"host\"\n" -" same as \"close:host\". Use host\n" -" \"all\" to close all current clients.\n" -" If you know the client internal hex ID,\n" -" e.g. 0x3 (returned by \"-query clients\"\n" -" and RFB_CLIENT_ID) you can use that too.\n" -/* access */ -" allowonce:host For the next connection only, allow\n" -" connection from \"host\".\n" -/* access */ -" allow:hostlist set -allow list to (comma separated)\n" -" \"hostlist\". See -allow and -localhost.\n" -" Do not use with -allow /path/to/file\n" -" Use \"+host\" to add a single host, and\n" -" use \"-host\" to delete a single host\n" -" localhost enable -localhost mode\n" -" nolocalhost disable -localhost mode\n" -" listen:str set -listen to str, empty to disable.\n" -" nolookup enable -nolookup mode.\n" -" lookup disable -nolookup mode.\n" -" input:str set -input to \"str\", empty to disable.\n" -" client_input:str set the K, M, B -input on a per-client\n" -" basis. select which client as for\n" -" disconnect, e.g. client_input:host:MB\n" -" or client_input:0x2:K\n" -/* ext. cmd. */ -" accept:cmd set -accept \"cmd\" (empty to disable).\n" -" gone:cmd set -gone \"cmd\" (empty to disable).\n" -" noshm enable -noshm mode.\n" -" shm disable -noshm mode (i.e. use shm).\n" -" flipbyteorder enable -flipbyteorder mode, you may need\n" -" to set noshm for this to do something.\n" -" noflipbyteorder disable -flipbyteorder mode.\n" -" onetile enable -onetile mode. (you may need to\n" -" set shm for this to do something)\n" -" noonetile disable -onetile mode.\n" -/* ext. cmd. */ -" solid enable -solid mode\n" -" nosolid disable -solid mode.\n" -" solid_color:color set -solid color (and apply it).\n" -" blackout:str set -blackout \"str\" (empty to disable).\n" -" See -blackout for the form of \"str\"\n" -" (basically: WxH+X+Y,...)\n" -" Use \"+WxH+X+Y\" to append a single\n" -" rectangle use \"-WxH+X+Y\" to delete one\n" -" xinerama enable -xinerama mode. (if applicable)\n" -" noxinerama disable -xinerama mode.\n" -" xtrap enable -xtrap input mode(if applicable)\n" -" noxtrap disable -xtrap input mode.\n" -" xrandr enable -xrandr mode. (if applicable)\n" -" noxrandr disable -xrandr mode.\n" -" xrandr_mode:mode set the -xrandr mode to \"mode\".\n" -" padgeom:WxH set -padgeom to WxH (empty to disable)\n" -" If WxH is \"force\" or \"do\" the padded\n" -" geometry fb is immediately applied.\n" -" quiet enable -quiet mode.\n" -" noquiet disable -quiet mode.\n" -" modtweak enable -modtweak mode.\n" -" nomodtweak enable -nomodtweak mode.\n" -" xkb enable -xkb modtweak mode.\n" -" noxkb disable -xkb modtweak mode.\n" -" skip_keycodes:str enable -xkb -skip_keycodes \"str\".\n" -" sloppy_keys enable -sloppy_keys mode.\n" -" nosloppy_keys disable -sloppy_keys mode.\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" -" clear_mods enable -clear_mods mode and clear them.\n" -" noclear_mods disable -clear_mods mode.\n" -" clear_keys enable -clear_keys mode and clear them.\n" -" noclear_keys disable -clear_keys mode.\n" -/* filename */ -" remap:str set -remap \"str\" (empty to disable).\n" -" See -remap for the form of \"str\"\n" -" (basically: key1-key2,key3-key4,...)\n" -" Use \"+key1-key2\" to append a single\n" -" keymapping, use \"-key1-key2\" to delete.\n" -" norepeat enable -norepeat mode.\n" -" repeat disable -norepeat mode.\n" -" nofb enable -nofb mode.\n" -" fb disable -nofb mode.\n" -" bell enable bell (if supported).\n" -" nobell disable bell.\n" -" nosel enable -nosel mode.\n" -" sel disable -nosel mode.\n" -" noprimary enable -noprimary mode.\n" -" primary disable -noprimary mode.\n" -" seldir:str set -seldir to \"str\"\n" -" cursor:mode enable -cursor \"mode\".\n" -" show_cursor enable showing a cursor.\n" -" noshow_cursor disable showing a cursor. (same as\n" -" \"nocursor\")\n" -" arrow:n set -arrow to alternate n.\n" -" xfixes enable xfixes cursor shape mode.\n" -" noxfixes disable xfixes cursor shape mode.\n" -" alphacut:n set -alphacut to n.\n" -" alphafrac:f set -alphafrac to f.\n" -" alpharemove enable -alpharemove mode.\n" -" noalpharemove disable -alpharemove mode.\n" -" alphablend disable -noalphablend mode.\n" -" noalphablend enable -noalphablend mode.\n" -" cursorshape disable -nocursorshape mode.\n" -" nocursorshape enable -nocursorshape mode.\n" -" cursorpos disable -nocursorpos mode.\n" -" nocursorpos enable -nocursorpos mode.\n" -" xwarp enable -xwarppointer mode.\n" -" noxwarp disable -xwarppointer mode.\n" -" buttonmap:str set -buttonmap \"str\", empty to disable\n" -" dragging disable -nodragging mode.\n" -" nodragging enable -nodragging mode.\n" -" wireframe enable -wireframe mode. same as \"wf\"\n" -" nowireframe disable -wireframe mode. same as \"nowf\"\n" -" wireframe:str enable -wireframe mode string.\n" -" wireframe_mode:str enable -wireframe mode string.\n" -" wirecopyrect:str set -wirecopyrect string. same as \"wcr:\"\n" -" 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_term:list set -scr_term to \"list\"\n" -" scr_keyrepeat:str set -scr_keyrepeat to \"str\"\n" -" scr_parms:str set -scr_parms parameters.\n" -" fixscreen:str set -fixscreen to \"str\".\n" -" noxrecord disable all use of RECORD extension.\n" -" xrecord enable use of RECORD extension.\n" -" reset_record reset RECORD extension (if avail.)\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" -" wmdt:str set -wmdt to str.\n" -" debug_pointer enable -debug_pointer, same as \"dp\"\n" -" nodebug_pointer disable -debug_pointer, same as \"nodp\"\n" -" debug_keyboard enable -debug_keyboard, same as \"dk\"\n" -" nodebug_keyboard disable -debug_keyboard, same as \"nodk\"\n" -" defer:n set -defer to n ms,same as deferupdate:n\n" -" wait:n set -wait to n ms.\n" -" wait_ui:f set -wait_ui factor to f.\n" -" wait_bog disable -nowait_bog mode.\n" -" nowait_bog enable -nowait_bog mode.\n" -" slow_fb:f set -slow_fb to f seconds.\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" -" xdamage enable xdamage polling hints.\n" -" noxdamage disable xdamage polling hints.\n" -" xd_area:A set -xd_area max pixel area to \"A\"\n" -" xd_mem:f set -xd_mem remembrance to \"f\"\n" -" fs:frac set -fs fraction to \"frac\", e.g. 0.5\n" -" gaps:n set -gaps to n.\n" -" grow:n set -grow to n.\n" -" fuzz:n set -fuzz to n.\n" -" snapfb enable -snapfb mode.\n" -" nosnapfb disable -snapfb mode.\n" -" rawfb:str set -rawfb mode to \"str\".\n" -" progressive:n set libvncserver -progressive slice\n" -" height parameter to n.\n" -" desktop:str set -desktop name to str for new clients.\n" -" rfbport:n set -rfbport to n.\n" -/* access */ -" httpport:n set -httpport to n.\n" -" httpdir:dir set -httpdir to dir (and enable http).\n" -" enablehttpproxy enable -enablehttpproxy mode.\n" -" noenablehttpproxy disable -enablehttpproxy mode.\n" -" alwaysshared enable -alwaysshared mode.\n" -" noalwaysshared disable -alwaysshared mode.\n" -" (may interfere with other options)\n" -" nevershared enable -nevershared mode.\n" -" nonevershared disable -nevershared mode.\n" -" (may interfere with other options)\n" -" dontdisconnect enable -dontdisconnect mode.\n" -" nodontdisconnect disable -dontdisconnect mode.\n" -" (may interfere with other options)\n" -" debug_xevents enable debugging X events.\n" -" nodebug_xevents disable debugging X events.\n" -" debug_xdamage enable debugging X DAMAGE mechanism.\n" -" nodebug_xdamage disable debugging X DAMAGE mechanism.\n" -" debug_wireframe enable debugging wireframe mechanism.\n" -" 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" -" debug_grabs enable -debug_grabs\n" -" nodebug_grabs disable -debug_grabs\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" -"\n" -" The vncconnect(1) command from standard VNC\n" -" distributions may also be used if string is prefixed\n" -" with \"cmd=\" E.g. 'vncconnect cmd=stop'. Under some\n" -" circumstances xprop(1) can used if it supports -set\n" -" (see the FAQ).\n" -"\n" -" If \"-connect /path/to/file\" has been supplied to the\n" -" running x11vnc server then that file can be used as a\n" -" communication channel (this is the only way to remote\n" -" control one of many x11vnc's polling the same X display)\n" -" Simply run: 'x11vnc -connect /path/to/file -remote ...'\n" -" or you can directly write to the file via something\n" -" like: \"echo cmd=stop > /path/to/file\", etc.\n" -"\n" -"-query variable Like -remote, except just query the value of\n" -" \"variable\". \"-Q\" is an alias for \"-query\".\n" -" Multiple queries can be done by separating variables\n" -" by commas, e.g. -query var1,var2. The results come\n" -" back in the form ans=var1:value1,ans=var2:value2,...\n" -" to the standard output. If a variable is read-only,\n" -" it comes back with prefix \"aro=\" instead of \"ans=\".\n" -"\n" -" Some -remote commands are pure actions that do not make\n" -" sense as variables, e.g. \"stop\" or \"disconnect\",\n" -" in these cases the value returned is \"N/A\". To direct\n" -" a query straight to the VNC_CONNECT property or connect\n" -" file use \"qry=...\" instead of \"cmd=...\"\n" -"\n" -" Here is the current list of \"variables\" that can\n" -" be supplied to the -query command. This includes the\n" -" \"N/A\" ones that return no useful info. For variables\n" -" names that do not correspond to an x11vnc option or\n" -" remote command, we hope the name makes it obvious what\n" -" the returned value corresponds to (hint: the ext_*\n" -" variables correspond to the presence of X extensions):\n" -"\n" -" ans= stop quit exit shutdown ping blacken zero\n" -" refresh reset close disconnect id sid waitmapped\n" -" nowaitmapped clip flashcmap noflashcmap shiftcmap\n" -" truecolor notruecolor overlay nooverlay overlay_cursor\n" -" overlay_yescursor nooverlay_nocursor nooverlay_cursor\n" -" nooverlay_yescursor overlay_nocursor visual scale\n" -" scale_cursor viewonly noviewonly shared noshared\n" -" forever noforever once timeout deny lock nodeny unlock\n" -" connect allowonce allow localhost nolocalhost listen\n" -" lookup nolookup accept gone shm noshm flipbyteorder\n" -" noflipbyteorder onetile noonetile solid_color solid\n" -" nosolid blackout xinerama noxinerama xtrap noxtrap\n" -" xrandr noxrandr xrandr_mode padgeom quiet q noquiet\n" -" modtweak nomodtweak xkb noxkb skip_keycodes sloppy_keys\n" -" nosloppy_keys skip_dups noskip_dups add_keysyms\n" -" noadd_keysyms clear_mods noclear_mods clear_keys\n" -" noclear_keys remap repeat norepeat fb nofb bell\n" -" nobell sel nosel primary noprimary seldir cursorshape\n" -" nocursorshape cursorpos nocursorpos cursor show_cursor\n" -" noshow_cursor nocursor arrow xfixes noxfixes xdamage\n" -" noxdamage xd_area xd_mem alphacut alphafrac alpharemove\n" -" noalpharemove alphablend noalphablend xwarppointer\n" -" xwarp noxwarppointer noxwarp buttonmap dragging\n" -" nodragging wireframe_mode wireframe wf nowireframe\n" -" nowf wirecopyrect wcr nowirecopyrect nowcr scr_area\n" -" scr_skip scr_inc scr_keys scr_term scr_keyrepeat\n" -" scr_parms scrollcopyrect scr noscrollcopyrect noscr\n" -" fixscreen noxrecord xrecord reset_record pointer_mode\n" -" pm input_skip input client_input speeds wmdt\n" -" debug_pointer dp nodebug_pointer nodp debug_keyboard\n" -" dk nodebug_keyboard nodk deferupdate defer wait_ui\n" -" wait_bog nowait_bog slow_fb 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 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\n" -" debug_grabs nodebug_grabs dbg nodbg noremote\n" -"\n" -" aro= noop display vncdisplay desktopname guess_desktop\n" -" http_url auth xauth users rootshift clipshift\n" -" scale_str scaled_x scaled_y scale_numer scale_denom\n" -" scale_fac scaling_blend scaling_nomult4 scaling_pad\n" -" scaling_interpolate inetd privremote unsafe safer nocmds\n" -" passwdfile using_shm logfile o flag rc norc h help V\n" -" version lastmod bg sigpipe threads readrate netrate\n" -" netlatency pipeinput clients client_count pid ext_xtest\n" -" ext_xtrap ext_xrecord ext_xkb ext_xshm ext_xinerama\n" -" ext_overlay ext_xfixes ext_xdamage ext_xrandr rootwin\n" -" num_buttons button_mask mouse_x mouse_y bpp depth\n" -" indexed_color dpy_x dpy_y wdpy_x wdpy_y off_x off_y\n" -" cdpy_x cdpy_y coff_x coff_y rfbauth passwd viewpasswd\n" -"\n" -"-QD variable Just like -query variable, but returns the default\n" -" value for that parameter (no running x11vnc server\n" -" is consulted)\n" -"\n" -"-sync By default -remote commands are run asynchronously, that\n" -" is, the request is posted and the program immediately\n" -" exits. Use -sync to have the program wait for an\n" -" acknowledgement from the x11vnc server that command was\n" -" processed (somehow). On the other hand -query requests\n" -" are always processed synchronously because they have\n" -" to wait for the answer.\n" -"\n" -" Also note that if both -remote and -query requests are\n" -" supplied on the command line, the -remote is processed\n" -" first (synchronously: no need for -sync), and then\n" -" the -query request is processed in the normal way.\n" -" This allows for a reliable way to see if the -remote\n" -" command was processed by querying for any new settings.\n" -" Note however that there is timeout of a few seconds so\n" -" if the x11vnc takes longer than that to process the\n" -" requests the requestor will think that a failure has\n" -" taken place.\n" -"\n" -"-noremote Do not process any remote control commands or queries.\n" -"-yesremote Do process remote control commands or queries.\n" -" Default: %s\n" -"\n" -" A note about security wrt remote control commands.\n" -" If someone can connect to the X display and change\n" -" the property VNC_CONNECT, then they can remotely\n" -" control x11vnc. Normally access to the X display is\n" -" protected. Note that if they can modify VNC_CONNECT\n" -" on the X server, they have enough permissions to also\n" -" run their own x11vnc and thus have complete control\n" -" of the desktop. If the \"-connect /path/to/file\"\n" -" channel is being used, obviously anyone who can write\n" -" to /path/to/file can remotely control x11vnc. So be\n" -" sure to protect the X display and that file's write\n" -" permissions. See -privremote below.\n" -"\n" -" If you are paranoid and do not think -noremote is\n" -" enough, to disable the VNC_CONNECT property channel\n" -" completely use -novncconnect, or use the -safer\n" -" option that shuts many things off.\n" -"\n" -"-unsafe A few remote commands are disabled by default\n" -" (currently: id:pick, accept:<cmd>, gone:<cmd>, and\n" -" rawfb:setup:<cmd>) because they are associated with\n" -" running external programs. If you specify -unsafe, then\n" -" these remote-control commands are allowed. Note that\n" -" you can still specify these parameters on the command\n" -" line, they just cannot be invoked via remote-control.\n" -"-safer Equivalent to: -novncconnect -noremote and prohibiting\n" -" -gui and the -connect file. Shuts off communcation\n" -" channels.\n" -"-privremote Perform some sanity checks and disable remote-control\n" -" commands if it appears that the X DISPLAY and/or\n" -" connectfile can be accessed by other users. Once\n" -" remote-control is disabled it cannot be turned back on.\n" -"-nocmds No external commands (e.g. system(3), popen(3), exec(3))\n" -" will be run.\n" -"\n" -"-deny_all For use with -remote nodeny: start out denying all\n" -" incoming clients until \"-remote nodeny\" is used to\n" -" let them in.\n" -"%s\n" -"\n" -"These options are passed to libvncserver:\n" -"\n" -; - /* have both our help and rfbUsage to stdout for more(1), etc. */ - dup2(1, 2); - - /* register extention(s) to get their help output */ -#ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER - rfbRegisterTightVNCFileTransferExtension(); -#endif - - if (mode == 1) { - char *p; - int l = 0; - fprintf(stderr, "x11vnc: allow VNC connections to real " - "X11 displays. %s\n\nx11vnc options:\n", lastmod); - p = strtok(help, "\n"); - while (p) { - int w = 23; - char tmp[100]; - if (p[0] == '-') { - strncpy(tmp, p, w); - fprintf(stderr, " %s", tmp); - l++; - if (l % 2 == 0) { - fprintf(stderr, "\n"); - } - } - p = strtok(NULL, "\n"); - } - fprintf(stderr, "\n\nlibvncserver options:\n"); - rfbUsage(); - fprintf(stderr, "\n"); - exit(1); - } - fprintf(stderr, help, lastmod, - scaling_copyrect ? ":cr":":nocr", - view_only ? "on":"off", - 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, - alpha_frac, - cursor_pos_updates ? "-cursorpos":"-nocursorpos", - wireframe ? "-wireframe":"-nowireframe", - WIREFRAME_PARMS, - wireframe_copyrect_default, - scroll_copyrect_default, - scrollcopyrect_min_area, - scroll_skip_str0 ? scroll_skip_str0 : "(empty)", - scroll_term_str0, - max_keyrepeat_str0, - SCROLL_COPYRECT_PARMS, - pointer_mode_max, pointer_mode, - ui_skip, - defer_update, - waitms, - wait_ui, - rfbMaxClientWait/1000, - take_naps ? "take naps":"no naps", - screen_blank, - xdamage_max_area, NSCAN, xdamage_memory, - use_threads ? "-threads":"-nothreads", - fs_frac, - gaps_fill, - grow_fill, - tile_fuzz, - accept_remote_cmds ? "-yesremote":"-noremote", - "" - ); - - rfbUsage(); -#endif - exit(1); -} - -void set_vnc_desktop_name(void) { - int sz = 256; - sprintf(vnc_desktop_name, "unknown"); - if (inetd) { - sprintf(vnc_desktop_name, "inetd-no-further-clients"); - } - if (screen->port) { - char *host = this_host(); - int lport = screen->port; - char *iface = listen_str; - - if (iface != NULL && *iface != '\0' && strcmp(iface, "any")) { - host = iface; - } - if (host != NULL) { - /* note that vncviewer special cases 5900-5999 */ - if (inetd) { - ; /* should not occur (port) */ - } else if (quiet) { - if (lport >= 5900) { - snprintf(vnc_desktop_name, sz, "%s:%d", - host, lport - 5900); - fprintf(stderr, "The VNC desktop is " - "%s\n", vnc_desktop_name); - } else { - snprintf(vnc_desktop_name, sz, "%s:%d", - host, lport); - fprintf(stderr, "The VNC desktop is " - "%s\n", vnc_desktop_name); - } - } else if (lport >= 5900) { - snprintf(vnc_desktop_name, sz, "%s:%d", - host, lport - 5900); - rfbLog("\n"); - rfbLog("The VNC desktop is %s\n", - vnc_desktop_name); - if (lport >= 6000) { - rfbLog("possible aliases: %s:%d, " - "%s::%d\n", host, lport, - host, lport); - } - } else { - snprintf(vnc_desktop_name, sz, "%s:%d", - host, lport); - rfbLog("\n"); - rfbLog("The VNC desktop is %s\n", - vnc_desktop_name); - rfbLog("possible alias: %s::%d\n", - host, lport); - } - } - fflush(stderr); - if (inetd) { - ; /* should not occur (port != 0) */ - } else { - fprintf(stdout, "PORT=%d\n", screen->port); - fflush(stdout); - if (flagfile) { - FILE *flag = fopen(flagfile, "w"); - if (flag) { - fprintf(flag, "PORT=%d\n",screen->port); - fflush(flag); - fclose(flag); - } else { - rfbLog("could not open flag file: %s\n", - flagfile); - } - } - } - fflush(stdout); - } -} - -/* - * utility to get the current host name - */ -#define MAXN 256 - -char *this_host(void) { - char host[MAXN]; -#if LIBVNCSERVER_HAVE_GETHOSTNAME - if (gethostname(host, MAXN) == 0) { - return strdup(host); - } -#endif - return NULL; -} - -/* * choose a desktop name */ static char *choose_title(char *display) { @@ -32331,7 +633,7 @@ static void check_rcfile(int argc, char **argv) { for (i=1; i < argc; i++) { if (!strcmp(argv[i], "-printgui")) { - fprintf(stdout, "%s", gui_code); + fprintf(stdout, "%s", get_gui_code()); fflush(stdout); exit(0); } @@ -32546,8 +848,7 @@ static void check_rcfile(int argc, char **argv) { } else { pwlast = 1; } - while (*p != '\0') - *p++ = '\0'; + strzero(p); } if (argc2 >= argmax) { fprintf(stderr, "too many rcfile options\n"); @@ -32556,7 +857,7 @@ static void check_rcfile(int argc, char **argv) { } } -void immediate_switch_user(int argc, char* argv[]) { +static void immediate_switch_user(int argc, char* argv[]) { int i; for (i=1; i < argc; i++) { char *u; @@ -32591,79 +892,7 @@ void immediate_switch_user(int argc, char* argv[]) { } } -void xopen_display_fail_message(char *disp) { - fprintf(stderr, "\n"); - fprintf(stderr, "*** x11vnc was unable to open the X DISPLAY: \"%s\"," - " it cannot continue.\n", disp); - fprintf(stderr, "*** There may be \"Xlib:\" error messages above" - " with details about the failure.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Some tips and guidelines:\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " * An X server (the one you wish to view) must" - " be running before x11vnc is\n"); - fprintf(stderr, " started: x11vnc does not start the X server.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " * You must use -display <disp>, -OR- set and" - " export your DISPLAY\n"); - fprintf(stderr, " environment variable to refer to the display of" - " the desired X server.\n"); - fprintf(stderr, " - Usually the display is simply \":0\" (in fact" - " x11vnc uses this if you forget\n"); - fprintf(stderr, " to specify it), but in some multi-user" - " situations it could be \":1\", \":2\",\n"); - fprintf(stderr, " or even \":137\". Ask your administrator" - " or a guru if you are having\n"); - fprintf(stderr, " difficulty determining what your X DISPLAY is.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " * Next, you need to have sufficient permissions" - " (Xauthority) \n"); - fprintf(stderr, " to connect to the X DISPLAY. Here are some" - " Tips:\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " - Often, you just need to run x11vnc as the user" - " logged into the X session.\n"); - fprintf(stderr, " So make sure to be that user when you type" - " x11vnc.\n"); - fprintf(stderr, " - Being root is usually not enough because the" - " incorrect MIT-MAGIC-COOKIE\n"); - fprintf(stderr, " file will be accessed. The cookie file contains" - " the secret key that\n"); - fprintf(stderr, " allows x11vnc to connect to the desired" - " X DISPLAY.\n"); - fprintf(stderr, " - You can explicity indicate which MIT-MAGIC-COOKIE" - " file should be used\n"); - fprintf(stderr, " by the -auth option, e.g.:\n"); - fprintf(stderr, " x11vnc -auth /home/someuser/.Xauthority" - " -display :0\n"); - fprintf(stderr, " you must have read permission for that file.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " - If NO ONE is logged into an X session yet, but" - " there is a greeter login\n"); - fprintf(stderr, " program like \"gdm\", \"kdm\", \"xdm\", or" - " \"dtlogin\" running, you will need\n"); - fprintf(stderr, " to find and use the raw display manager" - " MIT-MAGIC-COOKIE file.\n"); - fprintf(stderr, " Some examples for various display managers:\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " gdm: -auth /var/gdm/:0.Xauth\n"); - fprintf(stderr, " kdm: -auth /var/lib/kdm/A:0-crWk72\n"); - fprintf(stderr, " xdm: -auth /var/lib/xdm/authdir/authfiles/A:0-XQvaJk\n"); - fprintf(stderr, " dtlogin: -auth /var/dt/A:0-UgaaXa\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " Only root will have read permission for the" - " file, and so x11vnc must be run\n"); - fprintf(stderr, " as root. The random characters in the filenames" - " will of course change,\n"); - fprintf(stderr, " and the directory the cookie file resides in may" - " also be system dependent.\n"); - fprintf(stderr, " Sometimes the command \"ps wwaux | grep auth\"" - " can reveal the file location.\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "See also: http://www.karlrunge.com/x11vnc/#faq\n"); -} - -void print_settings(int try_http, int bg, char *gui_str) { +static void print_settings(int try_http, int bg, char *gui_str) { fprintf(stderr, "\n"); fprintf(stderr, "Settings:\n"); @@ -32703,6 +932,8 @@ void print_settings(int try_http, int bg, char *gui_str) { : "null"); fprintf(stderr, " accept: %s\n", accept_cmd ? accept_cmd : "null"); + fprintf(stderr, " accept: %s\n", afteraccept_cmd ? afteraccept_cmd + : "null"); fprintf(stderr, " gone: %s\n", gone_cmd ? gone_cmd : "null"); fprintf(stderr, " users: %s\n", users_list ? users_list @@ -32834,95 +1065,8 @@ void print_settings(int try_http, int bg, char *gui_str) { rfbLog("x11vnc version: %s\n", lastmod); } -void nopassword_warning_msg(int gotloc) { - - char str1[] = -"###############################################################\n" -"#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n" -"#@ @#\n" -"#@ ** WARNING ** WARNING ** WARNING ** WARNING ** @#\n" -"#@ @#\n" -"#@ YOU ARE RUNNING X11VNC WITHOUT A PASSWORD!! @#\n" -"#@ @#\n" -"#@ This means anyone with network access to this computer @#\n" -"#@ will be able to easily view and control your desktop. @#\n" -"#@ @#\n" -"#@ >>> If you did not mean to do this Press CTRL-C now!! <<< @#\n" -"#@ @#\n" -"#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n" -; - char str2[] = -"#@ @#\n" -"#@ You can create an x11vnc password file by running: @#\n" -"#@ @#\n" -"#@ x11vnc -storepasswd password /path/to/passfile @#\n" -"#@ @#\n" -"#@ and then starting x11vnc via: @#\n" -"#@ @#\n" -"#@ x11vnc -rfbauth /path/to/passfile @#\n" -"#@ @#\n" -"#@ an existing ~/.vnc/passwd file will work too. @#\n" -"#@ @#\n" -"#@ You can also use the -passwdfile or -passwd options. @#\n" -"#@ (note -passwd is unsafe if local users are not trusted) @#\n" -"#@ @#\n" -"#@ Make sure any -rfbauth and -passwdfile password files @#\n" -"#@ cannot be read by untrusted users. @#\n" -"#@ @#\n" -"#@ Even with a password, the subsequent VNC traffic is @#\n" -"#@ sent in the clear. Consider tunnelling via ssh(1): @#\n" -"#@ @#\n" -"#@ http://www.karlrunge.com/x11vnc/#tunnelling @#\n" -"#@ @#\n" -"#@ Please Read the documention for more info about @#\n" -"#@ passwords, security, and encryption. @#\n" -"#@ @#\n" -"#@ http://www.karlrunge.com/x11vnc/#faq-passwd @#\n" -; - char str3[] = -"#@ @#\n" -"#@ You are using the -localhost option and that is a good @#\n" -"#@ thing!! Especially if you ssh(1) into this machine and @#\n" -"#@ use port redirection. Nevertheless, without a password @#\n" -"#@ other users could possibly do redirection as well to @#\n" -"#@ gain access to your desktop. @#\n" -; - char str4[] = -"#@ @#\n" -"#@ To disable this warning use the -nopw option, or put @#\n" -"#@ the setting in your ~/.x11vncrc file. @#\n" -"#@ @#\n" -"#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n" -"###############################################################\n" -; - char str5[] = -"###############################################################\n\n" -; - if (inetd) { - return; - } - fprintf(stderr, "%s", str1); - fflush(stderr); -#if !PASSWD_REQUIRED - usleep(2500 * 1000); -#endif - if (!quiet) { - fprintf(stderr, "%s", str2); - if (gotloc) { - fprintf(stderr, "%s", str3); - } - fprintf(stderr, "%s", str4); - } else { - fprintf(stderr, "%s", str5); - } - fflush(stderr); -#if !PASSWD_REQUIRED - usleep(500 * 1000); -#endif -} - -void check_loop_mode(int argc, char* argv[]) { +static void check_loop_mode(int argc, char* argv[]) { int i; int loop_mode = 0, loop_sleep = 2000, loop_max = 0; @@ -33209,6 +1353,9 @@ int main(int argc, char* argv[]) { } else if (!strcmp(arg, "-accept")) { CHECK_ARGC accept_cmd = strdup(argv[++i]); + } else if (!strcmp(arg, "-afteraccept")) { + CHECK_ARGC + afteraccept_cmd = strdup(argv[++i]); } else if (!strcmp(arg, "-gone")) { CHECK_ARGC gone_cmd = strdup(argv[++i]); @@ -33800,144 +1947,24 @@ int main(int argc, char* argv[]) { for (i=pw_loc; i <= pw_loc+1; i++) { if (i < argc) { char *p = argv[i]; - while (*p != '\0') { - *p++ = '\0'; - } + strzero(p); } } } else if (passwdfile) { /* read passwd(s) from file */ - char line[1024]; - char *filename; - int remove = 0; - struct stat sbuf; - int linecount = 0, i, max; - FILE *in; - - filename = passwdfile; - if (strstr(filename, "rm:") == filename) { - filename += strlen("rm:"); - remove = 1; - } - - if (stat(filename, &sbuf) == 0) { - /* upper bound to number of lines... */ - max = (int) sbuf.st_size; - } else { - max = 16; - } - - /* create 1 more than max to have it be the ending NULL */ - passwd_list = (char **) malloc( (max+1) * (sizeof(char *)) ); - for (i=0; i<max+1; i++) { - passwd_list[i] = NULL; - } - - in = fopen(filename, "r"); - if (in == NULL) { - rfbLog("cannot open passwdfile: %s\n", passwdfile); - rfbLogPerror("fopen"); - if (remove) { - unlink(filename); - } - exit(1); - } - - while (fgets(line, 1024, in) != NULL) { - char *p; - int blank = 1; - int len = strlen(line); - - if (len == 0) { - continue; - } else if (line[len-1] == '\n') { - line[len-1] = '\0'; - } - if (line[0] == '\0') { - continue; - } - if (strstr(line, "__SKIP__") != NULL) { - continue; - } - if (strstr(line, "__COMM__") == line) { - continue; - } - if (!strcmp(line, "__BEGIN_VIEWONLY__")) { - if (begin_viewonly < 0) { - begin_viewonly = linecount; - } - continue; - } - if (line[0] == '#') { - /* commented out, cannot have password beginning with # */ - continue; - } - p = line; - while (*p != '\0') { - if (! isspace(*p)) { - blank = 0; - break; - } - p++; - } - if (blank) { - continue; - } - - passwd_list[linecount++] = strdup(line); - - if (linecount >= max) { - break; - } - } - fclose(in); - - for (i=0; i<1024; i++) { - line[i] = '\0'; - } - - if (remove) { - unlink(filename); - } - - if (! linecount) { - rfbLog("cannot read a valid line from passwdfile: %s\n", - passwdfile); - exit(1); + if (read_passwds(passwdfile)) { + argv_vnc[argc_vnc++] = strdup("-passwd"); + argv_vnc[argc_vnc++] = strdup(passwd_list[0]); + got_passwd = 1; + pw_loc = 100; /* just for pw_loc check below */ } - - for (i=0; i<linecount; i++) { - char *q, *p = passwd_list[i]; - if (!strcmp(p, "__EMPTY__")) { - *p = '\0'; - } else if ((q = strstr(p, "__COMM__")) != NULL) { - *q = '\0'; - } - passwd_list[i] = strdup(p); - while (*p != '\0') { - *p = '\0'; p++; - } - } - argv_vnc[argc_vnc++] = strdup("-passwd"); - argv_vnc[argc_vnc++] = strdup(passwd_list[0]); - got_passwd = 1; - pw_loc = 100; /* just for pw_loc check below */ - - if (begin_viewonly < 0 && linecount == 2) { - /* for compatibility with previous 2-line usage: */ - viewonly_passwd = strdup(passwd_list[1]); - begin_viewonly = 1; - } - } if (vpw_loc > 0) { int i; for (i=vpw_loc; i <= vpw_loc+1; i++) { if (i < argc) { char *p = argv[i]; - while (*p != '\0') { - *p++ = '\0'; - } + strzero(p); } } } @@ -34655,4 +2682,5 @@ int main(int argc, char* argv[]) { #undef argc #undef argv + } diff --git a/x11vnc/x11vnc.h b/x11vnc/x11vnc.h new file mode 100644 index 0000000..ce20af5 --- /dev/null +++ b/x11vnc/x11vnc.h @@ -0,0 +1,434 @@ +#ifndef _X11VNC_X11VNC_H +#define _X11VNC_X11VNC_H + +/* -- x11vnc.h -- */ +/* + * These ' -- filename.[ch] -- ' comments represent a partial cleanup: + * they are an odd way to indicate how this huge file would be split up + * someday into multiple files. + * + * The primary reason we have not broken up this file is for user + * convenience: those wanting to use the latest version download a single + * file, x11vnc.c, and off they go... + */ + +/****************************************************************************/ + +/* Standard includes and libvncserver */ + +#include <unistd.h> +#include <signal.h> +#include <sys/utsname.h> +#include <time.h> +#include <errno.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include <X11/keysym.h> +#include <X11/Xatom.h> + +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> + +#include <rfb/rfb.h> +#include <rfb/rfbregion.h> + +/****************************************************************************/ + +/* Build-time customization via CPPFLAGS. */ + +/* + * Summary of options to include in CPPFLAGS for custom builds: + * + * -DVNCSHARED 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. + * -DEXTERNAL_COMMANDS=0 to disable the running of all external commands. + * -DFILEXFER=0 disable filexfer. + * + * -DHARDWIRE_PASSWD=... hardwired passwords, quoting necessary. + * -DHARDWIRE_VIEWPASSWD=... + * -DNOPW=1 make -nopw the default (skip warning) + * -DPASSWD_REQUIRED=1 exit unless a password is supplied. + * -DPASSWD_UNLESS_NOPW=1 exit unless a password is supplied and no -nopw. + * + * -DWIREFRAME=0 to have -nowireframe as the default. + * -DWIREFRAME_COPYRECT=0 to have -nowirecopyrect as the default. + * -DWIREFRAME_PARMS=... set default -wirecopyrect parameters. + * -DSCROLL_COPYRECT=0 to have -noscrollcopyrect as the default. + * -DSCROLL_COPYRECT_PARMS=... set default -scrollcopyrect parameters. + * -DXDAMAGE=0 to have -noxdamage as the default. + * -DSKIPDUPS=0 to have -noskip_dups as the default or vice versa. + * + * -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. + * -DNOGUI do not include the gui tkx11vnc. + * + * Set these in CPPFLAGS before running configure. E.g.: + * + * % env CPPFLAGS="-DFOREVER -DREMOTE_CONTROL=0" ./configure + * % make + */ + +/* + * This can be used to disable the remote control mechanism. + */ +#ifndef REMOTE_CONTROL +#define REMOTE_CONTROL 1 +#endif + +#ifndef NOPW +#define NOPW 0 +#endif + +#ifndef PASSWD_REQUIRED +#define PASSWD_REQUIRED 0 +#endif + +#ifndef PASSWD_UNLESS_NOPW +#define PASSWD_UNLESS_NOPW 0 +#endif + +/* + * Beginning of support for small binary footprint build for embedded + * systems, PDA's etc. It currently just cuts out the low-hanging + * fruit (large text passages). Set to 2, 3 to cut out some of the + * more esoteric extensions. More tedious is to modify LDFLAGS in the + * Makefile to not link against the extension libraries... but that + * should be done too (manually for now). + * + * If there is interest more of the bloat can be removed... Currently + * these shrink the binary from 500K to about 270K. + */ +#ifndef SMALL_FOOTPRINT +#define SMALL_FOOTPRINT 0 +#endif + +#if SMALL_FOOTPRINT +#define NOGUI +#endif + +#if (SMALL_FOOTPRINT > 1) +#define LIBVNCSERVER_HAVE_XKEYBOARD 0 +#define LIBVNCSERVER_HAVE_LIBXINERAMA 0 +#define LIBVNCSERVER_HAVE_LIBXRANDR 0 +#define LIBVNCSERVER_HAVE_LIBXFIXES 0 +#define LIBVNCSERVER_HAVE_LIBXDAMAGE 0 +#endif + +#if (SMALL_FOOTPRINT > 2) +#define LIBVNCSERVER_HAVE_UTMPX_H 0 +#define LIBVNCSERVER_HAVE_PWD_H 0 +#define REMOTE_CONTROL 0 +#endif + +/* + * Not recommended unless you know what you are getting into, but if you + * define the HARDWIRE_PASSWD or HARDWIRE_VIEWPASSWD variables here or in + * CPPFLAGS you can set a default -passwd and -viewpasswd string values, + * perhaps this would be better than nothing on an embedded system, etc. + * These default values will be overridden by the command line. + * We don't even give an example ;-) + */ + +/****************************************************************************/ + +/* Extensions and related includes: */ + +#if LIBVNCSERVER_HAVE_XSHM +# if defined(__hpux) && defined(__ia64) /* something weird on hp/itanic */ +# undef _INCLUDE_HPUX_SOURCE +# endif +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> +#endif + +#if LIBVNCSERVER_HAVE_XTEST +#include <X11/extensions/XTest.h> +#endif +extern int xtest_base_event_type; + +#if LIBVNCSERVER_HAVE_LIBXTRAP +#define NEED_EVENTS +#define NEED_REPLIES +#include <X11/extensions/xtraplib.h> +#include <X11/extensions/xtraplibp.h> +extern XETC *trap_ctx; +#endif +extern int xtrap_base_event_type; + +#if LIBVNCSERVER_HAVE_RECORD +#include <X11/Xproto.h> +#include <X11/extensions/record.h> +#endif + +#if LIBVNCSERVER_HAVE_XKEYBOARD +#include <X11/XKBlib.h> +#endif + +#if LIBVNCSERVER_HAVE_LIBXINERAMA +#include <X11/extensions/Xinerama.h> +#endif + +#if LIBVNCSERVER_HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#include <netdb.h> +extern int h_errno; + +#if LIBVNCSERVER_HAVE_NETINET_IN_H +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#endif + +/* XXX autoconf */ +#if LIBVNCSERVER_HAVE_PWD_H +#include <pwd.h> +#endif +#if LIBVNCSERVER_HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#if LIBVNCSERVER_HAVE_UTMPX_H +#include <utmpx.h> +#endif + +#if LIBVNCSERVER_HAVE_MMAP +#include <sys/mman.h> +#endif + +/* + * overlay/multi-depth screen reading support + * undef SOLARIS_OVERLAY or IRIX_OVERLAY if there are problems building. + */ + +/* solaris/sun */ +#if defined (__SVR4) && defined (__sun) +# define SOLARIS +# ifdef LIBVNCSERVER_HAVE_SOLARIS_XREADSCREEN +# define SOLARIS_OVERLAY +# define OVERLAY_OS +# endif +#endif + +#ifdef SOLARIS_OVERLAY +#include <X11/extensions/transovl.h> +#endif + +/* irix/sgi */ +#if defined(__sgi) +# define IRIX +# ifdef LIBVNCSERVER_HAVE_IRIX_XREADDISPLAY +# define IRIX_OVERLAY +# define OVERLAY_OS +# endif +#endif + +#ifdef IRIX_OVERLAY +#include <X11/extensions/readdisplay.h> +#endif + +extern int overlay_present; + +#if LIBVNCSERVER_HAVE_LIBXRANDR +#include <X11/extensions/Xrandr.h> +#endif +extern int xrandr_base_event_type; + +#if LIBVNCSERVER_HAVE_LIBXFIXES +#include <X11/extensions/Xfixes.h> +#endif +extern int xfixes_base_event_type; + +#if LIBVNCSERVER_HAVE_LIBXDAMAGE +#include <X11/extensions/Xdamage.h> +#endif +extern int xdamage_base_event_type; + +extern char lastmod[]; + +/* X display info */ + +extern Display *dpy; /* the single display screen we connect to */ +extern int scr; +extern Window window, rootwin; /* polled window, root window (usu. same) */ +extern Visual *default_visual; /* the default visual (unless -visual) */ +extern int bpp, depth; +extern int indexed_color; +extern int dpy_x, dpy_y; /* size of display */ +extern int off_x, off_y; /* offsets for -sid */ +extern int wdpy_x, wdpy_y; /* for actual sizes in case of -clip */ +extern int cdpy_x, cdpy_y, coff_x, coff_y; /* the -clip params */ +extern int button_mask; /* button state and info */ +extern int button_mask_prev; +extern int num_buttons; + +/* image structures */ +extern XImage *scanline; +extern XImage *fullscreen; +extern XImage **tile_row; /* for all possible row runs */ +extern XImage *fb0; +extern XImage *snaprect; /* for XShmGetImage (fs_factor) */ +extern XImage *snap; /* the full snap fb */ +extern XImage *raw_fb_image; /* the raw fb */ + +#if !LIBVNCSERVER_HAVE_XSHM +/* + * for simplicity, define this struct since we'll never use them + * under using_shm = 0. + */ +typedef struct { + int shmid; char *shmaddr; Bool readOnly; +} XShmSegmentInfo; +#endif + +/* corresponding shm structures */ +extern XShmSegmentInfo scanline_shm; +extern XShmSegmentInfo fullscreen_shm; +extern XShmSegmentInfo *tile_row_shm; /* for all possible row runs */ +extern XShmSegmentInfo snaprect_shm; + +/* rfb screen info */ +extern rfbScreenInfoPtr screen; +extern char *rfb_desktop_name; +extern char *http_dir; +extern char vnc_desktop_name[]; +extern char *main_fb; /* our copy of the X11 fb */ +extern char *rfb_fb; /* same as main_fb unless transformation */ +extern char *fake_fb; /* used under -padgeom */ +extern char *snap_fb; /* used under -snapfb */ +extern char *raw_fb; +extern char *raw_fb_addr; +extern int raw_fb_offset; +extern int raw_fb_shm; +extern int raw_fb_mmap; +extern int raw_fb_seek; +extern int raw_fb_fd; + +extern int rfb_bytes_per_line; +extern int main_bytes_per_line; +extern unsigned long main_red_mask, main_green_mask, main_blue_mask; +extern unsigned short main_red_max, main_green_max, main_blue_max; +extern unsigned short main_red_shift, main_green_shift, main_blue_shift; + +/* scaling parameters */ +extern char *scale_str; +extern double scale_fac; +extern int scaling; +extern int scaling_blend; /* for no blending option (very course) */ +extern int scaling_nomult4; /* do not require width = n * 4 */ +extern int scaling_pad; /* pad out scaled sizes to fit denominator */ +extern int scaling_interpolate; /* use interpolation scheme when shrinking */ +extern int scaled_x, scaled_y; /* dimensions of scaled display */ +extern int scale_numer, scale_denom; /* n/m */ + +/* scale cursor */ +extern char *scale_cursor_str; +extern double scale_cursor_fac; +extern int scaling_cursor; +extern int scaling_cursor_blend; +extern int scaling_cursor_interpolate; +extern int scale_cursor_numer, scale_cursor_denom; + +/* size of the basic tile unit that is polled for changes: */ +extern int tile_x; +extern int tile_y; +extern int ntiles, ntiles_x, ntiles_y; + +/* arrays that indicate changed or checked tiles. */ +extern unsigned char *tile_has_diff, *tile_tried, *tile_copied; +extern unsigned char *tile_has_xdamage_diff, *tile_row_has_xdamage_diff; + +/* times of recent events */ +extern time_t last_event, last_input, last_client; +extern time_t last_keyboard_input, last_pointer_input; +extern time_t last_fb_bytes_sent; +extern double last_keyboard_time; +extern double last_pointer_time; +extern double last_pointer_click_time; +extern double last_pointer_motion_time; +extern double last_key_to_button_remap_time; +extern double last_copyrect; +extern double last_copyrect_fix; +extern double servertime_diff; +extern double x11vnc_start; + +/* last client to move pointer */ +extern rfbClientPtr last_pointer_client; + +extern int client_count; +extern int clients_served; + +/* more transient kludge variables: */ +extern int cursor_x, cursor_y; /* x and y from the viewer(s) */ +extern int button_change_x, button_change_y; +extern int got_user_input; +extern int got_pointer_input; +extern int got_pointer_calls; +extern int got_keyboard_input; +extern int got_keyboard_calls; +extern int urgent_update; +extern int last_keyboard_keycode; +extern rfbBool last_rfb_down; +extern rfbBool last_rfb_key_accepted; +extern rfbKeySym last_rfb_keysym; +extern double last_rfb_keytime; +extern int fb_copy_in_progress; +extern int drag_in_progress; +extern int shut_down; +extern int do_copy_screen; +extern time_t damage_time; +extern int damage_delay; + +extern int program_pid; +extern char *program_name; +extern char *program_cmdline; + +extern struct utsname UT; + +typedef struct hint { + /* location x, y, height, and width of a change-rectangle */ + /* (grows as adjacent horizontal tiles are glued together) */ + int x, y, w, h; +} hint_t; + +/* struct with client specific data: */ +#define CILEN 10 +typedef struct _ClientData { + int uid; + char *hostname; + char *username; + int client_port; + int server_port; + char *server_ip; + char input[CILEN]; + int login_viewonly; + time_t login_time; + + int had_cursor_shape_updates; + int had_cursor_pos_updates; + + double timer; + double send_cmp_rate; + double send_raw_rate; + double latency; + int cmp_bytes_sent; + int raw_bytes_sent; +} ClientData; + +#include "params.h" +#include "enums.h" +#include "options.h" +#include "util.h" + +#endif /* _X11VNC_X11VNC_H */ diff --git a/x11vnc/x11vnc_defs.c b/x11vnc/x11vnc_defs.c new file mode 100644 index 0000000..c3d77c8 --- /dev/null +++ b/x11vnc/x11vnc_defs.c @@ -0,0 +1,150 @@ +/* -- x11vnc_defs.c -- */ + +#include "x11vnc.h" + +int overlay_present = 0; + +int xrandr_base_event_type = 0; + +int xfixes_base_event_type = 0; +int xtest_base_event_type = 0; +#if LIBVNCSERVER_HAVE_LIBXTRAP +XETC *trap_ctx = NULL; +#endif +int xtrap_base_event_type = 0; +int xdamage_base_event_type = 0; + +/* date +'lastmod: %Y-%m-%d' */ +char lastmod[] = "0.7.3 lastmod: 2006-01-08"; + +/* X display info */ + +Display *dpy = NULL; /* the single display screen we connect to */ +int scr = 0; +Window window = None, rootwin = None; /* polled window, root window (usu. same) */ +Visual *default_visual = NULL; /* the default visual (unless -visual) */ +int bpp = 0, depth = 0; +int indexed_color = 0; +int dpy_x, dpy_y; /* size of display */ +int off_x, off_y; /* offsets for -sid */ +int wdpy_x, wdpy_y; /* for actual sizes in case of -clip */ +int cdpy_x, cdpy_y, coff_x, coff_y; /* the -clip params */ +int button_mask = 0; /* button state and info */ +int button_mask_prev = 0; +int num_buttons = -1; + +/* image structures */ +XImage *scanline = NULL; +XImage *fullscreen = NULL; +XImage **tile_row = NULL; /* for all possible row runs */ +XImage *fb0 = NULL; +XImage *snaprect = NULL; /* for XShmGetImage (fs_factor) */ +XImage *snap = NULL; /* the full snap fb */ +XImage *raw_fb_image = NULL; /* the raw fb */ + +/* corresponding shm structures */ +XShmSegmentInfo scanline_shm; +XShmSegmentInfo fullscreen_shm; +XShmSegmentInfo *tile_row_shm; /* for all possible row runs */ +XShmSegmentInfo snaprect_shm; + +/* rfb screen info */ +rfbScreenInfoPtr screen = NULL; +char *rfb_desktop_name = NULL; +char *http_dir = NULL; +char vnc_desktop_name[256]; +char *main_fb = NULL; /* our copy of the X11 fb */ +char *rfb_fb = NULL; /* same as main_fb unless transformation */ +char *fake_fb = NULL; /* used under -padgeom */ +char *snap_fb = NULL; /* used under -snapfb */ +char *raw_fb = NULL; +char *raw_fb_addr = NULL; +int raw_fb_offset = 0; +int raw_fb_shm = 0; +int raw_fb_mmap = 0; +int raw_fb_seek = 0; +int raw_fb_fd = -1; + +int rfb_bytes_per_line; +int main_bytes_per_line; +unsigned long main_red_mask, main_green_mask, main_blue_mask; +unsigned short main_red_max, main_green_max, main_blue_max; +unsigned short main_red_shift, main_green_shift, main_blue_shift; + +/* scaling parameters */ +char *scale_str = NULL; +double scale_fac = 1.0; +int scaling = 0; +int scaling_blend = 1; /* for no blending option (very course) */ +int scaling_nomult4 = 0; /* do not require width = n * 4 */ +int scaling_pad = 0; /* pad out scaled sizes to fit denominator */ +int scaling_interpolate = 0; /* use interpolation scheme when shrinking */ +int scaled_x = 0, scaled_y = 0; /* dimensions of scaled display */ +int scale_numer = 0, scale_denom = 0; /* n/m */ + +/* scale cursor */ +char *scale_cursor_str = NULL; +double scale_cursor_fac = 1.0; +int scaling_cursor = 0; +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; +int ntiles, ntiles_x, ntiles_y; + +/* arrays that indicate changed or checked tiles. */ +unsigned char *tile_has_diff = NULL, *tile_tried = NULL, *tile_copied = NULL; +unsigned char *tile_has_xdamage_diff = NULL, *tile_row_has_xdamage_diff = NULL; + +/* times of recent events */ +time_t last_event, last_input = 0, last_client = 0; +time_t last_keyboard_input = 0, last_pointer_input = 0; +time_t last_fb_bytes_sent = 0; +double last_keyboard_time = 0.0; +double last_pointer_time = 0.0; +double last_pointer_click_time = 0.0; +double last_pointer_motion_time = 0.0; +double last_key_to_button_remap_time = 0.0; +double last_copyrect = 0.0; +double last_copyrect_fix = 0.0; +double servertime_diff = 0.0; +double x11vnc_start = 0.0; + +/* last client to move pointer */ +rfbClientPtr last_pointer_client = NULL; + +int client_count = 0; +int clients_served = 0; + +/* more transient kludge variables: */ +int cursor_x, cursor_y; /* x and y from the viewer(s) */ +int button_change_x, button_change_y; +int got_user_input = 0; +int got_pointer_input = 0; +int got_pointer_calls = 0; +int got_keyboard_input = 0; +int got_keyboard_calls = 0; +int urgent_update = 0; +int last_keyboard_keycode = 0; +rfbBool last_rfb_down = FALSE; +rfbBool last_rfb_key_accepted = FALSE; +rfbKeySym last_rfb_keysym = 0; +double last_rfb_keytime = 0.0; +int fb_copy_in_progress = 0; +int drag_in_progress = 0; +int shut_down = 0; +int do_copy_screen = 0; +time_t damage_time = 0; +int damage_delay = 0; + +int program_pid = 0; +char *program_name = NULL; +char *program_cmdline = NULL; + +struct utsname UT; + + + diff --git a/x11vnc/xdamage.c b/x11vnc/xdamage.c new file mode 100644 index 0000000..e54c0b9 --- /dev/null +++ b/x11vnc/xdamage.c @@ -0,0 +1,532 @@ +/* -- xdamage.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "userinput.h" + +#if LIBVNCSERVER_HAVE_LIBXDAMAGE +Damage xdamage = 0; +#endif +#ifndef XDAMAGE +#define XDAMAGE 1 +#endif +int use_xdamage = XDAMAGE; /* use the xdamage rects for scanline hints */ +int xdamage_present = 0; +int xdamage_max_area = 20000; /* pixels */ +double xdamage_memory = 1.0; /* in units of NSCAN */ +int xdamage_tile_count = 0, xdamage_direct_count = 0; +double xdamage_scheduled_mark = 0.0; +sraRegionPtr xdamage_scheduled_mark_region = NULL; +sraRegionPtr *xdamage_regions = NULL; +int xdamage_ticker = 0; +int XD_skip = 0, XD_tot = 0, XD_des = 0; /* for stats */ + +void add_region_xdamage(sraRegionPtr new_region); +void clear_xdamage_mark_region(sraRegionPtr markregion, int flush); +int collect_xdamage(int scancnt, int call); +int xdamage_hint_skip(int y); +void initialize_xdamage(void); +void create_xdamage_if_needed(void); +void destroy_xdamage_if_needed(void); +void check_xdamage_state(void); + + +static void record_desired_xdamage_rect(int x, int y, int w, int h); + + +static void record_desired_xdamage_rect(int x, int y, int w, int h) { + /* + * Unfortunately we currently can't trust an xdamage event + * to correspond to real screen damage. E.g. focus-in for + * mozilla (depending on wm) will mark the whole toplevel + * area as damaged, when only the border has changed. + * Similar things for terminal windows. + * + * This routine uses some heuristics to detect small enough + * damage regions that we will not have a performance problem + * if we believe them even though they are wrong. We record + * the corresponding tiles the damage regions touch. + */ + 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; + /* + * 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; + } + + if (!always_accept && area > xdamage_max_area) { + return; + } + + dt_x = w / tile_x; + dt_y = h / tile_y; + + 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?). + */ + return; + } + + 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); + nt_x2 = nfix((x+w)/tile_x, ntiles_x); + nt_y1 = nfix( (y)/tile_y, ntiles_y); + nt_y2 = nfix((y+h)/tile_y, ntiles_y); + + /* + * loop over the rectangle of tiles (1 tile for a small + * input rect). + */ + for (ix = nt_x1; ix <= nt_x2; ix++) { + for (iy = nt_y1; iy <= nt_y2; iy++) { + nt = ix + iy * ntiles_x; + cnt++; + if (! tile_has_xdamage_diff[nt]) { + XD_des++; + tile_has_xdamage_diff[nt] = 1; + } + /* not used: */ + tile_row_has_xdamage_diff[iy] = 1; + xdamage_tile_count++; + } + } + } + if (debug_xdamage > 1) { + fprintf(stderr, "xdamage: desired: %dx%d+%d+%d\tA: %6d tiles=" + "%02d-%02d/%02d-%02d tilecnt: %d\n", w, h, x, y, + w * h, nt_x1, nt_x2, nt_y1, nt_y2, cnt); + } +} + +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) { +if (0) fprintf(stderr, "add_region_xdamage: prev_tick: %d reg %p\n", prev_tick, (void *)reg); + sraRgnOr(reg, new_region); + } +} + +void clear_xdamage_mark_region(sraRegionPtr markregion, int flush) { +#if LIBVNCSERVER_HAVE_LIBXDAMAGE + XEvent ev; + sraRegionPtr tmpregion; + int count = 0; + + 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)) { + count++; + } + /* clear the whole damage region */ + XDamageSubtract(dpy, xdamage, None, None); + X_UNLOCK; + + if (debug_tiles || debug_xdamage) { + fprintf(stderr, "clear_xdamage_mark_region: %d\n", count); + } + + 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); + } +#else + if (0) flush++; /* compiler warnings */ + if (0) markregion = NULL; +#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, 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 32 + int dup_x[DUPSZ], dup_y[DUPSZ], dup_w[DUPSZ], dup_h[DUPSZ]; + double tm, dt; + + if (scancnt) {} /* unused vars warning: */ + + if (! xdamage_present || ! use_xdamage) { + return 0; + } + if (! xdamage) { + return 0; + } + if (! xdamage_base_event_type) { + return 0; + } + + dtime0(&tm); + + nreg = (xdamage_memory * NSCAN) + 1; + + 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? + * Could check QLength and if huge just mark the whole + * screen. + */ + ecount++; + if (ev.type != xdamage_base_event_type + XDamageNotify) { + break; + } + dev = (XDamageNotifyEvent *) &ev; + if (dev->damage != xdamage) { + continue; /* not ours! */ + } + + x = dev->area.x; + y = dev->area.y; + w = dev->area.width; + h = dev->area.height; + + /* + * we try to manually remove some duplicates because + * certain activities can lead to many 10's of dups + * in a row. The region work can be costly and reg is + * later used in xdamage_hint_skip loops, so it is good + * to skip them if possible. + */ + dup = 0; + for (i=0; i < dup_max; i++) { + if (dup_x[i] == x && dup_y[i] == y && dup_w[i] == w && + dup_h[i] == h) { + dup = 1; + break; + } + } + if (dup) { + dcount++; + continue; + } + if (dup_max < DUPSZ) { + next = dup_max; + dup_max++; + } else { + next = (next+1) % DUPSZ; + } + dup_x[next] = x; + dup_y[next] = y; + dup_w[next] = w; + dup_h[next] = h; + + /* translate if needed */ + if (clipshift) { + /* set coords relative to fb origin */ + if (0 && rootshift) { + /* + * Note: not needed because damage is + * relative to subwin, not rootwin. + */ + x = x - off_x; + y = y - off_y; + } + if (clipshift) { + x = x - coff_x; + y = y - coff_y; + } + + x2 = x + w; /* upper point */ + x = nfix(x, dpy_x); /* place both in fb area */ + x2 = nfix(x2, dpy_x+1); + w = x2 - x; /* recompute w */ + + y2 = y + h; + y = nfix(y, dpy_y); + y2 = nfix(y2, dpy_y+1); + h = y2 - y; + + if (w <= 0 || h <= 0) { + continue; + } + } + if (debug_xdamage > 2) { + fprintf(stderr, "xdamage: -> event %dx%d+%d+%d area:" + " %d dups: %d %s\n", w, h, x, y, w*h, dcount, + (w*h > xdamage_max_area) ? "TOO_BIG" : ""); + } + + record_desired_xdamage_rect(x, y, w, h); + + tmpregion = sraRgnCreateRect(x, y, x + w, y + h); + sraRgnOr(reg, tmpregion); + sraRgnDestroy(tmpregion); + rect_count++; + ccount++; + } + /* clear the whole damage region for next time. XXX check */ + 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; + } + if (now > last_rpt + 15) { + double rat = -1.0; + + if (XD_tot) { + rat = ((double) XD_skip)/XD_tot; + } + 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); + } + + XD_skip = 0; + XD_tot = 0; + XD_des = 0; + rect_count = 0; + last_rpt = now; + } +#else + if (0) scancnt++; /* compiler warnings */ + if (0) call++; + if (0) record_desired_xdamage_rect(0, 0, 0, 0); +#endif + return 0; +} + +int xdamage_hint_skip(int y) { + static sraRegionPtr scanline = NULL; + sraRegionPtr reg, tmpl; + int ret, i, n, nreg; + + if (! xdamage_present || ! use_xdamage) { + return 0; /* cannot skip */ + } + if (! xdamage_regions) { + return 0; /* cannot skip */ + } + + if (! scanline) { + /* keep it around to avoid malloc etc, recreate */ + scanline = sraRgnCreate(); + } + + tmpl = sraRgnCreateRect(0, y, dpy_x, y+1); + + nreg = (xdamage_memory * NSCAN) + 1; + ret = 1; + for (i=0; i<nreg; i++) { + /* go back thru the history starting at most recent */ + n = (xdamage_ticker + nreg - i) % nreg; + reg = xdamage_regions[n]; + if (sraRgnEmpty(reg)) { + /* checking for emptiness is very fast */ + continue; + } + sraRgnMakeEmpty(scanline); + sraRgnOr(scanline, tmpl); + if (sraRgnAnd(scanline, reg)) { + ret = 0; + break; + } + } + sraRgnDestroy(tmpl); + + return ret; +} + +void initialize_xdamage(void) { + sraRegionPtr *ptr; + int i, nreg; + + if (! xdamage_present) { + use_xdamage = 0; + } + if (xdamage_regions) { + ptr = xdamage_regions; + while (*ptr != NULL) { + sraRgnDestroy(*ptr); + ptr++; + } + free(xdamage_regions); + xdamage_regions = NULL; + } + if (use_xdamage) { + nreg = (xdamage_memory * NSCAN) + 2; + xdamage_regions = (sraRegionPtr *) + malloc(nreg * sizeof(sraRegionPtr)); + for (i = 0; i < nreg; i++) { + ptr = xdamage_regions+i; + if (i == nreg - 1) { + *ptr = NULL; + } else { + *ptr = sraRgnCreate(); + sraRgnMakeEmpty(*ptr); + } + } + /* set so will be 0 in first collect_xdamage call */ + xdamage_ticker = -1; + } +} + +void create_xdamage_if_needed(void) { + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + +#if LIBVNCSERVER_HAVE_LIBXDAMAGE + if (! xdamage) { + X_LOCK; + xdamage = XDamageCreate(dpy, window, XDamageReportRawRectangles); + XDamageSubtract(dpy, xdamage, None, None); + X_UNLOCK; + rfbLog("created xdamage object: 0x%lx\n", xdamage); + } +#endif +} + +void destroy_xdamage_if_needed(void) { + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + +#if LIBVNCSERVER_HAVE_LIBXDAMAGE + if (xdamage) { + XEvent ev; + X_LOCK; + XDamageDestroy(dpy, xdamage); + XFlush(dpy); + if (xdamage_base_event_type) { + while (XCheckTypedEvent(dpy, + xdamage_base_event_type+XDamageNotify, &ev)) { + ; + } + } + X_UNLOCK; + rfbLog("destroyed xdamage object: 0x%lx\n", xdamage); + xdamage = 0; + } +#endif +} + +void check_xdamage_state(void) { + if (! xdamage_present) { + return; + } + /* + * Create or destroy the Damage object as needed, we don't want + * one if no clients are connected. + */ + if (client_count && use_xdamage) { + create_xdamage_if_needed(); + if (xdamage_scheduled_mark > 0.0 && dnow() > + xdamage_scheduled_mark) { + if (xdamage_scheduled_mark_region) { + mark_region_for_xdamage( + xdamage_scheduled_mark_region); + sraRgnDestroy(xdamage_scheduled_mark_region); + xdamage_scheduled_mark_region = NULL; + } else { + mark_for_xdamage(0, 0, dpy_x, dpy_y); + } + xdamage_scheduled_mark = 0.0; + } + } else { + destroy_xdamage_if_needed(); + } +} + + diff --git a/x11vnc/xdamage.h b/x11vnc/xdamage.h new file mode 100644 index 0000000..ce895a8 --- /dev/null +++ b/x11vnc/xdamage.h @@ -0,0 +1,29 @@ +#ifndef _X11VNC_XDAMAGE_H +#define _X11VNC_XDAMAGE_H + +/* -- xdamage.h -- */ + +#if LIBVNCSERVER_HAVE_LIBXDAMAGE +extern Damage xdamage; +#endif +extern int use_xdamage; +extern int xdamage_present; +extern int xdamage_max_area; +extern double xdamage_memory; +extern int xdamage_tile_count, xdamage_direct_count; +extern double xdamage_scheduled_mark; +extern sraRegionPtr xdamage_scheduled_mark_region; +extern sraRegionPtr *xdamage_regions; +extern int xdamage_ticker; +extern int XD_skip, XD_tot, XD_des; + +extern void add_region_xdamage(sraRegionPtr new_region); +extern void clear_xdamage_mark_region(sraRegionPtr markregion, int flush); +extern int collect_xdamage(int scancnt, int call); +extern int xdamage_hint_skip(int y); +extern void initialize_xdamage(void); +extern void create_xdamage_if_needed(void); +extern void destroy_xdamage_if_needed(void); +extern void check_xdamage_state(void); + +#endif /* _X11VNC_XDAMAGE_H */ diff --git a/x11vnc/xevents.c b/x11vnc/xevents.c new file mode 100644 index 0000000..5272333 --- /dev/null +++ b/x11vnc/xevents.c @@ -0,0 +1,981 @@ +/* -- xevents.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "xkb_bell.h" +#include "xrandr.h" +#include "xdamage.h" +#include "xrecord.h" +#include "selection.h" +#include "keyboard.h" +#include "cursor.h" +#include "gui.h" +#include "connections.h" + +/* XXX CHECK BEFORE RELEASE */ +int grab_buster = 0; +int sync_tod_delay = 3; + +void initialize_vnc_connect_prop(void); +void spawn_grab_buster(void); +void sync_tod_with_servertime(void); +void check_keycode_state(void); +void check_autorepeat(void); +void check_xevents(void); +void xcut_receive(char *text, int len, rfbClientPtr cl); + + +static void initialize_xevents(void); +static void print_xevent_bases(void); +static void get_prop(char *str, int len, Atom prop); +static void bust_grab(int reset); +static int process_watch(char *str, int parent, int db); +static void grab_buster_watch(int parent, char *dstr); + + +void initialize_vnc_connect_prop(void) { + vnc_connect_str[0] = '\0'; + vnc_connect_prop = XInternAtom(dpy, "VNC_CONNECT", False); +} + +static void initialize_xevents(void) { + static int did_xselect_input = 0; + static int did_xcreate_simple_window = 0; + static int did_vnc_connect_prop = 0; + static int did_xfixes = 0; + static int did_xdamage = 0; + static int did_xrandr = 0; + + if ((watch_selection || vnc_connect) && !did_xselect_input) { + /* + * register desired event(s) for notification. + * PropertyChangeMask is for CUT_BUFFER0 changes. + * XXX: does this cause a flood of other stuff? + */ + X_LOCK; + XSelectInput(dpy, rootwin, PropertyChangeMask); + X_UNLOCK; + did_xselect_input = 1; + } + if (watch_selection && !did_xcreate_simple_window) { + /* create fake window for our selection ownership, etc */ + + X_LOCK; + selwin = XCreateSimpleWindow(dpy, rootwin, 0, 0, 1, 1, 0, 0, 0); + X_UNLOCK; + did_xcreate_simple_window = 1; + } + + if (xrandr && !did_xrandr) { + initialize_xrandr(); + did_xrandr = 1; + } + if (vnc_connect && !did_vnc_connect_prop) { + initialize_vnc_connect_prop(); + did_vnc_connect_prop = 1; + } + if (xfixes_present && use_xfixes && !did_xfixes) { + initialize_xfixes(); + did_xfixes = 1; + } + if (xdamage_present && !did_xdamage) { + initialize_xdamage(); + did_xdamage = 1; + } +} + +static void print_xevent_bases(void) { + fprintf(stderr, "X event bases: xkb=%d, xtest=%d, xrandr=%d, " + "xfixes=%d, xdamage=%d, xtrap=%d\n", xkb_base_event_type, + xtest_base_event_type, xrandr_base_event_type, + xfixes_base_event_type, xdamage_base_event_type, + xtrap_base_event_type); + fprintf(stderr, " MapNotify=%d, ClientMsg=%d PropNotify=%d " + "SelNotify=%d, SelRequest=%d\n", MappingNotify, ClientMessage, + PropertyNotify, SelectionNotify, SelectionRequest); + fprintf(stderr, " SelClear=%d, Expose=%d\n", SelectionClear, Expose); +} + +static void get_prop(char *str, int len, Atom prop) { + Atom type; + int format, slen, dlen, i; + unsigned long nitems = 0, bytes_after = 0; + unsigned char* data = NULL; + + for (i=0; i<len; i++) { + str[i] = '\0'; + } + if (prop == None) { + return; + } + + slen = 0; + + do { + if (XGetWindowProperty(dpy, DefaultRootWindow(dpy), + prop, nitems/4, len/16, False, + AnyPropertyType, &type, &format, &nitems, &bytes_after, + &data) == Success) { + + dlen = nitems * (format/8); + if (slen + dlen > len) { + /* too big */ + XFree(data); + break; + } + memcpy(str+slen, data, dlen); + slen += dlen; + str[slen] = '\0'; + XFree(data); + } + } while (bytes_after > 0); +} + +static void bust_grab(int reset) { + static int bust_count = 0; + static time_t last_bust = 0; + time_t now = time(0); + KeyCode key; + int button, x, y, nb; + + if (now > last_bust + 180) { + bust_count = 0; + } + if (reset) { + bust_count = 0; + return; + } + + x = 0; + y = 0; + button = 0; + key = NoSymbol; + + nb = 8; + if (bust_count >= 3 * nb) { + fprintf(stderr, "too many bust_grab's %d for me\n", bust_count); + exit(0); + } + if (bust_count % nb == 0) { + button = 1; + } else if (bust_count % nb == 1) { + button = 1; + } else if (bust_count % nb == 2) { + key = XKeysymToKeycode(dpy, XK_Escape); + } else if (bust_count % nb == 3) { + button = 3; + } else if (bust_count % nb == 4) { + key = XKeysymToKeycode(dpy, XK_space); + } else if (bust_count % nb == 5) { + x = bust_count * 23; + y = bust_count * 17; + } else if (bust_count % nb == 5) { + button = 2; + } else if (bust_count % nb == 6) { + key = XKeysymToKeycode(dpy, XK_a); + } + + if (key == NoSymbol) { + key = XKeysymToKeycode(dpy, XK_a); + if (key == NoSymbol) { + button = 1; + } + } + + bust_count++; + + if (button) { + /* try button press+release */ + fprintf(stderr, "**bust_grab: button%d %.4f\n", + button, dnowx()); + XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); + } else if (x > 0) { + /* try button motion*/ + int scr = DefaultScreen(dpy); + + fprintf(stderr, "**bust_grab: x=%d y=%d %.4f\n", x, y, + dnowx()); + XTestFakeMotionEvent_wr(dpy, scr, x, y, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + + /* followed by button press */ + button = 1; + fprintf(stderr, "**bust_grab: button%d\n", button); + XTestFakeButtonEvent_wr(dpy, button, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeButtonEvent_wr(dpy, button, False, CurrentTime); + } else { + /* try Escape or Space press+release */ + fprintf(stderr, "**bust_grab: keycode: %d %.4f\n", + (int) key, dnowx()); + XTestFakeKeyEvent_wr(dpy, key, True, CurrentTime); + XFlush(dpy); + usleep(50 * 1000); + XTestFakeKeyEvent_wr(dpy, key, False, CurrentTime); + } + XFlush(dpy); + last_bust = time(0); +} + +typedef struct _grabwatch { + int pid; + int tick; + unsigned long time; + time_t change; +} grabwatch_t; +#define GRABWATCH 16 + +static int grab_buster_delay = 20; +static pid_t grab_buster_pid = 0; + +static int grab_npids = 1; + +static int process_watch(char *str, int parent, int db) { + int i, pid, ticker, npids; + char diff[128]; + unsigned long xtime; + static grabwatch_t watches[GRABWATCH]; + static int first = 1; + time_t now = time(0); + static time_t last_bust = 0; + int too_long, problems = 0; + + if (first) { + for (i=0; i < GRABWATCH; i++) { + watches[i].pid = 0; + watches[i].tick = 0; + watches[i].time = 0; + watches[i].change = 0; + } + first = 0; + } + + /* record latest value of prop */ + if (str && *str != '\0') { + if (sscanf(str, "%d/%d/%lu/%s", &pid, &ticker, &xtime, diff) + == 4) { + int got = -1, free = -1; + + if (db) fprintf(stderr, "grab_buster %d - %d - %lu - %s" + "\n", pid, ticker, xtime, diff); + + if (pid == parent && !strcmp(diff, "QUIT")) { + /* that's it. */ + return 0; + } + if (pid == 0 || ticker == 0 || xtime == 0) { + /* bad prop read. */ + goto badtickerstr; + } + for (i=0; i < GRABWATCH; i++) { + if (watches[i].pid == pid) { + got = i; + break; + } + if (free == -1 && watches[i].pid == 0) { + free = i; + } + } + if (got == -1) { + if (free == -1) { + /* bad news */; + free = GRABWATCH - 1; + } + watches[free].pid = pid; + watches[free].tick = ticker; + watches[free].time = xtime; + watches[free].change = now; + if (db) fprintf(stderr, "grab_buster free slot: %d\n", free); + } else { + if (db) fprintf(stderr, "grab_buster got slot: %d\n", got); + if (watches[got].tick != ticker) { + watches[got].change = now; + } + if (watches[got].time != xtime) { + watches[got].change = now; + } + watches[got].tick = ticker; + watches[got].time = xtime; + } + } else { + if (db) fprintf(stderr, "grab_buster bad prop str: %s\n", str); + } + } + + badtickerstr: + + too_long = grab_buster_delay; + if (too_long < 3 * sync_tod_delay) { + too_long = 3 * sync_tod_delay; + } + + npids = 0; + for (i=0; i < GRABWATCH; i++) { + if (watches[i].pid) { + npids++; + } + } + grab_npids = npids; + if (npids > 4) { + npids = 4; + } + + /* now check everyone we are tracking */ + for (i=0; i < GRABWATCH; i++) { + int fac = 1; + if (!watches[i].pid) { + continue; + } + if (watches[i].change == 0) { + watches[i].change = now; /* just to be sure */ + continue; + } + + pid = watches[i].pid; + + if (pid != parent) { + fac = 2; + } + if (npids > 0) { + fac *= npids; + } + + if (now > watches[i].change + fac*too_long) { + int process_alive = 1; + + fprintf(stderr, "grab_buster: problem with pid: " + "%d - %d/%d/%d\n", pid, (int) now, + (int) watches[i].change, too_long); + + if (kill((pid_t) pid, 0) != 0) { + if (1 || errno == ESRCH) { + process_alive = 0; + } + } + + if (!process_alive) { + watches[i].pid = 0; + watches[i].tick = 0; + watches[i].time = 0; + watches[i].change = 0; + fprintf(stderr, "grab_buster: pid gone: %d\n", + pid); + if (pid == parent) { + /* that's it */ + return 0; + } + } else { + int sleep = sync_tod_delay * 1000 * 1000; + + bust_grab(0); + problems++; + last_bust = now; + usleep(1 * sleep); + break; + } + } + } + + if (!problems) { + bust_grab(1); + } + return 1; +} + +static void grab_buster_watch(int parent, char *dstr) { + Atom ticker_atom = None; + int sleep = sync_tod_delay * 921 * 1000; + char propval[200]; + int ev, er, maj, min; + int db = 0; + + if (grab_buster > 1) { + db = 1; + } + + /* overwrite original dpy, we let orig connection sit unused. */ + dpy = XOpenDisplay(dstr); + if (!dpy) { + fprintf(stderr, "grab_buster_watch: could not reopen: %s\n", + dstr); + return; + } + rfbLogEnable(0); + + /* check for XTEST, etc, and then disable grabs for us */ + if (! XTestQueryExtension_wr(dpy, &ev, &er, &maj, &min)) { + xtest_present = 0; + } else { + xtest_present = 1; + } + if (! XETrapQueryExtension_wr(dpy, &ev, &er, &maj)) { + xtrap_present = 0; + } else { + xtrap_present = 1; + } + + if (! xtest_present && ! xtrap_present) { + fprintf(stderr, "grab_buster_watch: no grabserver " + "protection on display: %s\n", dstr); + return; + } + disable_grabserver(dpy, 0); + + usleep(3 * sleep); + + ticker_atom = XInternAtom(dpy, "X11VNC_TICKER", False); + if (! ticker_atom) { + fprintf(stderr, "grab_buster_watch: no ticker atom\n"); + return; + } + + while(1) { + int slp = sleep; + if (grab_npids > 1) { + slp = slp / 8; + } + usleep(slp); + usleep((int) (0.60 * rfac() * slp)); + + if (kill((pid_t) parent, 0) != 0) { + break; + } + + get_prop(propval, 128, ticker_atom); + if (db) fprintf(stderr, "got_prop: %s\n", propval); + + if (!process_watch(propval, parent, db)) { + break; + } + } +} + +void spawn_grab_buster(void) { +#if LIBVNCSERVER_HAVE_FORK + pid_t pid; + int parent = (int) getpid(); + char *dstr = strdup(DisplayString(dpy)); + + XCloseDisplay(dpy); + dpy = NULL; + + if ((pid = fork()) > 0) { + grab_buster_pid = pid; + if (! quiet) { + rfbLog("grab_buster pid is: %d\n", (int) pid); + } + } else if (pid == -1) { + fprintf(stderr, "spawn_grab_buster: could not fork\n"); + rfbLogPerror("fork"); + } else { + grab_buster_watch(parent, dstr); + exit(0); + } + + dpy = XOpenDisplay(dstr); + if (!dpy) { + rfbLog("failed to reopen display %s in spawn_grab_buster\n", + dstr); + exit(1); + } +#endif +} + +void sync_tod_with_servertime(void) { + static Atom ticker_atom = None; + XEvent xev; + char diff[128]; + static int seq = 0; + static unsigned long xserver_ticks = 1; + int i, db = 0; + + if (! ticker_atom) { + ticker_atom = XInternAtom(dpy, "X11VNC_TICKER", False); + } + if (! ticker_atom) { + return; + } + + XSync(dpy, False); + while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + ; + } + + snprintf(diff, 128, "%d/%08d/%lu/%.6f", (int) getpid(), seq++, + xserver_ticks, servertime_diff); + XChangeProperty(dpy, rootwin, ticker_atom, XA_STRING, 8, + PropModeReplace, (unsigned char *) diff, strlen(diff)); + XSync(dpy, False); + + for (i=0; i < 10; i++) { + int k, got = 0; + + for (k=0; k < 5; k++) { + while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + if (xev.xproperty.atom == ticker_atom) { + double stime; + + xserver_ticks = xev.xproperty.time; + stime = (double) xev.xproperty.time; + stime = stime/1000.0; + servertime_diff = dnow() - stime; + if (db) rfbLog("set servertime_diff: " + "%.6f\n", servertime_diff); + got = 1; + } + } + } + if (got) { + break; + } + usleep(1000); + } +} + +void check_keycode_state(void) { + static time_t last_check = 0; + int delay = 10, noinput = 3; + time_t now = time(0); + + if (! client_count) { + return; + } + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + /* + * periodically update our model of the keycode_state[] + * by correlating with the Xserver. wait for a pause in + * keyboard input to be on the safe side. the idea here + * is to remove stale keycode state, not to be perfectly + * in sync with the Xserver at every instant of time. + */ + if (now > last_check + delay && now > last_keyboard_input + noinput) { + init_track_keycode_state(); + last_check = now; + } +} + +void check_autorepeat(void) { + 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. + */ +void check_xevents(void) { + XEvent xev; + int tmp, have_clients = 0; + static int sent_some_sel = 0; + static time_t last_request = 0; + static time_t last_call = 0; + static time_t last_bell = 0; + static time_t last_init_check = 0; + static time_t last_sync = 0; + static time_t last_time_sync = 0; + time_t now = time(0); + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (now > last_init_check+1) { + last_init_check = now; + initialize_xevents(); + } + + if (screen && screen->clientHead) { + have_clients = 1; + } + + X_LOCK; + /* + * There is a bug where we have to wait before sending text to + * the client... so instead of sending right away we wait a + * the few seconds. + */ + if (have_clients && watch_selection && !sent_some_sel + && now > last_client + sel_waittime) { + if (XGetSelectionOwner(dpy, XA_PRIMARY) == None) { + cutbuffer_send(); + } + sent_some_sel = 1; + } + if (! have_clients) { + /* + * If we don't have clients we can miss the X server + * going away until a client connects. + */ + static time_t last_X_ping = 0; + if (now > last_X_ping + 5) { + last_X_ping = now; + XGetSelectionOwner(dpy, XA_PRIMARY); + } + } + + if (now > last_call+1) { + /* we only check these once a second or so. */ + int n = 0; + while (XCheckTypedEvent(dpy, MappingNotify, &xev)) { + XRefreshKeyboardMapping((XMappingEvent *) &xev); + n++; + } + if (n && use_modifier_tweak) { + X_UNLOCK; + initialize_modtweak(); + X_LOCK; + } + if (xtrap_base_event_type) { + int base = xtrap_base_event_type; + while (XCheckTypedEvent(dpy, base, &xev)) { + ; + } + } + if (xtest_base_event_type) { + int base = xtest_base_event_type; + while (XCheckTypedEvent(dpy, base, &xev)) { + ; + } + } + /* + * we can get ClientMessage from our XSendEvent() call in + * selection_request(). + */ + while (XCheckTypedEvent(dpy, ClientMessage, &xev)) { + ; + } + } + + /* check for CUT_BUFFER0 and VNC_CONNECT changes: */ + if (XCheckTypedEvent(dpy, PropertyNotify, &xev)) { + if (xev.type == PropertyNotify) { + if (xev.xproperty.atom == XA_CUT_BUFFER0) { + /* + * Go retrieve CUT_BUFFER0 and send it. + * + * set_cutbuffer is a flag to try to avoid + * processing our own cutbuffer changes. + */ + if (have_clients && watch_selection + && ! set_cutbuffer) { + cutbuffer_send(); + sent_some_sel = 1; + } + set_cutbuffer = 0; + } else if (vnc_connect && vnc_connect_prop != None + && xev.xproperty.atom == vnc_connect_prop) { + + /* + * Go retrieve VNC_CONNECT string. + */ + read_vnc_connect_prop(); + } + } + } + + /* do this now that we have just cleared PropertyNotify */ + tmp = 0; + if (rfac() < 0.6) { + tmp = 1; + } + if (now > last_time_sync + sync_tod_delay + tmp) { + sync_tod_with_servertime(); + last_time_sync = now; + } + +#if LIBVNCSERVER_HAVE_LIBXRANDR + if (xrandr) { + check_xrandr_event("check_xevents"); + } +#endif +#if LIBVNCSERVER_HAVE_LIBXFIXES + if (xfixes_present && use_xfixes && xfixes_base_event_type) { + if (XCheckTypedEvent(dpy, xfixes_base_event_type + + XFixesCursorNotify, &xev)) { + got_xfixes_cursor_notify++; + } + } +#endif + + /* check for our PRIMARY request notification: */ + if (watch_primary) { + if (XCheckTypedEvent(dpy, SelectionNotify, &xev)) { + if (xev.type == SelectionNotify && + xev.xselection.requestor == selwin && + xev.xselection.selection == XA_PRIMARY && + xev.xselection.property != None && + xev.xselection.target == XA_STRING) { + + /* go retrieve PRIMARY and check it */ + if (now > last_client + sel_waittime + || sent_some_sel) { + selection_send(&xev); + } + } + } + if (now > last_request) { + /* + * Every second or two, request PRIMARY, unless we + * already own it or there is no owner or we have + * no clients. + * TODO: even at this low rate we should look into + * and performance problems in odds cases, etc. + */ + last_request = now; + if (! own_selection && have_clients && + XGetSelectionOwner(dpy, XA_PRIMARY) != None) { + XConvertSelection(dpy, XA_PRIMARY, XA_STRING, + XA_STRING, selwin, CurrentTime); + } + } + } + + if (own_selection) { + /* we own PRIMARY, see if someone requested it: */ + if (XCheckTypedEvent(dpy, SelectionRequest, &xev)) { + if (xev.type == SelectionRequest && + xev.xselectionrequest.selection == XA_PRIMARY) { + selection_request(&xev); + } + } + + /* we own PRIMARY, see if we no longer own it: */ + if (XCheckTypedEvent(dpy, SelectionClear, &xev)) { + if (xev.type == SelectionClear && + xev.xselectionclear.selection == XA_PRIMARY) { + + own_selection = 0; + if (xcut_str) { + free(xcut_str); + xcut_str = NULL; + } + } + } + } + + if (watch_bell || now > last_bell+1) { + last_bell = now; + check_bell_event(); + } + if (tray_request != None) { + static time_t last_tray_request = 0; + if (now > last_tray_request + 2) { + last_tray_request = now; + if (tray_embed(tray_request, tray_unembed)) { + tray_window = tray_request; + tray_request = None; + } + } + } + +#ifndef DEBUG_XEVENTS +#define DEBUG_XEVENTS 1 +#endif +#if DEBUG_XEVENTS + if (debug_xevents) { + static time_t last_check = 0; + static time_t reminder = 0; + static int freq = 0; + + if (! freq) { + if (getenv("X11VNC_REMINDER_RATE")) { + freq = atoi(getenv("X11VNC_REMINDER_RATE")); + } else { + freq = 300; + } + } + + if (now > last_check + 1) { + int ev_type_max = 300, ev_size = 400; + XEvent xevs[400]; + int i, tot = XEventsQueued(dpy, QueuedAlready); + + if (reminder == 0 || (tot && now > reminder + freq)) { + print_xevent_bases(); + reminder = now; + } + last_check = now; + + if (tot) { + fprintf(stderr, "Total events queued: %d\n", + tot); + } + for (i=1; i<ev_type_max; i++) { + int k, n = 0; + while (XCheckTypedEvent(dpy, i, xevs+n)) { + if (++n >= ev_size) { + break; + } + } + if (n) { + fprintf(stderr, " %d%s events of type" + " %d queued\n", n, + (n >= ev_size) ? "+" : "", i); + } + for (k=n-1; k >= 0; k--) { + XPutBackEvent(dpy, xevs+k); + } + } + } + } +#endif + + if (now > last_sync + 1200) { + /* kludge for any remaining event leaks */ + int bugout = use_xdamage ? 500 : 50; + int qlen, i; + if (last_sync != 0) { + qlen = XEventsQueued(dpy, QueuedAlready); + if (qlen >= bugout) { + rfbLog("event leak: %d queued, " + " calling XSync(dpy, True)\n", qlen); + rfbLog(" for diagnostics run: 'x11vnc -R" + " debug_xevents:1'\n"); + XSync(dpy, True); + } + } + last_sync = now; + + /* clear these, we don't want any events on them */ + if (rdpy_ctrl) { + qlen = XEventsQueued(rdpy_ctrl, QueuedAlready); + for (i=0; i<qlen; i++) { + XNextEvent(rdpy_ctrl, &xev); + } + } + if (gdpy_ctrl) { + qlen = XEventsQueued(gdpy_ctrl, QueuedAlready); + for (i=0; i<qlen; i++) { + XNextEvent(gdpy_ctrl, &xev); + } + } + } + X_UNLOCK; + + last_call = now; +} + +/* + * hook called when a VNC client sends us some "XCut" text (rfbClientCutText). + */ +void xcut_receive(char *text, int len, rfbClientPtr cl) { + allowed_input_t input; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (!watch_selection) { + return; + } + if (view_only) { + return; + } + if (text == NULL || len == 0) { + return; + } + get_allowed_input(cl, &input); + if (!input.keystroke && !input.motion && !input.button) { + /* maybe someday KMBC for cut text... */ + return; + } + + if (! check_sel_direction("recv", "xcut_receive", text, len)) { + return; + } + + X_LOCK; + + /* associate this text with PRIMARY (and SECONDARY...) */ + if (! own_selection) { + own_selection = 1; + /* we need to grab the PRIMARY selection */ + XSetSelectionOwner(dpy, XA_PRIMARY, selwin, CurrentTime); + XFlush(dpy); + } + + /* duplicate the text string for our own use. */ + if (xcut_str != NULL) { + free(xcut_str); + xcut_str = NULL; + } + xcut_str = (char *) malloc((size_t) (len+1)); + strncpy(xcut_str, text, len); + xcut_str[len] = '\0'; /* make sure null terminated */ + + /* copy this text to CUT_BUFFER0 as well: */ + XChangeProperty(dpy, rootwin, XA_CUT_BUFFER0, XA_STRING, 8, + PropModeReplace, (unsigned char *) text, len); + XFlush(dpy); + + X_UNLOCK; + + set_cutbuffer = 1; +} + + diff --git a/x11vnc/xevents.h b/x11vnc/xevents.h new file mode 100644 index 0000000..0126ec2 --- /dev/null +++ b/x11vnc/xevents.h @@ -0,0 +1,17 @@ +#ifndef _X11VNC_XEVENTS_H +#define _X11VNC_XEVENTS_H + +/* -- xevents.h -- */ + +extern int grab_buster; +extern int sync_tod_delay; + +extern void initialize_vnc_connect_prop(void); +extern void spawn_grab_buster(void); +extern void sync_tod_with_servertime(void); +extern void check_keycode_state(void); +extern void check_autorepeat(void); +extern void check_xevents(void); +extern void xcut_receive(char *text, int len, rfbClientPtr cl); + +#endif /* _X11VNC_XEVENTS_H */ diff --git a/x11vnc/xinerama.c b/x11vnc/xinerama.c new file mode 100644 index 0000000..975298b --- /dev/null +++ b/x11vnc/xinerama.c @@ -0,0 +1,409 @@ +/* -- xinerama.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "blackout_t.h" +#include "scan.h" + +/* + * routines related to xinerama and blacking out rectangles + */ + +/* blacked-out region (-blackout, -xinerama) */ + +#define BLACKR_MAX 100 +blackout_t blackr[BLACKR_MAX]; /* hardwired max blackouts */ +tile_blackout_t *tile_blackout; +int blackouts = 0; + +void initialize_blackouts_and_xinerama(void); +void push_sleep(int n); +void push_black_screen(int n); +void refresh_screen(int push); +void zero_fb(int x1, int y1, int x2, int y2); + + +static void initialize_blackouts(char *list); +static void blackout_tiles(void); +static void initialize_xinerama (void); + + +/* + * Take a comma separated list of geometries: WxH+X+Y and register them as + * rectangles to black out from the screen. + */ +static void initialize_blackouts(char *list) { + char *p, *blist = strdup(list); + int x, y, X, Y, h, w, t; + + p = strtok(blist, ", \t"); + while (p) { + if (!strcmp("noptr", p)) { + blackout_ptr = 1; + rfbLog("pointer will be blocked from blackout " + "regions\n"); + p = strtok(NULL, ", \t"); + continue; + } + if (! parse_geom(p, &w, &h, &x, &y, dpy_x, dpy_y)) { + if (*p != '\0') { + rfbLog("skipping invalid geometry: %s\n", p); + } + p = strtok(NULL, ", \t"); + continue; + } + w = nabs(w); + h = nabs(h); + x = nfix(x, dpy_x); + y = nfix(y, dpy_y); + X = x + w; + Y = y + h; + X = nfix(X, dpy_x+1); + Y = nfix(Y, dpy_y+1); + if (x > X) { + t = X; X = x; x = t; + } + if (y > Y) { + t = Y; Y = y; y = t; + } + if (x < 0 || x > dpy_x || y < 0 || y > dpy_y || + X < 0 || X > dpy_x || Y < 0 || Y > dpy_y || + x == X || y == Y) { + rfbLog("skipping invalid blackout geometry: %s x=" + "%d-%d,y=%d-%d,w=%d,h=%d\n", p, x, X, y, Y, w, h); + } else { + rfbLog("blackout rect: %s: x=%d-%d y=%d-%d\n", p, + x, X, y, Y); + + /* + * note that the black out is x1 <= x but x < x2 + * for the region. i.e. the x2, y2 are outside + * by 1 pixel. + */ + blackr[blackouts].x1 = x; + blackr[blackouts].y1 = y; + blackr[blackouts].x2 = X; + blackr[blackouts].y2 = Y; + blackouts++; + if (blackouts >= BLACKR_MAX) { + rfbLog("too many blackouts: %d\n", blackouts); + break; + } + } + p = strtok(NULL, ", \t"); + } + free(blist); +} + +/* + * Now that all blackout rectangles have been constructed, see what overlap + * they have with the tiles in the system. If a tile is touched by a + * blackout, record information. + */ +static void blackout_tiles(void) { + int tx, ty; + int debug_bo = 0; + if (! blackouts) { + return; + } + if (getenv("DEBUG_BLACKOUT") != NULL) { + debug_bo = 1; + } + + /* + * to simplify things drop down to single copy mode, etc... + */ + single_copytile = 1; + /* loop over all tiles. */ + for (ty=0; ty < ntiles_y; ty++) { + for (tx=0; tx < ntiles_x; tx++) { + sraRegionPtr tile_reg, black_reg; + sraRect rect; + sraRectangleIterator *iter; + int n, b, x1, y1, x2, y2, cnt; + + /* tile number and coordinates: */ + n = tx + ty * ntiles_x; + x1 = tx * tile_x; + y1 = ty * tile_y; + x2 = x1 + tile_x; + y2 = y1 + tile_y; + if (x2 > dpy_x) { + x2 = dpy_x; + } + if (y2 > dpy_y) { + y2 = dpy_y; + } + + /* make regions for the tile and the blackouts: */ + black_reg = (sraRegionPtr) sraRgnCreate(); + tile_reg = (sraRegionPtr) sraRgnCreateRect(x1, y1, + x2, y2); + + tile_blackout[n].cover = 0; + tile_blackout[n].count = 0; + + /* union of blackouts */ + for (b=0; b < blackouts; b++) { + sraRegionPtr tmp_reg = (sraRegionPtr) + sraRgnCreateRect(blackr[b].x1, + blackr[b].y1, blackr[b].x2, blackr[b].y2); + + sraRgnOr(black_reg, tmp_reg); + sraRgnDestroy(tmp_reg); + } + + if (! sraRgnAnd(black_reg, tile_reg)) { + /* + * no intersection for this tile, so we + * are done. + */ + sraRgnDestroy(black_reg); + sraRgnDestroy(tile_reg); + continue; + } + + /* + * loop over rectangles that make up the blackout + * region: + */ + cnt = 0; + iter = sraRgnGetIterator(black_reg); + while (sraRgnIteratorNext(iter, &rect)) { + + /* make sure x1 < x2 and y1 < y2 */ + if (rect.x1 > rect.x2) { + int tmp = rect.x2; + rect.x2 = rect.x1; + rect.x1 = tmp; + } + if (rect.y1 > rect.y2) { + int tmp = rect.y2; + rect.y2 = rect.y1; + rect.y1 = tmp; + } + + /* store coordinates */ + tile_blackout[n].bo[cnt].x1 = rect.x1; + tile_blackout[n].bo[cnt].y1 = rect.y1; + tile_blackout[n].bo[cnt].x2 = rect.x2; + tile_blackout[n].bo[cnt].y2 = rect.y2; + + /* note if the tile is completely obscured */ + if (rect.x1 == x1 && rect.y1 == y1 && + rect.x2 == x2 && rect.y2 == y2) { + tile_blackout[n].cover = 2; + if (debug_bo) { + fprintf(stderr, "full: %d=%d,%d" + " (%d-%d) (%d-%d)\n", + n, tx, ty, x1, x2, y1, y2); + } + } else { + tile_blackout[n].cover = 1; + if (debug_bo) { + fprintf(stderr, "part: %d=%d,%d" + " (%d-%d) (%d-%d)\n", + n, tx, ty, x1, x2, y1, y2); + } + } + + if (++cnt >= BO_MAX) { + rfbLog("too many blackout rectangles " + "for tile %d=%d,%d.\n", n, tx, ty); + break; + } + } + + sraRgnReleaseIterator(iter); + sraRgnDestroy(black_reg); + sraRgnDestroy(tile_reg); + + tile_blackout[n].count = cnt; + if (debug_bo && cnt > 1) { + rfbLog("warning: multiple region overlaps[%d] " + "for tile %d=%d,%d.\n", cnt, n, tx, ty); + } + } + } +} + +static void initialize_xinerama (void) { +#if !LIBVNCSERVER_HAVE_LIBXINERAMA + rfbLog("Xinerama: Library libXinerama is not available to determine\n"); + rfbLog("Xinerama: the head geometries, consider using -blackout\n"); + rfbLog("Xinerama: if the screen is non-rectangular.\n"); +#else + XineramaScreenInfo *sc, *xineramas; + sraRegionPtr black_region, tmp_region; + sraRectangleIterator *iter; + sraRect rect; + char *bstr, *tstr; + int ev, er, i, n, rcnt; + + if (raw_fb && ! dpy) return; /* raw_fb hack */ + + if (! XineramaQueryExtension(dpy, &ev, &er)) { + rfbLog("Xinerama: disabling: display does not support it.\n"); + xinerama = 0; + xinerama_present = 0; + return; + } + if (! XineramaIsActive(dpy)) { + /* n.b. change to XineramaActive(dpy, window) someday */ + rfbLog("Xinerama: disabling: not active on display.\n"); + xinerama = 0; + xinerama_present = 0; + return; + } + xinerama_present = 1; + + /* n.b. change to XineramaGetData() someday */ + xineramas = XineramaQueryScreens(dpy, &n); + rfbLog("Xinerama: number of sub-screens: %d\n", n); + + if (n == 1) { + rfbLog("Xinerama: no blackouts needed (only one" + " sub-screen)\n"); + XFree(xineramas); + return; /* must be OK w/o change */ + } + + black_region = sraRgnCreateRect(0, 0, dpy_x, dpy_y); + + sc = xineramas; + for (i=0; i<n; i++) { + int x, y, w, h; + + x = sc->x_org; + y = sc->y_org; + w = sc->width; + h = sc->height; + + tmp_region = sraRgnCreateRect(x, y, x + w, y + h); + + sraRgnSubtract(black_region, tmp_region); + sraRgnDestroy(tmp_region); + sc++; + } + XFree(xineramas); + + if (sraRgnEmpty(black_region)) { + rfbLog("Xinerama: no blackouts needed (screen fills" + " rectangle)\n"); + sraRgnDestroy(black_region); + return; + } + + /* max len is 10000x10000+10000+10000 (23 chars) per geometry */ + rcnt = (int) sraRgnCountRects(black_region); + bstr = (char *) malloc(30 * (rcnt+1)); + tstr = (char *) malloc(30); + bstr[0] = '\0'; + + iter = sraRgnGetIterator(black_region); + while (sraRgnIteratorNext(iter, &rect)) { + int x, y, w, h; + + /* make sure x1 < x2 and y1 < y2 */ + if (rect.x1 > rect.x2) { + int tmp = rect.x2; + rect.x2 = rect.x1; + rect.x1 = tmp; + } + if (rect.y1 > rect.y2) { + int tmp = rect.y2; + rect.y2 = rect.y1; + rect.y1 = tmp; + } + x = rect.x1; + y = rect.y1; + w = rect.x2 - x; + h = rect.y2 - y; + sprintf(tstr, "%dx%d+%d+%d,", w, h, x, y); + strcat(bstr, tstr); + } + initialize_blackouts(bstr); + + free(bstr); + free(tstr); +#endif +} + +void initialize_blackouts_and_xinerama(void) { + + blackouts = 0; + blackout_ptr = 0; + + if (blackout_str != NULL) { + initialize_blackouts(blackout_str); + } + if (xinerama) { + initialize_xinerama(); + } + if (blackouts) { + blackout_tiles(); + /* schedule a copy_screen(), now is too early. */ + do_copy_screen = 1; + } +} + +void push_sleep(int n) { + int i; + for (i=0; i<n; i++) { + rfbPE(-1); + if (i != n-1 && defer_update) { + usleep(defer_update * 1000); + } + } +} + +/* + * try to forcefully push a black screen to all connected clients + */ +void push_black_screen(int n) { + if (!screen) { + return; + } + zero_fb(0, 0, dpy_x, dpy_y); + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + push_sleep(n); +} + +void refresh_screen(int push) { + int i; + if (!screen) { + return; + } + mark_rect_as_modified(0, 0, dpy_x, dpy_y, 0); + for (i=0; i<push; i++) { + rfbPE(-1); + } +} + +/* + * Fill the framebuffer with zero for the prescribed rectangle + */ +void zero_fb(int x1, int y1, int x2, int y2) { + int pixelsize = bpp/8; + int line, fill = 0; + char *dst; + + if (x1 < 0 || x2 <= x1 || x2 > dpy_x) { + return; + } + if (y1 < 0 || y2 <= y1 || y2 > dpy_y) { + return; + } + if (! main_fb) { + return; + } + + dst = main_fb + y1 * main_bytes_per_line + x1 * pixelsize; + line = y1; + while (line++ < y2) { + memset(dst, fill, (size_t) (x2 - x1) * pixelsize); + dst += main_bytes_per_line; + } +} + + diff --git a/x11vnc/xinerama.h b/x11vnc/xinerama.h new file mode 100644 index 0000000..ab4c216 --- /dev/null +++ b/x11vnc/xinerama.h @@ -0,0 +1,17 @@ +#ifndef _X11VNC_XINERAMA_H +#define _X11VNC_XINERAMA_H + +/* -- xinerama.h -- */ +#include "blackout_t.h" + +extern blackout_t blackr[]; +extern tile_blackout_t *tile_blackout; +extern int blackouts; + +extern void initialize_blackouts_and_xinerama(void); +extern void push_sleep(int n); +extern void push_black_screen(int n); +extern void refresh_screen(int push); +extern void zero_fb(int x1, int y1, int x2, int y2); + +#endif /* _X11VNC_XINERAMA_H */ diff --git a/x11vnc/xkb_bell.c b/x11vnc/xkb_bell.c new file mode 100644 index 0000000..2476390 --- /dev/null +++ b/x11vnc/xkb_bell.c @@ -0,0 +1,125 @@ +/* -- xkb_bell.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "connections.h" + +/* + * Bell event handling. Requires XKEYBOARD extension. + */ +int xkb_base_event_type = 0; + +void initialize_xkb(void); +void initialize_watch_bell(void); +void check_bell_event(void); + + +#if LIBVNCSERVER_HAVE_XKEYBOARD + +/* + * check for XKEYBOARD, set up xkb_base_event_type + */ +void initialize_xkb(void) { + int ir, reason; + int op, ev, er, maj, min; + + if (xkbcompat) { + xkb_present = 0; + } else if (! XkbQueryExtension(dpy, &op, &ev, &er, &maj, &min)) { + if (! quiet) { + rfbLog("warning: XKEYBOARD extension not present.\n"); + } + xkb_present = 0; + } else { + xkb_present = 1; + } + + if (! xkb_present) { + return; + } + + if (! XkbOpenDisplay(DisplayString(dpy), &xkb_base_event_type, &ir, + NULL, NULL, &reason) ) { + if (! quiet) { + rfbLog("warning: disabling XKEYBOARD. XkbOpenDisplay" + " failed.\n"); + } + xkb_base_event_type = 0; + xkb_present = 0; + } +} + +void initialize_watch_bell(void) { + if (! xkb_present) { + if (! quiet) { + rfbLog("warning: disabling bell. XKEYBOARD ext. " + "not present.\n"); + } + watch_bell = 0; + sound_bell = 0; + return; + } + + XkbSelectEvents(dpy, XkbUseCoreKbd, XkbBellNotifyMask, 0); + + if (! watch_bell) { + return; + } + if (! XkbSelectEvents(dpy, XkbUseCoreKbd, XkbBellNotifyMask, + XkbBellNotifyMask) ) { + if (! quiet) { + rfbLog("warning: disabling bell. XkbSelectEvents" + " failed.\n"); + } + watch_bell = 0; + sound_bell = 0; + } +} + +/* + * We call this periodically to process any bell events that have + * taken place. + */ +void check_bell_event(void) { + XEvent xev; + XkbAnyEvent *xkb_ev; + int got_bell = 0; + + if (! xkb_base_event_type) { + return; + } + + /* caller does X_LOCK */ + if (! XCheckTypedEvent(dpy, xkb_base_event_type, &xev)) { + return; + } + if (! watch_bell) { + /* we return here to avoid xkb events piling up */ + return; + } + + xkb_ev = (XkbAnyEvent *) &xev; + if (xkb_ev->xkb_type == XkbBellNotify) { + got_bell = 1; + } + + if (got_bell && sound_bell) { + if (! all_clients_initialized()) { + rfbLog("check_bell_event: not sending bell: " + "uninitialized clients\n"); + } else { + if (screen && client_count) { + rfbSendBell(screen); + } + } + } +} +#else +void initialize_watch_bell(void) { + watch_bell = 0; + sound_bell = 0; +} +void check_bell_event(void) {} +#endif + + diff --git a/x11vnc/xkb_bell.h b/x11vnc/xkb_bell.h new file mode 100644 index 0000000..bb0aef2 --- /dev/null +++ b/x11vnc/xkb_bell.h @@ -0,0 +1,12 @@ +#ifndef _X11VNC_XKB_BELL_H +#define _X11VNC_XKB_BELL_H + +/* -- xkb_bell.h -- */ + +extern int xkb_base_event_type; + +extern void initialize_xkb(void); +extern void initialize_watch_bell(void); +extern void check_bell_event(void); + +#endif /* _X11VNC_XKB_BELL_H */ diff --git a/x11vnc/xrandr.c b/x11vnc/xrandr.c new file mode 100644 index 0000000..61cd24e --- /dev/null +++ b/x11vnc/xrandr.c @@ -0,0 +1,225 @@ +/* -- xrandr.c -- */ + +#include "x11vnc.h" +#include "cleanup.h" +#include "connections.h" +#include "remote.h" +#include "screen.h" +#include "win_utils.h" + +time_t last_subwin_trap = 0; +int subwin_trap_count = 0; +XErrorHandler old_getimage_handler; + +int xrandr_present = 0; +int xrandr_width = -1; +int xrandr_height = -1; +int xrandr_rotation = -1; +Time xrandr_timestamp = 0; +Time xrandr_cfg_time = 0; + +void initialize_xrandr(void); +int check_xrandr_event(char *msg); +int known_xrandr_mode(char *s); + + +static int handle_subwin_resize(char *msg); +static void handle_xrandr_change(int new_x, int new_y); + + +void initialize_xrandr(void) { + if (xrandr_present) { +#if LIBVNCSERVER_HAVE_LIBXRANDR + Rotation rot; + + X_LOCK; + xrandr_width = XDisplayWidth(dpy, scr); + xrandr_height = XDisplayHeight(dpy, scr); + XRRRotations(dpy, scr, &rot); + xrandr_rotation = (int) rot; + if (xrandr) { + XRRSelectInput(dpy, rootwin, RRScreenChangeNotifyMask); + } else { + XRRSelectInput(dpy, rootwin, 0); + } + X_UNLOCK; +#endif + } else if (xrandr) { + rfbLog("-xrandr mode specified, but no RANDR support on\n"); + rfbLog(" display or in client library. Disabling -xrandr " + "mode.\n"); + xrandr = 0; + } +} + +static int handle_subwin_resize(char *msg) { + int new_x, new_y; + int i, check = 10, ms = 250; /* 2.5 secs total... */ + + if (msg) {} /* unused vars warning: */ + if (! subwin) { + return 0; /* hmmm... */ + } + if (! valid_window(subwin, NULL, 0)) { + rfbLogEnable(1); + rfbLog("subwin 0x%lx went away!\n", subwin); + X_UNLOCK; + clean_up_exit(1); + } + if (! get_window_size(subwin, &new_x, &new_y)) { + rfbLogEnable(1); + rfbLog("could not get size of subwin 0x%lx\n", subwin); + X_UNLOCK; + clean_up_exit(1); + } + if (wdpy_x == new_x && wdpy_y == new_y) { + /* no change */ + return 0; + } + + /* window may still be changing (e.g. drag resize) */ + for (i=0; i < check; i++) { + int newer_x, newer_y; + usleep(ms * 1000); + + if (! get_window_size(subwin, &newer_x, &newer_y)) { + rfbLogEnable(1); + rfbLog("could not get size of subwin 0x%lx\n", subwin); + clean_up_exit(1); + } + if (new_x == newer_x && new_y == newer_y) { + /* go for it... */ + break; + } else { + rfbLog("subwin 0x%lx still changing size...\n", subwin); + new_x = newer_x; + new_y = newer_y; + } + } + + rfbLog("subwin 0x%lx new size: x: %d -> %d, y: %d -> %d\n", + subwin, wdpy_x, new_x, wdpy_y, new_y); + rfbLog("calling handle_xrandr_change() for resizing\n"); + + X_UNLOCK; + handle_xrandr_change(new_x, new_y); + return 1; +} + +static void handle_xrandr_change(int new_x, int new_y) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + + /* sanity check xrandr_mode */ + if (! xrandr_mode) { + xrandr_mode = strdup("default"); + } else if (! known_xrandr_mode(xrandr_mode)) { + free(xrandr_mode); + xrandr_mode = strdup("default"); + } + rfbLog("xrandr_mode: %s\n", xrandr_mode); + if (!strcmp(xrandr_mode, "exit")) { + close_all_clients(); + rfbLog(" shutting down due to XRANDR event.\n"); + clean_up_exit(0); + } + if (!strcmp(xrandr_mode, "newfbsize") && screen) { + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + if (cl->useNewFBSize) { + continue; + } + rfbLog(" closing client %s (no useNewFBSize" + " support).\n", cl->host); + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + } + rfbReleaseClientIterator(iter); + } + + /* default, resize, and newfbsize create a new fb: */ + rfbLog("check_xrandr_event: trying to create new framebuffer...\n"); + if (new_x < wdpy_x || new_y < wdpy_y) { + check_black_fb(); + } + do_new_fb(1); + rfbLog("check_xrandr_event: fb WxH: %dx%d\n", wdpy_x, wdpy_y); +} + +int check_xrandr_event(char *msg) { + XEvent xev; + if (subwin) { + return handle_subwin_resize(msg); + } +#if LIBVNCSERVER_HAVE_LIBXRANDR + if (! xrandr || ! xrandr_present) { + return 0; + } + if (xrandr_base_event_type && XCheckTypedEvent(dpy, + xrandr_base_event_type + RRScreenChangeNotify, &xev)) { + int do_change; + XRRScreenChangeNotifyEvent *rev; + + rev = (XRRScreenChangeNotifyEvent *) &xev; + rfbLog("check_xrandr_event():\n"); + rfbLog("Detected XRANDR event at location '%s':\n", msg); + rfbLog(" serial: %d\n", (int) rev->serial); + rfbLog(" timestamp: %d\n", (int) rev->timestamp); + rfbLog(" cfg_timestamp: %d\n", (int) rev->config_timestamp); + rfbLog(" size_id: %d\n", (int) rev->size_index); + rfbLog(" sub_pixel: %d\n", (int) rev->subpixel_order); + rfbLog(" rotation: %d\n", (int) rev->rotation); + rfbLog(" width: %d\n", (int) rev->width); + rfbLog(" height: %d\n", (int) rev->height); + rfbLog(" mwidth: %d mm\n", (int) rev->mwidth); + rfbLog(" mheight: %d mm\n", (int) rev->mheight); + rfbLog("\n"); + rfbLog("check_xrandr_event: previous WxH: %dx%d\n", + wdpy_x, wdpy_y); + if (wdpy_x == rev->width && wdpy_y == rev->height && + xrandr_rotation == (int) rev->rotation) { + rfbLog("check_xrandr_event: no change detected.\n"); + do_change = 0; + } else { + do_change = 1; + } + + xrandr_width = rev->width; + xrandr_height = rev->height; + xrandr_timestamp = rev->timestamp; + xrandr_cfg_time = rev->config_timestamp; + xrandr_rotation = (int) rev->rotation; + + rfbLog("check_xrandr_event: updating config...\n"); + XRRUpdateConfiguration(&xev); + + if (do_change) { + X_UNLOCK; + handle_xrandr_change(rev->width, rev->height); + } + rfbLog("check_xrandr_event: current WxH: %dx%d\n", + XDisplayWidth(dpy, scr), XDisplayHeight(dpy, scr)); + rfbLog("check_xrandr_event(): returning control to" + " caller...\n"); + return do_change; + } +#endif + return 0; +} + +int known_xrandr_mode(char *s) { +/* + * default: + * resize: the default + * exit: shutdown clients and exit. + * newfbsize: shutdown clients that do not support NewFBSize encoding. + */ + if (strcmp(s, "default") && strcmp(s, "resize") && + strcmp(s, "exit") && strcmp(s, "newfbsize")) { + return 0; + } else { + return 1; + } +} + + diff --git a/x11vnc/xrandr.h b/x11vnc/xrandr.h new file mode 100644 index 0000000..baab196 --- /dev/null +++ b/x11vnc/xrandr.h @@ -0,0 +1,60 @@ +#ifndef _X11VNC_XRANDR_H +#define _X11VNC_XRANDR_H + +/* -- xrandr.h -- */ + +extern time_t last_subwin_trap; +extern int subwin_trap_count; +extern XErrorHandler old_getimage_handler; + +extern int xrandr_present; +extern int xrandr_width; +extern int xrandr_height; +extern int xrandr_rotation; +extern Time xrandr_timestamp; +extern Time xrandr_cfg_time; + +extern void initialize_xrandr(void); +extern int check_xrandr_event(char *msg); +extern int known_xrandr_mode(char *s); + +#define XRANDR_SET_TRAP_RET(x,y) \ + if (subwin || xrandr) { \ + trapped_getimage_xerror = 0; \ + old_getimage_handler = XSetErrorHandler(trap_getimage_xerror); \ + if (check_xrandr_event(y)) { \ + trapped_getimage_xerror = 0; \ + XSetErrorHandler(old_getimage_handler); \ + return(x); \ + } \ + } +#define XRANDR_CHK_TRAP_RET(x,y) \ + if (subwin || xrandr) { \ + if (trapped_getimage_xerror) { \ + if (subwin) { \ + static int last = 0; \ + subwin_trap_count++; \ + if (time(0) > last_subwin_trap + 60) { \ + rfbLog("trapped GetImage xerror" \ + " in SUBWIN mode. [%d]\n", \ + subwin_trap_count); \ + last_subwin_trap = time(0); \ + last = subwin_trap_count; \ + } \ + if (subwin_trap_count - last > 30) { \ + /* window probably iconified */ \ + usleep(1000*1000); \ + } \ + } else { \ + rfbLog("trapped GetImage xerror" \ + " in XRANDR mode.\n"); \ + } \ + trapped_getimage_xerror = 0; \ + XSetErrorHandler(old_getimage_handler); \ + check_xrandr_event(y); \ + X_UNLOCK; \ + return(x); \ + } \ + } + +#endif /* _X11VNC_XRANDR_H */ diff --git a/x11vnc/xrecord.c b/x11vnc/xrecord.c new file mode 100644 index 0000000..21b7d61 --- /dev/null +++ b/x11vnc/xrecord.c @@ -0,0 +1,1903 @@ +/* -- xrecord.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "win_utils.h" +#include "cleanup.h" +#include "userinput.h" +#include "winattr_t.h" +#include "scrollevent_t.h" + +#define SCR_EV_MAX 128 +scroll_event_t scr_ev[SCR_EV_MAX]; +int scr_ev_cnt; + +int xrecording = 0; +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; +KeySym xrecord_keysym = NoSymbol; + +#define NAMEINFO 2048 +char xrecord_name_info[NAMEINFO]; + +#define SCR_ATTR_CACHE 8 +winattr_t scr_attr_cache[SCR_ATTR_CACHE]; +static double attr_cache_max_age = 1.5; + +Display *rdpy_data = NULL; /* Data connection for RECORD */ +Display *rdpy_ctrl = NULL; /* Control connection for RECORD */ + +Display *gdpy_ctrl = NULL; +int xserver_grabbed = 0; + +int trap_record_xerror(Display *, XErrorEvent *); + +void initialize_xrecord(void); +void shutdown_xrecord(void); +int xrecord_skip_keysym(rfbKeySym keysym); +int xrecord_skip_button(int new, int old); +int xrecord_scroll_keysym(rfbKeySym keysym); +void check_xrecord_reset(int force); +void xrecord_watch(int start, int setby); + + +#if LIBVNCSERVER_HAVE_RECORD +static XRecordRange *rr_CA = NULL; +static XRecordRange *rr_CW = NULL; +static XRecordRange *rr_GS = NULL; +static XRecordRange *rr_scroll[10]; +static XRecordContext rc_scroll; +static XRecordClientSpec rcs_scroll; +static XRecordRange *rr_grab[10]; +static XRecordContext rc_grab; +static XRecordClientSpec rcs_grab; +#endif +static XErrorEvent *trapped_record_xerror_event; +static Display *gdpy_data = NULL; + +static void xrecord_grabserver(int start); +static int xrecord_vi_scroll_keysym(rfbKeySym keysym); +static int xrecord_emacs_scroll_keysym(rfbKeySym keysym); +static int lookup_attr_cache(Window win, int *cache_index, int *next_index); +#if LIBVNCSERVER_HAVE_RECORD +static void record_CA(XPointer ptr, XRecordInterceptData *rec_data); +static void record_CW(XPointer ptr, XRecordInterceptData *rec_data); +static void record_switch(XPointer ptr, XRecordInterceptData *rec_data); +static void record_grab(XPointer ptr, XRecordInterceptData *rec_data); +static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen); +#endif +static void check_xrecord_grabserver(void); + + +int trap_record_xerror(Display *d, XErrorEvent *error) { + trapped_record_xerror = 1; + trapped_record_xerror_event = error; + + if (d) {} /* unused vars warning: */ + + return 0; +} + +static void xrecord_grabserver(int start) { + XErrorHandler old_handler = NULL; + int rc; + + if (debug_grabs) { + fprintf(stderr, "xrecord_grabserver%d/%d %.5f\n", + xserver_grabbed, start, dnowx()); + } + + if (! gdpy_ctrl || ! gdpy_data) { + return; + } +#if LIBVNCSERVER_HAVE_RECORD + if (!start) { + if (! rc_grab) { + return; + } + XRecordDisableContext(gdpy_ctrl, rc_grab); + XRecordFreeContext(gdpy_ctrl, rc_grab); + XFlush(gdpy_ctrl); + rc_grab = 0; + return; + } + + xserver_grabbed = 0; + + rr_grab[0] = rr_GS; + rcs_grab = XRecordAllClients; + + rc_grab = XRecordCreateContext(gdpy_ctrl, 0, &rcs_grab, 1, rr_grab, 1); + trapped_record_xerror = 0; + old_handler = XSetErrorHandler(trap_record_xerror); + + XSync(gdpy_ctrl, True); + + if (! rc_grab || trapped_record_xerror) { + XCloseDisplay(gdpy_ctrl); + XCloseDisplay(gdpy_data); + gdpy_ctrl = NULL; + gdpy_data = NULL; + XSetErrorHandler(old_handler); + return; + } + rc = XRecordEnableContextAsync(gdpy_data, rc_grab, record_grab, NULL); + if (!rc || trapped_record_xerror) { + XCloseDisplay(gdpy_ctrl); + XCloseDisplay(gdpy_data); + gdpy_ctrl = NULL; + gdpy_data = NULL; + XSetErrorHandler(old_handler); + return; + } + XSetErrorHandler(old_handler); + XFlush(gdpy_data); +#endif + if (debug_grabs) { + fprintf(stderr, "xrecord_grabserver-done: %.5f\n", dnowx()); + } +} + +void initialize_xrecord(void) { + use_xrecord = 0; + if (! xrecord_present) { + return; + } + if (nofb) { + return; + } + if (noxrecord) { + return; + } +#if LIBVNCSERVER_HAVE_RECORD + + if (rr_CA) XFree(rr_CA); + if (rr_CW) XFree(rr_CW); + if (rr_GS) XFree(rr_GS); + + rr_CA = XRecordAllocRange(); + rr_CW = XRecordAllocRange(); + rr_GS = XRecordAllocRange(); + if (!rr_CA || !rr_CW || !rr_GS) { + return; + } + /* protocol request ranges: */ + rr_CA->core_requests.first = X_CopyArea; + rr_CA->core_requests.last = X_CopyArea; + + rr_CW->core_requests.first = X_ConfigureWindow; + rr_CW->core_requests.last = X_ConfigureWindow; + + rr_GS->core_requests.first = X_GrabServer; + rr_GS->core_requests.last = X_UngrabServer; + + X_LOCK; + /* open a 2nd control connection to DISPLAY: */ + if (rdpy_data) { + XCloseDisplay(rdpy_data); + rdpy_data = NULL; + } + if (rdpy_ctrl) { + XCloseDisplay(rdpy_ctrl); + rdpy_ctrl = NULL; + } + rdpy_ctrl = XOpenDisplay(DisplayString(dpy)); + XSync(dpy, True); + XSync(rdpy_ctrl, True); + /* open datalink connection to DISPLAY: */ + rdpy_data = XOpenDisplay(DisplayString(dpy)); + if (!rdpy_ctrl || ! rdpy_data) { + X_UNLOCK; + return; + } + disable_grabserver(rdpy_ctrl, 0); + disable_grabserver(rdpy_data, 0); + + use_xrecord = 1; + + /* + * now set up the GrabServer watcher. We get GrabServer + * deadlock in XRecordCreateContext() even with XTestGrabServer + * in place, why? Not sure, so we manually watch for grabs... + */ + if (gdpy_data) { + XCloseDisplay(gdpy_data); + gdpy_data = NULL; + } + if (gdpy_ctrl) { + XCloseDisplay(gdpy_ctrl); + gdpy_ctrl = NULL; + } + xserver_grabbed = 0; + + gdpy_ctrl = XOpenDisplay(DisplayString(dpy)); + XSync(dpy, True); + XSync(gdpy_ctrl, True); + gdpy_data = XOpenDisplay(DisplayString(dpy)); + if (gdpy_ctrl && gdpy_data) { + disable_grabserver(gdpy_ctrl, 0); + disable_grabserver(gdpy_data, 0); + xrecord_grabserver(1); + } + X_UNLOCK; +#endif +} + +void shutdown_xrecord(void) { +#if LIBVNCSERVER_HAVE_RECORD + + if (debug_grabs) { + fprintf(stderr, "shutdown_xrecord%d %.5f\n", + xserver_grabbed, dnowx()); + } + + if (rr_CA) XFree(rr_CA); + if (rr_CW) XFree(rr_CW); + if (rr_GS) XFree(rr_GS); + + rr_CA = NULL; + rr_CW = NULL; + rr_GS = NULL; + + X_LOCK; + if (rdpy_ctrl && rc_scroll) { + XRecordDisableContext(rdpy_ctrl, rc_scroll); + XRecordFreeContext(rdpy_ctrl, rc_scroll); + XSync(rdpy_ctrl, False); + rc_scroll = 0; + } + + if (gdpy_ctrl && rc_grab) { + XRecordDisableContext(gdpy_ctrl, rc_grab); + XRecordFreeContext(gdpy_ctrl, rc_grab); + XSync(gdpy_ctrl, False); + rc_grab = 0; + } + + if (rdpy_data) { + XCloseDisplay(rdpy_data); + rdpy_data = NULL; + } + if (rdpy_ctrl) { + XCloseDisplay(rdpy_ctrl); + rdpy_ctrl = NULL; + } + if (gdpy_data) { + XCloseDisplay(gdpy_data); + gdpy_data = NULL; + } + if (gdpy_ctrl) { + XCloseDisplay(gdpy_ctrl); + gdpy_ctrl = NULL; + } + xserver_grabbed = 0; + X_UNLOCK; +#endif + use_xrecord = 0; + + if (debug_grabs) { + fprintf(stderr, "shutdown_xrecord-done: %.5f\n", dnowx()); + } +} + +int xrecord_skip_keysym(rfbKeySym keysym) { + KeySym sym = (KeySym) keysym; + int ok = -1, matched = 0; + + 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; + } + + /* apply various heuristics: */ + + if (IsModifierKey(sym)) { + /* Shift, Control, etc, usu. generate no scrolls */ + return 1; + } + if (sym == XK_space && scroll_term) { + /* space in a terminal is usu. full page... */ + Window win; + static Window prev_top = None; + int size = 256; + static char name[256]; + + X_LOCK; + win = query_pointer(rootwin); + X_UNLOCK; + if (win != None && win != rootwin) { + if (prev_top != None && win == prev_top) { + ; /* use cached result */ + } else { + prev_top = win; + X_LOCK; + win = descend_pointer(6, win, name, size); + X_UNLOCK; + } + if (match_str_list(name, scroll_term)) { + return 1; + } + } + } + + /* TBD use typing_rate() so */ + return 0; +} + +int xrecord_skip_button(int new, int old) { + /* unused vars warning: */ + if (new || old) {} + + return 0; +} + +static 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; +} + +static 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 */ + + if (sym == XK_Return || sym == XK_KP_Enter || sym == XK_Linefeed) { + return 1; /* Enter */ + } + if (sym==XK_Up || sym==XK_KP_Up || sym==XK_Down || sym==XK_KP_Down) { + return 1; /* U/D arrows */ + } + if (sym == XK_Left || sym == XK_KP_Left || sym == XK_Right || + sym == XK_KP_Right) { + return 1; /* L/R arrows */ + } + if (xrecord_vi_scroll_keysym(keysym)) { + return 1; + } + if (xrecord_emacs_scroll_keysym(keysym)) { + return 1; + } + return 0; +} + +static int lookup_attr_cache(Window win, int *cache_index, int *next_index) { + double now, 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(&now); + for (i=0; i < SCR_ATTR_CACHE; i++) { + + cwin = scr_attr_cache[i].win; + t = scr_attr_cache[i].time; + + if (now > 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; + } +} + + +static XID xrecord_seq = 0; +static double xrecord_start = 0.0; + +#if LIBVNCSERVER_HAVE_RECORD +static void record_CA(XPointer ptr, XRecordInterceptData *rec_data) { + xCopyAreaReq *req; + Window src = None, dst = None, c; + XWindowAttributes attr; + int src_x, src_y, dst_x, dst_y, rx, ry; + int good = 1, dx, dy, k=0, i; + unsigned int w, h; + int dba = 0, db = debug_scroll; + int cache_index, next_index, valid; + + if (dba || db) { + if (rec_data->category == XRecordFromClient) { + req = (xCopyAreaReq *) rec_data->data; + if (req->reqType == X_CopyArea) { + src = req->srcDrawable; + dst = req->dstDrawable; + } + } + } + +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); + + if (! xrecording) { + return; + } +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); + + if (rec_data->id_base == 0) { + return; + } +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); + + if ((XID) ptr != xrecord_seq) { + return; + } +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); + + if (rec_data->category != XRecordFromClient) { + return; + } +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); + + req = (xCopyAreaReq *) rec_data->data; + + if (req->reqType != X_CopyArea) { + return; + } +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); + +/* + +xterm, gnome-terminal, others. + +Note we miss the X_ImageText8 that clears the block cursor. So there is a +short period of time with a painting error: two cursors, one above the other. + + X_ImageText8 + draw: 0x8c00017 nChars: 1, gc: 0x8c00013, x: 101, y: 585, chars=' ' + X_ClearArea + window: 0x8c00018, x: 2, y: 217, w: 10, h: 5 + X_FillPoly + draw: 0x8c00018 gc: 0x8c0000a, shape: 0, coordMode: 0, + X_FillPoly + draw: 0x8c00018 gc: 0x8c0000b, shape: 0, coordMode: 0, + X_CopyArea + src: 0x8c00017, dst: 0x8c00017, gc: 0x8c00013, srcX: 17, srcY: 15, dstX: 17, dstY: 2, w: 480, h: 572 + X_ChangeWindowAttributes + X_ClearArea + window: 0x8c00017, x: 17, y: 574, w: 480, h: 13 + X_ChangeWindowAttributes + + */ + + src = req->srcDrawable; + dst = req->dstDrawable; + src_x = req->srcX; + src_y = req->srcY; + dst_x = req->dstX; + dst_y = req->dstY; + w = req->width; + h = req->height; + + if (w*h < (unsigned int) scrollcopyrect_min_area) { + good = 0; + } else if (!src || !dst) { + good = 0; + } else if (src != dst) { + good = 0; + } else if (scr_ev_cnt >= SCR_EV_MAX) { + good = 0; + } + + dx = dst_x - src_x; + dy = dst_y - src_y; + + if (dx != 0 && dy != 0) { + good = 0; + } + + if (! good) { + return; + } +if (db > 1) fprintf(stderr, "record_CA-%d\n", k++); + + /* + * 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) { + if (!xtranslate(src, rootwin, 0, 0, &rx, &ry, &c, 1)) { + valid = 0; + } + } + 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 > 1) fprintf(stderr, "record_CA-%d\n", k++); + + if (attr.map_state != IsViewable) { + return; + } + + + if (0 || 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; + + scr_ev[i].win = src; + scr_ev[i].frame = None; + scr_ev[i].dx = dx; + scr_ev[i].dy = dy; + scr_ev[i].x = rx + dst_x; + scr_ev[i].y = ry + dst_y; + scr_ev[i].w = w; + scr_ev[i].h = h; + 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; + scr_ev[i].win_h = attr.height; + scr_ev[i].new_x = 0; + scr_ev[i].new_y = 0; + scr_ev[i].new_w = 0; + scr_ev[i].new_h = 0; + + if (dx == 0) { + if (dy > 0) { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = ry + src_y; + scr_ev[i].new_w = w; + scr_ev[i].new_h = dy; + } else { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = ry + dst_y + h; + scr_ev[i].new_w = w; + scr_ev[i].new_h = -dy; + } + } else if (dy == 0) { + if (dx > 0) { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = rx + src_y; + scr_ev[i].new_w = dx; + scr_ev[i].new_h = h; + } else { + scr_ev[i].new_x = rx + dst_x + w; + scr_ev[i].new_y = ry + src_y; + scr_ev[i].new_w = -dx; + scr_ev[i].new_h = h; + } + } + + scr_ev_cnt++; +} + +typedef struct cw_event { + Window win; + int x, y, w, h; +} cw_event_t; + +#define MAX_CW 128 +static cw_event_t cw_events[MAX_CW]; + +static void record_CW(XPointer ptr, XRecordInterceptData *rec_data) { + xConfigureWindowReq *req; + Window win = None, c; + Window src = None, dst = None; + XWindowAttributes attr; + int absent = 0x100000; + int src_x, src_y, dst_x, dst_y, rx, ry; + int good = 1, dx, dy, k=0, i, j, match, list[3]; + int f_x, f_y, f_w, f_h; + int x, y, w, h; + int x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2; + static int index = 0; + unsigned int vals[4]; + unsigned tmask; + char *data; + int dba = 0, db = debug_scroll; + int cache_index, next_index, valid; + + if (db) { + if (rec_data->category == XRecordFromClient) { + req = (xConfigureWindowReq *) rec_data->data; + if (req->reqType == X_ConfigureWindow) { + src = req->window; + } + } + } + +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); + + + if (! xrecording) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if ((XID) ptr != xrecord_seq) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if (rec_data->id_base == 0) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if (rec_data->category == XRecordStartOfData) { + index = 0; + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if (rec_data->category != XRecordFromClient) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if (rec_data->client_swapped) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + req = (xConfigureWindowReq *) rec_data->data; + + if (req->reqType != X_ConfigureWindow) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + tmask = req->mask; + + tmask &= ~CWX; + tmask &= ~CWY; + tmask &= ~CWWidth; + tmask &= ~CWHeight; + + if (tmask) { + /* require no more than these 4 flags */ + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + f_x = req->mask & CWX; + f_y = req->mask & CWY; + f_w = req->mask & CWWidth; + f_h = req->mask & CWHeight; + + if (! f_x || ! f_y) { + if (f_w && f_h) { + ; /* netscape 4.x style */ + } else { + return; + } + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if ( (f_w && !f_h) || (!f_w && f_h) ) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + for (i=0; i<4; i++) { + vals[i] = 0; + } + + data = (char *)req; + data += sz_xConfigureWindowReq; + + for (i=0; i<req->length; i++) { + unsigned int v; + /* + * We use unsigned int for the values. There were + * some crashes on 64bit machines with unsigned longs. + * Need to check that X protocol sends 32bit values. + */ + v = *( (unsigned int *) data); +if (db > 1) fprintf(stderr, " vals[%d] 0x%x/%d\n", i, v, v); + vals[i] = v; + data += sizeof(unsigned int); + } + + if (index >= MAX_CW) { + int i, j; + + /* FIXME, circular, etc. */ + for (i=0; i<2; i++) { + j = MAX_CW - 2 + i; + cw_events[i].win = cw_events[j].win; + cw_events[i].x = cw_events[j].x; + 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; + } + + if (! f_x && ! f_y) { + /* netscape 4.x style CWWidth,CWHeight */ + vals[2] = vals[0]; + vals[3] = vals[1]; + vals[0] = 0; + vals[1] = 0; + } + + cw_events[index].win = req->window; + + if (! f_x) { + cw_events[index].x = absent; + } else { + cw_events[index].x = (int) vals[0]; + } + if (! f_y) { + cw_events[index].y = absent; + } else { + cw_events[index].y = (int) vals[1]; + } + + if (! f_w) { + cw_events[index].w = absent; + } else { + cw_events[index].w = (int) vals[2]; + } + if (! f_h) { + cw_events[index].h = absent; + } else { + cw_events[index].h = (int) vals[3]; + } + + x = cw_events[index].x; + y = cw_events[index].y; + w = cw_events[index].w; + h = cw_events[index].h; + win = cw_events[index].win; + +if (dba || db) fprintf(stderr, " record_CW ind: %d win: 0x%lx x: %d y: %d w: %d h: %d\n", + index, win, x, y, w, h); + + index++; + + if (index < 3) { + good = 0; + } else if (w != absent && h != absent && + w*h < scrollcopyrect_min_area) { + good = 0; + } + + if (! good) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + match = 0; + for (j=index - 1; j >= 0; j--) { + if (cw_events[j].win == win) { + list[match++] = j; + } + if (match >= 3) { + break; + } + } + + if (match != 3) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + +/* + +Mozilla: + +Up arrow: window moves down a bit (dy > 0): + + X_ConfigureWindow + length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 -18, v2 760, v3 906, v4 327692, v5 48234701, v6 3, + CW-mask: CWX,CWY,CWWidth,CWHeight, + X_ConfigureWindow + length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 0, v2 506636, v3 48234701, v4 48234511, + CW-mask: CWX,CWY, + X_ConfigureWindow + length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 65579, v5 0, v6 108009, + CW-mask: CWX,CWY,CWWidth,CWHeight, + +Down arrow: window moves up a bit (dy < 0): + + X_ConfigureWindow + length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 906, v4 327692, v5 48234701, v6 262147, + CW-mask: CWX,CWY,CWWidth,CWHeight, + X_ConfigureWindow + length: 5, window: 0x2e000cd, mask: 0x3, v0 0, v1 -18, v2 506636, v3 48234701, v4 48234511, + CW-mask: CWX,CWY, + X_ConfigureWindow + length: 7, window: 0x2e000cd, mask: 0xf, v0 0, v1 0, v2 760, v3 888, v4 96555, v5 48265642, v6 48265262, + CW-mask: CWX,CWY,CWWidth,CWHeight, + + +Netscape 4.x + +Up arrow: +71.76142 0.01984 X_ConfigureWindow + length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 -15, v2 785, v3 882, v4 327692, v5 159384712, v6 1769484, + CW-mask: CWX,CWY,CWWidth,CWHeight, +71.76153 0.00011 X_ConfigureWindow + length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 867, v2 329228, v3 159384712, v4 159383555, + CW-mask: CWWidth,CWHeight, + XXX,XXX +71.76157 0.00003 X_ConfigureWindow + length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 0, v2 131132, v3 159385313, v4 328759, + CW-mask: CWX,CWY, + XXX,XXX + +Down arrow: +72.93147 0.01990 X_ConfigureWindow + length: 5, window: 0x9800488, mask: 0xc, v0 785, v1 882, v2 328972, v3 159384712, v4 159383555, + CW-mask: CWWidth,CWHeight, + XXX,XXX +72.93156 0.00009 X_ConfigureWindow + length: 5, window: 0x9800488, mask: 0x3, v0 0, v1 -15, v2 458764, v3 159384712, v4 159383567, + CW-mask: CWX,CWY, +72.93160 0.00004 X_ConfigureWindow + length: 7, window: 0x9800488, mask: 0xf, v0 0, v1 0, v2 785, v3 867, v4 131132, v5 159385335, v6 328759, + CW-mask: CWX,CWY,CWWidth,CWHeight, + + +sadly, probably need to handle some more... + + */ + x0 = cw_events[list[2]].x; + y0 = cw_events[list[2]].y; + w0 = cw_events[list[2]].w; + h0 = cw_events[list[2]].h; + + x1 = cw_events[list[1]].x; + y1 = cw_events[list[1]].y; + w1 = cw_events[list[1]].w; + h1 = cw_events[list[1]].h; + + x2 = cw_events[list[0]].x; + y2 = cw_events[list[0]].y; + w2 = cw_events[list[0]].w; + h2 = cw_events[list[0]].h; + + /* see NS4 XXX's above: */ + if (w2 == absent || h2 == absent) { + /* up arrow */ + if (w2 == absent) { + w2 = w1; + } + if (h2 == absent) { + h2 = h1; + } + } + if (x1 == absent || y1 == absent) { + /* up arrow */ + if (x1 == absent) { + x1 = x2; + } + if (y1 == absent) { + y1 = y2; + } + } + if (x0 == absent || y0 == absent) { + /* down arrow */ + if (x0 == absent) { + /* hmmm... what to do */ + x0 = x2; + } + if (y0 == absent) { + y0 = y2; + } + } + +if (dba) fprintf(stderr, "%d/%d/%d/%d %d/%d/%d/%d %d/%d/%d/%d\n", x0, y0, w0, h0, x1, y1, w1, h1, x2, y2, w2, h2); + + dy = y1 - y0; + dx = x1 - x0; + + src_x = x2; + src_y = y2; + w = w2; + h = h2; + + /* check w and h before we modify them */ + if (w <= 0 || h <= 0) { + good = 0; + } else if (w == absent || h == absent) { + good = 0; + } + if (! good) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if (dy > 0) { + h -= dy; + } else { + h += dy; + src_y -= dy; + } + if (dx > 0) { + w -= dx; + } else { + w += dx; + src_x -= dx; + } + + dst_x = src_x + dx; + dst_y = src_y + dy; + + if (x0 == absent || x1 == absent || x2 == absent) { + good = 0; + } else if (y0 == absent || y1 == absent || y2 == absent) { + good = 0; + } else if (dx != 0 && dy != 0) { + good = 0; + } else if (w0 - w2 != nabs(dx)) { + 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 > 1) fprintf(stderr, "record_CW-%d\n", k++); + + /* + * 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) { + if (!xtranslate(win, rootwin, 0, 0, &rx, &ry, &c, 1)) { + valid = 0; + } + } + 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 (! valid) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if (attr.map_state != IsViewable) { + return; + } +if (db > 1) fprintf(stderr, "record_CW-%d\n", k++); + + if (0 || 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; + + scr_ev[i].win = win; + scr_ev[i].frame = None; + scr_ev[i].dx = dx; + scr_ev[i].dy = dy; + scr_ev[i].x = rx + dst_x; + scr_ev[i].y = ry + dst_y; + scr_ev[i].w = w; + scr_ev[i].h = h; + 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; + scr_ev[i].win_h = attr.height; + scr_ev[i].new_x = 0; + scr_ev[i].new_y = 0; + scr_ev[i].new_w = 0; + scr_ev[i].new_h = 0; + + if (dx == 0) { + if (dy > 0) { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = ry + src_y; + scr_ev[i].new_w = w; + scr_ev[i].new_h = dy; + } else { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = ry + dst_y + h; + scr_ev[i].new_w = w; + scr_ev[i].new_h = -dy; + } + } else if (dy == 0) { + if (dx > 0) { + scr_ev[i].new_x = rx + src_x; + scr_ev[i].new_y = rx + src_y; + scr_ev[i].new_w = dx; + scr_ev[i].new_h = h; + } else { + scr_ev[i].new_x = rx + dst_x + w; + scr_ev[i].new_y = ry + src_y; + scr_ev[i].new_w = -dx; + scr_ev[i].new_h = h; + } + } + + /* indicate we have a new one */ + scr_ev_cnt++; + + index = 0; +} + +static void record_switch(XPointer ptr, XRecordInterceptData *rec_data) { + static int first = 1; + xReq *req; + + 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); + } else if (rec_data->category == XRecordEndOfData) { + ; + } else if (rec_data->category == XRecordClientStarted) { + ; + } else if (rec_data->category == XRecordClientDied) { + ; + } else if (rec_data->category == XRecordFromServer) { + ; + } + + if (rec_data->category != XRecordFromClient) { + XRecordFreeData(rec_data); + return; + } + + req = (xReq *) rec_data->data; + + if (req->reqType == X_CopyArea) { + record_CA(ptr, rec_data); + } else if (req->reqType == X_ConfigureWindow) { + record_CW(ptr, rec_data); + } else { + ; + } + XRecordFreeData(rec_data); +} + +static void record_grab(XPointer ptr, XRecordInterceptData *rec_data) { + xReq *req; + int db = 0; + + if (debug_grabs) db = 1; + + /* should handle control msgs, start/stop/etc */ + if (rec_data->category == XRecordStartOfData) { + ; + } else if (rec_data->category == XRecordEndOfData) { + ; + } else if (rec_data->category == XRecordClientStarted) { + ; + } else if (rec_data->category == XRecordClientDied) { + ; + } else if (rec_data->category == XRecordFromServer) { + ; + } + + if (rec_data->category != XRecordFromClient) { + XRecordFreeData(rec_data); + return; + } + + req = (xReq *) rec_data->data; + + if (req->reqType == X_GrabServer) { + double now = dnow() - x11vnc_start; + xserver_grabbed++; + if (db) rfbLog("X server Grabbed: %d %.5f\n", xserver_grabbed, now); + if (xserver_grabbed > 1) { + /* + * some apps do multiple grabs... very unlikely + * two apps will be doing it at same time. + */ + xserver_grabbed = 1; + } + } else if (req->reqType == X_UngrabServer) { + double now = dnow() - x11vnc_start; + xserver_grabbed--; + if (xserver_grabbed < 0) { + xserver_grabbed = 0; + } + if (db) rfbLog("X server Un-Grabbed: %d %.5f\n", xserver_grabbed, now); + } else { + ; + } + XRecordFreeData(rec_data); + + /* unused vars warning: */ + if (ptr) {} +} +#endif + +static void check_xrecord_grabserver(void) { + int last_val, cnt = 0, i, max = 10; + double d; +#if LIBVNCSERVER_HAVE_RECORD + if (!gdpy_ctrl || !gdpy_data) { + return; + } + + dtime0(&d); + XFlush(gdpy_ctrl); + for (i=0; i<max; i++) { + last_val = xserver_grabbed; + XRecordProcessReplies(gdpy_data); + if (xserver_grabbed != last_val) { + cnt++; + } else if (i > 2) { + break; + } + } + if (cnt) { + XFlush(gdpy_ctrl); + } + if (debug_grabs && cnt > 0) { + d = dtime(&d); +fprintf(stderr, "check_xrecord_grabserver: cnt=%d i=%d %.4f\n", cnt, i, d); + } +#endif +} + +#if LIBVNCSERVER_HAVE_RECORD +static void shutdown_record_context(XRecordContext rc, int bequiet, int reopen) { + int ret1, ret2; + int verb = (!bequiet && !quiet); + + if (0 || debug_scroll) { + rfbLog("shutdown_record_context(0x%lx, %d, %d)\n", rc, + bequiet, reopen); + verb = 1; + } + + ret1 = XRecordDisableContext(rdpy_ctrl, rc); + if (!ret1 && verb) { + rfbLog("XRecordDisableContext(0x%lx) failed.\n", rc); + } + ret2 = XRecordFreeContext(rdpy_ctrl, rc); + if (!ret2 && verb) { + 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 && gdpy_ctrl) { + check_xrecord_grabserver(); + if (xserver_grabbed) { + rfbLog("shutdown_record_context: skip reopen," + " server grabbed\n"); + reopen = 0; + } + } + if (reopen) { + char *dpystr = DisplayString(dpy); + + if (debug_scroll) { + rfbLog("closing RECORD data connection.\n"); + } + XCloseDisplay(rdpy_data); + rdpy_data = NULL; + + if (debug_scroll) { + rfbLog("closing RECORD control connection.\n"); + } + XCloseDisplay(rdpy_ctrl); + rdpy_ctrl = NULL; + + 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; + } + XSync(dpy, False); + + disable_grabserver(rdpy_ctrl, 0); + XSync(rdpy_ctrl, True); + + rdpy_data = XOpenDisplay(dpystr); + + if (! rdpy_data) { + rfbLog("Failed to reopen RECORD data connection:" + "%s\n", dpystr); + rfbLog(" disabling RECORD scroll detection.\n"); + XCloseDisplay(rdpy_ctrl); + rdpy_ctrl = NULL; + use_xrecord = 0; + return; + } + disable_grabserver(rdpy_data, 0); + + if (debug_scroll || (! bequiet && reopen == 2)) { + rfbLog("reopened RECORD data and control display" + " connections: %s\n", dpystr); + } + } +} +#endif + +void check_xrecord_reset(int force) { + static double last_reset = 0.0; + int reset_time = 60, require_idle = 10; + int reset_time2 = 600, require_idle2 = 40; + double now; + XErrorHandler old_handler = NULL; + + if (gdpy_ctrl) { + X_LOCK; + check_xrecord_grabserver(); + X_UNLOCK; + } else { + /* more dicey if not watching grabserver */ + reset_time = reset_time2; + require_idle = require_idle2; + } + + if (!use_xrecord) { + return; + } + if (xrecording) { + return; + } + if (button_mask) { + return; + } + if (xserver_grabbed) { + return; + } + +#if LIBVNCSERVER_HAVE_RECORD + if (! rc_scroll) { + return; + } + now = dnow(); + if (last_reset == 0.0) { + last_reset = now; + return; + } + /* + * try to wait for a break in input to reopen the displays + * this is only to avoid XGrabServer deadlock on the repopens. + */ + if (force) { + ; + } else if (now < last_reset + reset_time) { + return; + } else if (now < last_pointer_click_time + require_idle) { + return; + } else if (now < last_keyboard_time + require_idle) { + return; + } + X_LOCK; + trapped_record_xerror = 0; + old_handler = XSetErrorHandler(trap_record_xerror); + + /* unlikely, but check again since we will definitely be doing it. */ + if (gdpy_ctrl) { + check_xrecord_grabserver(); + if (xserver_grabbed) { + XSetErrorHandler(old_handler); + X_UNLOCK; + return; + } + } + + shutdown_record_context(rc_scroll, 0, 1); + rc_scroll = 0; + + XSetErrorHandler(old_handler); + X_UNLOCK; + + last_reset = now; +#endif +} + +#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, c, clast; + static double create_time = 0.0; + double now; + static double last_error = 0.0; + int rc, db = debug_scroll; + int do_shutdown = 0; + int reopen_dpys = 1; + XErrorHandler old_handler = NULL; + static Window last_win = None, last_result = None; + +if (0) db = 1; + + if (nofb) { + xrecording = 0; + return; + } + if (use_threads) { + /* XXX not working */ + use_xrecord = 0; + xrecording = 0; + return; + } + + dtime0(&now); + if (now < last_error + 0.5) { + return; + } + + if (gdpy_ctrl) { + X_LOCK; + check_xrecord_grabserver(); + X_UNLOCK; + if (xserver_grabbed) { +if (db || debug_grabs) fprintf(stderr, "xrecord_watch: %d/%d out xserver_grabbed\n", start, setby); + return; + } + } + +#if LIBVNCSERVER_HAVE_RECORD + if (! start) { + int shut_reopen = 2, shut_time = 25; +if (db || debug_grabs) fprintf(stderr, "XRECORD OFF: %d/%d %.4f\n", xrecording, setby, now - x11vnc_start); + xrecording = 0; + if (! rc_scroll) { + xrecord_focus_window = None; + xrecord_wm_window = None; + xrecord_ptr_window = None; + xrecord_keysym = NoSymbol; + rcs_scroll = 0; + return; + } + + if (! do_shutdown && now > create_time + shut_time) { + /* XXX unstable if we keep a RECORD going forever */ + do_shutdown = 1; + } + + SCR_LOCK; + + if (do_shutdown) { +if (db > 1) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll); + X_LOCK; + trapped_record_xerror = 0; + old_handler = XSetErrorHandler(trap_record_xerror); + + shutdown_record_context(rc_scroll, 0, shut_reopen); + rc_scroll = 0; + + /* + * n.b. there is a grabserver issue wrt + * XRecordCreateContext() even though rdpy_ctrl + * is set imprevious to grabs. Perhaps a bug + * in the X server or library... + * + * If there are further problems, a thought + * to recreate rc_scroll right after the + * reopen. + */ + + if (! use_xrecord) { + XSetErrorHandler(old_handler); + X_UNLOCK; + SCR_UNLOCK; + return; + } + + XRecordProcessReplies(rdpy_data); + + if (trapped_record_xerror) { + RECORD_ERROR_MSG; + last_error = now; + } + + XSetErrorHandler(old_handler); + X_UNLOCK; + SCR_UNLOCK; + + } else { + if (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); + rc_scroll = 0; + + last_error = now; + + if (! use_xrecord) { + XSetErrorHandler(old_handler); + X_UNLOCK; + SCR_UNLOCK; + return; + } + } + XSetErrorHandler(old_handler); + X_UNLOCK; + } + } + + SCR_UNLOCK; + /* + * XXX if we do a XFlush(rdpy_ctrl) here we get: + * + + X Error of failed request: XRecordBadContext + Major opcode of failed request: 145 (RECORD) + Minor opcode of failed request: 5 (XRecordEnableContext) + Context in failed request: 0x2200013 + Serial number of failed request: 29 + Current serial number in output stream: 29 + + * + * need to figure out what is going on... since it may lead + * infrequent failures. + */ + xrecord_focus_window = None; + xrecord_wm_window = None; + xrecord_ptr_window = None; + xrecord_keysym = NoSymbol; + rcs_scroll = 0; + return; + } +if (db || debug_grabs) fprintf(stderr, "XRECORD ON: %d/%d %.4f\n", xrecording, setby, now - x11vnc_start); + + if (xrecording) { + return; + } + + if (do_shutdown && rc_scroll) { + static int didmsg = 0; + /* should not happen... */ + if (0 || !didmsg) { + rfbLog("warning: do_shutdown && rc_scroll\n"); + didmsg = 1; + } + xrecord_watch(0, SCR_NONE); + } + + xrecording = 0; + xrecord_focus_window = None; + xrecord_wm_window = None; + xrecord_ptr_window = None; + xrecord_keysym = NoSymbol; + xrecord_set_by_keys = 0; + xrecord_set_by_mouse = 0; + + /* get the window with focus and mouse pointer: */ + clast = None; + focus = None; + wm = None; + + X_LOCK; + SCR_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 + + wm = query_pointer(rootwin); + if (wm) { + c = wm; + } else { + c = rootwin; + } + + /* 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. */ + xrecord_name_info[0] = '\0'; + clast = descend_pointer(6, c, NULL, 0); + } else { + char *nm; + int matched_good = 0, matched_skip = 0; + + clast = descend_pointer(6, c, xrecord_name_info, NAMEINFO); +if (db) fprintf(stderr, "name_info: %s\n", xrecord_name_info); + + nm = xrecord_name_info; + + if (scroll_good_all) { + matched_good += match_str_list(nm, scroll_good_all); + } + 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; + } + } + 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"); + X_UNLOCK; + SCR_UNLOCK; + return; + } + + /* set protocol request ranges: */ + 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 */ + + if (gdpy_ctrl) { + /* + * Even though rdpy_ctrl is impervious to grabs + * at this point, we still get deadlock, why? + * It blocks in the library find_display() call. + */ + check_xrecord_grabserver(); + if (xserver_grabbed) { + XSetErrorHandler(old_handler); + X_UNLOCK; + SCR_UNLOCK; + return; + } + } + rcs_scroll = (XRecordClientSpec) clast; + rc_scroll = XRecordCreateContext(rdpy_ctrl, 0, &rcs_scroll, 1, + rr_scroll, 2); + + if (! do_shutdown) { + XSync(rdpy_ctrl, False); + } +if (db) fprintf(stderr, "NEW rc: 0x%lx\n", rc_scroll); + if (rc_scroll) { + dtime0(&create_time); + } else { + rcs_scroll = 0; + } + + } else if (! do_shutdown) { + if (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 > 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)) { + if (1 || now > last_error + 60) { + rfbLog("failed to register client 0x%lx with" + " X RECORD context rc_scroll.\n", clast); + } + last_error = now; + 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; + SCR_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); + rc_scroll = 0; + last_error = now; + + XSetErrorHandler(old_handler); + X_UNLOCK; + SCR_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++; + dtime0(&xrecord_start); + + rc = XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch, + (XPointer) xrecord_seq); + + if (!rc || trapped_record_xerror) { + if (1 || now > last_error + 60) { + rfbLog("failed to enable RECORD context " + "rc_scroll: 0x%lx rc: %d\n", rc_scroll, rc); + if (trapped_record_xerror) { + RECORD_ERROR_MSG; + } + } + shutdown_record_context(rc_scroll, 0, reopen_dpys); + rc_scroll = 0; + last_error = now; + xrecording = 0; + /* continue on for now... */ + } + XSetErrorHandler(old_handler); + + /* XXX this may cause more problems than it solves... */ + if (use_xrecord) { + XFlush(rdpy_data); + } + + X_UNLOCK; + SCR_UNLOCK; +#endif +} + + diff --git a/x11vnc/xrecord.h b/x11vnc/xrecord.h new file mode 100644 index 0000000..996b131 --- /dev/null +++ b/x11vnc/xrecord.h @@ -0,0 +1,35 @@ +#ifndef _X11VNC_XRECORD_H +#define _X11VNC_XRECORD_H + +/* -- xrecord.h -- */ +#include "scrollevent_t.h" +#include "winattr_t.h" + +extern scroll_event_t scr_ev[]; +extern int scr_ev_cnt; +extern int xrecording; +extern int xrecord_set_by_keys; +extern int xrecord_set_by_mouse; +extern Window xrecord_focus_window; +extern Window xrecord_wm_window; +extern Window xrecord_ptr_window; +extern KeySym xrecord_keysym; +extern char xrecord_name_info[]; + +extern winattr_t scr_attr_cache[]; + +extern Display *rdpy_data; +extern Display *rdpy_ctrl; + +extern Display *gdpy_ctrl; +extern int xserver_grabbed; + +extern void initialize_xrecord(void); +extern void shutdown_xrecord(void); +extern int xrecord_skip_keysym(rfbKeySym keysym); +extern int xrecord_skip_button(int new, int old); +extern int xrecord_scroll_keysym(rfbKeySym keysym); +extern void check_xrecord_reset(int force); +extern void xrecord_watch(int start, int setby); + +#endif /* _X11VNC_XRECORD_H */ diff --git a/x11vnc/xwrappers.c b/x11vnc/xwrappers.c new file mode 100644 index 0000000..a99d519 --- /dev/null +++ b/x11vnc/xwrappers.c @@ -0,0 +1,697 @@ +/* -- xwrappers.c -- */ + +#include "x11vnc.h" +#include "xrecord.h" +#include "keyboard.h" + +int xshm_present = 0; +int xtest_present = 0; +int xtrap_present = 0; +int xrecord_present = 0; +int xkb_present = 0; +int xinerama_present = 0; + +int keycode_state[256]; +int rootshift = 0; +int clipshift = 0; + + +int guess_bits_per_color(int bits_per_pixel); + +Status XShmGetImage_wr(Display *disp, Drawable d, XImage *image, int x, int y, + unsigned long mask); +XImage *XShmCreateImage_wr(Display* disp, Visual* vis, unsigned int depth, + int format, char* data, XShmSegmentInfo* shminfo, unsigned int width, + unsigned int height); +Status XShmAttach_wr(Display *disp, XShmSegmentInfo *shminfo); +Status XShmDetach_wr(Display *disp, XShmSegmentInfo *shminfo); +Bool XShmQueryExtension_wr(Display *disp); + +XImage *xreadscreen(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, Bool show_cursor); +XImage *XGetSubImage_wr(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, unsigned long plane_mask, + int format, XImage *dest_image, int dest_x, int dest_y); +XImage *XGetImage_wr(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, unsigned long plane_mask, + int format); +XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth, + int format, int offset, char *data, unsigned int width, + unsigned int height, int bitmap_pad, int bytes_per_line); +void copy_image(XImage *dest, int x, int y, unsigned int w, unsigned int h); +void init_track_keycode_state(void); + +void XTRAP_FakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, + unsigned long delay); +void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, + unsigned long delay); +void XTRAP_FakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, + unsigned long delay); +void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, + unsigned long delay); +void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y, + unsigned long delay); +void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y, + unsigned long delay); + +Bool XTestCompareCurrentCursorWithWindow_wr(Display* dpy, Window w); +Bool XTestCompareCursorWithWindow_wr(Display* dpy, Window w, Cursor cursor); +Bool XTestQueryExtension_wr(Display *dpy, int *ev, int *er, int *maj, + int *min); +void XTestDiscard_wr(Display *dpy); +Bool XETrapQueryExtension_wr(Display *dpy, int *ev, int *er, int *op); +int XTestGrabControl_wr(Display *dpy, Bool impervious); +int XTRAP_GrabControl_wr(Display *dpy, Bool impervious); +void disable_grabserver(Display *in_dpy, int change); + +Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min); + + +static void copy_raw_fb(XImage *dest, int x, int y, unsigned int w, unsigned int h); +static void upup_downdown_warning(KeyCode key, Bool down); + + +/* + * used in rfbGetScreen and rfbNewFramebuffer: and estimate to the number + * of bits per color, of course for some visuals, e.g. 565, the number + * is not the same for each color. This is just a sane default. + */ +int guess_bits_per_color(int bits_per_pixel) { + int bits_per_color; + + /* first guess, spread them "evenly" over R, G, and B */ + bits_per_color = bits_per_pixel/3; + if (bits_per_color < 1) { + bits_per_color = 1; /* 1bpp, 2bpp... */ + } + + /* choose safe values for usual cases: */ + if (bits_per_pixel == 8) { + bits_per_color = 2; + } else if (bits_per_pixel == 15 || bits_per_pixel == 16) { + bits_per_color = 5; + } else if (bits_per_pixel == 24 || bits_per_pixel == 32) { + bits_per_color = 8; + } + return bits_per_color; +} + +/* + * Kludge to interpose image gets and limit to a subset rectangle of + * the rootwin. This is the -sid option trying to work around invisible + * saveUnders menu, etc, windows. Also -clip option. + */ + +#define ADJUST_ROOTSHIFT \ + if (rootshift && subwin) { \ + d = rootwin; \ + x += off_x; \ + y += off_y; \ + } \ + if (clipshift) { \ + x += coff_x; \ + y += coff_y; \ + } + +/* + * Wrappers for Image related X calls + */ +Status XShmGetImage_wr(Display *disp, Drawable d, XImage *image, int x, int y, + unsigned long mask) { + + ADJUST_ROOTSHIFT + + /* Note: the Solaris overlay stuff is all non-shm (using_shm = 0) */ + +#if LIBVNCSERVER_HAVE_XSHM + return XShmGetImage(disp, d, image, x, y, mask); +#else + return (Status) 0; +#endif +} + +XImage *XShmCreateImage_wr(Display* disp, Visual* vis, unsigned int depth, + int format, char* data, XShmSegmentInfo* shminfo, unsigned int width, + unsigned int height) { + +#if LIBVNCSERVER_HAVE_XSHM + return XShmCreateImage(disp, vis, depth, format, data, shminfo, + width, height); +#else + return (XImage *) 0; +#endif +} + +Status XShmAttach_wr(Display *disp, XShmSegmentInfo *shminfo) { +#if LIBVNCSERVER_HAVE_XSHM + return XShmAttach(disp, shminfo); +#else + return (Status) 0; +#endif +} + +Status XShmDetach_wr(Display *disp, XShmSegmentInfo *shminfo) { +#if LIBVNCSERVER_HAVE_XSHM + return XShmDetach(disp, shminfo); +#else + return (Status) 0; +#endif +} + +Bool XShmQueryExtension_wr(Display *disp) { +#if LIBVNCSERVER_HAVE_XSHM + return XShmQueryExtension(disp); +#else + return False; +#endif +} + +/* wrapper for overlay screen reading: */ + +XImage *xreadscreen(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, Bool show_cursor) { +#ifdef SOLARIS_OVERLAY + return XReadScreen(disp, d, x, y, width, height, + show_cursor); +#else +# ifdef IRIX_OVERLAY + { unsigned long hints = 0, hints_ret; + if (show_cursor) hints |= XRD_READ_POINTER; + return XReadDisplay(disp, d, x, y, width, height, + hints, &hints_ret); + } +# else + /* unused vars warning: */ + if (disp || d || x || y || width || height || show_cursor) {} + + return NULL; +# endif +#endif +} + +XImage *XGetSubImage_wr(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, unsigned long plane_mask, + int format, XImage *dest_image, int dest_x, int dest_y) { + + ADJUST_ROOTSHIFT + + if (overlay && dest_x == 0 && dest_y == 0) { + size_t size = dest_image->height * dest_image->bytes_per_line; + XImage *xi; + + xi = xreadscreen(disp, d, x, y, width, height, + (Bool) overlay_cursor); + + if (! xi) return NULL; + + /* + * There is extra overhead from memcpy and free... + * this is not like the real XGetSubImage(). We hope + * this significant overhead is still small compared to + * the time to retrieve the fb data. + */ + memcpy(dest_image->data, xi->data, size); + + XDestroyImage(xi); + return (dest_image); + } + return XGetSubImage(disp, d, x, y, width, height, plane_mask, + format, dest_image, dest_x, dest_y); +} + +XImage *XGetImage_wr(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, unsigned long plane_mask, + int format) { + + ADJUST_ROOTSHIFT + + if (overlay) { + return xreadscreen(disp, d, x, y, width, height, + (Bool) overlay_cursor); + } + return XGetImage(disp, d, x, y, width, height, plane_mask, format); +} + +XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth, + int format, int offset, char *data, unsigned int width, + unsigned int height, int bitmap_pad, int bytes_per_line) { + /* + * This is a kludge to get a created XImage to exactly match what + * XReadScreen returns: we noticed the rgb masks are different + * from XCreateImage with the high color visual (red mask <-> + * blue mask). Note we read from the root window(!) then free + * the data. + */ + + if (raw_fb) { /* raw_fb hack */ + XImage *xi; + xi = (XImage *) malloc(sizeof(XImage)); + memset(xi, 0, sizeof(XImage)); + xi->depth = depth; + xi->bits_per_pixel = (depth == 24) ? 32 : depth; + xi->format = format; + xi->xoffset = offset; + xi->data = data; + xi->width = width; + xi->height = height; + xi->bitmap_pad = bitmap_pad; + xi->bytes_per_line = bytes_per_line ? bytes_per_line : + xi->width * xi->bits_per_pixel / 8; + return xi; + } + + if (overlay) { + XImage *xi; + xi = xreadscreen(disp, window, 0, 0, width, height, False); + if (xi == NULL) { + return xi; + } + if (xi->data != NULL) { + free(xi->data); + } + xi->data = data; + return xi; + } + + return XCreateImage(disp, visual, depth, format, offset, data, + width, height, bitmap_pad, bytes_per_line); +} + +static void copy_raw_fb(XImage *dest, int x, int y, unsigned int w, unsigned int h) { + char *src, *dst; + unsigned int line; + int pixelsize = bpp/8; + int bpl = wdpy_x * pixelsize; + + if (clipshift) { + x += coff_x; + y += coff_y; + } + if (! raw_fb_seek) { + src = raw_fb_addr + raw_fb_offset + bpl*y + pixelsize*x; + dst = dest->data; + + for (line = 0; line < h; line++) { + memcpy(dst, src, w * pixelsize); + src += bpl; + dst += dest->bytes_per_line; + } + } else{ + int n, len, del, sz = w * pixelsize; + off_t off = (off_t) (raw_fb_offset + bpl*y + pixelsize*x); + + lseek(raw_fb_fd, off, SEEK_SET); + dst = dest->data; + + for (line = 0; line < h; line++) { + len = sz; + del = 0; + while (len > 0) { + n = read(raw_fb_fd, dst + del, len); + + if (n > 0) { + del += n; + len -= n; + } else if (n == 0) { + break; + } else { + /* overkill... */ + if (errno != EINTR && errno != EAGAIN) { + break; + } + } + } + if (bpl > sz) { + off = (off_t) (bpl - sz); + lseek(raw_fb_fd, off, SEEK_CUR); + } + dst += dest->bytes_per_line; + } + } +} + +void copy_image(XImage *dest, int x, int y, unsigned int w, unsigned int h) { + /* default (w=0, h=0) is the fill the entire XImage */ + if (w < 1) { + w = dest->width; + } + if (h < 1) { + h = dest->height; + } + + if (use_snapfb && snap_fb && dest != snaprect) { + char *src, *dst; + unsigned int line; + int pixelsize = bpp/8; + + src = snap->data + snap->bytes_per_line*y + pixelsize*x; + dst = dest->data; + for (line = 0; line < h; line++) { + memcpy(dst, src, w * pixelsize); + src += snap->bytes_per_line; + dst += dest->bytes_per_line; + } + + } else if (raw_fb) { + copy_raw_fb(dest, x, y, w, h); + + } else if (using_shm && (int) w == dest->width && + (int) h == dest->height) { + XShmGetImage_wr(dpy, window, dest, x, y, AllPlanes); + + } else { + XGetSubImage_wr(dpy, window, x, y, w, h, AllPlanes, + ZPixmap, dest, 0, 0); + } +} + +#define DEBUG_SKIPPED_INPUT(dbg, str) \ + if (dbg) { \ + rfbLog("skipped input: %s\n", str); \ + } + +void init_track_keycode_state(void) { + int i; + for (i=0; i<256; i++) { + keycode_state[i] = 0; + } + get_keystate(keycode_state); +} + +static void upup_downdown_warning(KeyCode key, Bool down) { + if ((down ? 1:0) == keycode_state[(int) key]) { + rfbLog("XTestFakeKeyEvent: keycode=0x%x \"%s\" is *already* " + "%s\n", key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)), + down ? "down":"up"); + } +} + +/* + * wrappers for XTestFakeKeyEvent, etc.. + * also for XTrap equivalents XESimulateXEventRequest + */ + +void XTRAP_FakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, + unsigned long delay) { + + if (! xtrap_present) { + DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTRAP"); + return; + } + /* unused vars warning: */ + if (dpy || key || down || delay) {} + +#if LIBVNCSERVER_HAVE_LIBXTRAP + XESimulateXEventRequest(trap_ctx, down ? KeyPress : KeyRelease, + key, 0, 0, 0); + if (debug_keyboard) { + upup_downdown_warning(key, down); + } + keycode_state[(int) key] = down ? 1 : 0; +#else + DEBUG_SKIPPED_INPUT(debug_keyboard, "keyboard: no-XTRAP-build"); +#endif +} + +void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, + unsigned long delay) { + static int first = 1; + if (debug_keyboard) { + rfbLog("XTestFakeKeyEvent(dpy, keycode=0x%x \"%s\", %s)\n", + key, XKeysymToString(XKeycodeToKeysym(dpy, key, 0)), + down ? "down":"up"); + } + if (first) { + init_track_keycode_state(); + first = 0; + } + if (down) { + last_keyboard_keycode = -key; + } else { + last_keyboard_keycode = key; + } + + if (xtrap_input) { + XTRAP_FakeKeyEvent_wr(dpy, key, down, delay); + return; + } + + if (! xtest_present) { + 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); + if (debug_keyboard) { + upup_downdown_warning(key, down); + } + keycode_state[(int) key] = down ? 1 : 0; +#endif +} + +void XTRAP_FakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, + unsigned long delay) { + + if (! xtrap_present) { + DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTRAP"); + return; + } + /* unused vars warning: */ + if (dpy || button || is_press || delay) {} + +#if LIBVNCSERVER_HAVE_LIBXTRAP + XESimulateXEventRequest(trap_ctx, + is_press ? ButtonPress : ButtonRelease, button, 0, 0, 0); +#else + DEBUG_SKIPPED_INPUT(debug_keyboard, "button: no-XTRAP-build"); +#endif +} + +void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, + unsigned long delay) { + + if (xtrap_input) { + XTRAP_FakeButtonEvent_wr(dpy, button, is_press, delay); + return; + } + + if (! xtest_present) { + 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 +} + +void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y, + unsigned long delay) { + + if (! xtrap_present) { + DEBUG_SKIPPED_INPUT(debug_keyboard, "motion: no-XTRAP"); + return; + } + /* unused vars warning: */ + if (dpy || screen || x || y || delay) {} + +#if LIBVNCSERVER_HAVE_LIBXTRAP + XESimulateXEventRequest(trap_ctx, MotionNotify, 0, x, y, 0); +#else + DEBUG_SKIPPED_INPUT(debug_keyboard, "motion: no-XTRAP-build"); +#endif +} + +void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y, + unsigned long delay) { + + if (xtrap_input) { + XTRAP_FakeMotionEvent_wr(dpy, screen, x, y, delay); + return; + } + + 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 +} + +Bool XTestCompareCurrentCursorWithWindow_wr(Display* dpy, Window w) { + if (! xtest_present) { + return False; + } +#if LIBVNCSERVER_HAVE_XTEST + return XTestCompareCurrentCursorWithWindow(dpy, w); +#else + return False; +#endif +} + +Bool XTestCompareCursorWithWindow_wr(Display* dpy, Window w, Cursor cursor) { + if (! xtest_present) { + return False; + } +#if LIBVNCSERVER_HAVE_XTEST + return XTestCompareCursorWithWindow(dpy, w, cursor); +#else + return False; +#endif +} + +Bool XTestQueryExtension_wr(Display *dpy, int *ev, int *er, int *maj, + int *min) { +#if LIBVNCSERVER_HAVE_XTEST + return XTestQueryExtension(dpy, ev, er, maj, min); +#else + return False; +#endif +} + +void XTestDiscard_wr(Display *dpy) { + if (! xtest_present) { + return; + } +#if LIBVNCSERVER_HAVE_XTEST + XTestDiscard(dpy); +#endif +} + +Bool XETrapQueryExtension_wr(Display *dpy, int *ev, int *er, int *op) { +#if LIBVNCSERVER_HAVE_LIBXTRAP + return XETrapQueryExtension(dpy, (INT32 *)ev, (INT32 *)er, + (INT32 *)op); +#else + /* unused vars warning: */ + if (dpy || ev || er || op) {} + return False; +#endif +} + +int XTestGrabControl_wr(Display *dpy, Bool impervious) { + if (! xtest_present) { + return 0; + } +#if LIBVNCSERVER_HAVE_XTEST && LIBVNCSERVER_HAVE_XTESTGRABCONTROL + XTestGrabControl(dpy, impervious); + return 1; +#else + return 0; +#endif +} + +int XTRAP_GrabControl_wr(Display *dpy, Bool impervious) { + if (! xtrap_present) { + /* unused vars warning: */ + if (dpy || impervious) {} + return 0; + } +#if LIBVNCSERVER_HAVE_LIBXTRAP + else { + ReqFlags requests; + + if (! impervious) { + if (trap_ctx) { + XEFreeTC(trap_ctx); + } + trap_ctx = NULL; + return 1; + } + + if (! trap_ctx) { + trap_ctx = XECreateTC(dpy, 0, NULL); + if (! trap_ctx) { + rfbLog("DEC-XTRAP XECreateTC failed. Watch " + "out for XGrabServer from wm's\n"); + return 0; + } + XEStartTrapRequest(trap_ctx); + memset(requests, 0, sizeof(requests)); + BitTrue(requests, X_GrabServer); + BitTrue(requests, X_UngrabServer); + XETrapSetRequests(trap_ctx, True, requests); + XETrapSetGrabServer(trap_ctx, True); + } + return 1; + } +#endif + return 0; +} + +void disable_grabserver(Display *in_dpy, int change) { + int ok = 0; + static int didmsg = 0; + + if (debug_grabs) { + fprintf(stderr, "disable_grabserver/%d %.5f\n", + xserver_grabbed, dnowx()); + didmsg = 0; + } + + if (! xtrap_input) { + if (XTestGrabControl_wr(in_dpy, True)) { + if (change) { + XTRAP_GrabControl_wr(in_dpy, False); + } + if (! didmsg) { + rfbLog("GrabServer control via XTEST.\n"); + didmsg = 1; + } + ok = 1; + } else { + if (XTRAP_GrabControl_wr(in_dpy, True)) { + ok = 1; + if (! didmsg) { + rfbLog("Using DEC-XTRAP for protection" + " from XGrabServer.\n"); + didmsg = 1; + } + } + } + } else { + if (XTRAP_GrabControl_wr(in_dpy, True)) { + if (change) { + XTestGrabControl_wr(in_dpy, False); + } + if (! didmsg) { + rfbLog("GrabServer control via DEC-XTRAP.\n"); + didmsg = 1; + } + ok = 1; + } else { + if (XTestGrabControl_wr(in_dpy, True)) { + ok = 1; + if (! didmsg) { + rfbLog("DEC-XTRAP XGrabServer " + "protection not available, " + "using XTEST.\n"); + didmsg = 1; + } + } + } + } + if (! ok && ! didmsg) { + rfbLog("No XTEST or DEC-XTRAP protection from XGrabServer.\n"); + rfbLog("Deadlock if your window manager calls XGrabServer!!\n"); + } + XFlush(in_dpy); +} + +Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min) { +#if LIBVNCSERVER_HAVE_RECORD + return XRecordQueryVersion(dpy, maj, min); +#else + return False; +#endif +} + + diff --git a/x11vnc/xwrappers.h b/x11vnc/xwrappers.h new file mode 100644 index 0000000..601c582 --- /dev/null +++ b/x11vnc/xwrappers.h @@ -0,0 +1,68 @@ +#ifndef _X11VNC_XWRAPPERS_H +#define _X11VNC_XWRAPPERS_H + +/* -- xwrappers.h -- */ + +extern int xshm_present; +extern int xtest_present; +extern int xtrap_present; +extern int xrecord_present; +extern int xkb_present; +extern int xinerama_present; + +extern int keycode_state[]; +extern int rootshift; +extern int clipshift; + + +extern int guess_bits_per_color(int bits_per_pixel); + +extern Status XShmGetImage_wr(Display *disp, Drawable d, XImage *image, int x, int y, + unsigned long mask); +extern XImage *XShmCreateImage_wr(Display* disp, Visual* vis, unsigned int depth, + int format, char* data, XShmSegmentInfo* shminfo, unsigned int width, + unsigned int height); +extern Status XShmAttach_wr(Display *disp, XShmSegmentInfo *shminfo); +extern Status XShmDetach_wr(Display *disp, XShmSegmentInfo *shminfo); +extern Bool XShmQueryExtension_wr(Display *disp); + +extern XImage *xreadscreen(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, Bool show_cursor); +extern XImage *XGetSubImage_wr(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, unsigned long plane_mask, + int format, XImage *dest_image, int dest_x, int dest_y); +extern XImage *XGetImage_wr(Display *disp, Drawable d, int x, int y, + unsigned int width, unsigned int height, unsigned long plane_mask, + int format); +extern XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth, + int format, int offset, char *data, unsigned int width, + unsigned int height, int bitmap_pad, int bytes_per_line); +extern void copy_image(XImage *dest, int x, int y, unsigned int w, unsigned int h); +extern void init_track_keycode_state(void); + +extern void XTRAP_FakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, + unsigned long delay); +extern void XTestFakeKeyEvent_wr(Display* dpy, KeyCode key, Bool down, + unsigned long delay); +extern void XTRAP_FakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, + unsigned long delay); +extern void XTestFakeButtonEvent_wr(Display* dpy, unsigned int button, Bool is_press, + unsigned long delay); +extern void XTRAP_FakeMotionEvent_wr(Display* dpy, int screen, int x, int y, + unsigned long delay); +extern void XTestFakeMotionEvent_wr(Display* dpy, int screen, int x, int y, + unsigned long delay); + +extern Bool XTestCompareCurrentCursorWithWindow_wr(Display* dpy, Window w); +extern Bool XTestCompareCursorWithWindow_wr(Display* dpy, Window w, Cursor cursor); +extern Bool XTestQueryExtension_wr(Display *dpy, int *ev, int *er, int *maj, + int *min); +extern void XTestDiscard_wr(Display *dpy); +extern Bool XETrapQueryExtension_wr(Display *dpy, int *ev, int *er, int *op); +extern int XTestGrabControl_wr(Display *dpy, Bool impervious); +extern int XTRAP_GrabControl_wr(Display *dpy, Bool impervious); +extern void disable_grabserver(Display *in_dpy, int change); + +extern Bool XRecordQueryVersion_wr(Display *dpy, int *maj, int *min); + +#endif /* _X11VNC_XWRAPPERS_H */ |