summaryrefslogtreecommitdiffstats
path: root/x11vnc
diff options
context:
space:
mode:
Diffstat (limited to 'x11vnc')
-rw-r--r--x11vnc/ChangeLog11
-rw-r--r--x11vnc/README258
-rwxr-xr-xx11vnc/tkx11vnc7
-rw-r--r--x11vnc/tkx11vnc.h7
-rw-r--r--x11vnc/x11vnc.1160
-rw-r--r--x11vnc/x11vnc.c4127
6 files changed, 3685 insertions, 885 deletions
diff --git a/x11vnc/ChangeLog b/x11vnc/ChangeLog
index dffbb81..5a28e34 100644
--- a/x11vnc/ChangeLog
+++ b/x11vnc/ChangeLog
@@ -1,3 +1,14 @@
+2005-05-02 Karl Runge <runge@karlrunge.com>
+ * initial support for using RECORD to detect some types of window
+ scrolls. This is "-scrollcopyrect" mode, use -noscrollcopyrect
+ to disable. Much tuning and painting error repair still required.
+ * more build time customizations: REMOTE_DEFAULT, REMOTE_CONTROL,
+ EXTERNAL_COMMANDS, NOREPEAT, WIREFRAME*, SCROLL*, ...
+ * added bandwidth and latency measurements.
+ * added XListHosts to -privremote check.
+ * debug_* remote-control variables.
+ * removed OLD_TREE stuff.
+
2005-04-19 Karl Runge <runge@karlrunge.com>
* somewhat safer remote-control defaults, and addnl options for
more safe operation: -privremote, -safer, -nocmds, -unsafe
diff --git a/x11vnc/README b/x11vnc/README
index 74b92ad..13a0977 100644
--- a/x11vnc/README
+++ b/x11vnc/README
@@ -1,5 +1,5 @@
-x11vnc README file Date: Tue Apr 19 16:26:20 EDT 2005
+x11vnc README file Date: Mon May 2 21:41:13 EDT 2005
The following information is taken from these URLs:
@@ -527,7 +527,9 @@ make
Here is what is shaping up to be [52]the release notes for 0.7.2..
Note that the [53]X DAMAGE feature will be on by default and so I am
- interested if that causes any problems. Thanks!
+ interested if that causes any problems. I'd also like to have the new
+ "wireframe" move/resize stuff on by default as well, let me know of
+ any issues you find. Thanks!
_________________________________________________________________
Some Notes:
@@ -576,11 +578,10 @@ make
note below)
Options: x11vnc has (far too) many features that may be activated
- via its [57]command line options. Useful options are -nap to use fewer
- resources (it sleeps more between polls when activity is low) and
- -rfbauth passwd-file to use VNC password protection (the vncpasswd or
- storepasswd programs, or the x11vnc [58]-storepasswd option can be
- used to create the password file).
+ via its [57]command line options. Useful options are, e.g., -scale to
+ do server-side scaling, and -rfbauth passwd-file to use VNC password
+ protection (the vncpasswd or storepasswd programs, or the x11vnc
+ [58]-storepasswd option can be used to create the password file).
Algorithm: How does x11vnc do it? Rather brute-forcedly: it
continuously polls the X11 framebuffer for changes using
@@ -588,18 +589,18 @@ make
which rectangular regions of the framebuffer have changed, and
libvncserver compresses the changes and sends them off to any
connected VNC viewers. A number of applications do similar things,
- such as x0rfbserver, krfb, x0vncserver. x11vnc uses a 32 x 32 pixel
- tile model (the desktop is decomposed into roughly 1000 such tiles),
- where changed tiles are found by pseudo-randomly polling 1 pixel tall
- horizontal scanlines. This is a surprisingly effective algorithm for
- finding changed regions. For keyboard and mouse user input the XTEST
- extension is used to pass the input events to the X server. To detect
- XBell "beeps" the XKEYBOARD extension is used. If available, the
- XFIXES extension is used to retrieve the current mouse cursor shape.
- Also, if available the X DAMAGE extension is used to receive hints
- from the X server where modified regions on the screen are. This
- greatly reduces the system load when not much is changing on the
- screen and also improves how quickly the screen is updated.
+ such as x0rfbserver, krfb, x0vncserver, vino. x11vnc uses a 32 x 32
+ pixel tile model (the desktop is decomposed into roughly 1000 such
+ tiles), where changed tiles are found by pseudo-randomly polling 1
+ pixel tall horizontal scanlines. This is a surprisingly effective
+ algorithm for finding changed regions. For keyboard and mouse user
+ input the XTEST extension is used to pass the input events to the X
+ server. To detect XBell "beeps" the XKEYBOARD extension is used. If
+ available, the XFIXES extension is used to retrieve the current mouse
+ cursor shape. Also, if available the X DAMAGE extension is used to
+ receive hints from the X server where modified regions on the screen
+ are. This greatly reduces the system load when not much is changing on
+ the screen and also improves how quickly the screen is updated.
Barbershop mirrors effect: What if x11vnc is started up, and
vncviewer is then started up on the same machine and displayed on the
@@ -1375,7 +1376,9 @@ display :0
unintendeds. Perhaps this is of use in remote access for an embedded
application, etc...
- Let us know if more build-time customizations would be useful.
+ Let us know if more build-time customizations would be useful. Look
+ near the top of the source file for any additional customization
+ macros.
If the system does not have the XTEST XTestGrabControl interface (some
early X11R5 systems have XTEST but not this interface), then configure
@@ -2761,18 +2764,20 @@ ied)
polling and updates will be suspended and only an animated "wireframe"
(a rectangle outline drawn where the moved/resized window would be) is
shown. When the window move/resize stops, it returns to normal
- processing: you should just see the window appear in the new position.
- This spares you from interacting with a "lurching" window during all
- of the intermediate steps. (the lurching is due to [297]slow video
- card read rates)
-
- The mode is currently on be default because most people are inflicted
+ processing: you should only see the window appear in the new position.
+ This spares you from interacting with a "lurching" window between all
+ of the intermediate steps. BTW the lurching is due to [297]slow video
+ card read rates. A displacement, even a small one, of a large window
+ requires a non-negligible amount of time, a good fraction of a second,
+ to read in from the hardware framebuffer.
+
+ The mode is currently on by default because most people are inflicted
with the problem. It can be disabled with the [298]-nowireframe
option. Why might one want to turn off the wireframing? Since x11vnc
is merely guessing when windows are being moved/resized, it may guess
poorly for your window-manager or desktop, or even for the way you
move the pointer. If your window-manager or desktop already does its
- own wireframing then this mode is a waste of time or could do the
+ own wireframing then this mode is a waste of time and could do the
wrong thing occasionally. There may be other reasons the new mode
feels unnatural. If you have very expensive video hardware (SGI) or
are using an in-RAM video framebuffer (SunRay, ShadowFB, Xvfb), the
@@ -2785,16 +2790,16 @@ ied)
are not fool proof: x11vnc is sometimes tricked and so you'll
occasionally see the lurching opaque move and rarely something even
worse. First it assumes that the move/resize will occur with a mouse
- button pressed and held down (of course this is only mostly true).
- Next it will only consider a window for wireframing if the mouse
- pointer is initially "close enough" to the edges of the window frame,
- e.g. you have grabbed the title bar or a resizer edge (this
+ button pressed, held down and dragged (of course this is only mostly
+ true). Next it will only consider a window for wireframing if the
+ mouse pointer is initially "close enough" to the edges of the window
+ frame, e.g. you have grabbed the title bar or a resizer edge (this
requirement can be disabled). If these are true, it will wait an
amount of time to see if the window starts moving or resizing. If it
- does, it starts drawing the wireframe "animation" of where the window
- would be. If the mouse button is released, or a timeout occurs, it
- goes back to the standard mode to allow the framebuffer changes to
- propagate to the viewers.
+ does, it starts drawing the wireframe "outline" of where the window
+ would be. When the mouse button is released, or a timeout occurs, it
+ goes back to the standard mode to allow the actual framebuffer changes
+ to propagate to the viewers.
These parameters can be tweaked:
* Color/Shade of the wireframe.
@@ -2836,6 +2841,12 @@ ied)
data is translated! Use -nowirecopyrect if this yields undesirable
effects for your desktop.
+ Also, the CopyRect encoding may give incorrect results under -scale
+ (depending on the scale factor the CopyRect operation is often only
+ approximate: the correctly scaled framebuffer will be slightly
+ different from the translated one). Use -nowirecopyrect if this or
+ other painting errors are unacceptable.
+
Q-51: Does x11vnc support the X DAMAGE Xserver extension to find
modified regions of the screen quickly and efficiently?
@@ -4222,7 +4233,7 @@ x11vnc: a VNC server for real X displays
Here are all of x11vnc command line options:
% x11vnc -opts (see below for -help long descriptions)
-x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-04-19
+x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-02
x11vnc options:
-display disp -auth file
@@ -4266,21 +4277,23 @@ x11vnc options:
-xwarppointer -buttonmap string
-nodragging -wireframe [str]
-nowireframe -wirecopyrect mode
- -nowirecopyrect -pointer_mode n
- -input_skip n -speeds rd,bw,lat
- -debug_pointer -debug_keyboard
- -defer time -wait time
- -nap -nonap
- -sb time -noxdamage
- -xd_area A -xd_mem f
- -sigpipe string -threads
- -nothreads -fs f
- -gaps n -grow n
- -fuzz n -snapfb
- -rawfb string -pipeinput cmd
- -gui [gui-opts] -remote command
- -query variable -sync
- -noremote -unsafe
+ -nowirecopyrect -scrollcopyrect mode
+ -noscrollcopyrect -scr_area n
+ -pointer_mode n -input_skip n
+ -speeds rd,bw,lat -debug_pointer
+ -debug_keyboard -defer time
+ -wait time -nap
+ -nonap -sb time
+ -noxdamage -xd_area A
+ -xd_mem f -sigpipe string
+ -threads -nothreads
+ -fs f -gaps n
+ -grow n -fuzz n
+ -snapfb -rawfb string
+ -pipeinput cmd -gui [gui-opts]
+ -remote command -query variable
+ -sync -noremote
+ -yesremote -unsafe
-safer -privremote
-nocmds -deny_all
@@ -4309,7 +4322,7 @@ libvncserver options:
% x11vnc -help
-x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-04-19
+x11vnc: allow VNC connections to real X11 displays. 0.7.2 lastmod: 2005-05-02
Typical usage is:
@@ -5009,13 +5022,16 @@ Options:
heuristics and may not always work: it depends on your
window manager and even how you move things around.
See -pointer_mode below for discussion of the "bogging
- down" problem this tries to avoid. Default: -wireframe
+ down" problem this tries to avoid.
+ Default: -wireframe
+
+ Shorter aliases: -wf [str] and -nowf
The value "str" is optional and, of course, is
packed with many tunable parameters for this scheme:
Format: shade,linewidth,percent,T+B+L+R,t1+t2+t3+t4
- Default: 0xff,3,0,32+8+8+8,0.15+0.35+4.0+0.1
+ Default: 0xff,3,0,32+8+8+8,0.15+0.30+5.0+0.125
If you leave nothing between commas: ",," the default
value is used. If you don't specify enough commas,
@@ -5047,16 +5063,67 @@ Options:
link this might be a better choice: 0.25+0.6+6.0+0.15
-wirecopyrect mode Since the -wireframe mechanism evidently tracks moving
--nowirecopyrect windows, a speedup can be obtained by telling the VNC
- viewers to locally copy the translated window region.
- This is the VNC CopyRect encoding: the framebuffer
- update doesn't need to send the actual new image data.
+-nowirecopyrect windows accurately, a speedup can be obtained by
+ telling the VNC viewers to locally copy the translated
+ window region. This is the VNC CopyRect encoding:
+ the framebuffer update doesn't need to send the actual
+ new image data.
+
+ Shorter aliases: -wcr [mode] and -nowcr
+
"mode" can be "never" (same as -nowirecopyrect)
to never try the copyrect, "top" means only do it if
the window was not covered by any other windows, and
"always" means to translate the orginally unobscured
region (this may look odd as the remaining pieces come
- in, but helps on a slow link) Default: always
+ in, but helps on a slow link). Default: "always"
+
+ Note: there can be painting errors when using -scale
+ so CopyRect is skipped when scaling unless you specify
+ -wirecopyrect on the command line or by remote-control.
+
+-scrollcopyrect mode Like -wirecopyrect, but use heuristics to try to guess
+-noscrollcopyrect if a window has scrolled its contents (either vertically
+ or horizontally). This requires the RECORD X extension
+ to "snoop" on X applications (currently for certain
+ XCopyArea and XConfigureWindow X protocol requests).
+ Examples: Hitting <Return> in a terminal window when the
+ cursor was at the bottom, the text scrolls up one line.
+ Hitting <Down> arrow in a web browser window, the web
+ page scrolls up a small amount.
+
+ Shorter aliases: -scr [mode] and -noscr
+
+ This scheme will not always detect scrolls, but when
+ it does there is a nice speedup from using the VNC
+ CopyRect encoding (see -wirecopyrect). The speedup
+ is both in reduced network traffic and reduced X
+ framebuffer polling/copying. On the other hand,
+ it may induce undesired transients (e.g. a terminal
+ cursor being scrolled up when it should not be) or other
+ painting errors. These are automatically repaired in a
+ short period of time. If this is unacceptable disable
+ the feature with -noscrollcopyrect.
+
+ "mode" can be "never" (same as -noscrollcopyrect)
+ to never try the copyrect, "keys" means to try it
+ in response to keystrokes only, "mouse" means to
+ try it in response to mouse events only, "always"
+ means to do both. Default: "always"
+
+ Note: there can be painting errors when using
+ -scale so CopyRect is skipped when scaling unless
+ you specify -scrollcopyrect on the command line or
+ by remote-control.
+
+-scr_area n Set the minimum area in pixels for a rectangle
+ to be considered for the -scrollcopyrect detection
+ scheme. This is to avoid wasting the effort on small
+ rectangles that would be quickly updated the normal way.
+ E.g. suppose an app updated the position of its skinny
+ scrollbar first and then shifted the large panel
+ it controlled. We want to be sure to skip the small
+ scrollbar and get the large panel. Default: 60000
-pointer_mode n Various pointer motion update schemes. "-pm" is
an alias. The problem is pointer motion can cause
@@ -5503,11 +5570,16 @@ Options:
buttonmap:str set -buttonmap "str", empty to disable
dragging disable -nodragging mode.
nodragging enable -nodragging mode.
- wireframe enable -wireframe mode.
- nowireframe disable -wireframe mode.
+ wireframe enable -wireframe mode. same as "wf"
+ nowireframe disable -wireframe mode. same as "nowf"
wireframe:str enable -wireframe mode string.
wireframe_mode:str enable -wireframe mode string.
- wirecopyrect:str set -wirecopyrect string.
+ wirecopyrect:str set -wirecopyrect string. same as "wcr:
+"
+ scrollcopyrect:str set -scrollcopyrect string. same "scr
+"
+ noscrollcopyrect disable -scrollcopyrect mode. "noscr"
+ scr_area:n set -scr_area to n
pointer_mode:n set -pointer_mode to n. same as "pm"
input_skip:n set -input_skip to n.
speeds:str set -speeds to str.
@@ -5550,6 +5622,15 @@ Options:
dontdisconnect enable -dontdisconnect mode.
nodontdisconnect disable -dontdisconnect mode.
(may interfere with other options)
+ debug_xevents enable debugging X events.
+ nodebug_xevents disable debugging X events.
+ debug_xdamage enable debugging X DAMAGE mechanism.
+ nodebug_xdamage disable debugging X DAMAGE mechanism.
+ debug_wireframe enable debugging wireframe mechanism.
+ nodebug_wireframe disable debugging wireframe mechanism.
+ debug_scroll enable debugging scrollcopy mechanism.
+ nodebug_scroll disable debugging scrollcopy mechanism.
+
noremote disable the -remote command processing,
it cannot be turned back on.
@@ -5606,13 +5687,14 @@ Options:
add_keysyms noadd_keysyms clear_mods noclear_mods
clear_keys noclear_keys remap repeat norepeat
fb nofb bell nobell sel nosel primary noprimary
- cursorshape nocursorshape cursorpos nocursorpos
- cursor show_cursor noshow_cursor nocursor arrow
- xfixes noxfixes xdamage noxdamage xd_area xd_mem
- alphacut alphafrac alpharemove noalpharemove alphablend
- noalphablend xwarp xwarppointer noxwarp noxwarppointer
- buttonmap dragging nodragging wireframe_mode
- wireframe nowireframe wirecopyrect nowirecopyrect
+ cursorshape nocursorshape cursorpos nocursorpos cursor
+ show_cursor noshow_cursor nocursor arrow xfixes noxfixes
+ xdamage noxdamage xd_area xd_mem alphacut alphafrac
+ alpharemove noalpharemove alphablend noalphablend
+ xwarp xwarppointer noxwarp noxwarppointer buttonmap
+ dragging nodragging wireframe_mode wireframe wf
+ nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr
+ scr_area scrollcopyrect scr noscrollcopyrect noscr
pointer_mode pm input_skip input client_input speeds
debug_pointer dp nodebug_pointer nodp debug_keyboard dk
nodebug_keyboard nodk deferupdate defer wait rfbwait
@@ -5622,19 +5704,23 @@ Options:
noalwaysshared nevershared noalwaysshared dontdisconnect
nodontdisconnect desktop noremote
- aro= debug_xevents debug_xdamage display vncdisplay
- desktopname http_url auth users rootshift clipshift
- scale_str scaled_x scaled_y scale_numer scale_denom
- scale_fac scaling_blend scaling_nomult4 scaling_pad
- scaling_interpolate inetd privremote unsafe safer
- nocmds passwdfile using_shm logfile o flag rc norc h
- help V version lastmod bg sigpipe threads pipeinput
- clients client_count pid ext_xtest ext_xtrap ext_xkb
- ext_xshm ext_xinerama ext_overlay ext_xfixes ext_xdamage
- ext_xrandr rootwin num_buttons button_mask mouse_x
- mouse_y bpp depth indexed_color dpy_x dpy_y wdpy_x
- wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y rfbauth
- passwd
+ aro= debug_xevents nodebug_xevents debug_xevents
+ debug_xdamage nodebug_xdamage debug_xdamage
+ debug_wireframe nodebug_wireframe debug_wireframe
+ debug_scroll nodebug_scroll debug_scroll display
+ vncdisplay desktopname http_url auth users rootshift
+ clipshift scale_str scaled_x scaled_y scale_numer
+ scale_denom scale_fac scaling_blend scaling_nomult4
+ scaling_pad scaling_interpolate inetd privremote
+ unsafe safer nocmds passwdfile using_shm logfile
+ o flag rc norc h help V version lastmod bg sigpipe
+ threads readrate netrate netlatency pipeinput clients
+ client_count pid ext_xtest ext_xtrap ext_xrecord
+ ext_xkb ext_xshm ext_xinerama ext_overlay ext_xfixes
+ ext_xdamage ext_xrandr rootwin num_buttons button_mask
+ mouse_x mouse_y bpp depth indexed_color dpy_x dpy_y
+ wdpy_x wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y
+ rfbauth passwd
-sync By default -remote commands are run asynchronously, that
is, the request is posted and the program immediately
@@ -5656,6 +5742,8 @@ Options:
taken place.
-noremote Do not process any remote control commands or queries.
+-yesremote Do process remote control commands or queries.
+ Default: -yesremote
A note about security wrt remote control commands.
If someone can connect to the X display and change
@@ -5685,10 +5773,10 @@ Options:
-safer Equivalent to: -novncconnect -noremote and prohibiting
-gui and the -connect file. Shuts off communcation
channels.
--privremote Perform some sanity checks and only allow remote-control
+-privremote Perform some sanity checks and disable remote-control
commands if it appears that the X DISPLAY and/or
- connectfile cannot be accessed by other users. (not
- complete, does not check for empty access control list)
+ connectfile can be accessed by other users. Once
+ remote-control is disabled it cannot be turned back on.
-nocmds No external commands (e.g. system(3), popen(3), exec(3))
will be run.
diff --git a/x11vnc/tkx11vnc b/x11vnc/tkx11vnc
index 0ed781b..5128d52 100755
--- a/x11vnc/tkx11vnc
+++ b/x11vnc/tkx11vnc
@@ -189,6 +189,11 @@ Debugging
=GA tail-logfile
quiet
--
+ debug_xevents
+ debug_xdamage
+ debug_wireframe
+ debug_scroll
+ --
=GA show-start-cmd
=DG debug_gui
@@ -232,6 +237,8 @@ Tuning
wireframe
wireframe_mode:
=-C:never,top,always wirecopyrect:
+ =-C:never,keys,mouse,always scrollcopyrect:
+ scr_area:
--
noshm
flipbyteorder
diff --git a/x11vnc/tkx11vnc.h b/x11vnc/tkx11vnc.h
index b00fc1d..a92a212 100644
--- a/x11vnc/tkx11vnc.h
+++ b/x11vnc/tkx11vnc.h
@@ -195,6 +195,11 @@
" =GA tail-logfile\n"
" quiet\n"
" --\n"
+" debug_xevents\n"
+" debug_xdamage\n"
+" debug_wireframe\n"
+" debug_scroll\n"
+" --\n"
" =GA show-start-cmd\n"
" =DG debug_gui\n"
"\n"
@@ -238,6 +243,8 @@
" wireframe\n"
" wireframe_mode:\n"
" =-C:never,top,always wirecopyrect:\n"
+" =-C:never,keys,mouse,always scrollcopyrect:\n"
+" scr_area:\n"
" --\n"
" noshm\n"
" flipbyteorder\n"
diff --git a/x11vnc/x11vnc.1 b/x11vnc/x11vnc.1
index 2acc247..2086491 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" "April 2005" "x11vnc " "User Commands"
+.TH X11VNC "1" "May 2005" "x11vnc " "User Commands"
.SH NAME
x11vnc - allow VNC connections to real X11 displays
- version: 0.7.2, lastmod: 2005-04-19
+ version: 0.7.2, lastmod: 2005-05-02
.SH SYNOPSIS
.B x11vnc
[OPTION]...
@@ -956,13 +956,16 @@ the full opaque window. This is based completely on
heuristics and may not always work: it depends on your
window manager and even how you move things around.
See \fB-pointer_mode\fR below for discussion of the "bogging
-down" problem this tries to avoid. Default: \fB-wireframe\fR
+down" problem this tries to avoid.
+Default: \fB-wireframe\fR
+.IP
+Shorter aliases: \fB-wf\fR [str] and \fB-nowf\fR
.IP
The value "str" is optional and, of course, is
packed with many tunable parameters for this scheme:
.IP
Format: shade,linewidth,percent,T+B+L+R,t1+t2+t3+t4
-Default: 0xff,3,0,32+8+8+8,0.15+0.35+4.0+0.1
+Default: 0xff,3,0,32+8+8+8,0.15+0.30+5.0+0.125
.IP
If you leave nothing between commas: ",," the default
value is used. If you don't specify enough commas,
@@ -996,16 +999,71 @@ link this might be a better choice: 0.25+0.6+6.0+0.15
\fB-wirecopyrect\fR \fImode,\fR \fB-nowirecopyrect\fR
.IP
Since the \fB-wireframe\fR mechanism evidently tracks moving
-windows, a speedup can be obtained by telling the VNC
-viewers to locally copy the translated window region.
-This is the VNC CopyRect encoding: the framebuffer
-update doesn't need to send the actual new image data.
+windows accurately, a speedup can be obtained by
+telling the VNC viewers to locally copy the translated
+window region. This is the VNC CopyRect encoding:
+the framebuffer update doesn't need to send the actual
+new image data.
+.IP
+Shorter aliases: \fB-wcr\fR [mode] and \fB-nowcr\fR
+.IP
"mode" can be "never" (same as \fB-nowirecopyrect)\fR
to never try the copyrect, "top" means only do it if
the window was not covered by any other windows, and
"always" means to translate the orginally unobscured
region (this may look odd as the remaining pieces come
-in, but helps on a slow link) Default: always
+in, but helps on a slow link). Default: "always"
+.IP
+Note: there can be painting errors when using \fB-scale\fR
+so CopyRect is skipped when scaling unless you specify
+\fB-wirecopyrect\fR on the command line or by remote-control.
+.PP
+\fB-scrollcopyrect\fR \fImode,\fR \fB-noscrollcopyrect\fR
+.IP
+Like \fB-wirecopyrect,\fR but use heuristics to try to guess
+if a window has scrolled its contents (either vertically
+or horizontally). This requires the RECORD X extension
+to "snoop" on X applications (currently for certain
+XCopyArea and XConfigureWindow X protocol requests).
+Examples: Hitting <Return> in a terminal window when the
+cursor was at the bottom, the text scrolls up one line.
+Hitting <Down> arrow in a web browser window, the web
+page scrolls up a small amount.
+.IP
+Shorter aliases: \fB-scr\fR [mode] and \fB-noscr\fR
+.IP
+This scheme will not always detect scrolls, but when
+it does there is a nice speedup from using the VNC
+CopyRect encoding (see \fB-wirecopyrect).\fR The speedup
+is both in reduced network traffic and reduced X
+framebuffer polling/copying. On the other hand,
+it may induce undesired transients (e.g. a terminal
+cursor being scrolled up when it should not be) or other
+painting errors. These are automatically repaired in a
+short period of time. If this is unacceptable disable
+the feature with \fB-noscrollcopyrect.\fR
+.IP
+"mode" can be "never" (same as \fB-noscrollcopyrect)\fR
+to never try the copyrect, "keys" means to try it
+in response to keystrokes only, "mouse" means to
+try it in response to mouse events only, "always"
+means to do both. Default: "always"
+.IP
+Note: there can be painting errors when using
+\fB-scale\fR so CopyRect is skipped when scaling unless
+you specify \fB-scrollcopyrect\fR on the command line or
+by remote-control.
+.PP
+\fB-scr_area\fR \fIn\fR
+.IP
+Set the minimum area in pixels for a rectangle
+to be considered for the \fB-scrollcopyrect\fR detection
+scheme. This is to avoid wasting the effort on small
+rectangles that would be quickly updated the normal way.
+E.g. suppose an app updated the position of its skinny
+scrollbar first and then shifted the large panel
+it controlled. We want to be sure to skip the small
+scrollbar and get the large panel. Default: 60000
.PP
\fB-pointer_mode\fR \fIn\fR
.IP
@@ -1633,15 +1691,21 @@ dragging disable \fB-nodragging\fR mode.
.IP
nodragging enable \fB-nodragging\fR mode.
.IP
-wireframe enable \fB-wireframe\fR mode.
+wireframe enable \fB-wireframe\fR mode. same as "wf"
.IP
-nowireframe disable \fB-wireframe\fR mode.
+nowireframe disable \fB-wireframe\fR mode. same as "nowf"
.IP
wireframe:str enable \fB-wireframe\fR mode string.
.IP
wireframe_mode:str enable \fB-wireframe\fR mode string.
.IP
-wirecopyrect:str set \fB-wirecopyrect\fR string.
+wirecopyrect:str set \fB-wirecopyrect\fR string. same as "wcr:"
+.IP
+scrollcopyrect:str set \fB-scrollcopyrect\fR string. same "scr"
+.IP
+noscrollcopyrect disable \fB-scrollcopyrect__mode_.\fR "noscr"
+.IP
+scr_area:n set \fB-scr_area\fR to n
.IP
pointer_mode:n set \fB-pointer_mode\fR to n. same as "pm"
.IP
@@ -1721,6 +1785,23 @@ dontdisconnect enable \fB-dontdisconnect\fR mode.
nodontdisconnect disable \fB-dontdisconnect\fR mode.
(may interfere with other options)
.IP
+debug_xevents enable debugging X events.
+.IP
+nodebug_xevents disable debugging X events.
+.IP
+debug_xdamage enable debugging X DAMAGE mechanism.
+.IP
+nodebug_xdamage disable debugging X DAMAGE mechanism.
+.IP
+debug_wireframe enable debugging wireframe mechanism.
+.IP
+nodebug_wireframe disable debugging wireframe mechanism.
+.IP
+debug_scroll enable debugging scrollcopy mechanism.
+.IP
+nodebug_scroll disable debugging scrollcopy mechanism.
+.IP
+.IP
noremote disable the \fB-remote\fR command processing,
it cannot be turned back on.
.IP
@@ -1795,13 +1876,14 @@ noquiet modtweak nomodtweak xkb noxkb skip_keycodes
add_keysyms noadd_keysyms clear_mods noclear_mods
clear_keys noclear_keys remap repeat norepeat
fb nofb bell nobell sel nosel primary noprimary
-cursorshape nocursorshape cursorpos nocursorpos
-cursor show_cursor noshow_cursor nocursor arrow
-xfixes noxfixes xdamage noxdamage xd_area xd_mem
-alphacut alphafrac alpharemove noalpharemove alphablend
-noalphablend xwarp xwarppointer noxwarp noxwarppointer
-buttonmap dragging nodragging wireframe_mode
-wireframe nowireframe wirecopyrect nowirecopyrect
+cursorshape nocursorshape cursorpos nocursorpos cursor
+show_cursor noshow_cursor nocursor arrow xfixes noxfixes
+xdamage noxdamage xd_area xd_mem alphacut alphafrac
+alpharemove noalpharemove alphablend noalphablend
+xwarp xwarppointer noxwarp noxwarppointer buttonmap
+dragging nodragging wireframe_mode wireframe wf
+nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr
+scr_area scrollcopyrect scr noscrollcopyrect noscr
pointer_mode pm input_skip input client_input speeds
debug_pointer dp nodebug_pointer nodp debug_keyboard dk
nodebug_keyboard nodk deferupdate defer wait rfbwait
@@ -1811,19 +1893,23 @@ httpdir enablehttpproxy noenablehttpproxy alwaysshared
noalwaysshared nevershared noalwaysshared dontdisconnect
nodontdisconnect desktop noremote
.IP
-aro= debug_xevents debug_xdamage display vncdisplay
-desktopname http_url auth users rootshift clipshift
-scale_str scaled_x scaled_y scale_numer scale_denom
-scale_fac scaling_blend scaling_nomult4 scaling_pad
-scaling_interpolate inetd privremote unsafe safer
-nocmds passwdfile using_shm logfile o flag rc norc h
-help V version lastmod bg sigpipe threads pipeinput
-clients client_count pid ext_xtest ext_xtrap ext_xkb
-ext_xshm ext_xinerama ext_overlay ext_xfixes ext_xdamage
-ext_xrandr rootwin num_buttons button_mask mouse_x
-mouse_y bpp depth indexed_color dpy_x dpy_y wdpy_x
-wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y rfbauth
-passwd
+aro= debug_xevents nodebug_xevents debug_xevents
+debug_xdamage nodebug_xdamage debug_xdamage
+debug_wireframe nodebug_wireframe debug_wireframe
+debug_scroll nodebug_scroll debug_scroll display
+vncdisplay desktopname http_url auth users rootshift
+clipshift scale_str scaled_x scaled_y scale_numer
+scale_denom scale_fac scaling_blend scaling_nomult4
+scaling_pad scaling_interpolate inetd privremote
+unsafe safer nocmds passwdfile using_shm logfile
+o flag rc norc h help V version lastmod bg sigpipe
+threads readrate netrate netlatency pipeinput clients
+client_count pid ext_xtest ext_xtrap ext_xrecord
+ext_xkb ext_xshm ext_xinerama ext_overlay ext_xfixes
+ext_xdamage ext_xrandr rootwin num_buttons button_mask
+mouse_x mouse_y bpp depth indexed_color dpy_x dpy_y
+wdpy_x wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y
+rfbauth passwd
.PP
\fB-sync\fR
.IP
@@ -1846,9 +1932,11 @@ if the x11vnc takes longer than that to process the
requests the requestor will think that a failure has
taken place.
.PP
-\fB-noremote\fR
+\fB-noremote,\fR \fB-yesremote\fR
.IP
Do not process any remote control commands or queries.
+Do process remote control commands or queries.
+Default: \fB-yesremote\fR
.IP
A note about security wrt remote control commands.
If someone can connect to the X display and change
@@ -1886,10 +1974,10 @@ channels.
.PP
\fB-privremote\fR
.IP
-Perform some sanity checks and only allow remote-control
+Perform some sanity checks and disable remote-control
commands if it appears that the X DISPLAY and/or
-connectfile cannot be accessed by other users. (not
-complete, does not check for empty access control list)
+connectfile can be accessed by other users. Once
+remote-control is disabled it cannot be turned back on.
.PP
\fB-nocmds\fR
.IP
diff --git a/x11vnc/x11vnc.c b/x11vnc/x11vnc.c
index e09d9ca..092c236 100644
--- a/x11vnc/x11vnc.c
+++ b/x11vnc/x11vnc.c
@@ -127,60 +127,9 @@
/* -- x11vnc.h -- */
-/*
- ***************************************************************************
- * if you are inserting this file, x11vnc.c into an old CVS tree you
- * may need to set OLD_TREE to 1. Or use -DOLD_TREE=1 in CPPFLAGS.
- * See below for LibVNCServer 0.7 tips.
- */
-
-#ifndef OLD_TREE
-#define OLD_TREE 0
-#endif
-#if OLD_TREE
-
-/* BEGIN OLD TREE */
-
-/*
- * if you have a very old tree (LibVNCServer 0.6) and get errors these may
- * be need to be uncommented. LibVNCServer <= 0.5 is no longer supported.
- * note the maxRectsPerUpdate below is a hack that may break some usage.
-#define oldCursorX cursorX
-#define oldCursorY cursorY
-#define thisHost rfbThisHost
-#define framebufferUpdateMessagesSent rfbFramebufferUpdateMessagesSent
-#define bytesSent rfbBytesSent
-#define rawBytesEquivalent rfbRawBytesEquivalent
-#define progressiveSliceHeight maxRectsPerUpdate
- */
-
-/*
- * If you are building in an older libvncserver tree with this newer
- * x11vnc.c file using OLD_TREE=1 you may need to set some of these lines
- * since your older libvncserver configure is not setting them.
- *
- * For the features LIBVNCSERVER_HAVE_LIBXINERAMA and
- * LIBVNCSERVER_HAVE_XFIXES you may also need to add
- * -lXinerama or -lXfixes, respectively, to the linking line, e.g.
- * by setting them in LD_FLAGS before running configure.
- */
-
-#define LIBVNCSERVER_HAVE_XSHM 1
-#define LIBVNCSERVER_HAVE_XTEST 1
-#define LIBVNCSERVER_HAVE_XTESTGRABCONTROL 1
-
-#define LIBVNCSERVER_HAVE_PWD_H 1
-#define LIBVNCSERVER_HAVE_SYS_WAIT_H 1
-#define LIBVNCSERVER_HAVE_UTMPX_H 1
-#define LIBVNCSERVER_HAVE_MMAP 1
-
-/*
-#define LIBVNCSERVER_HAVE_LIBXINERAMA 1
-#define LIBVNCSERVER_HAVE_XFIXES 1
-#define LIBVNCSERVER_HAVE_LIBXDAMAGE 1
- */
-
-/* END OLD TREE */
+/* TODO: autoconf */
+#if 1
+#define LIBVNCSERVER_HAVE_RECORD 1
#endif
/****************************************************************************/
@@ -206,63 +155,41 @@
#include <rfb/rfbregion.h>
/****************************************************************************/
-#if OLD_TREE
-/*
- * This is another transient for building in older libvncserver trees,
- * due to the API change:
- */
-#define dontDisconnect rfbDontDisconnect
-#define neverShared rfbNeverShared
-#define alwaysShared rfbAlwaysShared
-#define clientHead rfbClientHead
-#define serverFormat rfbServerFormat
-#define port rfbPort
-#define listenSock rfbListenSock
-#define deferUpdateTime rfbDeferUpdateTime
-#define authPasswdData rfbAuthPasswdData
-#define rfbEncryptAndStorePasswd vncEncryptAndStorePasswd
-#define maxClientWait rfbMaxClientWait
-#define rfbHttpInitSockets httpInitSockets
-
-#define RFBUNDRAWCURSOR(s) if (s) {rfbUndrawCursor(s);}
-#else /* OLD_TREE */
-#define RFBUNDRAWCURSOR(s)
-#endif /* !OLD_TREE */
+
+/* Build-time customization via CPPFLAGS. */
+
/*
- * To get a clean build in a LibVNCServer 0.7 source tree no need for
- * OLD_TREE, you just need to either download the forgotten tkx11vnc.h
- * file or run:
+ * Summary of options to include in CPPFLAGS for custom builds:
*
- * echo 'char gui_code[] = "";' > tkx11vnc.h
+ * -DSHARED to have the vnc display shared by default.
+ * -DFOREVER to have -forever on by default.
+ * -DNOREPEAT=0 to have -repeat on by default.
*
- * (this disables the gui) and uncomment this line:
-#define rfbSetCursor(a, b) rfbSetCursor((a), (b), FALSE)
- */
-
-/*
- * To get a clean build on LibVNCServer 0.7.1 no need for OLD_TREE,
- * just uncomment this line (note the maxRectsPerUpdate below is a hack
- * that may break some usage):
+ * -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.
*
-#define listenInterface maxRectsPerUpdate
+ * -DHARDWIRE_PASSWD=... hardwired passwords, quoting necessary.
+ * -DHARDWIRE_VIEWPASSWD=...
*
- */
-/****************************************************************************/
-
-
-/* Build-time customization via CPPFLAGS. */
-
-/*
- * -DX11VNC_SHARED to have the vnc display shared by default.
- */
-
-/*
- * -DX11VNC_FOREVER to have -forever on by default.
+ * -DWIREFRAME=0 to have -nowireframe as the default.
+ * -DWIREFRAME_COPYRECT=0 to have -nowirecopyrect as the default.
+ * -DSCROLL_COPYRECT=0 to have -noscrollcopyrect as the default.
+ * -DXDAMAGE=0 to have -noxdamage as the default.
+ *
+ * -DPOINTER_MODE_DEFAULT={0,1,2,3,4,5} set default -pointer_mode.
+ * -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.
+ *
+ * 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.
- * E.g. -DREMOTE_CONTROL=0 in CPPFLAGS.
*/
#ifndef REMOTE_CONTROL
#define REMOTE_CONTROL 1
@@ -334,6 +261,11 @@ 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
@@ -423,8 +355,11 @@ int alt_arrow = 1;
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;
-int use_xdamage = 1; /* just use the xdamage rects. for scanline hints */
#if LIBVNCSERVER_HAVE_LIBXDAMAGE
#include <X11/extensions/Xdamage.h>
Damage xdamage = 0;
@@ -436,7 +371,7 @@ int xdamage_tile_count;
/* date +'lastmod: %Y-%m-%d' */
-char lastmod[] = "0.7.2 lastmod: 2005-04-19";
+char lastmod[] = "0.7.2 lastmod: 2005-05-02";
int hack_val = 0;
/* X display info */
@@ -504,7 +439,6 @@ 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 RATE_SAMPLES 5
#define CILEN 10
typedef struct _ClientData {
int uid;
@@ -522,11 +456,9 @@ typedef struct _ClientData {
double timer;
double send_cmp_rate;
double send_raw_rate;
- int set_cmp_bytes;
- int set_raw_bytes;
- double cmp_samp[RATE_SAMPLES];
- double raw_samp[RATE_SAMPLES];
- int sample;
+ double latency;
+ int cmp_bytes_sent;
+ int raw_bytes_sent;
} ClientData;
/* scaling parameters */
@@ -560,6 +492,7 @@ unsigned char *tile_has_xdamage_diff, *tile_row_has_xdamage_diff;
/* times of recent events */
time_t last_event, last_input, last_client = 0;
+double servertime_diff = 0.0;
/* last client to move pointer */
rfbClientPtr last_pointer_client = NULL;
@@ -574,7 +507,9 @@ int button_change_x, button_change_y;
int got_user_input = 0;
int got_pointer_input = 0;
int got_keyboard_input = 0;
+int urgent_update = 0;
int last_keyboard_input = 0;
+rfbKeySym last_keysym = 0;
int fb_copy_in_progress = 0;
int drag_in_progress = 0;
int shut_down = 0;
@@ -649,6 +584,7 @@ void initialize_watch_bell(void);
void initialize_xinerama(void);
void initialize_xfixes(void);
void initialize_xdamage(void);
+int valid_window(Window, XWindowAttributes *);
void create_xdamage_if_needed(void);
void destroy_xdamage_if_needed(void);
void initialize_xrandr(void);
@@ -686,12 +622,21 @@ int check_pipeinput(void);
void cursor_position(int, int);
void parse_wireframe(void);
+void parse_scroll_copyrect(void);
void set_wirecopyrect_mode(char *);
+void set_scrollcopyrect_mode(char *);
+int try_copyrect(Window, int, int, int, int, int, int, int *);
+void do_copyregion(sraRegionPtr, int, int);
+int direct_fb_copy(int, int, int, int, int);
+int get_wm_frame_pos(int *, int *, int *, int *, int *, int *, Window *);
+int 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);
char *process_remote_cmd(char *, int);
-void rfbPE(rfbScreenInfoPtr, long);
-void rfbCFD(rfbScreenInfoPtr, long);
+void rfbPE(long);
+void rfbCFD(long);
int scan_for_updates(int);
void set_colormap(int);
void set_offset(void);
@@ -728,8 +673,10 @@ 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);
@@ -771,29 +718,42 @@ char *solid_default = "cyan4";
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;
char *rc_rcfile = NULL; /* -rc */
int rc_norc = 0;
int opts_bg = 0;
-#ifndef X11VNC_SHARED
+#ifndef SHARED
int shared = 0; /* share vnc display. */
#else
int shared = 1;
#endif
-#ifndef X11VNC_FOREVER
+#ifndef FOREVER
int connect_once = 1; /* disconnect after first connection session. */
#else
int connect_once = 0;
#endif
int deny_all = 0; /* global locking of new clients */
-int accept_remote_cmds = 1; /* -noremote */
+#ifndef REMOTE_DEFAULT
+#define REMOTE_DEFAULT 1
+#endif
+int accept_remote_cmds = REMOTE_DEFAULT; /* -noremote */
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 */
@@ -829,6 +789,8 @@ int subwin_wait_mapped = 0;
int debug_xevents = 0; /* -R debug_xevents:1 */
int debug_xdamage = 0; /* -R debug_xdamage:1 or 2 ... */
+int debug_wireframe = 0;
+int debug_wireframe2 = 0;
int xtrap_input = 0; /* -xtrap for user input insertion */
int xinerama = 0; /* -xinerama */
@@ -844,6 +806,10 @@ char *pad_geometry = NULL;
time_t pad_geometry_time;
int use_snapfb = 0;
+Display *rdpy_data = 0; /* Data connection for RECORD */
+Display *rdpy_ctrl = 0; /* Control connection for RECORD */
+int use_xrecord = 0;
+
char *client_connect = NULL; /* strings for -connect option */
char *client_connect_file = NULL;
int vnc_connect = 1; /* -vncconnect option */
@@ -861,15 +827,47 @@ int show_dragging = 1; /* process mouse movement events */
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,0.15+0.35+4.0+0.1"
+#define WIREFRAME_PARMS "0xff,3,0,32+8+8+8,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;
Window *stack_list = NULL;
int stack_num = 0;
-int no_autorepeat = 1; /* turn off autorepeat with clients */
+
+/* T+B+L+R,tkey+presist_key,tmouse+persist_mouse */
+#ifndef SCROLL_COPYRECT_PARMS
+#define SCROLL_COPYRECT_PARMS "0+64+32+32,0.02+0.4,0.08+0.4"
+#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
+int scrollcopyrect_min_area = 60000; /* minimum rectangle area */
+int debug_scroll = 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 */
@@ -925,6 +923,7 @@ 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;
@@ -947,6 +946,8 @@ 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;
/* threaded vs. non-threaded (default) */
#if LIBVNCSERVER_X11VNC_THREADED && ! defined(X11VNC_THREADED)
@@ -1013,6 +1014,14 @@ int nabs(int n) {
}
}
+double dabs(double x) {
+ if (x < 0.0) {
+ return -x;
+ } else {
+ return x;
+ }
+}
+
void lowercase(char *str) {
char *p;
if (str == NULL) {
@@ -1136,7 +1145,7 @@ int pick_windowid(unsigned long *num) {
* select timedout or error.
* note this rfbPE takes about 30ms too:
*/
- rfbPE(screen, -1);
+ rfbPE(-1);
XFlush(dpy);
continue;
}
@@ -1952,6 +1961,30 @@ char *host2ip(char *host) {
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
@@ -2558,15 +2591,6 @@ Bool XTestCompareCursorWithWindow_wr(Display* dpy, Window w, Cursor cursor) {
#endif
}
-/* how to handle old tree for this w/o OLD_TREE? */
-#if 0
-#if LIBVNCSERVER_HAVE_XTEST
-#ifndef LIBVNCSERVER_HAVE_XTESTGRABCONTROL
-#define LIBVNCSERVER_HAVE_XTESTGRABCONTROL 1
-#endif
-#endif
-#endif
-
Bool XTestQueryExtension_wr(Display *dpy, int *ev, int *er, int *maj,
int *min) {
#if LIBVNCSERVER_HAVE_XTEST
@@ -2642,40 +2666,1010 @@ int XTRAP_GrabControl_wr(Display *dpy, Bool impervious) {
return 0;
}
-void disable_grabserver(void) {
+void disable_grabserver(Display *dpy) {
int ok = 0;
+ static int didmsg = 0;
if (! xtrap_input) {
if (XTestGrabControl_wr(dpy, True)) {
XTRAP_GrabControl_wr(dpy, False);
- rfbLog("GrabServer control via XTEST.\n");
+ if (! didmsg) {
+ rfbLog("GrabServer control via XTEST.\n");
+ didmsg = 1;
+ }
ok = 1;
} else {
if (XTRAP_GrabControl_wr(dpy, True)) {
ok = 1;
- rfbLog("Using DEC-XTRAP for protection from "
- "XGrabServer.\n");
+ if (! didmsg) {
+ rfbLog("Using DEC-XTRAP for protection"
+ " from XGrabServer.\n");
+ didmsg = 1;
+ }
}
}
} else {
if (XTRAP_GrabControl_wr(dpy, True)) {
XTestGrabControl_wr(dpy, False);
- rfbLog("GrabServer control via DEC-XTRAP.\n");
+ if (! didmsg) {
+ rfbLog("GrabServer control via DEC-XTRAP.\n");
+ didmsg = 1;
+ }
ok = 1;
} else {
if (XTestGrabControl_wr(dpy, True)) {
ok = 1;
- rfbLog("DEC-XTRAP XGrabServer protection not "
- "available, using XTEST.\n");
+ if (! didmsg) {
+ rfbLog("DEC-XTRAP XGrabServer "
+ "protection not available, "
+ "using XTEST.\n");
+ didmsg = 1;
+ }
}
}
}
- if (! ok) {
+ if (! ok && ! didmsg) {
rfbLog("No XTEST or DEC-XTRAP protection from XGrabServer.\n");
rfbLog("Deadlock if your window manager calls XGrabServer!!\n");
}
}
+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;
+XRecordRange *rr_CW;
+XRecordRange *rr_scroll[10];
+XRecordContext rc_scroll;
+XRecordClientSpec rcs_scroll;
+#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;
+
+void initialize_xrecord(void) {
+ use_xrecord = 0;
+ if (! xrecord_present) {
+ return;
+ }
+#if LIBVNCSERVER_HAVE_RECORD
+ rr_CA = XRecordAllocRange();
+ rr_CW = XRecordAllocRange();
+ if (! rr_CA || ! rr_CW) {
+ 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;
+
+ /* open a 2nd control connection to DISPLAY: */
+ 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) {
+ return;
+ }
+ disable_grabserver(rdpy_ctrl);
+ disable_grabserver(rdpy_data);
+ use_xrecord = 1;
+#endif
+}
+
+int xrecord_skip_keysym(rfbKeySym keysym) {
+ KeySym sym = (KeySym) keysym;
+
+ if (IsModifierKey(sym)) {
+ return 1;
+ }
+ return 0;
+}
+
+int xrecord_skip_button(int new, int old) {
+ 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 (sym == XK_J || sym == XK_j || sym == XK_K || sym == XK_k) {
+ return 1; /* vi */
+ }
+ if (sym == XK_N || sym == XK_n || sym == XK_P || sym == XK_p) {
+ return 1; /* emacs */
+ }
+ return 0;
+}
+
+typedef struct scroll_event {
+ Window win, frame;
+ int dx, dy;
+ int x, y, w, h, 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;
+
+//dba = 1;
+
+ 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) 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) fprintf(stderr, "record_CA-%d\n", k++);
+
+ if (rec_data->id_base == 0) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CA-%d\n", k++);
+
+ if ((XID) ptr != xrecord_seq) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CA-%d\n", k++);
+
+ if (rec_data->category != XRecordFromClient) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CA-%d\n", k++);
+
+ req = (xCopyAreaReq *) rec_data->data;
+
+ if (req->reqType != X_CopyArea) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CA-%d\n", k++);
+
+/*
+
+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 < scrollcopyrect_min_area) {
+ good = 0;
+ } else if (!src || !dst) {
+ good = 0;
+ } else if (src != dst) {
+ good = 0;
+ }
+
+ dx = dst_x - src_x;
+ dy = dst_y - src_y;
+
+ if (dx != 0 && dy != 0) {
+ good = 0;
+ }
+
+ if (! good) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CA-%d\n", k++);
+
+ if (! valid_window(src, &attr)) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CA-%d\n", k++);
+
+ if (attr.map_state != IsViewable) {
+ return;
+ }
+
+ XTranslateCoordinates(dpy, src, rootwin, 0, 0, &rx, &ry, &c);
+
+if (dba || db) fprintf(stderr, "record_CA-%d *FOUND: src: 0x%lx dx: %d dy: %d "
+ "x: %d y: %d w: %d h: %d st: %.4f\n", k++, src, dx, dy, src_x,
+ src_y, w, h, (double) rec_data->server_time/1000.0);
+
+ if (scr_ev_cnt >= SCR_EV_MAX) {
+ return;
+ }
+
+ 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 = (int) rec_data->server_time;
+ 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 long vals[4];
+ unsigned tmask;
+ char *data;
+ int dba = 0, db = debug_scroll;
+
+//dba = 1;
+//db = 1;
+
+ if (1 || db) {
+ if (rec_data->category == XRecordFromClient) {
+ req = (xConfigureWindowReq *) rec_data->data;
+ if (req->reqType == X_ConfigureWindow) {
+ src = req->window;
+ }
+ }
+ }
+
+if (dba || db) 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) fprintf(stderr, "record_CW-%d\n", k++);
+
+ if ((XID) ptr != xrecord_seq) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ if (rec_data->id_base == 0) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ if (rec_data->category == XRecordStartOfData) {
+ index = 0;
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ if (rec_data->category != XRecordFromClient) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ if (rec_data->client_swapped) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ req = (xConfigureWindowReq *) rec_data->data;
+
+ if (req->reqType != X_ConfigureWindow) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ tmask = req->mask;
+
+ tmask &= ~CWX;
+ tmask &= ~CWY;
+ tmask &= ~CWWidth;
+ tmask &= ~CWHeight;
+
+ if (tmask) {
+ /* require no more than these 4 flags */
+ return;
+ }
+if (db) 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) fprintf(stderr, "record_CW-%d\n", k++);
+
+ if ( (f_w && !f_h) || (!f_w && f_h) ) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ for (i=0; i<4; i++) {
+ vals[i] = 0;
+ }
+
+ data = (char *)req;
+ data += sz_xConfigureWindowReq;
+
+ for (i=0; i<req->length; i++) {
+ unsigned long v;
+ v = *( (unsigned long *) data);
+ vals[i] = v;
+ data += 4;
+ }
+
+
+ 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) 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) 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) 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;
+ }
+
+ if (! good) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ /* geometry OK, now check with the X server: */
+ if (! valid_window(win, &attr)) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ if (attr.map_state != IsViewable) {
+ return;
+ }
+if (db) fprintf(stderr, "record_CW-%d\n", k++);
+
+ XTranslateCoordinates(dpy, win, rootwin, 0, 0, &rx, &ry, &c);
+
+if (dba || db) fprintf(stderr, "record_CW-%d *FOUND: win: 0x%lx dx: %d dy: %d "
+ "x: %d y: %d w: %d h: %d st: %.4f\n", k++, win, dx, dy, src_x, src_y,
+ w, h, (double) rec_data->server_time/1000.0);
+
+ if (scr_ev_cnt >= SCR_EV_MAX) {
+ return;
+ }
+
+ 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 = (int) rec_data->server_time;
+ 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) {
+ xReq *req;
+
+//fprintf(stderr, "XRecordFreeData-0: %p\n", rec_data);
+ /* 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) {
+//fprintf(stderr, "XRecordFreeData-1: %p\n", rec_data);
+ 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 {
+ ;
+ }
+//fprintf(stderr, "XRecordFreeData-2: %p\n", rec_data);
+ XRecordFreeData(rec_data);
+}
+#endif
+
+void xrecord_watch(int start) {
+ Window focus, wm, r, c, clast;
+ int rx, ry, wx, wy;
+ int depth = 0, i;
+ unsigned int m;
+ int db = debug_scroll;
+ int do_shutdown = 0;
+
+//db = 1;
+
+#if LIBVNCSERVER_HAVE_RECORD
+ if (! start) {
+ xrecording = 0;
+ if (! rc_scroll) {
+ xrecord_focus_window = None;
+ xrecord_wm_window = None;
+ rcs_scroll = 0;
+ return;
+ }
+ if (do_shutdown) {
+if (db) fprintf(stderr, "=== shutdown-scroll 0x%lx\n", rc_scroll);
+ X_LOCK;
+ if (! XRecordDisableContext(rdpy_ctrl, rc_scroll)) {
+ rfbLog("XRecordDisableContext(rc_scroll)"
+ " failed.\n");
+ }
+ if (! XRecordFreeContext(rdpy_ctrl, rc_scroll)) {
+ rfbLog("XRecordFreeContext(rc_scroll)"
+ " failed.\n");
+ }
+ XRecordProcessReplies(rdpy_data);
+ X_UNLOCK;
+ rc_scroll = 0;
+ } else {
+ if (rcs_scroll) {
+if (db) fprintf(stderr, "=== disab-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
+ X_LOCK;
+ rcs_scroll = XRecordCurrentClients;
+ XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
+ &rcs_scroll, 1);
+ XRecordDisableContext(rdpy_ctrl, rc_scroll);
+ XFlush(rdpy_ctrl);
+ XRecordProcessReplies(rdpy_data);
+ X_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;
+ rcs_scroll = 0;
+ return;
+ }
+
+ if (xrecording) {
+ return;
+ }
+
+ if (do_shutdown && rc_scroll) {
+ static int didmsg = 0;
+ /* should not happen... */
+ if (!didmsg) {
+ rfbLog("warning: do_shutdown && rc_scroll\n");
+ didmsg = 1;
+ }
+ xrecord_watch(0);
+ }
+
+ xrecording = 0;
+ xrecord_focus_window = None;
+ xrecord_wm_window = None;
+ xrecord_set_by_keys = 0;
+ xrecord_set_by_mouse = 0;
+
+ X_LOCK;
+
+ /* get the window with focus and mouse pointer: */
+ clast = None;
+ focus = None;
+ wm = None;
+
+ XGetInputFocus(dpy, &focus, &i);
+
+ XQueryPointer(dpy, rootwin, &r, &wm, &rx, &ry, &wx, &wy, &m);
+ if (wm) {
+ c = wm;
+ } else {
+ c = rootwin;
+ }
+ for (i=0; i<3; i++) {
+ /* descend a bit to avoid wm frames: */
+ clast = c;
+ if (! XQueryPointer(dpy, c, &r, &c, &rx, &ry, &wx, &wy, &m)) {
+ break;
+ }
+ if (! c) {
+ break;
+ }
+ depth++;
+ }
+ if (!clast || clast == rootwin) {
+if (db) fprintf(stderr, "--- xrecord_watch: skip.\n");
+ X_UNLOCK;
+ return;
+ }
+
+ /* set protocol request ranges: */
+ rr_scroll[0] = rr_CA;
+ rr_scroll[1] = rr_CW;
+
+ if (! rc_scroll) {
+ /* do_shutdown case or first time in */
+ rcs_scroll = (XRecordClientSpec) clast;
+ rc_scroll = XRecordCreateContext(rdpy_ctrl, 0, &rcs_scroll, 1,
+ rr_scroll, 2);
+
+ } else if (! do_shutdown) {
+ if (rcs_scroll) {
+if (db) fprintf(stderr, "=2= unreg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
+ rcs_scroll = XRecordCurrentClients;
+ XRecordUnregisterClients(rdpy_ctrl, rc_scroll,
+ &rcs_scroll, 1);
+ }
+
+ rcs_scroll = (XRecordClientSpec) clast;
+if (db) fprintf(stderr, "=-= reg-scroll 0x%lx 0x%lx\n", rc_scroll, rcs_scroll);
+
+ if (!XRecordRegisterClients(rdpy_ctrl, rc_scroll, 0,
+ &rcs_scroll, 1, rr_scroll, 2)) {
+ static time_t last_err = 0;
+ time_t now = time(0);
+ if (now > last_err + 60) {
+ rfbLog("failed to register client 0x%lx with"
+ " X RECORD context rc_scroll.\n", clast);
+ last_err = now;
+ }
+ /* continue on for now... */
+ }
+ }
+ XFlush(rdpy_ctrl);
+
+if (db) fprintf(stderr, "rc_scroll: 0x%lx\n", rc_scroll);
+
+ if (! rc_scroll) {
+ X_UNLOCK;
+ use_xrecord = 0;
+ rfbLog("failed to create X RECORD context rc_scroll.\n");
+ rfbLog(" switching to -noscrollcopyrect mode.\n");
+ return;
+ }
+
+ xrecord_focus_window = focus;
+ if (! xrecord_focus_window) {
+ xrecord_focus_window = clast;
+ }
+ xrecord_wm_window = wm;
+ if (! xrecord_wm_window) {
+ xrecord_wm_window = clast;
+ }
+
+ xrecording = 1;
+ xrecord_seq++;
+ xrecord_start = 0.0;
+ dtime(&xrecord_start);
+
+ if (!XRecordEnableContextAsync(rdpy_data, rc_scroll, record_switch,
+ (XPointer) xrecord_seq)) {
+ static time_t last_err = 0;
+ time_t now = time(0);
+ if (now > last_err + 60) {
+ rfbLog("failed to enable RECORD context "
+ "rc_scroll.\n");
+ last_err = now;
+ }
+ /* continue on for now... */
+ }
+ XFlush(rdpy_data);
+ X_UNLOCK;
+#endif
+}
/* -- cleanup.c -- */
/*
@@ -2766,6 +3760,14 @@ void clean_up_exit (int ret) {
XEFreeTC(trap_ctx);
}
#endif
+#if LIBVNCSERVER_HAVE_RECORD
+ /* XXX currently blocks: */
+#if 0
+ if (rdpy_ctrl && rc_scroll) XRecordDisableContext(rdpy_ctrl, rc_scroll);
+ if (rdpy_data) XCloseDisplay(rdpy_data);
+ if (rdpy_ctrl) XCloseDisplay(rdpy_ctrl);
+#endif
+#endif
XCloseDisplay(dpy);
X_UNLOCK;
@@ -4229,7 +5231,7 @@ static void reverse_connect(char *str) {
p = strtok(tmp, ", \t\r\n");
while (p) {
if ((n = do_reverse_connect(p)) != 0) {
- rfbPE(screen, -1);
+ rfbPE(-1);
}
cnt += n;
@@ -4238,7 +5240,7 @@ static void reverse_connect(char *str) {
t = 0;
while (t < sleep_between_host) {
usleep(dt * 1000);
- rfbPE(screen, -1);
+ rfbPE(-1);
t += dt;
}
}
@@ -4264,7 +5266,7 @@ static void reverse_connect(char *str) {
t = 0;
while (t < tot) {
- rfbPE(screen, -1);
+ rfbPE(-1);
usleep(dt * 1000);
t += dt;
}
@@ -4374,7 +5376,8 @@ void check_connect_inputs(void) {
*/
enum rfbNewClientAction new_client(rfbClientPtr client) {
ClientData *cd;
- int i;
+ double tmr = 0.0;
+
last_event = last_input = time(0);
if (inetd) {
@@ -4441,11 +5444,13 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
install_padded_fb(pad_geometry);
}
- for (i=0; i<RATE_SAMPLES; i++) {
- cd->cmp_samp[i] = 5000; /* 56k modem */
- cd->raw_samp[i] = 50000;
- }
- cd->sample = 0;
+ dtime(&tmr);
+ cd->timer = tmr;
+ 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);
@@ -6103,6 +7108,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
if (! view_only || raw_fb) { /* raw_fb hack */
last_keyboard_client = client;
last_event = last_input = time(0);
+ last_keysym = keysym;
got_user_input++;
got_keyboard_input++;
}
@@ -6120,6 +7126,7 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
last_keyboard_client = client;
last_event = last_input = time(0);
+ last_keysym = keysym;
got_user_input++;
got_keyboard_input++;
@@ -6170,6 +7177,20 @@ void keyboard(rfbBool down, rfbKeySym keysym, rfbClientPtr client) {
return;
}
+ if (use_xrecord && ! xrecording && down) {
+ if (scaling && ! got_scrollcopyrect) {
+ ;
+ } else if (!strcmp(scroll_copyrect, "never")) {
+ ;
+ } else if (!strcmp(scroll_copyrect, "mouse")) {
+ ;
+ } else if (! xrecord_skip_keysym(keysym)) {
+ snapshot_stack_list(0, 0.25);
+ xrecord_watch(1);
+ xrecord_set_by_keys = 1;
+ }
+ }
+
if (use_modifier_tweak) {
modifier_tweak_keyboard(down, keysym, client);
X_LOCK;
@@ -6457,18 +7478,50 @@ void initialize_pointer_map(char *pointer_remap) {
* children of the root window. Ideally done before we send ButtonPress
* to the X server.
*/
-void snapshot_stack_list(void) {
+void snapshot_stack_list(int free_only, double allowed_age) {
+ static double last_snap = 0.0, last_sync = 0.0, last_free = 0.0;
+ double now = 0.0, xsync_max = 0.25;
Window r, w;
+
+ dtime(&now);
+ if (free_only) {
+ if (stack_list) {
+ X_LOCK;
+ XFree(stack_list);
+ X_UNLOCK;
+ stack_list = NULL;
+ stack_num = 0;
+ last_free = now;
+ }
+ return;
+ }
+
+ if (stack_list && now < last_snap + allowed_age) {
+ return;
+ }
+
if (stack_list) {
XFree(stack_list);
stack_list = NULL;
stack_num = 0;
+ last_free = now;
}
- XSync(dpy, False);
+
+ X_LOCK;
+ if (now > last_sync + xsync_max) {
+ XSync(dpy, False);
+ last_sync = now;
+ }
+
if (! XQueryTree(dpy, rootwin, &r, &w, &stack_list, &stack_num)) {
stack_list = NULL;
stack_num = 0;
+ last_free = now;
+ last_snap = 0.0;
+ } else {
+ last_snap = now;
}
+ X_UNLOCK;
}
/*
@@ -6509,20 +7562,69 @@ static void update_x11_pointer_position(int x, int y) {
*/
static void update_x11_pointer_mask(int mask) {
int i, mb;
+ int xr_mouse = 1;
+ int snapped = 0;
last_event = last_input = time(0);
if (raw_fb && ! dpy) return; /* raw_fb hack */
- X_LOCK;
+ if (scaling && ! got_scrollcopyrect) {
+ xr_mouse = 0;
+ } else if (!strcmp(scroll_copyrect, "never")) {
+ xr_mouse = 0;
+ } else if (!strcmp(scroll_copyrect, "keys")) {
+ 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, ok;
+ Window frame;
+ int skip = 0;
+
+ if (!button_mask) {
+ if (get_wm_frame_pos(&px, &py, &x, &y, &w, &h,
+ &frame)) {
+ ok = 1;
+ } else {
+ ok = 0;
+ }
+ }
+ if (ok) {
+ if (! near_scrollbar_edge(x, y, w, h, px, py)) {
+ skip = 1;
+ }
+ if (near_wm_edge(x, y, w, h, px, py)) {
+ /* step out of wireframe's way */
+ skip = 1;
+ }
+ }
+
+ if (! skip) {
+ snapshot_stack_list(0, 0.25);
+ snapped = 1;
+ xrecord_watch(1);
+ if (button_mask) {
+ xrecord_set_by_mouse = 1;
+ } else {
+ 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")) {
- snapshot_stack_list();
+ 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)) ) {
@@ -7714,6 +8816,55 @@ void print_xevent_bases(void) {
fprintf(stderr, " SelClear=%d, Expose=%d\n", SelectionClear, Expose);
}
+void sync_tod_with_servertime() {
+ static Atom servertime = None;
+ XEvent xev;
+ char diff[64];
+ static int seq = 0;
+ int i;
+
+ if (! servertime) {
+ servertime = XInternAtom(dpy, "X11VNC_SERVERTIME_DIFF", False);
+ }
+ if (! servertime) {
+ return;
+ }
+
+ XSync(dpy, False);
+ while (XCheckTypedEvent(dpy, PropertyNotify, &xev)) {
+ ;
+ }
+
+ snprintf(diff, 64, "%.6f/%08d", servertime_diff, seq++);
+ XChangeProperty(dpy, rootwin, servertime, 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 == servertime) {
+ double now = 0.0, stime;
+
+ dtime(&now);
+ stime = (double) xev.xproperty.time;
+ stime = stime/1000.0;
+ servertime_diff = now - stime;
+ if (0) rfbLog("set servertime_diff: "
+ "%.6f\n", servertime_diff);
+ got = 1;
+ }
+ }
+ }
+ if (got) {
+ break;
+ }
+ usleep(1000);
+ }
+}
+
/*
* This routine is periodically called to check for selection related
* and other X11 events and respond to them as needed.
@@ -7727,6 +8878,7 @@ void check_xevents(void) {
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 */
@@ -7739,6 +8891,7 @@ void check_xevents(void) {
if (screen && screen->clientHead) {
have_clients = 1;
}
+
X_LOCK;
/*
* There is a bug where we have to wait before sending text to
@@ -7848,6 +9001,11 @@ void check_xevents(void) {
}
}
+ if (now > last_time_sync + 30) {
+ sync_tod_with_servertime();
+ last_time_sync = now;
+ }
+
#if LIBVNCSERVER_HAVE_LIBXRANDR
if (xrandr) {
check_xrandr_event("check_xevents");
@@ -8460,6 +9618,9 @@ int remote_control_access_ok(void) {
char *home, *xauth;
char *dpy_str = DisplayString(dpy);
Display *dpy2;
+ XHostAddress *xha;
+ Bool enabled;
+ int n;
home = get_home_dir();
if (getenv("XAUTHORITY") != NULL) {
@@ -8499,6 +9660,32 @@ int remote_control_access_ok(void) {
}
}
+ 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 {
@@ -8508,8 +9695,11 @@ int remote_control_access_ok(void) {
fprintf(stderr, "\nChecking if display %s requires "
"XAUTHORITY\n", dpy_str);
- fprintf(stderr, " (ignore any Xlib: errors that follow)\n");
+ 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);
@@ -8526,6 +9716,7 @@ int remote_control_access_ok(void) {
XCloseDisplay(dpy2);
return 0;
}
+
}
return 1;
}
@@ -8542,22 +9733,20 @@ char *process_remote_cmd(char *cmd, int stringonly) {
int bufn = VNC_CONNECT_MAX;
int query = 0;
static char *prev_cursors_mode = NULL;
- static int first = 1;
if (! accept_remote_cmds) {
rfbLog("remote commands disabled: %s\n", cmd);
return NULL;
}
- if (first && priv_remote) {
+ if (priv_remote) {
if (! remote_control_access_ok()) {
- rfbLog("disabling remote commands in -privremote "
+ rfbLog("** Disabling remote commands in -privremote "
"mode.\n");
accept_remote_cmds = 0;
return NULL;
}
}
- first = 0;
strcpy(buf, "");
if (strstr(cmd, "cmd=") == cmd) {
@@ -9542,7 +10731,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
"(if applicable).\n");
if (! xtrap_input) {
xtrap_input = 1;
- disable_grabserver();
+ disable_grabserver(dpy);
}
} else if (!strcmp(p, "noxtrap")) {
@@ -9554,7 +10743,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
"(if applicable).\n");
if (xtrap_input) {
xtrap_input = 0;
- disable_grabserver();
+ disable_grabserver(dpy);
}
} else if (!strcmp(p, "xrandr")) {
@@ -9964,7 +11153,7 @@ char *process_remote_cmd(char *cmd, int stringonly) {
set_no_cursor();
for (i=0; i<max; i++) {
/* XXX: try to force empty cursor back to client */
- rfbPE(screen, -1);
+ rfbPE(-1);
}
cursor_shape_updates = 0;
disable_cursor_shape_updates(screen);
@@ -10315,14 +11504,30 @@ char *process_remote_cmd(char *cmd, int stringonly) {
}
rfbLog("remote_cmd: enabling -wireframe mode.\n");
wireframe = 1;
- } else if (!strcmp(p, "wireframe")) {
+ } 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")) {
+ } else if (!strcmp(p, "nowireframe") || !strcmp(p, "nowf")) {
if (query) {
snprintf(buf, bufn, "ans=%s:%d", p, !wireframe);
goto qry;
@@ -10342,7 +11547,21 @@ char *process_remote_cmd(char *cmd, int stringonly) {
set_wirecopyrect_mode(p);
rfbLog("remote_cmd: changed -wirecopyrect mode "
"to: %s\n", NONUL(wireframe_copyrect));
- } else if (!strcmp(p, "nowirecopyrect")) {
+ 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));
@@ -10353,6 +11572,56 @@ char *process_remote_cmd(char *cmd, int stringonly) {
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, "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 (strstr(p, "scr") == p) {
+ 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, "pointer_mode") == p) {
int pm;
COLON_CHECK("pointer_mode:")
@@ -10854,6 +12123,20 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog("remote_cmd: disabling remote commands.\n");
accept_remote_cmds = 0; /* cannot be turned back on. */
+ } else if (!strcmp(p, "debug_xevents")) {
+ if (query) {
+ snprintf(buf, bufn, "ans=%s:%d", p, debug_xevents);
+ 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) {
@@ -10864,6 +12147,20 @@ char *process_remote_cmd(char *cmd, int stringonly) {
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) {
@@ -10874,6 +12171,56 @@ char *process_remote_cmd(char *cmd, int stringonly) {
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 (strstr(p, "hack") == p) { /* skip-cmd-list */
COLON_CHECK("hack:")
if (query) {
@@ -10980,6 +12327,12 @@ char *process_remote_cmd(char *cmd, int stringonly) {
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));
@@ -10995,6 +12348,8 @@ char *process_remote_cmd(char *cmd, int stringonly) {
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")) {
@@ -11114,6 +12469,7 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) {
int dt_x, dt_y, nt_x1, nt_y1, nt_x2, nt_y2, nt;
int ix, iy, cnt = 0;
int area = w*h, always_accept = 0;
+ int use_direct_fb_copy = 0; /* TBD: not working yet */
if (xdamage_max_area <= 0) {
always_accept = 1;
@@ -11133,24 +12489,33 @@ void record_desired_xdamage_rect(int x, int y, int w, int h) {
*/
return;
}
+
+ if (use_direct_fb_copy) {
+ X_UNLOCK;
+ direct_fb_copy(x, y, x + w, y + h, 1);
+ 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);
- 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++;
+ /*
+ * 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++;
}
- tile_has_xdamage_diff[nt] = 1;
- tile_row_has_xdamage_diff[iy] = 1;
- xdamage_tile_count++;
}
}
if (debug_xdamage > 1) {
@@ -11940,7 +13305,6 @@ void setup_cursors(void) {
first = 0;
if (screen) {
- RFBUNDRAWCURSOR(screen);
screen->cursor = NULL;
LOCK(screen->cursorMutex);
}
@@ -12515,7 +13879,6 @@ rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h,
c->cleanupRichSource = FALSE;
c->richSource = rich;
-#if !OLD_TREE
if (alpha_blend && !indexed_color) {
c->alphaSource = alpha;
c->alphaPreMultiplied = TRUE;
@@ -12523,8 +13886,6 @@ rfbCursorPtr pixels2curs(unsigned long *pixels, int w, int h,
free(alpha);
c->alphaSource = NULL;
}
-#endif
-
return c;
}
@@ -12618,8 +13979,6 @@ int get_xfixes_cursor(int init) {
}
}
- RFBUNDRAWCURSOR(screen);
-
/* we need to create the cursor and overwrite oldest */
use = oldest;
if (cursors[use]->rfb) {
@@ -12627,11 +13986,9 @@ int get_xfixes_cursor(int init) {
if (cursors[use]->rfb->richSource) {
free(cursors[use]->rfb->richSource);
}
-#if !OLD_TREE
if (cursors[use]->rfb->alphaSource) {
free(cursors[use]->rfb->alphaSource);
}
-#endif
if (cursors[use]->rfb->source) {
free(cursors[use]->rfb->source);
}
@@ -12831,47 +14188,6 @@ int get_which_cursor(void) {
return which;
}
-#if OLD_TREE
-/*
- * Some utilities for marking the little cursor patch region as
- * modified, etc.
- */
-void mark_cursor_patch_modified(rfbScreenInfoPtr s, int old) {
- int curx, cury, xhot, yhot, w, h;
- int x1, x2, y1, y2;
-
- if (! s || ! s->cursor) {
- return;
- }
-
- if (old) {
- /* use oldCursor pos */
- curx = s->oldCursorX;
- cury = s->oldCursorY;
- } else {
- curx = s->cursorX;
- cury = s->cursorY;
- }
-
- xhot = s->cursor->xhot;
- yhot = s->cursor->yhot;
- w = s->cursor->width;
- h = s->cursor->height;
-
- x1 = curx - xhot;
- x2 = x1 + w;
- x1 = nfix(x1, s->width);
- x2 = nfix(x2, s->width);
-
- y1 = cury - yhot;
- y2 = y1 + h;
- y1 = nfix(y1, s->height);
- y2 = nfix(y2, s->height);
-
- rfbMarkRectAsModified(s, x1, y1, x1+x2, y1+y2);
-}
-#endif
-
void set_cursor_was_changed(rfbScreenInfoPtr s) {
rfbClientIteratorPtr iter;
rfbClientPtr cl;
@@ -13006,9 +14322,6 @@ void cursor_position(int x, int y) {
rfbClientPtr cl;
int cnt = 0, nonCursorPosUpdates_clients = 0;
int x_in = x, y_in = y;
-#if OLD_TREE
- int x_old, y_old;
-#endif
/* x and y are current positions of X11 pointer on the X11 display */
if (!screen) {
@@ -13026,26 +14339,10 @@ void cursor_position(int x, int y) {
return;
}
-#if OLD_TREE
- x_old = screen->oldCursorX;
- y_old = screen->oldCursorY;
-
- if (screen->cursorIsDrawn) {
- rfbUndrawCursor(screen);
- }
-
- LOCK(screen->cursorMutex);
- if (! screen->cursorIsDrawn) {
- screen->cursorX = x;
- screen->cursorY = y;
- }
- UNLOCK(screen->cursorMutex);
-#else
LOCK(screen->cursorMutex);
screen->cursorX = x;
screen->cursorY = y;
UNLOCK(screen->cursorMutex);
-#endif
iter = rfbGetClientIterator(screen);
while( (cl = rfbClientIteratorNext(iter)) ) {
@@ -13080,21 +14377,12 @@ void cursor_position(int x, int y) {
}
rfbReleaseClientIterator(iter);
-#if OLD_TREE
- if (nonCursorPosUpdates_clients && show_cursor) {
- if (x_old != x || y_old != y) {
- mark_cursor_patch_modified(screen, 0);
- }
- }
-#endif
-
if (debug_pointer && cnt) {
rfbLog("cursor_position: sent position x=%3d y=%3d to %d"
" clients\n", x, y, cnt);
}
}
-#if !OLD_TREE
void set_rfb_cursor(int which) {
if (! show_cursor) {
@@ -13112,57 +14400,7 @@ void set_rfb_cursor(int which) {
}
}
-#else
-
-void set_rfb_cursor(int which) {
-
- if (! show_cursor) {
- return;
- }
- if (! screen) {
- return;
- }
-
- if (screen->cursor) {
- int all_are_cursor_pos = 1;
- rfbClientIteratorPtr iter;
- rfbClientPtr cl;
-
- iter = rfbGetClientIterator(screen);
- while( (cl = rfbClientIteratorNext(iter)) ) {
- if (! cl->enableCursorPosUpdates) {
- all_are_cursor_pos = 0;
- }
- if (! cl->enableCursorShapeUpdates) {
- all_are_cursor_pos = 0;
- }
- }
- rfbReleaseClientIterator(iter);
-
- if (! all_are_cursor_pos) {
- mark_cursor_patch_modified(screen, 1);
- }
- }
-
- if (!cursors[which] || !cursors[which]->rfb) {
- rfbLog("non-existent cursor: which=%d\n", which);
- return;
- } else {
- rfbSetCursor(screen, cursors[which]->rfb, FALSE);
- }
-
- if (screen->underCursorBuffer == NULL &&
- screen->underCursorBufferLen != 0) {
- LOCK(screen->cursorMutex);
- screen->underCursorBufferLen = 0;
- UNLOCK(screen->cursorMutex);
- }
- set_cursor_was_changed(screen);
-}
-#endif
-
void set_no_cursor(void) {
- RFBUNDRAWCURSOR(screen);
set_rfb_cursor(CURS_EMPTY);
}
@@ -14607,38 +15845,28 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
clean_up_exit(1);
}
-/*
- * This ifdef is a transient for source compatibility for people who download
- * the x11vnc.c file by itself and plop it down into their libvncserver tree.
- * Remove at some point. BTW, this assumes no usage of earlier "0.7pre".
- */
-#if OLD_TREE && defined(LIBVNCSERVER_VERSION)
- if (strcmp(LIBVNCSERVER_VERSION, "0.6"))
-#endif
- {
- if (create_screen && *argc != 1) {
- int i;
- 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 -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);
+ if (create_screen && *argc != 1) {
+ int i;
+ 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 -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: */
@@ -14840,6 +16068,7 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
/* may need, bpp, main_red_max, etc. */
parse_wireframe();
+ parse_scroll_copyrect();
setup_cursors_and_push();
@@ -15936,7 +17165,7 @@ void initialize_blackouts_and_xinerama(void) {
void push_sleep(n) {
int i;
for (i=0; i<n; i++) {
- rfbPE(screen, -1);
+ rfbPE(-1);
if (i != n-1 && defer_update) {
usleep(defer_update * 1000);
}
@@ -15960,7 +17189,7 @@ void refresh_screen(void) {
return;
}
mark_rect_as_modified(0, 0, dpy_x, dpy_y, 1);
- rfbPE(screen, -1);
+ rfbPE(-1);
}
/*
@@ -17928,7 +19157,7 @@ static void nap_sleep(int ms, int split) {
for (i=0; i<split; i++) {
usleep(ms * 1000 / split);
if (! use_threads && i != split - 1) {
- rfbPE(screen, -1);
+ rfbPE(-1);
}
if (input != got_user_input) {
break;
@@ -18451,7 +19680,7 @@ int scan_for_updates(int count_only) {
}
/* -- gui.c -- */
-#if OLD_TREE || SMALL_FOOTPRINT
+#if SMALL_FOOTPRINT
char gui_code[] = "";
#else
#include "tkx11vnc.h"
@@ -18800,7 +20029,77 @@ int get_wm_frame_pos(int *px, int *py, int *x, int *y, int *w, int *h, Window *w
}
static int defer_update_nofb = 6; /* defer a shorter time under -nofb */
-static Window maybe_scrolling = 0;
+
+int scrollcopyrect_top, scrollcopyrect_bot;
+int scrollcopyrect_left, scrollcopyrect_right;
+double scr_key_time, scr_key_persist, scr_mouse_time, scr_mouse_persist;
+
+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;
+ if (sscanf(str, "%lf+%lf", &t1, &t2) == 2) {
+ scr_key_time = t1;
+ scr_key_persist = t2;
+ }
+ free(str);
+ }
+
+ /* mouse scrolling timing heuristics. */
+ if ((str = part[2]) != NULL) {
+ double t1, t2;
+ if (sscanf(str, "%lf+%lf", &t1, &t2) == 2) {
+ scr_mouse_time = t1;
+ scr_mouse_persist = t2;
+ }
+ 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);
+}
/*
WIREFRAME_PARMS "0xff,2,0,30+6+6+6,0.05+0.3+2.0,8"
@@ -18976,6 +20275,34 @@ void set_wirecopyrect_mode(char *str) {
}
}
+/*
+ * 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);
+ }
+ rfbLog("unknown -scrollcopyrect mode: %s, using: %s\n", str,
+ scroll_copyrect);
+ }
+ if (orig) {
+ free(orig);
+ }
+}
+
typedef struct saveline {
int x0, y0, x1, y1;
int shift;
@@ -19233,6 +20560,519 @@ if (0) fprintf(stderr, " DrawBox: %dx%d+%d+%d\n", w, h, x, y);
}
}
+int direct_fb_copy(int x1, int y1, int x2, int y2, int mark) {
+ char *src, *dst;
+ int y, pixelsize = bpp/8;
+ int xmin = -1, xmax = -1, ymin = -1, ymax = -1;
+ int do_cmp = 0;
+
+ x1 = nfix(x1, dpy_x);
+ y1 = nfix(y1, dpy_y);
+ x2 = nfix(x2, dpy_x);
+ y2 = nfix(y2, dpy_y);
+
+ 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, 1);
+ }
+ return 1;
+}
+
+#define PUSH_TEST(n) \
+if (n) { \
+ double dt = 0.0, tm = 0.0; dtime(&tm); \
+ fprintf(stderr, "PUSH---\n"); \
+ while (dt < 2.0) { rfbPE(50000); dt += dtime(&tm); } \
+ fprintf(stderr, "---PUSH\n"); \
+}
+
+int push_scr_ev(double bdpush) {
+ Window frame, win, win0;
+ int x, y, w, h, wx, wy, ww, wh, dx, dy;
+ int x0, y0, w0, h0;
+ int nx, ny, nw, nh;
+ int dret = 1, obscured;
+ int ev, ev_tot = scr_ev_cnt;
+ double st, dnow = 0.0;
+ int db = 0, rrate = get_read_rate();
+ sraRegionPtr backfill, whole, tmpregion;
+ XWindowAttributes attr;
+
+//db = 1;
+
+ dtime(&dnow);
+
+ if (ev_tot == 0) {
+ return dret;
+ }
+ backfill = sraRgnCreate();
+ whole = sraRgnCreateRect(0, 0, dpy_x, dpy_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;
+
+ for (ev=0; ev < ev_tot; ev++) {
+
+ 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 = (double) scr_ev[ev].t/1000.0;
+
+ if (dabs((dnow - servertime_diff) - st) > 0.15) {
+if (db) fprintf(stderr, "push_scr_ev: TOO OLD: %.4f\n", (dnow - servertime_diff) - st);
+ dret = 0;
+ break;
+ } else {
+if (db) fprintf(stderr, "push_scr_ev: AGE: %.4f\n", (dnow - servertime_diff) - st);
+ }
+ 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) fprintf(stderr, "push_scr_ev: got: %d x: %4d y: %3d"
+ " w: %4d h: %3d dx: %d dy: %d %dx%d+%d+%d win: 0x%lx\n",
+ ev, x, y, w, h, dx, dy, w, h, x, y, win);
+
+if (db) fprintf(stderr, "------------ got: %d x: %4d y: %3d"
+ " w: %4d h: %3d %dx%d+%d+%d\n",
+ ev, wx, wy, ww, wh, ww, wh, wx, wy);
+
+if (db) 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) {
+ Window r;
+ int rx, ry, wx, wy;
+ unsigned int m;
+ if (! XQueryPointer(dpy, rootwin, &r, &frame,
+ &rx, &ry, &wx, &wy, &m)) {
+ frame = None;
+ }
+ }
+ if (! frame) {
+ frame = win;
+ }
+
+ if (try_copyrect(frame, x, y, w, h, dx, dy, &obscured)) {
+ fb_push();
+ urgent_update = 1;
+PUSH_TEST(0);
+
+ } else {
+ dret = 0;
+ break;
+ }
+
+ if (ev > 0) {
+ sraRgnOffset(backfill, dx, dy);
+ sraRgnAnd(backfill, whole);
+ }
+
+ tmpregion = sraRgnCreateRect(nx, ny, nx + nw, ny + nh);
+ sraRgnAnd(tmpregion, whole);
+ sraRgnOr(backfill, tmpregion);
+ sraRgnDestroy(tmpregion);
+ }
+
+ if (dret != 0) {
+ double est, win_area = 0.0, area = 0.0;
+ sraRectangleIterator *iter;
+ sraRect rect;
+ int tx1, ty1, tx2, ty2;
+ double tm = 0.0, dt = 0.0;
+
+ tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0);
+ sraRgnAnd(tmpregion, whole);
+
+ 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.85 * 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 {
+ dtime(&tm);
+ iter = sraRgnGetIterator(backfill);
+ while (sraRgnIteratorNext(iter, &rect)) {
+ tx1 = rect.x1;
+ ty1 = rect.y1;
+ tx2 = rect.x2;
+ ty2 = rect.y2;
+ tx1 = nfix(tx1, dpy_x);
+ ty1 = nfix(ty1, dpy_y);
+ tx2 = nfix(tx2, dpy_x);
+ ty2 = nfix(ty2, dpy_y);
+
+ dtime(&tm);
+if (db) fprintf(stderr, " DFC(%d,%d-%d,%d)", tx1, ty1, tx2, ty2);
+ direct_fb_copy(tx1, ty1, tx2, ty2, 1);
+ dt = dtime(&tm);
+ fb_push();
+PUSH_TEST(0);
+ }
+ dt = dtime(&tm);
+ sraRgnReleaseIterator(iter);
+if (db) fprintf(stderr, " 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 > 0.0) fprintf(stderr, "BDPUSH-TIME: %.3f 0x%lx\n", bdpush, xrecord_wm_window);
+
+ if (bdpush > 0.0 && xrecord_wm_window != None &&
+ valid_window(xrecord_wm_window, &attr)) {
+
+ double wm_area = 0.0, win_area = 0.0, d_area;
+ sraRectangleIterator *iter;
+ sraRect rect;
+ sraRegionPtr frame;
+ int tx1, ty1, tx2, ty2;
+
+ /* wm frame: */
+ tx1 = attr.x;
+ ty1 = attr.y;
+ tx2 = attr.x + attr.width;
+ ty2 = attr.y + attr.height;
+
+ frame = sraRgnCreateRect(tx1, ty1, tx2, ty2);
+ sraRgnAnd(frame, whole);
+
+ iter = sraRgnGetIterator(frame);
+ while (sraRgnIteratorNext(iter, &rect)) {
+ tx1 = rect.x1;
+ ty1 = rect.y1;
+ tx2 = rect.x2;
+ ty2 = rect.y2;
+
+ wm_area += (tx2 - tx1)*(ty2 - ty1);
+ }
+ sraRgnReleaseIterator(iter);
+
+ /* scrolling window: */
+ tmpregion = sraRgnCreateRect(x0, y0, x0 + w0, y0 + h0);
+ sraRgnAnd(tmpregion, whole);
+
+ iter = sraRgnGetIterator(tmpregion);
+ while (sraRgnIteratorNext(iter, &rect)) {
+ tx1 = rect.x1;
+ ty1 = rect.y1;
+ tx2 = rect.x2;
+ ty2 = rect.y2;
+
+ win_area += (tx2 - tx1)*(ty2 - ty1);
+ }
+ sraRgnReleaseIterator(iter);
+
+ d_area = wm_area - win_area;
+ sraRgnSubtract(frame, tmpregion);
+ sraRgnDestroy(tmpregion);
+
+if (db) fprintf(stderr, "d_area: %.4f wm_area: %.4f\n", d_area, wm_area);
+
+ if (d_area >= 0.0 && d_area < bdpush * wm_area &&
+ !sraRgnEmpty(frame)) {
+ double dt = 0.0, dm = 0.0;
+ dtime(&dm);
+ iter = sraRgnGetIterator(frame);
+ while (sraRgnIteratorNext(iter, &rect)) {
+ tx1 = rect.x1;
+ ty1 = rect.y1;
+ tx2 = rect.x2;
+ ty2 = rect.y2;
+ direct_fb_copy(tx1, ty1, tx2, ty2, 1);
+ fb_push();
+ dt += dtime(&dm);
+if (db) fprintf(stderr, " BDP(%d,%d-%d,%d) dt: %.4f\n", tx1, ty1, tx2, ty2, dt);
+ }
+ sraRgnReleaseIterator(iter);
+ }
+ sraRgnDestroy(frame);
+ }
+
+ sraRgnDestroy(backfill);
+ sraRgnDestroy(whole);
+ return dret;
+}
+/*
+ * 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;
+ char *dst, *src;
+
+ if (!scaling || rfb_fb == main_fb) {
+ rfbDoCopyRegion(screen, region, dx, dy);
+ return;
+ }
+
+ stride = dpy_x * Bpp;
+
+ iter = sraRgnGetIterator(region);
+ 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_push0(int first_ms, int loop_ms, int loop_max) {
+ int t;
+ fb_update_sent(NULL);
+ rfbPE(1000 * first_ms); /* long select */
+ for (t=0; t<loop_max; t++) {
+ if (fb_update_sent(NULL)) {
+if (1 || debug_wireframe) fprintf(stderr, "FB_UPDATE_SENT: t=%d\n", t);
+ break;
+ }
+ rfbPE(1000 * loop_ms); /* short selects. */
+ }
+}
+
+void get_client_regions(int *req, int *mod, int *cpy) {
+
+ rfbClientIteratorPtr i;
+ rfbClientPtr cl;
+
+ *req = 0;
+ *mod = 0;
+ *cpy = 0;
+
+ i = rfbGetClientIterator(screen);
+ while( (cl = rfbClientIteratorNext(i)) ) {
+ *req += sraRgnCountRects(cl->requestedRegion);
+ *mod += sraRgnCountRects(cl->modifiedRegion);
+ *cpy += sraRgnCountRects(cl->copyRegion);
+ }
+ rfbReleaseClientIterator(i);
+}
+
+void fb_push(void) {
+ char *httpdir = screen->httpDir;
+ int defer = screen->deferUpdateTime;
+ int i, req0, mod0, cpy0, req1, mod1, cpy1;
+ int db = 0;
+//db = 1;
+
+ screen->httpDir = NULL;
+ screen->deferUpdateTime = 0;
+
+ get_client_regions(&req0, &mod0, &cpy0);
+
+ rfbPE(0);
+
+ screen->httpDir = httpdir;
+ screen->deferUpdateTime = defer;
+
+ get_client_regions(&req1, &mod1, &cpy1);
+if (db) fprintf(stderr, "\nFB_push: req: %d/%d mod: %d/%d cpy: %d/%d\n",
+ req0, req1, mod0, mod1, cpy0, cpy1);
+
+ for (i = 0; i < 0; i++) {
+ get_client_regions(&req0, &mod0, &cpy0);
+ rfbCFD(1000);
+ get_client_regions(&req1, &mod1, &cpy1);
+if (db) fprintf(stderr, "-------: req: %d/%d mod: %d/%d cpy: %d/%d\n",
+ req0, req1, mod0, mod1, cpy0, cpy1);
+ }
+}
+
/*
* utility routine for CopyRect of the window (but not CopyRegion)
*/
@@ -19252,6 +21092,541 @@ int crfix(int x, int dx, int Lx) {
return x;
}
+int check_xrecord_keys(void) {
+ double spin = 0.0, tm = 0.0;
+ int gk, gk0, extra_keys = 0, ret = 0;
+ int db = debug_scroll;
+ int get_out = 1, got_one = 0, flush1 = 0, flush2 = 0;
+ static double last_key_scroll = 0.0;
+ static double persist_start = 0.0;
+ double this_scroll, scroll_persist = scr_key_persist;
+ double spin_fac = 1.0, scroll_fac = 2.0;
+ double max_spin, tnow = 0.0;
+
+//db = 1;
+
+ if (got_keyboard_input) {
+ get_out = 0;
+ }
+
+ dtime(&tnow);
+ if (tnow < last_key_scroll + scroll_persist) {
+ get_out = 0;
+ }
+
+ if (get_out) {
+ persist_start = 0.0;
+ xrecord_watch(0);
+ return 0;
+ }
+
+if (db) fprintf(stderr, "xrecord_set_by_mouse: %d\n", xrecord_set_by_mouse);
+if (db) fprintf(stderr, "xrecord_set_by_keys: %d\n", xrecord_set_by_keys);
+
+ max_spin = scr_key_time;
+
+ if (tnow < last_key_scroll + scroll_persist) {
+ max_spin = 1.25*(tnow - last_key_scroll);
+ } else if (xrecord_scroll_keysym(last_keysym)) {
+ spin_fac = scroll_fac;
+ }
+
+ gk = gk0 = got_keyboard_input;
+ dtime(&tm);
+
+if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n",
+ scr_ev_cnt, max_spin);
+
+ while (1) {
+
+ if (scr_ev_cnt) {
+ got_one = 1;
+ this_scroll = 0.0;
+ dtime(&this_scroll);
+ break;
+ }
+
+ X_LOCK;
+ XFlush(dpy);
+ flush1 = 1;
+ X_UNLOCK;
+
+ if (use_threads) {
+ usleep(1000);
+ } else {
+ rfbCFD(1000);
+ }
+ spin += dtime(&tm);
+
+ if (spin >= max_spin * spin_fac) {
+if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin,
+ max_spin * spin_fac);
+ break;
+ }
+
+ if (got_keyboard_input > gk) {
+ gk = got_keyboard_input;
+ if (xrecord_scroll_keysym(last_keysym)) {
+ spin_fac = scroll_fac;
+ }
+ extra_keys++;
+if (db) fprintf(stderr, "check_xrecord: more keys: %d %.3f\n", extra_keys,
+ spin);
+ flush2 = 1;
+ }
+
+ X_LOCK;
+ if (flush2) {
+ XFlush(dpy);
+ }
+ XRecordProcessReplies(rdpy_data);
+ X_UNLOCK;
+ }
+
+if (db) fprintf(stderr, " f1: %d f2: %d spin: %.4f\n", flush1, flush2, spin);
+ /* since we've flushed it, we might as well avoid -input_skip */
+ if (flush1 || flush2) {
+ got_keyboard_input = 0;
+ got_pointer_input = 0;
+ }
+
+ if (scr_ev_cnt) {
+ int dret;
+ double bdpush = 0.0;
+ static double last_border_push = 0.0;
+
+ if (persist_start > 0.0 &&
+ this_scroll > last_border_push + 1.00) {
+ bdpush = 0.0;
+ last_border_push = this_scroll;
+ }
+ dret = push_scr_ev(bdpush);
+ ret = 1 + dret;
+ scr_ev_cnt = 0;
+ }
+
+ if (xrecording) {
+ if (ret < 2) {
+ xrecord_watch(0);
+ }
+ }
+
+ if (this_scroll > 0.0) {
+ last_key_scroll = this_scroll;
+ }
+
+ if (ret == 2) {
+ if (persist_start == 0.0) {
+ dtime(&persist_start);
+ }
+ } else {
+ persist_start = 0.0;
+ }
+
+ return ret;
+}
+
+int check_xrecord_mouse(void) {
+ double spin = 0.0, tm = 0.0;
+ int gp, gp0, ret = 0;
+ int db = debug_scroll;
+ int flush1 = 0, flush2 = 0;
+ int get_out = 1, got_one = 0;
+ static double last_mouse_scroll = 0.0;
+ static double persist_start = 0.0;
+ double this_scroll, scroll_persist = scr_mouse_persist;
+ double spin_fac = 1.0;
+ double max_spin, tnow = 0.0;
+
+//db = 1;
+
+ if (button_mask) {
+ get_out = 0;
+ }
+ dtime(&tnow);
+ if (tnow < last_mouse_scroll + scroll_persist) {
+ get_out = 0;
+ }
+
+ if (get_out) {
+ persist_start = 0.0;
+ xrecord_watch(0);
+ return 0;
+ }
+
+if (db) fprintf(stderr, "xrecord_set_by_mouse: %d\n", xrecord_set_by_mouse);
+if (db) fprintf(stderr, "xrecord_set_by_keys: %d\n", xrecord_set_by_keys);
+
+ max_spin = scr_mouse_time;
+
+ if (tnow < last_mouse_scroll + scroll_persist) {
+ max_spin = 1.25*(tnow - last_mouse_scroll);
+ }
+
+ gp = gp0 = got_pointer_input;
+ dtime(&tm);
+
+if (db) fprintf(stderr, "check_xrecord: LOOP: scr_ev_cnt: %d max: %.3f\n",
+ scr_ev_cnt, max_spin);
+
+ while (1) {
+
+ if (scr_ev_cnt) {
+ got_one = 1;
+ this_scroll = 0.0;
+ dtime(&this_scroll);
+ break;
+ }
+
+ X_LOCK;
+ XFlush(dpy);
+ flush1 = 1;
+ X_UNLOCK;
+
+ if (use_threads) {
+ usleep(1000);
+ } else {
+ rfbCFD(1000);
+ }
+ spin += dtime(&tm);
+
+ if (spin >= max_spin * spin_fac) {
+if (db) fprintf(stderr, "check_xrecord: SPIN-OUT: %.3f/%.3f\n", spin,
+ max_spin * spin_fac);
+ break;
+ }
+ if (got_pointer_input > gp) {
+ gp = got_pointer_input;
+ flush2 = 1;
+ }
+
+ X_LOCK;
+ if (flush2) {
+ XFlush(dpy);
+ }
+ XRecordProcessReplies(rdpy_data);
+ X_UNLOCK;
+ if (! button_mask) {
+ break;
+ }
+ }
+
+if (db) fprintf(stderr, " f1: %d f2: %d\n", flush1, flush2);
+ /* since we've flushed it, we might as well avoid -input_skip */
+ if (flush1 || flush2) {
+ got_keyboard_input = 0;
+ got_pointer_input = 0;
+ }
+
+ if (scr_ev_cnt) {
+ int dret;
+ double bdpush = 0.0;
+ static double last_border_push = 0.0;
+
+ if (persist_start > 0.0 &&
+ this_scroll > last_border_push + 1.00) {
+ bdpush = 0.0;
+ last_border_push = this_scroll;
+ }
+ dret = push_scr_ev(bdpush);
+ if (! button_mask) {
+ dret = 0;
+ }
+ ret = 1 + dret;
+ scr_ev_cnt = 0;
+ }
+
+ if (xrecording) {
+ if (ret < 2) {
+ xrecord_watch(0);
+ }
+ }
+
+ if (this_scroll > 0.0) {
+ last_mouse_scroll = this_scroll;
+ }
+
+ if (ret == 2) {
+ if (persist_start == 0.0) {
+ dtime(&persist_start);
+ }
+ } else {
+ persist_start = 0.0;
+ }
+
+ return ret;
+}
+
+int check_xrecord(void) {
+ int watch_keys = 0, watch_mouse = 0;
+
+ if (! use_xrecord) {
+ return 0;
+ }
+ if (scaling && ! got_scrollcopyrect) {
+ return 0;
+ }
+
+ 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 (watch_mouse && button_mask && xrecord_set_by_mouse) {
+ return check_xrecord_mouse();
+ } else if (watch_keys && xrecord_set_by_keys) {
+ return check_xrecord_keys();
+ } else {
+ 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) {
+
+ static int dt_bad = 0;
+ static time_t dt_bad_check = 0;
+ int x1, y1, x2, y2, sent_copyrect = 0;
+ DB_SET
+
+ *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 (dt_bad) {
+ 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), dx, dpy_x);
+ y2 = crfix(nfix(y+h, dpy_y), dy, dpy_y);
+
+ rect = sraRgnCreateRect(x1, y1, x2, y2);
+ do_copyregion(rect, dx, dy);
+ sraRgnDestroy(rect);
+
+ sent_copyrect = 1;
+ *obscured = 1; /* set to avoid an aggressive push */
+
+ } else if (stack_list) {
+ int k, tx1, tx2, ty1, ty2;
+ sraRegionPtr moved_win, tmp_win;
+ 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);
+ ty2 = nfix(orig_y+h, dpy_y);
+
+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);
+
+ X_LOCK;
+
+ /*
+ * loop over the stack, top to bottom until we
+ * find our wm frame:
+ */
+ for (k = stack_num - 1; k >= 0; k--) {
+ Window swin = stack_list[k];
+ if (swin == frame) {
+if (db2) {
+saw_me = 1;
+fprintf(stderr, " ----------\n");
+} else {
+ break;
+}
+ }
+
+ /* skip some unwanted cases: */
+ if (swin == None) {
+ continue;
+ }
+ if (!valid_window(swin, &attr)) {
+ continue;
+ }
+ if (attr.map_state != IsViewable) {
+ continue;
+ }
+
+ /*
+ * 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);
+ ty2 = nfix(attr.y + attr.height, dpy_y);
+
+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 woul
+ * window rectangle
+ */
+
+ /* 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);
+ ty2 = nfix(attr.y - dy + attr.height, dpy_y);
+
+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 (*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)) {
+if (db2) fprintf(stderr, "do_copyregion: %d %d %d %d dx: %d dy: %d\n", tx1, ty1, tx2, ty2, dx, dy);
+ do_copyregion(shifted_region, dx, dy);
+ 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;
+}
+
/*
* Applied just before any check_user_input() modes. Look for a
* ButtonPress; find window it happened in; find the wm frame window
@@ -19267,7 +21642,7 @@ int crfix(int x, int dx, int Lx) {
* reduces actual XShmGetImage work (i.e. if we correctly predicted how
* the X fb has changed.
*
- * -scale doesn't work under -wirecopyrect, but the wireframe does.
+ * -scale doesn't always work under -wirecopyrect, but the wireframe does.
*
* testing of this mode under -threads is incomplete.
*
@@ -19285,27 +21660,22 @@ int check_wireframe(void) {
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, g_in;
+ int orig_cursor_x, orig_cursor_y, g;
int already_down = 0, win_gone = 0, win_unmapped = 0;
double spin = 0.0, tm = 0.0, last_ptr, last_draw;
int frame_changed = 0, drew_box = 0, got_2nd_pointer = 0;
- int break_reason = 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: */
- int wf_t = wireframe_top;
- int wf_b = wireframe_bot;
- int wf_l = wireframe_left;
- int wf_r = wireframe_right;
double first_event_spin = wireframe_t1;
double frame_changed_spin = wireframe_t2;
double max_spin = wireframe_t3;
double min_draw = wireframe_t4;
-
- int db = 0;
- int db2 = 0;
+ int near_edge;
+ DB_SET
if (subwin) {
return 0; /* don't even bother for -id case */
@@ -19317,7 +21687,7 @@ int check_wireframe(void) {
return 0; /* need ptr input, e.g. button down, motion */
}
-if (db || db2) fprintf(stderr, "\n*** button down!! x: %d y: %d\n", cursor_x, cursor_y);
+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
@@ -19333,10 +21703,6 @@ if (db) fprintf(stderr, "NO get_wm_frame_pos: 0x%lx\n", frame);
X_UNLOCK;
if (db) fprintf(stderr, "a: %d wf: %.3f A: %d\n", w*h, wireframe_frac, (dpy_x*dpy_y));
- if (nabs(x + w - px) < 35) {
- maybe_scrolling = frame; /* not yet used... */
- }
-
/*
* apply the percentage size criterion (allow opaque moves for
* small windows)
@@ -19351,24 +21717,11 @@ if (db) fprintf(stderr, " frame: x: %d y: %d w: %d h: %d px: %d py: %d fr
* see if the pointer is within range of the assumed wm frame
* decorations on the edge of the window.
*/
- if (wf_t || wf_b || wf_l || wf_r) {
- int near_edge = 0;
- 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;
- }
- if (! near_edge) {
-if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r);
- return 0;
- }
+
+ near_edge = near_wm_edge(x, y, w, h, px, py);
+ if (! near_edge) {
+if (db) fprintf(stderr, "INTERIOR\n");
+ return 0;
}
wireframe_in_progress = 1;
@@ -19376,6 +21729,27 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r);
if (button_mask_prev) {
already_down = 1;
}
+
+ if (! wireframe_str || !strcmp(wireframe_str, WIREFRAME_PARMS)) {
+ int latency = get_net_latency();
+ int netrate = get_net_rate();
+ static int didmsg = 0;
+
+ if (latency > 100 || netrate < 10) { /* 100ms, 10KB/sec */
+ /* 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
@@ -19384,6 +21758,7 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r);
*/
if (strcmp(wireframe_copyrect, "never")) {
if (already_down) {
+ double age = 0.0;
/*
* see if we can reuse the stack list (pause
* with button down)
@@ -19396,26 +21771,18 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r);
break;
}
}
- if (! got_me) {
- last_save_stacklist = 0;
+ if (got_me) {
+ age = 1.0;
}
+ snapshot_stack_list(0, age);
}
- if (stack_list && time(0) > last_save_stacklist + 1) {
- /* stack_list too old */
- X_LOCK;
- XFree(stack_list);
- stack_list = NULL;
- stack_num = 0;
- X_UNLOCK;
- }
- } else if (! stack_list) {
- /* ! already_down, might as well get a copy */
- X_LOCK;
- snapshot_stack_list();
- X_UNLOCK;
+ }
+ if (! stack_list) {
+ snapshot_stack_list(0, 0.0);
}
}
+
/* store initial parameters, we look for changes in them */
orig_frame = frame;
orig_px = px; /* pointer position */
@@ -19444,7 +21811,7 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r);
pointer(-1, 0, 0, NULL);
}
- g = g_in = got_pointer_input;
+ g = got_pointer_input;
while (1) {
@@ -19456,28 +21823,37 @@ if (db) fprintf(stderr, "INTERIOR %d %d %d %d\n", wf_t, wf_b, wf_l, wf_r);
if (use_threads) {
usleep(1000);
} else if (drew_box) {
- rfbPE(screen, 1000);
+ rfbPE(1000);
} else {
- rfbCFD(screen, 1000);
+ rfbCFD(1000);
}
spin += dtime(&tm);
/* 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;
}
- /* pointer events slowing down: */
- if (spin > last_ptr + frame_changed_spin) {
+ /* 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) {
+ } else if (got_2nd_pointer) {
/*
* pointer is moving, max time we wait for wm
* move or resize to be detected
@@ -19502,6 +21878,10 @@ if (db) fprintf(stderr, " ++pointer event!! [%02d] dt: %.3f x: %d y: %d mas
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 0
@@ -19622,7 +22002,7 @@ if (db) fprintf(stderr, "FRAME MOVE 1st-dt: %.3f\n", first_dt_ave/n);
! drew_box) {
draw_box(x, y, w, h, 0);
drew_box = 1;
- rfbPE(screen, 1000);
+ rfbPE(1000);
last_draw = spin;
}
}
@@ -19667,15 +22047,19 @@ if (db || db2) fprintf(stderr, "NO button_mask\n");
/*
* see if we can apply CopyRect or CopyRegion to the change:
*/
- if (!scaling && w == orig_w && h == orig_h && (dx != 0 || dy != 0) &&
- !win_gone && !win_unmapped && strcmp(wireframe_copyrect, "never")) {
-
- int x1, y1, x2, y2, t, tmax = 50;
+ if (!strcmp(wireframe_copyrect, "never")) {
+ ;
+ } else if (win_gone || win_unmapped) {
+ ;
+ } else if (scaling && ! got_wirecopyrect) {
+ ;
+ } else if (w != orig_w || h != orig_h) {
+ ;
+ } else if (dx == 0 && dy == 0) {
+ ;
+ } else {
int spin_ms = (int) (spin * 1000 * 1000);
- int sent_copyrect = 0;
- int obscured = 0;
- static int dt_bad = 0;
- static time_t dt_bad_check = 0;
+ int obscured, sent_copyrect = 0;
/*
* set a timescale comparable to the spin time,
@@ -19688,176 +22072,18 @@ if (db || db2) fprintf(stderr, "NO button_mask\n");
}
/* try to flush the wireframe removal: */
- fb_update_sent(NULL);
- rfbPE(screen, spin_ms/3); /* long select */
- for (t=0; t<tmax; t++) {
- if (fb_update_sent(NULL)) {
-if (db2) fprintf(stderr, "A-FB_UPDATE_SENT: t=%d\n", t);
- break;
- }
- rfbPE(screen, 1000); /* short selects. */
- }
-
- /*
- * 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) {
- if (!strcmp(guess_desktop(), "kde")) {
- dt_bad = 1;
- } else if (!strcmp(guess_desktop(), "xfce")) {
- dt_bad = 1;
- } else {
- dt_bad = 0;
- }
- dt_bad_check = time(0);
- }
-
- if (dt_bad) {
- /* 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), dx, dpy_x);
- y2 = crfix(nfix(y+h, dpy_y), dy, dpy_y);
-
- rfbDoCopyRect(screen, x1, y1, x2, y2, dx, dy);
-
- sent_copyrect = 1;
- obscured = 1; /* set to avoid an aggressive push */
-
-if (db2) fprintf(stderr, "CopyRect: x1: %d y1: %d x2: %d y2: %d dx: %d dy: %d spin: %.3f\n", x1, y1, x2, y2, dx, dy, spin);
-
- } else if (stack_list) {
- int k, tx1, tx2, ty1, ty2;
- sraRegionPtr moved_win, tmp_win;
- int saw_me = 0;
-
- tx1 = nfix(orig_x, dpy_x);
- ty1 = nfix(orig_y, dpy_y);
- tx2 = nfix(orig_x+w, dpy_x);
- ty2 = nfix(orig_y+h, dpy_y);
-
-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);
-
- X_LOCK;
-
- /*
- * loop over the stack, top to bottom until we
- * find our wm frame:
- */
- for (k = stack_num - 1; k >= 0; k--) {
- Window win = stack_list[k];
- if (win == frame) {
-if (db2) {
- saw_me = 1;
- fprintf(stderr, " ----------\n");
-} else {
- break;
-}
- }
-
- /* skip some unwanted cases: */
- if (win == None) {
- continue;
- }
- if (!valid_window(win, &attr)) {
- continue;
- }
- if (attr.map_state != IsViewable) {
- continue;
- }
-
- /* clip the window to the visable screen: */
- tx1 = nfix(attr.x, dpy_x);
- ty1 = nfix(attr.y, dpy_y);
- tx2 = nfix(attr.x + attr.width, dpy_x);
- ty2 = nfix(attr.y + attr.height, dpy_y);
-
-if (db2) fprintf(stderr, " tmp_win: %4d %3d, %4d %3d 0x%lx\n", tx1, ty1, tx2, ty2, win);
-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);
- }
- X_UNLOCK;
+ fb_push();
- if (obscured && !strcmp(wireframe_copyrect, "top")) {
- ; /* cannot send CopyRegion */
- } else if (! sraRgnEmpty(moved_win)) {
- sraRectangleIterator *iter;
- sraRegionPtr whole, shifted_region;
- sraRect rect;
-
- /*
- * prepare for CopyRegion, apply dx and
- * dy to each rectangle in the region.
- * keep only the part on the screen.
- */
- whole = sraRgnCreateRect(0, 0, dpy_x, dpy_y);
- shifted_region = sraRgnCreate();
-
-if (db2) fprintf(stderr, "\n");
-
- iter = sraRgnGetIterator(moved_win);
- while (sraRgnIteratorNext(iter, &rect)) {
- tx1 = rect.x1 + dx;
- ty1 = rect.y1 + dy;
- tx2 = rect.x2 + dx;
- ty2 = rect.y2 + dy;
-
-if (db2) fprintf(stderr, " shf_win: %4d %3d, %4d %3d\n", tx1, ty1, tx2, ty2);
-
- tmp_win = sraRgnCreateRect(tx1, ty1,
- tx2, ty2);
- sraRgnAnd(tmp_win, whole);
- if (! sraRgnEmpty(tmp_win)) {
- sraRgnOr(shifted_region,
- tmp_win);
- }
- sraRgnDestroy(tmp_win);
- }
- sraRgnReleaseIterator(iter);
- sraRgnDestroy(whole);
-
- /* now send the CopyRegion: */
- if (! sraRgnEmpty(shifted_region)) {
- rfbDoCopyRegion(screen, shifted_region,
- dx, dy);
- sent_copyrect = 1;
-if (db2) fprintf(stderr, " rfbDoCopyRegion\n");
- }
- sraRgnDestroy(shifted_region);
- }
- sraRgnDestroy(moved_win);
- }
+ /* try to send a clipped copyrect of translation: */
+ sent_copyrect = try_copyrect(frame, x, y, w, h, dx, dy,
+ &obscured);
if (sent_copyrect) {
-
/* try to push the changes to viewers: */
- fb_update_sent(NULL);
- rfbPE(screen, spin_ms/3);
if (! obscured) {
- for (t=0; t<tmax; t++) {
- if (fb_update_sent(NULL)) {
-if (db2) fprintf(stderr, "B-FB_UPDATE_SENT: t=%d\n", t);
- break;
- }
- rfbPE(screen, 1000);
- }
+ fb_push();
+ } else {
+ fb_push();
}
}
}
@@ -19867,8 +22093,7 @@ if (db2) fprintf(stderr, "B-FB_UPDATE_SENT: t=%d\n", t);
if (break_reason == 1 || break_reason == 2) {
/*
* save the stack list, perhaps the user has
- * paused with button down. XXX unstable if
- * windows in the list go away?
+ * paused with button down.
*/
last_save_stacklist = time(0);
} else {
@@ -19881,7 +22106,7 @@ if (db2) fprintf(stderr, "B-FB_UPDATE_SENT: t=%d\n", t);
}
/* final push (for -nowirecopyrect) */
- rfbPE(screen, 1000);
+ rfbPE(1000);
wireframe_in_progress = 0;
return 1;
}
@@ -19934,9 +22159,9 @@ static void check_user_input2(double dt) {
*/
while (1) {
if (show_multiple_cursors) {
- rfbPE(screen, 1000);
+ rfbPE(1000);
} else {
- rfbCFD(screen, 1000);
+ rfbCFD(1000);
}
X_LOCK;
XFlush(dpy);
@@ -19999,9 +22224,9 @@ static void check_user_input2(double dt) {
for (i=0; i<split; i++) {
usleep(ms * 1000);
if (show_multiple_cursors) {
- rfbPE(screen, 1000);
+ rfbPE(1000);
} else {
- rfbCFD(screen, 1000);
+ rfbCFD(1000);
}
spin += dtime(&tm);
if (got_pointer_input > g) {
@@ -20055,9 +22280,9 @@ static void check_user_input3(double dt) {
int mcut = miss_max;
if (show_multiple_cursors) {
- rfbPE(screen, 1000);
+ rfbPE(1000);
} else {
- rfbCFD(screen, 1000);
+ rfbCFD(1000);
}
X_LOCK;
XFlush(dpy);
@@ -20136,9 +22361,9 @@ static void check_user_input3(double dt) {
for (i=0; i<split; i++) {
usleep(ms * 1000);
if (show_multiple_cursors) {
- rfbPE(screen, 1000);
+ rfbPE(1000);
} else {
- rfbCFD(screen, 1000);
+ rfbCFD(1000);
}
spin += dtime(&tm);
if (got_pointer_input > g) {
@@ -20212,7 +22437,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) {
drag_in_progress = 1;
}
- rfbCFD(screen, rfb_wait_ms * 1000);
+ rfbCFD(rfb_wait_ms * 1000);
dtm = dtime(&tm);
spin += dtm;
@@ -20272,7 +22497,7 @@ static void check_user_input4(double dt, double dtr, int tile_diffs) {
if (ginput >= 2) {
/* try for a couple more quick ones */
for (i=0; i<2; i++) {
- rfbCFD(screen, rfb_wait_ms * 1000);
+ rfbCFD(rfb_wait_ms * 1000);
}
}
@@ -20385,7 +22610,7 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) {
}
/* damn, they didn't push our frame! */
iter++;
- rfbPE(screen, rfb_wait_ms * 1000);
+ rfbPE(rfb_wait_ms * 1000);
push_spin += dtime(&tp);
}
@@ -20443,7 +22668,7 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) {
}
/* turn libvncserver crank to process events: */
- rfbCFD(screen, rfb_wait_ms * 1000);
+ rfbCFD(rfb_wait_ms * 1000);
dtm = dtime(&tm);
spin += dtm;
@@ -20507,7 +22732,7 @@ static void check_user_input5(double dt, double dtr, int tile_diffs) {
if (ginput >= 2) {
/* try for a couple more quick ones */
for (i=0; i<2; i++) {
- rfbCFD(screen, rfb_wait_ms * 1000);
+ rfbCFD(rfb_wait_ms * 1000);
}
}
drag_in_progress = 0;
@@ -20517,7 +22742,15 @@ static int check_user_input(double dt, double dtr, int tile_diffs, int *cnt) {
if (raw_fb && ! dpy) return 0; /* raw_fb hack */
- maybe_scrolling = None;
+ if (use_xrecord) {
+ int rc = check_xrecord();
+ if (rc == 1) {
+ return 0;
+ } else if (rc == 2) {
+if (0) fprintf(stderr, " CXR: check_user_input ret 1\n");
+ return 1;
+ }
+ }
if (wireframe) {
if (check_wireframe()) {
@@ -20593,67 +22826,90 @@ double dtime(double *t_old) {
void measure_display_hook(rfbClientPtr cl) {
ClientData *cd = (ClientData *) cl->clientData;
+#if 0
+double tm = 0.0;
+dtime(&tm);
+fprintf(stderr, " MDH: %.4f\n", tm);
+#endif
cd->timer = 0.0;
dtime(&cd->timer);
}
-void measure_send_rates_init(void) {
- int i, bs, rbs;
+int get_rate(int which) {
rfbClientIteratorPtr iter;
rfbClientPtr cl;
-
- screen->displayHook = measure_display_hook;
-
+ int irate, irate_min = 1; /* 1 KB/sec */
+ int irate_max = 100000; /* 100 MB/sec */
+ double slowest = -1.0, rate;
+ static double save_rate = 10000.0; /* 10000.0 B/sec */
+
iter = rfbGetClientIterator(screen);
while( (cl = rfbClientIteratorNext(iter)) ) {
ClientData *cd = (ClientData *) cl->clientData;
- bs = 0;
- for (i=0; i<MAX_ENCODINGS; i++) {
- bs += cl->bytesSent[i];
+
+ if (which == 0) {
+ rate = cd->send_cmp_rate;
+ } else {
+ rate = cd->send_raw_rate;
+ }
+ if (slowest == -1.0 || rate < slowest) {
+ slowest = rate;
}
- rbs = cl->rawBytesEquivalent;
- cd->set_cmp_bytes = bs;
- cd->set_raw_bytes = rbs;
- cd->timer = -1.0;
}
rfbReleaseClientIterator(iter);
+
+ 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;
+ }
+
+ return irate;
}
-int get_rate(int which) {
+int get_latency(void) {
rfbClientIteratorPtr iter;
rfbClientPtr cl;
- int i, samples = RATE_SAMPLES;
- double dslowest = -1.0, dsum;
+ int ilat, ilat_min = 1; /* 1 ms */
+ int ilat_max = 2000; /* 2 sec */
+ double slowest = -1.0, lat;
+ static double save_lat = 0.020; /* 20 ms */
iter = rfbGetClientIterator(screen);
while( (cl = rfbClientIteratorNext(iter)) ) {
ClientData *cd = (ClientData *) cl->clientData;
-
- dsum = 0.0;
- for (i=0; i<samples; i++) {
- if (which == 0) {
- dsum += cd->cmp_samp[i];
- } else {
- dsum += cd->raw_samp[i];
- }
- }
- dsum = dsum / samples;
- if (dsum > dslowest) {
- dslowest = dsum;
- }
+ lat = cd->latency;
+ if (slowest == -1.0 || lat > slowest) {
+ slowest = lat;
+ }
}
rfbReleaseClientIterator(iter);
- if (dslowest < 0.0) {
- if (which == 0) {
- dslowest = 5000.0;
- } else {
- dslowest = 50000.0;
- }
+ 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 (int) dslowest;
+
+ return ilat;
}
int get_cmp_rate(void) {
@@ -20665,24 +22921,26 @@ int get_raw_rate(void) {
}
void initialize_speeds(void) {
- char *s, *p;
+ 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') {
- return;
+ s_in = strdup("");
+ } else {
+ s_in = strdup(speeds_str);
}
- if (!strcmp(speeds_str, "modem")) {
+ if (!strcmp(s_in, "modem")) {
s = strdup("6,4,200");
- } else if (!strcmp(speeds_str, "dsl")) {
+ } else if (!strcmp(s_in, "dsl")) {
s = strdup("6,100,50");
- } else if (!strcmp(speeds_str, "lan")) {
+ } else if (!strcmp(s_in, "lan")) {
s = strdup("6,5000,1");
} else {
- s = strdup(speeds_str);
+ s = strdup(s_in);
}
p = strtok(s, ",");
@@ -20703,12 +22961,40 @@ void initialize_speeds(void) {
p = strtok(NULL, ",");
}
free(s);
+ free(s_in);
+
+ if (! speeds_read_rate) {
+ int n = 0;
+ double dt, timer = 0.0;
+ dtime(&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;
}
@@ -20716,6 +23002,12 @@ int get_net_rate(void) {
if (speeds_net_rate) {
return speeds_net_rate;
}
+ if (! speeds_net_rate_measured) {
+ speeds_net_rate_measured = get_cmp_rate();
+ }
+ if (speeds_net_rate_measured) {
+ return speeds_net_rate_measured;
+ }
return 0;
}
@@ -20723,143 +23015,319 @@ int get_net_latency(void) {
if (speeds_net_latency) {
return speeds_net_latency;
}
+ if (! speeds_net_latency_measured) {
+ speeds_net_latency_measured = get_latency();
+ }
+ if (speeds_net_latency_measured) {
+ return speeds_net_latency_measured;
+ }
return 0;
}
void measure_send_rates(int init) {
- int i, j, nclient = 0;
- int min_width = 200;
- double dt, cmp_rate, raw_rate;
- rfbClientPtr id[100];
- double dts[100], dts_sorted[100], dtmp;
- int sorted[100], did[100], best;
+ 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;
if (! measure_speeds) {
return;
}
- if (init) {
- measure_send_rates_init();
+ if (speeds_net_rate && speeds_net_latency) {
return;
}
+ if (! orig_display_hook) {
+ orig_display_hook = screen->displayHook;
+ }
+
+ if (start == 0.0) {
+ dtime(&start);
+ }
+ now = 0.0;
+ dtime(&now);
+ now = now - start;
+
+ nclients = 0;
+
iter = rfbGetClientIterator(screen);
while( (cl = rfbClientIteratorNext(iter)) ) {
- double tmp2;
+ 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;
- tmp2 = 0.0;
- dtime(&tmp2);
-if (init) {
- continue;
-}
- if (cd->timer <= 0.0) {
+
+ if (cd->send_cmp_rate > 0.0) {
continue;
}
- dt = dtime(&cd->timer);
- cd->timer = dt;
- if (nclient < 100) {
- id[nclient] = cl;
- dts[nclient] = dt;
- nclient++;
+ nclients++;
+
+ cbs = 0;
+ for (i=0; i<MAX_ENCODINGS; i++) {
+ cbs += cl->bytesSent[i];
}
- }
- rfbReleaseClientIterator(iter);
-if (init) {
- return;
-}
+ rbs = cl->rawBytesEquivalent;
- for (i=0; i<nclient; i++) {
- did[i] = 0;
- }
- for (i=0; i<nclient; i++) {
- dtmp = -1.0;
- best = -1;
- for (j=0; j<nclient; j++) {
- if (did[j]) {
- continue;
- }
- if (dts[j] > dtmp) {
- best = j;
- dtmp = dts[j];
- }
- }
- did[best] = 1;
- sorted[i] = best;
- dts_sorted[i] = dts[best];
- }
-
+ if (init) {
+ double tmr = 0.0;
- iter = rfbGetClientIterator(screen);
- while( (cl = rfbClientIteratorNext(iter)) ) {
- int db, dbr, cbs, rbs;
- ClientData *cd = (ClientData *) cl->clientData;
+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);
- dt = cd->timer;
- if (dt <= 0.0) {
+ dtime(&tmr);
+ cd->timer = tmr;
+ cd->cmp_bytes_sent = cbs;
+ cd->raw_bytes_sent = rbs;
continue;
}
- if (nclient > 1) {
- for (i=0; i<nclient; i++) {
- if (cl != id[i]) {
- 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);
+
+ tm = 0.0;
+ dtime(&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;
+ tm = 0.0;
+ dtime(&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;
+ }
}
- for (j=0; j<nclient; j++) {
- if (sorted[j] == i) {
- if (j < nclient - 1) {
- dt -= dts_sorted[j+1];
+ }
+
+ 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];
}
}
- break;
}
}
- if (dt <= 0.0) {
- continue;
+
+ 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;
}
- cbs = 0;
- for (i=0; i<MAX_ENCODINGS; i++) {
- cbs += cl->bytesSent[i];
+ 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;
}
- rbs = cl->rawBytesEquivalent;
- db = cbs - cd->set_cmp_bytes;
- dbr = rbs - cd->set_raw_bytes;
- cmp_rate = db/dt;
- raw_rate = dbr/dt;
- if (dbr > min_width * min_width * bpp/8) {
- cd->sample++;
- if (cd->sample >= RATE_SAMPLES) {
- cd->sample = 0;
- }
- i = cd->sample;
- cd->cmp_samp[i] = cmp_rate;
- cd->raw_samp[i] = raw_rate;
+ 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);
}
rfbReleaseClientIterator(iter);
+
+ if (init) {
+ if (nclients) {
+ screen->displayHook = measure_display_hook;
+ }
+ } else {
+ screen->displayHook = orig_display_hook;
+ }
}
/*
* utility wrapper to call rfbProcessEvents
* checks that we are not in threaded mode.
*/
-void rfbPE(rfbScreenInfoPtr scr, long usec) {
- if (! scr) {
+void rfbPE(long usec) {
+ if (! screen) {
return;
}
if (! use_threads) {
- rfbProcessEvents(scr, usec);
+ rfbProcessEvents(screen, usec);
}
}
-void rfbCFD(rfbScreenInfoPtr scr, long usec) {
- if (! scr) {
+void rfbCFD(long usec) {
+ if (! screen) {
return;
}
if (! use_threads) {
- rfbCheckFds(scr, usec);
+ rfbCheckFds(screen, usec);
}
}
@@ -20880,11 +23348,14 @@ static void watch_loop(void) {
got_user_input = 0;
got_pointer_input = 0;
got_keyboard_input = 0;
+ urgent_update = 0;
if (! use_threads) {
double tm = 0.0;
dtime(&tm);
- rfbPE(screen, -1);
+ measure_send_rates(1);
+ rfbPE(-1);
+ measure_send_rates(0);
dtr = dtime(&tm);
fb_update_sent(NULL);
@@ -20895,6 +23366,7 @@ static void watch_loop(void) {
if (screen && screen->clientHead &&
check_user_input(dt, dtr, tile_diffs, &cnt)) {
/* true means loop back for more input */
+if (0) fprintf(stderr, "watch_loop: LOOP-BACK\n");
continue;
}
} else if (use_threads && wireframe && button_mask) {
@@ -20905,23 +23377,25 @@ static void watch_loop(void) {
clean_up_exit(0);
}
- if (do_copy_screen) {
- do_copy_screen = 0;
- copy_screen();
- }
+ if (! urgent_update) {
+ if (do_copy_screen) {
+ do_copy_screen = 0;
+ copy_screen();
+ }
- check_new_clients();
- check_xevents();
- check_connect_inputs();
- check_padded_fb();
- check_xdamage_state();
- if (started_as_root) {
- check_switched_user();
- }
+ check_new_clients();
+ check_xevents();
+ check_connect_inputs();
+ check_padded_fb();
+ check_xdamage_state();
+ if (started_as_root) {
+ check_switched_user();
+ }
- if (first_conn_timeout < 0) {
- start = time(0);
- first_conn_timeout = -first_conn_timeout;
+ if (first_conn_timeout < 0) {
+ start = time(0);
+ first_conn_timeout = -first_conn_timeout;
+ }
}
if (! screen || ! screen->clientHead) {
@@ -20962,7 +23436,6 @@ static void watch_loop(void) {
double tm = 0.0;
dtime(&tm);
- RFBUNDRAWCURSOR(screen);
if (use_snapfb) {
int t, tries = 5;
copy_snap();
@@ -20973,6 +23446,9 @@ static void watch_loop(void) {
tile_diffs = scan_for_updates(0);
}
dt = dtime(&tm);
+if (0 && tile_diffs > 4) {
+fprintf(stderr, "TILES: %d dt: %.4f t: %.4f\n", tile_diffs, dt, tm);
+}
check_x11_pointer();
}
@@ -21701,7 +24177,10 @@ static void print_help(int mode) {
" 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. Default: %s\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"
@@ -21739,16 +24218,67 @@ static void print_help(int mode) {
" link this might be a better choice: 0.25+0.6+6.0+0.15\n"
"\n"
"-wirecopyrect mode Since the -wireframe mechanism evidently tracks moving\n"
-"-nowirecopyrect windows, a speedup can be obtained by telling the VNC\n"
-" viewers to locally copy the translated window region.\n"
-" This is the VNC CopyRect encoding: the framebuffer\n"
-" update doesn't need to send the actual new image data.\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"
+" in, but helps on a slow link). Default: \"%s\"\n"
+"\n"
+" Note: there can be painting errors when using -scale\n"
+" so CopyRect is skipped when scaling unless you specify\n"
+" -wirecopyrect on the command line or by remote-control.\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.\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,\n"
+" it may induce undesired transients (e.g. a terminal\n"
+" cursor being scrolled up when it should not be) or other\n"
+" painting errors. These are automatically repaired in a\n"
+" short period of time. If this is unacceptable disable\n"
+" the feature with -noscrollcopyrect.\n"
+"\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 when using\n"
+" -scale so CopyRect is skipped when scaling unless\n"
+" you specify -scrollcopyrect on the command line or\n"
+" by remote-control.\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"
"-pointer_mode n Various pointer motion update schemes. \"-pm\" is\n"
" an alias. The problem is pointer motion can cause\n"
@@ -22204,11 +24734,14 @@ static void print_help(int mode) {
" buttonmap:str set -buttonmap \"str\", empty to disable\n"
" dragging disable -nodragging mode.\n"
" nodragging enable -nodragging mode.\n"
-" wireframe enable -wireframe mode.\n"
-" nowireframe disable -wireframe 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.\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"
" 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"
@@ -22251,6 +24784,15 @@ static void print_help(int mode) {
" 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"
+"\n"
" noremote disable the -remote command processing,\n"
" it cannot be turned back on.\n"
"\n"
@@ -22307,13 +24849,14 @@ static void print_help(int mode) {
" add_keysyms noadd_keysyms clear_mods noclear_mods\n"
" clear_keys noclear_keys remap repeat norepeat\n"
" fb nofb bell nobell sel nosel primary noprimary\n"
-" cursorshape nocursorshape cursorpos nocursorpos\n"
-" cursor show_cursor noshow_cursor nocursor arrow\n"
-" xfixes noxfixes xdamage noxdamage xd_area xd_mem\n"
-" alphacut alphafrac alpharemove noalpharemove alphablend\n"
-" noalphablend xwarp xwarppointer noxwarp noxwarppointer\n"
-" buttonmap dragging nodragging wireframe_mode\n"
-" wireframe nowireframe wirecopyrect nowirecopyrect\n"
+" cursorshape nocursorshape cursorpos nocursorpos cursor\n"
+" show_cursor noshow_cursor nocursor arrow xfixes noxfixes\n"
+" xdamage noxdamage xd_area xd_mem alphacut alphafrac\n"
+" alpharemove noalpharemove alphablend noalphablend\n"
+" xwarp xwarppointer noxwarp noxwarppointer buttonmap\n"
+" dragging nodragging wireframe_mode wireframe wf\n"
+" nowireframe nowf wirecopyrect wcr nowirecopyrect nowcr\n"
+" scr_area scrollcopyrect scr noscrollcopyrect noscr\n"
" pointer_mode pm input_skip input client_input speeds\n"
" debug_pointer dp nodebug_pointer nodp debug_keyboard dk\n"
" nodebug_keyboard nodk deferupdate defer wait rfbwait\n"
@@ -22323,19 +24866,23 @@ static void print_help(int mode) {
" noalwaysshared nevershared noalwaysshared dontdisconnect\n"
" nodontdisconnect desktop noremote\n"
"\n"
-" aro= debug_xevents debug_xdamage display vncdisplay\n"
-" desktopname http_url auth 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\n"
-" nocmds passwdfile using_shm logfile o flag rc norc h\n"
-" help V version lastmod bg sigpipe threads pipeinput\n"
-" clients client_count pid ext_xtest ext_xtrap ext_xkb\n"
-" ext_xshm ext_xinerama ext_overlay ext_xfixes ext_xdamage\n"
-" ext_xrandr rootwin num_buttons button_mask mouse_x\n"
-" mouse_y bpp depth indexed_color dpy_x dpy_y wdpy_x\n"
-" wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y rfbauth\n"
-" passwd\n"
+" aro= debug_xevents nodebug_xevents debug_xevents\n"
+" debug_xdamage nodebug_xdamage debug_xdamage\n"
+" debug_wireframe nodebug_wireframe debug_wireframe\n"
+" debug_scroll nodebug_scroll debug_scroll display\n"
+" vncdisplay desktopname http_url auth users rootshift\n"
+" clipshift scale_str scaled_x scaled_y scale_numer\n"
+" scale_denom scale_fac scaling_blend scaling_nomult4\n"
+" scaling_pad scaling_interpolate inetd privremote\n"
+" unsafe safer nocmds passwdfile using_shm logfile\n"
+" o flag rc norc h help V version lastmod bg sigpipe\n"
+" threads readrate netrate netlatency pipeinput clients\n"
+" client_count pid ext_xtest ext_xtrap ext_xrecord\n"
+" ext_xkb ext_xshm ext_xinerama ext_overlay ext_xfixes\n"
+" ext_xdamage ext_xrandr rootwin num_buttons button_mask\n"
+" mouse_x mouse_y bpp depth indexed_color dpy_x dpy_y\n"
+" wdpy_x wdpy_y off_x off_y cdpy_x cdpy_y coff_x coff_y\n"
+" rfbauth passwd\n"
"\n"
"-sync By default -remote commands are run asynchronously, that\n"
" is, the request is posted and the program immediately\n"
@@ -22357,6 +24904,8 @@ static void print_help(int mode) {
" 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"
@@ -22386,10 +24935,10 @@ static void print_help(int mode) {
"-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 only allow remote-control\n"
+"-privremote Perform some sanity checks and disable remote-control\n"
" commands if it appears that the X DISPLAY and/or\n"
-" connectfile cannot be accessed by other users. (not\n"
-" complete, does not check for empty access control list)\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"
@@ -22441,6 +24990,8 @@ static void print_help(int mode) {
wireframe ? "-wireframe":"-nowireframe",
WIREFRAME_PARMS,
wireframe_copyrect_default,
+ scroll_copyrect_default,
+ scrollcopyrect_min_area,
pointer_mode_max, pointer_mode,
ui_skip,
defer_update,
@@ -22453,6 +25004,7 @@ static void print_help(int mode) {
gaps_fill,
grow_fill,
tile_fuzz,
+ accept_remote_cmds ? "-yesremote":"-noremote",
""
);
@@ -23134,6 +25686,8 @@ int main(int argc, char* argv[]) {
} else if (!strcmp(arg, "-arrow")) {
CHECK_ARGC
alt_arrow = atoi(argv[++i]);
+ } else if (!strcmp(arg, "-xfixes")) {
+ use_xfixes = 1;
} else if (!strcmp(arg, "-noxfixes")) {
use_xfixes = 0;
} else if (!strcmp(arg, "-alphacut")) {
@@ -23160,7 +25714,8 @@ int main(int argc, char* argv[]) {
pointer_remap = strdup(argv[++i]);
} else if (!strcmp(arg, "-nodragging")) {
show_dragging = 0;
- } else if (!strcmp(arg, "-wireframe")) {
+ } else if (!strcmp(arg, "-wireframe")
+ || !strcmp(arg, "-wf")) {
wireframe = 1;
if (i < argc-1) {
char *s = argv[i+1];
@@ -23168,13 +25723,32 @@ int main(int argc, char* argv[]) {
wireframe_str = strdup(argv[++i]);
}
}
- } else if (!strcmp(arg, "-nowireframe")) {
+ } else if (!strcmp(arg, "-nowireframe")
+ || !strcmp(arg, "-nowf")) {
wireframe = 0;
- } else if (!strcmp(arg, "-wirecopyrect")) {
+ } else if (!strcmp(arg, "-wirecopyrect")
+ || !strcmp(arg, "-wcr")) {
CHECK_ARGC
set_wirecopyrect_mode(argv[++i]);
- } else if (!strcmp(arg, "-nowirecopyrect")) {
+ got_wirecopyrect = 1;
+ } else if (!strcmp(arg, "-nowirecopyrect")
+ || !strcmp(arg, "-nowf")) {
set_wirecopyrect_mode("never");
+ } else if (!strcmp(arg, "-scrollcopyrect")
+ || !strcmp(arg, "-scr")) {
+ CHECK_ARGC
+ set_scrollcopyrect_mode(argv[++i]);
+ got_scrollcopyrect = 1;
+ } else if (!strcmp(arg, "-noscrollcopyrect")
+ || !strcmp(arg, "-noscr")) {
+ set_scrollcopyrect_mode("never");
+ } else if (!strcmp(arg, "-scr_area")) {
+ int tn;
+ CHECK_ARGC
+ tn = atoi(argv[++i]);
+ if (tn >= 0) {
+ scrollcopyrect_min_area = tn;
+ }
} else if (!strcmp(arg, "-pointer_mode")
|| !strcmp(arg, "-pm")) {
char *p, *s;
@@ -23219,6 +25793,8 @@ int main(int argc, char* argv[]) {
} else if (!strcmp(arg, "-sb")) {
CHECK_ARGC
screen_blank = atoi(argv[++i]);
+ } else if (!strcmp(arg, "-xdamage")) {
+ use_xdamage = 1;
} else if (!strcmp(arg, "-noxdamage")) {
use_xdamage = 0;
} else if (!strcmp(arg, "-xd_area")) {
@@ -23280,7 +25856,7 @@ int main(int argc, char* argv[]) {
}
}
} else if (!strcmp(arg, "-remote") || !strcmp(arg, "-R")
- || !strcmp(arg, "-r")) {
+ || !strcmp(arg, "-r") || !strcmp(arg, "-remote-control")) {
CHECK_ARGC
i++;
if (!strcmp(argv[i], "ping")) {
@@ -23299,6 +25875,8 @@ int main(int argc, char* argv[]) {
remote_sync = 1;
} else if (!strcmp(arg, "-noremote")) {
accept_remote_cmds = 0;
+ } else if (!strcmp(arg, "-yesremote")) {
+ accept_remote_cmds = 1;
} else if (!strcmp(arg, "-unsafe")) {
safe_remote_only = 0;
} else if (!strcmp(arg, "-privremote")) {
@@ -23562,6 +26140,9 @@ int main(int argc, char* argv[]) {
if (! wireframe_copyrect) {
set_wirecopyrect_mode(NULL);
}
+ if (! scroll_copyrect) {
+ set_scrollcopyrect_mode(NULL);
+ }
/* increase rfbwait if threaded */
if (use_threads && ! got_rfbwait) {
@@ -23852,6 +26433,14 @@ int main(int argc, char* argv[]) {
exit(rc);
}
+ if (priv_remote) {
+ if (! remote_control_access_ok()) {
+ rfbLog("** Disabling remote commands in -privremote "
+ "mode.\n");
+ accept_remote_cmds = 0;
+ }
+ }
+
#if LIBVNCSERVER_HAVE_LIBXFIXES
if (! XFixesQueryExtension(dpy, &xfixes_base_event_type, &er)) {
if (! quiet) {
@@ -24016,7 +26605,16 @@ int main(int argc, char* argv[]) {
* input is not processed) we tell the server to process our
* requests during all grabs:
*/
- disable_grabserver();
+ disable_grabserver(dpy);
+
+ /* check for RECORD */
+ if (! XRecordQueryVersion_wr(dpy, &maj, &min)) {
+ xrecord_present = 0;
+ } else {
+ xrecord_present = 1;
+ }
+
+ initialize_xrecord();
/* check for OS with small shm limits */
if (using_shm && ! single_copytile) {
@@ -24175,3 +26773,4 @@ int main(int argc, char* argv[]) {
#undef argv
}
+