Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
L
libvncserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
rasky
libvncserver
Commits
fba6f58a
Commit
fba6f58a
authored
Mar 12, 2005
by
runge
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
x11vnc: X DAMAGE support, -clip WxH+X+Y, identd.
parent
4ff783f5
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1700 additions
and
718 deletions
+1700
-718
ChangeLog
ChangeLog
+3
-0
ChangeLog
x11vnc/ChangeLog
+11
-0
README
x11vnc/README
+649
-521
tkx11vnc
x11vnc/tkx11vnc
+24
-17
tkx11vnc.h
x11vnc/tkx11vnc.h
+24
-17
x11vnc.1
x11vnc/x11vnc.1
+72
-14
x11vnc.c
x11vnc/x11vnc.c
+917
-149
No files found.
ChangeLog
View file @
fba6f58a
2005-03-12 Karl Runge <runge@karlrunge.com>
* x11vnc: X DAMAGE support, -clip WxH+X+Y, identd.
2005-03-05 Karl Runge <runge@karlrunge.com>
* autoconf: rpm -> rpmbuild and echo -n -> printf
...
...
x11vnc/ChangeLog
View file @
fba6f58a
2005-03-12 Karl Runge <runge@karlrunge.com>
* support for the X DAMAGE extension to receive damage
rectangle reports from the X server. On by default, disable
with -noxdamage. Currently only trusts small rects
(but see -xd_area n) and uses the rest as "hints" for the
scanline polling.
* -clip WxH+X+Y to show a clipped sub-region of the screen.
* use RFC 1413 (identd) to attach a name to a client in
friendly environments.
* fix XAUTHORITY wrt '-auth ... -gui other:0'.
2005-03-04 Karl Runge <runge@karlrunge.com>
* add changes to couple with -listen option, in particular
the behavior of -localhost and remote control cmds.
...
...
x11vnc/README
View file @
fba6f58a
This source diff could not be displayed because it is too large. You can
view the blob
instead.
x11vnc/tkx11vnc
View file @
fba6f58a
...
...
@@ -115,6 +115,7 @@ Screen
flashcmap
notruecolor
--
clip:
=P blackout:
xinerama
--
...
...
@@ -139,12 +140,16 @@ Keyboard
Pointer
=D-C:none,arrow,X,some,most cursor:
noxfixes
noalphablend
--
cursorpos
=D nocursorshape
--
noxfixes
noalphablend
alphacut:
alphafrac:
alpharemove
--
buttonmap:
--
xwarppointer
...
...
@@ -220,10 +225,6 @@ Tuning
noshm
flipbyteorder
onetile
--
alphacut:
alphafrac:
alpharemove
-- D
speeds:
=D wait:
...
...
@@ -237,6 +238,11 @@ Tuning
fuzz:
snapfb
--
xdamage
xd_mem:
xd_area:
xd_width:
--
threads
--
progressive:
...
...
@@ -2042,17 +2048,18 @@ proc client_dialog {client} {
global menu_var text_area cleanup_window item_bool
append_text
"
\n
Client info string:
$client
\n\n
"
if
{[
regexp
{
^
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
$}
\
$client
m0 m1 m2 m3 m4 m5 m6]
}
{
# id:ip:port:hostname:input:loginvo
if
{[
regexp
{
^
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
$}
\
$client
m0 m1 m2 m3 m4 m5 m6
m7
]
}
{
# id:ip:port:
user:
hostname:input:loginvo
set
cid
$m1
set
ip
$m2
set
port
$m3
set
host
$m4
set
user
$m4
set
host
$m5
regsub
{
\.
.
*
$}
$host
""
host
set
input
$m
5
set
logvo
$m
6
append_text
"Host:
$host
, Port:
$port
, IP:
$ip
, Id:
$cid
\n
"
set
input
$m
6
set
logvo
$m
7
append_text
"Host:
$host
, Port:
$port
,
User:
$user
,
IP:
$ip
, Id:
$cid
\n
"
append_text
" - originally logged in as: "
if
{
$logvo
==
"1"
}
{
append_text
"View-Only Client
\n
"
...
...
@@ -2184,10 +2191,10 @@ proc update_clients_menu {list} {
$subm
add separator
set
count 0
foreach client
[
split
$list
","
]
{
if
{[
regexp
{
^
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
$}
\
$client
m0 m1 m2 m3 m4 m5 m6]
}
{
# id:ip:port:hostname:input:loginvo
set
host
$m
4
if
{[
regexp
{
^
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
:
(
.
*
)
$}
\
$client
m0 m1 m2 m3 m4 m5 m6
m7
]
}
{
# id:ip:port:
user:
hostname:input:loginvo
set
host
$m
5
regsub
{
\.
.
*
$}
$host
""
host
set
clabel
"
$host
$m1
"
}
else
{
...
...
x11vnc/tkx11vnc.h
View file @
fba6f58a
...
...
@@ -121,6 +121,7 @@
" flashcmap
\n
"
" notruecolor
\n
"
" --
\n
"
" clip:
\n
"
" =P blackout:
\n
"
" xinerama
\n
"
" --
\n
"
...
...
@@ -145,12 +146,16 @@
"
\n
"
"Pointer
\n
"
" =D-C:none,arrow,X,some,most cursor:
\n
"
" noxfixes
\n
"
" noalphablend
\n
"
" --
\n
"
" cursorpos
\n
"
" =D nocursorshape
\n
"
" --
\n
"
" noxfixes
\n
"
" noalphablend
\n
"
" alphacut:
\n
"
" alphafrac:
\n
"
" alpharemove
\n
"
" --
\n
"
" buttonmap:
\n
"
" --
\n
"
" xwarppointer
\n
"
...
...
@@ -226,10 +231,6 @@
" noshm
\n
"
" flipbyteorder
\n
"
" onetile
\n
"
" --
\n
"
" alphacut:
\n
"
" alphafrac:
\n
"
" alpharemove
\n
"
" -- D
\n
"
" speeds:
\n
"
" =D wait:
\n
"
...
...
@@ -243,6 +244,11 @@
" fuzz:
\n
"
" snapfb
\n
"
" --
\n
"
" xdamage
\n
"
" xd_mem:
\n
"
" xd_area:
\n
"
" xd_width:
\n
"
" --
\n
"
" threads
\n
"
" --
\n
"
" progressive:
\n
"
...
...
@@ -2048,17 +2054,18 @@
" global menu_var text_area cleanup_window item_bool
\n
"
"
\n
"
" append_text
\"\\
nClient info string: $client
\\
n
\\
n
\"\n
"
" if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)$}
\\\n
"
" $client m0 m1 m2 m3 m4 m5 m6]} {
\n
"
" # id:ip:port:hostname:input:loginvo
\n
"
" if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)
:(.*)
$}
\\\n
"
" $client m0 m1 m2 m3 m4 m5 m6
m7
]} {
\n
"
" # id:ip:port:
user:
hostname:input:loginvo
\n
"
" set cid $m1
\n
"
" set ip $m2
\n
"
" set port $m3
\n
"
" set host $m4
\n
"
" set user $m4
\n
"
" set host $m5
\n
"
" regsub {
\\
..*$} $host
\"\"
host
\n
"
" set input $m
5
\n
"
" set logvo $m
6
\n
"
" append_text
\"
Host: $host, Port: $port, IP: $ip, Id: $cid
\\
n
\"\n
"
" set input $m
6
\n
"
" set logvo $m
7
\n
"
" append_text
\"
Host: $host, Port: $port,
User: $user,
IP: $ip, Id: $cid
\\
n
\"\n
"
" append_text
\"
- originally logged in as:
\"\n
"
" if {$logvo ==
\"
1
\"
} {
\n
"
" append_text
\"
View-Only Client
\\
n
\"\n
"
...
...
@@ -2190,10 +2197,10 @@
" $subm add separator
\n
"
" set count 0
\n
"
" foreach client [split $list
\"
,
\"
] {
\n
"
" if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)$}
\\\n
"
" $client m0 m1 m2 m3 m4 m5 m6]} {
\n
"
" # id:ip:port:hostname:input:loginvo
\n
"
" set host $m
4
\n
"
" if {[regexp {^(.*):(.*):(.*):(.*):(.*):(.*)
:(.*)
$}
\\\n
"
" $client m0 m1 m2 m3 m4 m5 m6
m7
]} {
\n
"
" # id:ip:port:
user:
hostname:input:loginvo
\n
"
" set host $m
5
\n
"
" regsub {
\\
..*$} $host
\"\"
host
\n
"
" set clabel
\"
$host $m1
\"\n
"
" } else {
\n
"
...
...
x11vnc/x11vnc.1
View file @
fba6f58a
...
...
@@ -2,7 +2,7 @@
.TH X11VNC "1" "March 2005" "x11vnc " "User Commands"
.SH NAME
x11vnc - allow VNC connections to real X11 displays
version: 0.7.2pre, lastmod: 2005-03-
04
version: 0.7.2pre, lastmod: 2005-03-
12
.SH SYNOPSIS
.B x11vnc
[OPTION]...
...
...
@@ -89,6 +89,15 @@ shifts a root view to it: this shows SaveUnders menus,
etc, although they will be clipped if they extend beyond
the window.
.PP
\fB-clip\fR \fIWxH+X+Y\fR
.IP
Only show the sub-region of the full display that
corresponds to the rectangle with size WxH and offset
+X+Y. The VNC display has size WxH (i.e. smaller than
the full display). This also works for \fB-id/-sid\fR mode
where the offset is relative to the upper left corner
of the selected window.
.PP
\fB-flashcmap\fR
.IP
In 8bpp indexed color, let the installed colormap flash
...
...
@@ -994,6 +1003,43 @@ Time in seconds after NO activity (e.g. screen blank)
to really throttle down the screen polls (i.e. sleep
for about 1.5 secs). Use 0 to disable. Default: 60
.PP
\fB-noxdamage\fR
.IP
Do not use the X DAMAGE extension to detect framebuffer
changes even if it is available.
.IP
x11vnc's use of the DAMAGE extension: 1) significantly
reduces the load when the screen is not changing much,
and 2) detects changed areas (small ones by default)
more quickly.
.IP
Currently the DAMAGE extension is overly conservative
and often reports large areas (e.g. a whole terminal
or browser window) as damaged even though the actual
changed region is much smaller (sometimes just a few
pixels). So heuristics were introduced to skip large
areas and use the damage rectangles only as "hints"
for the traditional scanline polling. The following
tuning parameters are introduced to adjust this
behavior:
.PP
\fB-xd_area\fR \fIA\fR
.IP
Set the largest DAMAGE rectangle area \fIA\fR (in
pixels: width * height) to trust as truly damaged:
the rectangle will be copied from the framebuffer
(slow) no matter what. Set to zero to trust *all*
rectangles. Default: 20000
.PP
\fB-xd_mem\fR \fIf\fR
.IP
Set how long DAMAGE rectangles should be "remembered",
\fIf\fR is a floating point number and is in units of the
scanline repeat cycle time (32 iterations). The default
(1.0) should give no painting problems. Increase it if
there are problems or decrease it to live on the edge
(perhaps useful on a slow machine).
.PP
\fB-sigpipe\fR \fIstring\fR
.IP
Broken pipe (SIGPIPE) handling. \fIstring\fR can be
...
...
@@ -1149,6 +1195,8 @@ id:windowid set \fB-id\fR window to "windowid". empty
.IP
sid:windowid set \fB-sid\fR window to "windowid"
.IP
clip:WxH+X+Y set \fB-clip\fR mode to "WxH+X+Y"
.IP
flashcmap enable \fB-flashcmap\fR mode.
.IP
noflashcmap disable \fB-flashcmap\fR mode.
...
...
@@ -1387,6 +1435,14 @@ nonap disable \fB-nap\fR mode.
.IP
sb:n set \fB-sb\fR to n s, same as screen_blank:n
.IP
xdamage enable xdamage polling hints.
.IP
noxdamage disable xdamage polling hints.
.IP
xd_area:A set \fB-xd_area\fR max pixel area to "A"
.IP
xd_mem:f set \fB-xd_mem\fR remembrance to "f"
.IP
fs:frac set \fB-fs\fR fraction to "frac", e.g. 0.5
.IP
gaps:n set \fB-gaps\fR to n.
...
...
@@ -1491,7 +1547,7 @@ the returned value corresponds to (hint: the ext_*
variables correspond to the presence of X extensions):
.IP
ans= stop quit exit shutdown ping blacken zero
refresh reset close disconnect id sid waitmapped
refresh reset close disconnect id sid
clip
waitmapped
nowaitmapped flashcmap noflashcmap truecolor notruecolor
overlay nooverlay overlay_cursor overlay_yescursor
nooverlay_nocursor nooverlay_cursor nooverlay_yescursor
...
...
@@ -1507,9 +1563,10 @@ 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 xfixes noxfixes alphacut alphafrac alpharemove
noalpharemove alphablend noalphablend xwarp xwarppointer
noxwarp noxwarppointer buttonmap dragging nodragging
nocursor xfixes noxfixes xdamage noxdamage xd_area
xd_mem alphacut alphafrac alpharemove noalpharemove
alphablend noalphablend xwarp xwarppointer noxwarp
noxwarppointer buttonmap dragging nodragging
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
...
...
@@ -1520,15 +1577,16 @@ noalwaysshared nevershared noalwaysshared dontdisconnect
nodontdisconnect desktop noremote
.IP
aro= display vncdisplay desktopname http_url auth
users rootshift scale_str scaled_x scaled_y scale_numer
scale_denom scale_fac scaling_noblend scaling_nomult4
scaling_pad scaling_interpolate inetd safer unsafe
passwdfile using_shm logfile o rc norc h help V version
lastmod bg sigpipe threads clients client_count pid
ext_xtest ext_xkb ext_xshm ext_xinerama ext_overlay
ext_xfixes ext_xdamage ext_xrandr rootwin num_buttons
button_mask mouse_x mouse_y bpp depth indexed_color
dpy_x dpy_y rfbauth passwd
users rootshift clipshift scale_str scaled_x scaled_y
scale_numer scale_denom scale_fac scaling_noblend
scaling_nomult4 scaling_pad scaling_interpolate inetd
safer unsafe passwdfile using_shm logfile o rc norc
h help V version lastmod bg sigpipe threads clients
client_count pid ext_xtest ext_xkb ext_xshm ext_xinerama
ext_overlay ext_xfixes ext_xdamage ext_xrandr rootwin
num_buttons button_mask mouse_x mouse_y bpp depth
indexed_color dpy_x dpy_y wdpy_x wdpy_y off_x off_y
cdpy_x cdpy_y coff_x coff_y rfbauth passwd
.PP
\fB-sync\fR
.IP
...
...
x11vnc/x11vnc.c
View file @
fba6f58a
...
...
@@ -169,7 +169,7 @@
/*
#define LIBVNCSERVER_HAVE_LIBXINERAMA 1
#define LIBVNCSERVER_HAVE_XFIXES 1
#define LIBVNCSERVER_HAVE_XDAMAGE 1
#define LIBVNCSERVER_HAVE_
LIB
XDAMAGE 1
*/
#endif
/* OLD_TREE */
...
...
@@ -329,13 +329,21 @@ static int xfixes_base_event_type;
#endif
int
xdamage_present
=
0
;
int
using_xdamage
=
0
;
int
use_xdamage_hints
=
1
;
/* just use the xdamage rects. for scanline hints */
#if LIBVNCSERVER_HAVE_LIBXDAMAGE
#include <X11/extensions/Xdamage.h>
static
int
xdamage_base_event_type
;
Damage
xdamage
=
0
;
#endif
int
xdamage_max_area
=
20000
;
/* pixels */
double
xdamage_memory
=
1
.
0
;
/* in units of NSCAN */
int
xdamage_tile_count
;
int
hack_val
=
0
;
/* date +'lastmod: %Y-%m-%d' */
char
lastmod
[]
=
"0.7.2pre lastmod: 2005-03-
04
"
;
char
lastmod
[]
=
"0.7.2pre lastmod: 2005-03-
12
"
;
/* X display info */
...
...
@@ -347,6 +355,8 @@ int bpp, depth;
int
indexed_color
=
0
;
int
dpy_x
,
dpy_y
;
/* size of display */
int
off_x
,
off_y
;
/* offsets for -sid */
int
wdpy_x
,
wdpy_y
;
/* for actual sizes in case of -clip */
int
cdpy_x
,
cdpy_y
,
coff_x
,
coff_y
;
/* the -clip params */
int
button_mask
=
0
;
/* button state and info */
int
num_buttons
=
-
1
;
...
...
@@ -395,6 +405,7 @@ unsigned short main_red_shift, main_green_shift, main_blue_shift;
typedef
struct
_ClientData
{
int
uid
;
char
*
hostname
;
char
*
username
;
int
client_port
;
int
server_port
;
char
*
server_ip
;
...
...
@@ -432,6 +443,7 @@ int ntiles, ntiles_x, ntiles_y;
/* arrays that indicate changed or checked tiles. */
unsigned
char
*
tile_has_diff
,
*
tile_tried
,
*
tile_copied
;
unsigned
char
*
tile_has_xdamage_diff
,
*
tile_row_has_xdamage_diff
;
/* times of recent events */
time_t
last_event
,
last_input
,
last_client
=
0
;
...
...
@@ -466,6 +478,15 @@ Atom vnc_connect_prop = None;
struct
utsname
UT
;
/* scan pattern jitter from x0rfbserver */
#define NSCAN 32
int
scanlines
[
NSCAN
]
=
{
0
,
16
,
8
,
24
,
4
,
20
,
12
,
28
,
10
,
26
,
18
,
2
,
22
,
6
,
30
,
14
,
1
,
17
,
9
,
25
,
7
,
23
,
15
,
31
,
19
,
3
,
27
,
11
,
29
,
13
,
5
,
21
};
/* function prototypes (see filename comment above) */
int
all_clients_initialized
(
void
);
...
...
@@ -511,6 +532,9 @@ void free_tiles(void);
void
initialize_watch_bell
(
void
);
void
initialize_xinerama
(
void
);
void
initialize_xfixes
(
void
);
void
initialize_xdamage
(
void
);
void
create_xdamage
(
void
);
void
destroy_xdamage
(
void
);
void
initialize_xrandr
(
void
);
XImage
*
initialize_xdisplay_fb
(
void
);
...
...
@@ -596,24 +620,25 @@ void refresh_screen(void);
/*
* variables for the command line options
*/
char
*
use_dpy
=
NULL
;
char
*
auth_file
=
NULL
;
char
*
visual_str
=
NULL
;
char
*
logfile
=
NULL
;
char
*
use_dpy
=
NULL
;
/* -display */
char
*
auth_file
=
NULL
;
/* -auth/-xauth */
char
*
visual_str
=
NULL
;
/* -visual */
char
*
logfile
=
NULL
;
/* -o, -logfile */
int
logfile_append
=
0
;
char
*
passwdfile
=
NULL
;
char
*
blackout_str
=
NULL
;
int
use_solid_bg
=
0
;
char
*
passwdfile
=
NULL
;
/* -passwdfile */
char
*
blackout_str
=
NULL
;
/* -blackout */
char
*
clip_str
=
NULL
;
/* -clip */
int
use_solid_bg
=
0
;
/* -solid */
char
*
solid_str
=
NULL
;
char
*
solid_default
=
"cyan4"
;
char
*
speeds_str
=
NULL
;
char
*
speeds_str
=
NULL
;
/* -speeds TBD */
int
measure_speeds
=
1
;
int
speeds_net_rate
=
0
;
int
speeds_net_latency
=
0
;
int
speeds_read_rate
=
0
;
char
*
rc_rcfile
=
NULL
;
char
*
rc_rcfile
=
NULL
;
/* -rc */
int
rc_norc
=
0
;
int
opts_bg
=
0
;
...
...
@@ -866,6 +891,32 @@ int scan_hexdec(char *str, unsigned long *num) {
return
1
;
}
int
parse_geom
(
char
*
str
,
int
*
wp
,
int
*
hp
,
int
*
xp
,
int
*
yp
,
int
W
,
int
H
)
{
int
w
,
h
,
x
,
y
;
/* handle +/-x and +/-y */
if
(
sscanf
(
str
,
"%dx%d+%d+%d"
,
&
w
,
&
h
,
&
x
,
&
y
)
==
4
)
{
;
}
else
if
(
sscanf
(
str
,
"%dx%d-%d+%d"
,
&
w
,
&
h
,
&
x
,
&
y
)
==
4
)
{
w
=
nabs
(
w
);
x
=
W
-
x
-
w
;
}
else
if
(
sscanf
(
str
,
"%dx%d+%d-%d"
,
&
w
,
&
h
,
&
x
,
&
y
)
==
4
)
{
h
=
nabs
(
h
);
y
=
H
-
y
-
h
;
}
else
if
(
sscanf
(
str
,
"%dx%d-%d-%d"
,
&
w
,
&
h
,
&
x
,
&
y
)
==
4
)
{
w
=
nabs
(
w
);
h
=
nabs
(
h
);
x
=
W
-
x
-
w
;
y
=
H
-
y
-
h
;
}
else
{
return
0
;
}
*
wp
=
w
;
*
hp
=
h
;
*
xp
=
x
;
*
yp
=
y
;
return
1
;
}
void
set_env
(
char
*
name
,
char
*
value
)
{
char
*
str
;
str
=
(
char
*
)
malloc
(
strlen
(
name
)
+
strlen
(
value
)
+
2
);
...
...
@@ -1704,6 +1755,7 @@ void try_to_switch_users(void) {
free
(
users
);
}
/* -- inet.c -- */
/*
* Simple utility to map host name to dotted IP address. Ignores aliases.
* Up to caller to free returned string.
...
...
@@ -1728,9 +1780,6 @@ char *ip2host(char *ip) {
char
*
str
;
#if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H
struct
hostent
*
hp
;
#ifndef in_addr_t
typedef
unsigned
int
in_addr_t
;
#endif
in_addr_t
iaddr
;
iaddr
=
inet_addr
(
ip
);
...
...
@@ -1821,6 +1870,96 @@ char *get_local_host(int sock) {
return
get_host
(
sock
,
0
);
}
char
*
ident_username
(
rfbClientPtr
client
)
{
ClientData
*
cd
=
(
ClientData
*
)
client
->
clientData
;
char
*
str
,
*
newhost
,
*
user
=
NULL
,
*
newuser
=
NULL
;
int
len
;
if
(
cd
)
{
user
=
cd
->
username
;
}
if
(
!
user
||
*
user
==
'\0'
)
{
char
msg
[
128
];
int
n
,
sock
,
ok
=
0
;
if
((
sock
=
rfbConnectToTcpAddr
(
client
->
host
,
113
))
<
0
)
{
rfbLog
(
"could not connect to ident: %s:%d
\n
"
,
client
->
host
,
113
);
}
else
{
int
ret
;
fd_set
rfds
;
struct
timeval
tv
;
int
rport
=
get_remote_port
(
client
->
sock
);
int
lport
=
get_local_port
(
client
->
sock
);
sprintf
(
msg
,
"%d, %d
\r\n
"
,
rport
,
lport
);
n
=
write
(
sock
,
msg
,
strlen
(
msg
));
FD_ZERO
(
&
rfds
);
FD_SET
(
sock
,
&
rfds
);
tv
.
tv_sec
=
4
;
tv
.
tv_usec
=
0
;
ret
=
select
(
sock
+
1
,
&
rfds
,
NULL
,
NULL
,
&
tv
);
if
(
ret
>
0
)
{
int
i
;
char
*
q
,
*
p
;
for
(
i
=
0
;
i
<
128
;
i
++
)
{
msg
[
i
]
=
'\0'
;
}
usleep
(
250
*
1000
);
n
=
read
(
sock
,
msg
,
127
);
close
(
sock
);
if
(
n
<=
0
)
goto
badreply
;
/* 32782 , 6000 : USERID : UNIX :runge */
q
=
strstr
(
msg
,
"USERID"
);
if
(
!
q
)
goto
badreply
;
q
=
strstr
(
q
,
":"
);
if
(
!
q
)
goto
badreply
;
q
++
;
q
=
strstr
(
q
,
":"
);
if
(
!
q
)
goto
badreply
;
q
++
;
q
=
lblanks
(
q
);
p
=
q
;
while
(
*
p
)
{
if
(
*
p
==
'\r'
||
*
p
==
'\n'
)
{
*
p
=
'\0'
;
}
p
++
;
}
ok
=
1
;
if
(
strlen
(
q
)
>
24
)
{
*
(
q
+
24
)
=
'\0'
;
}
newuser
=
strdup
(
q
);
badreply:
n
=
0
;
/* avoid syntax error */
}
else
{
close
(
sock
);
}
}
if
(
!
ok
||
!
newuser
)
{
newuser
=
strdup
(
"unknown-user"
);
}
if
(
cd
)
{
free
(
cd
->
username
);
cd
->
username
=
newuser
;
}
user
=
newuser
;
}
newhost
=
ip2host
(
client
->
host
);
len
=
strlen
(
user
)
+
1
+
strlen
(
newhost
)
+
1
;
str
=
(
char
*
)
malloc
(
len
);
sprintf
(
str
,
"%s@%s"
,
user
,
newhost
);
free
(
newhost
);
return
str
;
}
/* -- ximage.c -- */
/*
* used in rfbGetScreen and rfbNewFramebuffer: and estimate to the number
* of bits per color, of course for some visuals, e.g. 565, the number
...
...
@@ -1846,42 +1985,25 @@ int guess_bits_per_color(int bits_per_pixel) {
return
bits_per_color
;
}
/* count number of clients supporting NewFBSize */
int
new_fb_size_clients
(
rfbScreenInfoPtr
s
)
{
rfbClientIteratorPtr
iter
;
rfbClientPtr
cl
;
int
count
=
0
;
if
(
!
s
)
{
return
0
;
}
iter
=
rfbGetClientIterator
(
s
);
while
(
(
cl
=
rfbClientIteratorNext
(
iter
))
)
{
if
(
cl
->
useNewFBSize
)
{
count
++
;
}
}
rfbReleaseClientIterator
(
iter
);
return
count
;
}
/*
* Kludge to interpose image gets and limit to a subset rectangle of
* the rootwin. This is the -sid option trying to work around invisible
* saveUnders menu, etc, windows.
* saveUnders menu, etc, windows.
Also -clip option.
*/
int
rootshift
=
0
;
int
clipshift
=
0
;
#define ADJUST_ROOTSHIFT \
if (rootshift && subwin) { \
d = rootwin; \
x += off_x; \
y += off_y; \
} \
if (clipshift) { \
x += coff_x; \
y += coff_y; \
}
/* -- ximage.c -- */
/*
* Wrappers for Image related X calls
*/
...
...
@@ -2025,7 +2147,7 @@ XImage *XCreateImage_wr(Display *disp, Visual *visual, unsigned int depth,
void
copy_image
(
XImage
*
dest
,
int
x
,
int
y
,
unsigned
int
w
,
unsigned
int
h
)
{
/* default (w=0,h=0) is the fill the entire XImage */
/* default (w=0,
h=0) is the fill the entire XImage */
if
(
w
<
1
)
{
w
=
dest
->
width
;
}
...
...
@@ -2222,6 +2344,11 @@ void clean_up_exit (int ret) {
}
X_LOCK
;
XTestDiscard_wr
(
dpy
);
#if LIBVNCSERVER_HAVE_LIBXDAMAGE
if
(
xdamage
)
{
XDamageDestroy
(
dpy
,
xdamage
);
}
#endif
XCloseDisplay
(
dpy
);
X_UNLOCK
;
...
...
@@ -2493,9 +2620,9 @@ char *list_clients(void) {
/*
* each client:
* <id>:<ip>:<port>:<hostname>:<input>:<loginview>,
* 8+1+16+1+5+1+256+1+5+1+1+1
* 123.123.123.123:60000/0x11111111-rw,
= 297 bytes
* <id>:<ip>:<port>:<
user>:<
hostname>:<input>:<loginview>,
* 8+1+16+1+5+1+2
4+1+2
56+1+5+1+1+1
* 123.123.123.123:60000/0x11111111-rw,
* so count+1 * 400 must cover it.
*/
list
=
(
char
*
)
malloc
((
count
+
1
)
*
400
);
...
...
@@ -2514,6 +2641,12 @@ char *list_clients(void) {
strcat
(
list
,
":"
);
sprintf
(
tmp
,
"%d:"
,
cd
->
client_port
);
strcat
(
list
,
tmp
);
if
(
*
(
cd
->
username
)
==
'\0'
)
{
char
*
s
=
ident_username
(
cl
);
if
(
s
)
free
(
s
);
}
strcat
(
list
,
cd
->
username
);
strcat
(
list
,
":"
);
strcat
(
list
,
cd
->
hostname
);
strcat
(
list
,
":"
);
strcat
(
list
,
cd
->
input
);
...
...
@@ -2525,6 +2658,26 @@ char *list_clients(void) {
return
list
;
}
/* count number of clients supporting NewFBSize */
int
new_fb_size_clients
(
rfbScreenInfoPtr
s
)
{
rfbClientIteratorPtr
iter
;
rfbClientPtr
cl
;
int
count
=
0
;
if
(
!
s
)
{
return
0
;
}
iter
=
rfbGetClientIterator
(
s
);
while
(
(
cl
=
rfbClientIteratorNext
(
iter
))
)
{
if
(
cl
->
useNewFBSize
)
{
count
++
;
}
}
rfbReleaseClientIterator
(
iter
);
return
count
;
}
void
close_all_clients
(
void
)
{
rfbClientIteratorPtr
iter
;
rfbClientPtr
cl
;
...
...
@@ -2779,6 +2932,9 @@ static void client_gone(rfbClientPtr client) {
if
(
cd
->
hostname
)
{
free
(
cd
->
hostname
);
}
if
(
cd
->
username
)
{
free
(
cd
->
username
);
}
}
free
(
client
->
clientData
);
}
...
...
@@ -2958,8 +3114,8 @@ static int check_access(char *addr) {
* x11vnc's first (and only) visible widget: accept/reject dialog window.
* We go through this pain to avoid dependency on libXt...
*/
static
int
ugly_accept_window
(
char
*
addr
,
int
X
,
int
Y
,
int
timeout
,
char
*
mode
)
{
static
int
ugly_accept_window
(
char
*
addr
,
char
*
userhost
,
int
X
,
int
Y
,
int
timeout
,
char
*
mode
)
{
#define t2x2_width 16
#define t2x2_height 16
...
...
@@ -2989,6 +3145,7 @@ static char t2x2_bits[] = {
/* strings and geometries y/n */
KeyCode
key_y
,
key_n
,
key_v
;
char
strh
[
100
];
char
stri
[
100
];
char
str1_b
[]
=
"To accept: press
\"
y
\"
or click the
\"
Yes
\"
button"
;
char
str2_b
[]
=
"To reject: press
\"
n
\"
or click the
\"
No
\"
button"
;
char
str3_b
[]
=
"View only: press
\"
v
\"
or click the
\"
View
\"
button"
;
...
...
@@ -3002,7 +3159,7 @@ static char t2x2_bits[] = {
char
str_y
[]
=
"Yes"
;
char
str_n
[]
=
"No"
;
char
str_v
[]
=
"View"
;
int
x
,
y
,
w
=
345
,
h
=
1
50
,
ret
=
0
;
int
x
,
y
,
w
=
345
,
h
=
1
75
,
ret
=
0
;
int
X_sh
=
20
,
Y_sh
=
30
,
dY
=
20
;
int
Ye_x
=
20
,
Ye_y
=
0
,
Ye_w
=
45
,
Ye_h
=
20
;
int
No_x
=
75
,
No_y
=
0
,
No_w
=
45
,
No_h
=
20
;
...
...
@@ -3026,6 +3183,7 @@ static char t2x2_bits[] = {
h
-=
dY
;
}
/* XXX handle coff_x/coff_y? */
if
(
X
<
-
dpy_x
)
{
x
=
(
dpy_x
-
w
)
/
2
;
/* large negative: center */
if
(
x
<
0
)
x
=
0
;
...
...
@@ -3086,7 +3244,8 @@ static char t2x2_bits[] = {
XMapWindow
(
dpy
,
awin
);
XFlush
(
dpy
);
sprintf
(
strh
,
"x11vnc: accept connection from %s?"
,
addr
);
snprintf
(
strh
,
100
,
"x11vnc: accept connection from %s?"
,
addr
);
snprintf
(
stri
,
100
,
" (%s)"
,
userhost
);
key_y
=
XKeysymToKeycode
(
dpy
,
XStringToKeysym
(
"y"
));
key_n
=
XKeysymToKeycode
(
dpy
,
XStringToKeysym
(
"n"
));
key_v
=
XKeysymToKeycode
(
dpy
,
XStringToKeysym
(
"v"
));
...
...
@@ -3122,6 +3281,8 @@ static char t2x2_bits[] = {
/* instructions */
XDrawString
(
dpy
,
awin
,
gc
,
X_sh
,
Y_sh
+
(
k
++
)
*
dY
,
strh
,
strlen
(
strh
));
XDrawString
(
dpy
,
awin
,
gc
,
X_sh
,
Y_sh
+
(
k
++
)
*
dY
,
stri
,
strlen
(
stri
));
XDrawString
(
dpy
,
awin
,
gc
,
X_sh
,
Y_sh
+
(
k
++
)
*
dY
,
str1
,
strlen
(
str1
));
XDrawString
(
dpy
,
awin
,
gc
,
X_sh
,
Y_sh
+
(
k
++
)
*
dY
,
...
...
@@ -3336,6 +3497,7 @@ static int accept_client(rfbClientPtr client) {
addr
=
"unknown-host"
;
}
if
(
strstr
(
accept_cmd
,
"popup"
)
==
accept_cmd
)
{
/* use our builtin popup button */
...
...
@@ -3344,6 +3506,7 @@ static int accept_client(rfbClientPtr client) {
int
ret
,
timeout
=
120
;
int
x
=
-
64000
,
y
=
-
64000
;
char
*
p
,
*
mode
;
char
*
userhost
=
ident_username
(
client
);
/* extract timeout */
if
((
p
=
strchr
(
accept_cmd
,
':'
))
!=
NULL
)
{
...
...
@@ -3380,7 +3543,9 @@ static int accept_client(rfbClientPtr client) {
}
rfbLog
(
"accept_client: using builtin popup for: %s
\n
"
,
addr
);
if
((
ret
=
ugly_accept_window
(
addr
,
x
,
y
,
timeout
,
mode
)))
{
if
((
ret
=
ugly_accept_window
(
addr
,
userhost
,
x
,
y
,
timeout
,
mode
)))
{
free
(
userhost
);
if
(
ret
==
2
)
{
rfbLog
(
"accept_client: viewonly: %s
\n
"
,
addr
);
client
->
viewOnly
=
TRUE
;
...
...
@@ -3388,6 +3553,7 @@ static int accept_client(rfbClientPtr client) {
rfbLog
(
"accept_client: popup accepted: %s
\n
"
,
addr
);
return
1
;
}
else
{
free
(
userhost
);
rfbLog
(
"accept_client: popup rejected: %s
\n
"
,
addr
);
return
0
;
}
...
...
@@ -3787,6 +3953,7 @@ enum rfbNewClientAction new_client(rfbClientPtr client) {
cd
->
server_port
=
get_local_port
(
client
->
sock
);
cd
->
server_ip
=
get_local_host
(
client
->
sock
);
cd
->
hostname
=
ip2host
(
client
->
host
);
cd
->
username
=
strdup
(
""
);
cd
->
input
[
0
]
=
'-'
;
cd
->
login_viewonly
=
-
1
;
...
...
@@ -5755,11 +5922,15 @@ static void update_x11_pointer_position(int x, int y) {
X_LOCK
;
if
(
use_xwarppointer
)
{
/* off_x and off_y not needed with XWarpPointer */
XWarpPointer
(
dpy
,
None
,
window
,
0
,
0
,
0
,
0
,
x
,
y
);
/*
* off_x and off_y not needed with XWarpPointer since
* window is used:
*/
XWarpPointer
(
dpy
,
None
,
window
,
0
,
0
,
0
,
0
,
x
+
coff_x
,
y
+
coff_y
);
}
else
{
XTestFakeMotionEvent_wr
(
dpy
,
scr
,
x
+
off_x
,
y
+
off_y
,
CurrentTime
);
XTestFakeMotionEvent_wr
(
dpy
,
scr
,
x
+
off_x
+
coff_x
,
y
+
off_y
+
coff_y
,
CurrentTime
);
}
X_UNLOCK
;
...
...
@@ -5786,7 +5957,7 @@ static void update_x11_pointer_position(int x, int y) {
}
/*
* Send a pointer
positi
on event to the X server.
* Send a pointer
butt
on event to the X server.
*/
static
void
update_x11_pointer_mask
(
int
mask
)
{
int
i
,
mb
;
...
...
@@ -6164,8 +6335,8 @@ XErrorHandler old_getimage_handler;
/* -- xrandr.c -- */
void
initialize_xrandr
(
void
)
{
#if LIBVNCSERVER_HAVE_LIBXRANDR
if
(
xrandr_present
)
{
#if LIBVNCSERVER_HAVE_LIBXRANDR
Rotation
rot
;
xrandr_width
=
XDisplayWidth
(
dpy
,
scr
);
...
...
@@ -6177,15 +6348,20 @@ void initialize_xrandr(void) {
}
else
{
XRRSelectInput
(
dpy
,
rootwin
,
0
);
}
}
#endif
}
else
if
(
xrandr
)
{
rfbLog
(
"-xrandr mode specified, but no RANDR support on
\n
"
);
rfbLog
(
" display or in client library. Disabling -xrandr "
"mode.
\n
"
);
xrandr
=
0
;
}
}
void
handle_xrandr_change
(
int
,
int
);
int
handle_subwin_resize
(
char
*
msg
)
{
int
new_x
,
new_y
;
int
i
,
check
=
10
,
ms
=
250
;
/* 2.5 secs... */
int
i
,
check
=
10
,
ms
=
250
;
/* 2.5 secs
total
... */
if
(
!
subwin
)
{
return
0
;
/* hmmm... */
...
...
@@ -6200,7 +6376,7 @@ int handle_subwin_resize(char *msg) {
X_UNLOCK
;
clean_up_exit
(
1
);
}
if
(
dpy_x
==
new_x
&&
dpy_y
==
new_y
)
{
if
(
wdpy_x
==
new_x
&&
w
dpy_y
==
new_y
)
{
/* no change */
return
0
;
}
...
...
@@ -6225,7 +6401,7 @@ int handle_subwin_resize(char *msg) {
}
rfbLog
(
"subwin 0x%lx new size: x: %d -> %d, y: %d -> %d
\n
"
,
subwin
,
dpy_x
,
new_x
,
dpy_y
,
new_y
);
subwin
,
wdpy_x
,
new_x
,
w
dpy_y
,
new_y
);
rfbLog
(
"calling handle_xrandr_change() for resizing
\n
"
);
X_UNLOCK
;
...
...
@@ -6268,11 +6444,11 @@ void handle_xrandr_change(int new_x, int new_y) {
/* default, resize, and newfbsize create a new fb: */
rfbLog
(
"check_xrandr_event: trying to create new framebuffer...
\n
"
);
if
(
new_x
<
dpy_x
||
new_y
<
dpy_y
)
{
if
(
new_x
<
wdpy_x
||
new_y
<
w
dpy_y
)
{
check_black_fb
();
}
do_new_fb
(
1
);
rfbLog
(
"check_xrandr_event: fb WxH: %dx%d
\n
"
,
dpy_x
,
dpy_y
);
rfbLog
(
"check_xrandr_event: fb WxH: %dx%d
\n
"
,
wdpy_x
,
w
dpy_y
);
}
int
check_xrandr_event
(
char
*
msg
)
{
...
...
@@ -6304,8 +6480,8 @@ int check_xrandr_event(char *msg) {
rfbLog
(
" mheight: %d mm
\n
"
,
(
int
)
rev
->
mheight
);
rfbLog
(
"
\n
"
);
rfbLog
(
"check_xrandr_event: previous WxH: %dx%d
\n
"
,
dpy_x
,
dpy_y
);
if
(
dpy_x
==
rev
->
width
&&
dpy_y
==
rev
->
height
&&
wdpy_x
,
w
dpy_y
);
if
(
wdpy_x
==
rev
->
width
&&
w
dpy_y
==
rev
->
height
&&
xrandr_rotation
==
(
int
)
rev
->
rotation
)
{
rfbLog
(
"check_xrandr_event: no change detected.
\n
"
);
do_change
=
0
;
...
...
@@ -6624,6 +6800,7 @@ void initialize_xevents(void) {
static
int
did_xcreate_simple_window
=
0
;
static
int
did_vnc_connect_prop
=
0
;
static
int
did_xfixes
=
0
;
static
int
did_xdamage
=
0
;
static
int
did_xrandr
=
0
;
X_LOCK
;
...
...
@@ -6656,6 +6833,10 @@ void initialize_xevents(void) {
initialize_xfixes
();
did_xfixes
=
1
;
}
if
(
xdamage_present
&&
!
did_xdamage
)
{
initialize_xdamage
();
did_xdamage
=
1
;
}
}
/*
...
...
@@ -7407,7 +7588,8 @@ char *process_remote_cmd(char *cmd, int stringonly) {
Window
twin
;
COLON_CHECK
(
"id:"
)
if
(
query
)
{
snprintf
(
buf
,
bufn
,
"ans=%s%s0x%lx"
,
p
,
co
,
subwin
);
snprintf
(
buf
,
bufn
,
"ans=%s%s0x%lx"
,
p
,
co
,
rootshift
?
0
:
subwin
);
goto
qry
;
}
p
+=
strlen
(
"id:"
);
...
...
@@ -7444,7 +7626,8 @@ char *process_remote_cmd(char *cmd, int stringonly) {
Window
twin
;
COLON_CHECK
(
"sid:"
)
if
(
query
)
{
snprintf
(
buf
,
bufn
,
"ans=%s%s0x%lx"
,
p
,
co
,
subwin
);
snprintf
(
buf
,
bufn
,
"ans=%s%s0x%lx"
,
p
,
co
,
!
rootshift
?
0
:
subwin
);
goto
qry
;
}
p
+=
strlen
(
"sid:"
);
...
...
@@ -7475,6 +7658,20 @@ char *process_remote_cmd(char *cmd, int stringonly) {
do_new_fb
(
1
);
}
}
}
else
if
(
strstr
(
p
,
"clip"
)
==
p
)
{
COLON_CHECK
(
"clip:"
)
if
(
query
)
{
snprintf
(
buf
,
bufn
,
"ans=%s%s%s"
,
p
,
co
,
NONUL
(
clip_str
));
goto
qry
;
}
p
+=
strlen
(
"clip:"
);
if
(
clip_str
)
free
(
clip_str
);
clip_str
=
strdup
(
p
);
/* OK, this requires a new fb... */
do_new_fb
(
1
);
}
else
if
(
strstr
(
p
,
"waitmapped"
)
==
p
)
{
if
(
query
)
{
snprintf
(
buf
,
bufn
,
"ans=%s:%d"
,
p
,
...
...
@@ -8676,6 +8873,73 @@ char *process_remote_cmd(char *cmd, int stringonly) {
initialize_xfixes
();
first_cursor
();
}
else
if
(
!
strcmp
(
p
,
"xdamage"
))
{
int
orig
=
use_xdamage_hints
;
if
(
query
)
{
snprintf
(
buf
,
bufn
,
"ans=%s:%d"
,
p
,
use_xdamage_hints
);
goto
qry
;
}
if
(
!
xdamage_present
)
{
rfbLog
(
"process_remote_cmd: cannot enable xdamage hints "
"(not supported on X display)
\n
"
);
goto
done
;
}
rfbLog
(
"process_remote_cmd: enabling xdamage hints"
" (if supported).
\n
"
);
use_xdamage_hints
=
1
;
if
(
use_xdamage_hints
!=
orig
)
{
initialize_xdamage
();
create_xdamage
();
}
}
else
if
(
!
strcmp
(
p
,
"noxdamage"
))
{
int
orig
=
use_xdamage_hints
;
if
(
query
)
{
snprintf
(
buf
,
bufn
,
"ans=%s:%d"
,
p
,
!
use_xdamage_hints
);
goto
qry
;
}
if
(
!
xdamage_present
)
{
rfbLog
(
"process_remote_cmd: disabling xdamage hints "
"(but not supported on X display)
\n
"
);
goto
done
;
}
rfbLog
(
"process_remote_cmd: disabling xdamage hints.
\n
"
);
use_xdamage_hints
=
0
;
if
(
use_xdamage_hints
!=
orig
)
{
initialize_xdamage
();
destroy_xdamage
();
}
}
else
if
(
strstr
(
p
,
"xd_area"
)
==
p
)
{
int
a
;
COLON_CHECK
(
"xd_area:"
)
if
(
query
)
{
snprintf
(
buf
,
bufn
,
"ans=%s%s%d"
,
p
,
co
,
xdamage_max_area
);
goto
qry
;
}
p
+=
strlen
(
"xd_area:"
);
a
=
atoi
(
p
);
if
(
a
>=
0
)
{
rfbLog
(
"process_remote_cmd: setting xdamage_max_area "
"%d -> %d.
\n
"
,
xdamage_max_area
,
a
);
xdamage_max_area
=
a
;
}
}
else
if
(
strstr
(
p
,
"xd_mem"
)
==
p
)
{
double
a
;
COLON_CHECK
(
"xd_mem:"
)
if
(
query
)
{
snprintf
(
buf
,
bufn
,
"ans=%s%s%.3f"
,
p
,
co
,
xdamage_memory
);
goto
qry
;
}
p
+=
strlen
(
"xd_mem:"
);
a
=
atof
(
p
);
if
(
a
>=
0
.
0
)
{
rfbLog
(
"process_remote_cmd: setting xdamage_memory "
"%.3f -> %.3f.
\n
"
,
xdamage_memory
,
a
);
xdamage_memory
=
a
;
}
}
else
if
(
strstr
(
p
,
"alphacut"
)
==
p
)
{
int
a
;
COLON_CHECK
(
"alphacut:"
)
...
...
@@ -9282,6 +9546,12 @@ char *process_remote_cmd(char *cmd, int stringonly) {
rfbLog
(
"process_remote_cmd: disabling remote commands.
\n
"
);
accept_remote_cmds
=
0
;
/* cannot be turned back on. */
}
else
if
(
strstr
(
p
,
"hack:"
)
==
p
)
{
/* skip-cmd-list */
NOTAPP
p
+=
strlen
(
"hack:"
);
hack_val
=
atoi
(
p
);
rfbLog
(
"set hack_val to: %d
\n
"
,
hack_val
);
}
else
if
(
query
)
{
/* read-only variables that can only be queried: */
...
...
@@ -9314,6 +9584,8 @@ char *process_remote_cmd(char *cmd, int stringonly) {
snprintf
(
buf
,
bufn
,
"aro=%s:%s"
,
p
,
NONUL
(
users_list
));
}
else
if
(
!
strcmp
(
p
,
"rootshift"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
rootshift
);
}
else
if
(
!
strcmp
(
p
,
"clipshift"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
clipshift
);
}
else
if
(
!
strcmp
(
p
,
"scale_str"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%s"
,
p
,
NONUL
(
scale_str
));
}
else
if
(
!
strcmp
(
p
,
"scaled_x"
))
{
...
...
@@ -9406,6 +9678,22 @@ char *process_remote_cmd(char *cmd, int stringonly) {
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
dpy_x
);
}
else
if
(
!
strcmp
(
p
,
"dpy_y"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
dpy_y
);
}
else
if
(
!
strcmp
(
p
,
"wdpy_x"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
wdpy_x
);
}
else
if
(
!
strcmp
(
p
,
"wdpy_y"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
wdpy_y
);
}
else
if
(
!
strcmp
(
p
,
"off_x"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
off_x
);
}
else
if
(
!
strcmp
(
p
,
"off_y"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
off_y
);
}
else
if
(
!
strcmp
(
p
,
"cdpy_x"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
cdpy_x
);
}
else
if
(
!
strcmp
(
p
,
"cdpy_y"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
cdpy_y
);
}
else
if
(
!
strcmp
(
p
,
"coff_x"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
coff_x
);
}
else
if
(
!
strcmp
(
p
,
"coff_y"
))
{
snprintf
(
buf
,
bufn
,
"aro=%s:%d"
,
p
,
coff_y
);
}
else
if
(
!
strcmp
(
p
,
"rfbauth"
))
{
NOTAPPRO
}
else
if
(
!
strcmp
(
p
,
"passwd"
))
{
...
...
@@ -9447,6 +9735,329 @@ char *process_remote_cmd(char *cmd, int stringonly) {
return
NULL
;
}
/* -- xdamage.c -- */
sraRegionPtr
*
xdamage_regions
=
NULL
;
int
xdamage_ticker
=
0
;
/* for stats */
int
XD_skip
=
0
,
XD_tot
=
0
,
XD_des
=
0
;
void
record_desired_xdamage_rect
(
int
x
,
int
y
,
int
w
,
int
h
)
{
/*
* Unfortunately we currently can't trust an xdamage event
* to correspond to real screen damage. E.g. focus-in for
* mozilla (depending on wm) will mark the whole toplevel
* area as damaged, when only the border has changed.
* Similar things for terminal windows.
*
* This routine uses some heuristics to detect small enough
* damage regions that we will not have a performance problem
* if we believe them even though they are wrong. We record
* the corresponding tiles the damage regions touch.
*/
int
dt_x
,
dt_y
,
nt_x1
,
nt_y1
,
nt_x2
,
nt_y2
,
nt
;
int
ix
,
iy
,
cnt
=
0
;
int
area
=
w
*
h
,
always_accept
=
0
;
if
(
xdamage_max_area
<=
0
)
{
always_accept
=
1
;
}
if
(
!
always_accept
&&
area
>
xdamage_max_area
)
{
return
;
}
dt_x
=
w
/
tile_x
;
dt_y
=
h
/
tile_y
;
if
(
!
always_accept
&&
dt_y
>=
2
&&
area
>
1000
)
{
/*
* should be caught by a normal scanline poll, but we might
* as well keep if small.
*/
return
;
}
nt_x1
=
nfix
(
(
x
)
/
tile_x
,
ntiles_x
);
nt_x2
=
nfix
((
x
+
w
)
/
tile_x
,
ntiles_x
);
nt_y1
=
nfix
(
(
y
)
/
tile_y
,
ntiles_y
);
nt_y2
=
nfix
((
y
+
h
)
/
tile_y
,
ntiles_y
);
/* loop over the rectangle of tiles (1 tile for a small input rect */
for
(
ix
=
nt_x1
;
ix
<=
nt_x2
;
ix
++
)
{
for
(
iy
=
nt_y1
;
iy
<=
nt_y2
;
iy
++
)
{
nt
=
ix
+
iy
*
ntiles_x
;
cnt
++
;
if
(
!
tile_has_xdamage_diff
[
nt
])
{
XD_des
++
;
}
tile_has_xdamage_diff
[
nt
]
=
1
;
tile_row_has_xdamage_diff
[
iy
]
=
1
;
xdamage_tile_count
++
;
}
}
}
void
collect_xdamage
(
int
scancnt
)
{
#if LIBVNCSERVER_HAVE_LIBXDAMAGE
XDamageNotifyEvent
*
dev
;
#if 0
XserverRegion xregion;
#endif
XEvent
ev
;
sraRegionPtr
tmpregion
;
sraRegionPtr
reg
;
static
int
rect_count
=
0
;
int
nreg
,
ccount
=
0
,
dcount
=
0
;
static
time_t
last_rpt
=
0
;
time_t
now
;
int
x
,
y
,
w
,
h
,
x2
,
y2
;
int
i
,
dup
,
next
,
dup_max
=
0
;
#define DUPSZ 16
int
dup_x
[
DUPSZ
],
dup_y
[
DUPSZ
],
dup_w
[
DUPSZ
],
dup_h
[
DUPSZ
];
if
(
!
xdamage_present
||
!
using_xdamage
)
{
return
;
}
if
(
!
xdamage
)
{
return
;
}
nreg
=
(
xdamage_memory
*
NSCAN
)
+
1
;
xdamage_ticker
=
(
xdamage_ticker
+
1
)
%
nreg
;
reg
=
xdamage_regions
[
xdamage_ticker
];
sraRgnMakeEmpty
(
reg
);
X_LOCK
;
while
(
XCheckTypedEvent
(
dpy
,
xdamage_base_event_type
+
XDamageNotify
,
&
ev
))
{
/* TODO max cut off time in this loop? */
if
(
ev
.
type
!=
xdamage_base_event_type
+
XDamageNotify
)
{
break
;
}
dev
=
(
XDamageNotifyEvent
*
)
&
ev
;
if
(
dev
->
damage
!=
xdamage
)
{
continue
;
/* not ours! */
}
x
=
dev
->
area
.
x
;
y
=
dev
->
area
.
y
;
w
=
dev
->
area
.
width
;
h
=
dev
->
area
.
height
;
/*
* we try to manually remove some duplicates because
* certain activities can lead to many 10's of dups
* in a row. The region work can be costly and reg is
* later used in xdamage_hint_skip loops, so it is good
* to skip them if possible.
*/
dup
=
0
;
for
(
i
=
0
;
i
<
dup_max
;
i
++
)
{
if
(
dup_x
[
i
]
==
x
&&
dup_y
[
i
]
==
y
&&
dup_w
[
i
]
==
w
&&
dup_h
[
i
]
==
h
)
{
dup
=
1
;
break
;
}
}
if
(
dup
)
{
dcount
++
;
continue
;
}
if
(
dup_max
<
DUPSZ
)
{
next
=
dup_max
;
dup_max
++
;
}
else
{
next
=
(
next
+
1
)
%
DUPSZ
;
}
dup_x
[
next
]
=
x
;
dup_y
[
next
]
=
y
;
dup_w
[
next
]
=
w
;
dup_h
[
next
]
=
h
;
/* translate if needed */
if
(
clipshift
)
{
/* set coords relative to fb origin */
if
(
0
&&
rootshift
)
{
/*
* not needed because damage is relative
* to subwin, not rootwin.
*/
x
=
x
-
off_x
;
y
=
y
-
off_y
;
}
if
(
clipshift
)
{
x
=
x
-
coff_x
;
y
=
y
-
coff_y
;
}
x2
=
x
+
w
;
/* upper point */
x
=
nfix
(
x
,
dpy_x
);
/* place both in fb area */
x2
=
nfix
(
x2
,
dpy_x
);
w
=
x2
-
x
;
/* recompute w */
y2
=
y
+
h
;
y
=
nfix
(
y
,
dpy_y
);
y2
=
nfix
(
y2
,
dpy_y
);
h
=
y2
-
y
;
if
(
w
<=
0
||
h
<=
0
)
{
continue
;
}
}
record_desired_xdamage_rect
(
x
,
y
,
w
,
h
);
tmpregion
=
sraRgnCreateRect
(
x
,
y
,
x
+
w
,
y
+
h
);
sraRgnOr
(
reg
,
tmpregion
);
sraRgnDestroy
(
tmpregion
);
rect_count
++
;
ccount
++
;
}
/* clear the whole damage region for next time. XXX check */
XDamageSubtract
(
dpy
,
xdamage
,
None
,
None
);
X_UNLOCK
;
now
=
time
(
0
);
if
(
!
last_rpt
)
{
last_rpt
=
now
;
}
if
(
now
>
last_rpt
+
15
)
{
double
rat
=
-
1
.
0
;
if
(
XD_tot
)
{
rat
=
((
double
)
XD_skip
)
/
XD_tot
;
}
if
(
0
)
fprintf
(
stderr
,
"skip/tot: %04d/%04d rat=%.3f rect_count: %d desired_rects: %d
\n
"
,
XD_skip
,
XD_tot
,
rat
,
rect_count
,
XD_des
);
XD_skip
=
0
;
XD_tot
=
0
;
XD_des
=
0
;
rect_count
=
0
;
last_rpt
=
now
;
}
#endif
}
int
xdamage_hint_skip
(
int
y
)
{
static
sraRegionPtr
scanline
=
NULL
;
sraRegionPtr
reg
,
tmpl
;
int
ret
,
i
,
n
,
nreg
;
if
(
!
xdamage_present
||
!
using_xdamage
||
!
use_xdamage_hints
)
{
return
0
;
/* cannot skip */
}
if
(
!
xdamage_regions
)
{
return
0
;
/* cannot skip */
}
if
(
!
scanline
)
{
scanline
=
sraRgnCreate
();
}
tmpl
=
sraRgnCreateRect
(
0
,
y
,
dpy_x
,
y
+
1
);
nreg
=
(
xdamage_memory
*
NSCAN
)
+
1
;
ret
=
1
;
for
(
i
=
0
;
i
<
nreg
;
i
++
)
{
/* go back thru the history starting at most recent */
n
=
(
xdamage_ticker
+
nreg
-
i
)
%
nreg
;
reg
=
xdamage_regions
[
n
];
if
(
sraRgnEmpty
(
reg
))
{
/* checking for emptiness is very fast */
continue
;
}
sraRgnMakeEmpty
(
scanline
);
sraRgnOr
(
scanline
,
tmpl
);
if
(
sraRgnAnd
(
scanline
,
reg
))
{
ret
=
0
;
break
;
}
}
sraRgnDestroy
(
tmpl
);
return
ret
;
}
void
initialize_xdamage
(
void
)
{
sraRegionPtr
*
ptr
;
int
i
,
nreg
;
using_xdamage
=
0
;
if
(
xdamage_present
)
{
if
(
use_xdamage_hints
)
{
using_xdamage
=
1
;
}
}
if
(
xdamage_regions
)
{
ptr
=
xdamage_regions
;
while
(
*
ptr
!=
NULL
)
{
sraRgnDestroy
(
*
ptr
);
ptr
++
;
}
free
(
xdamage_regions
);
xdamage_regions
=
NULL
;
}
if
(
using_xdamage
)
{
nreg
=
(
xdamage_memory
*
NSCAN
)
+
2
;
xdamage_regions
=
(
sraRegionPtr
*
)
malloc
(
nreg
*
sizeof
(
sraRegionPtr
));
for
(
i
=
0
;
i
<
nreg
;
i
++
)
{
ptr
=
xdamage_regions
+
i
;
if
(
i
==
nreg
-
1
)
{
*
ptr
=
NULL
;
}
else
{
*
ptr
=
sraRgnCreate
();
sraRgnMakeEmpty
(
*
ptr
);
}
}
/* set so will be 0 in first collect_xdamage call */
xdamage_ticker
=
-
1
;
}
}
void
create_xdamage
(
void
)
{
#if LIBVNCSERVER_HAVE_LIBXDAMAGE
if
(
!
xdamage
)
{
X_LOCK
;
xdamage
=
XDamageCreate
(
dpy
,
window
,
XDamageReportRawRectangles
);
XDamageSubtract
(
dpy
,
xdamage
,
None
,
None
);
X_UNLOCK
;
rfbLog
(
"created xdamage object: 0x%lx
\n
"
,
xdamage
);
}
#endif
}
void
destroy_xdamage
(
void
)
{
#if LIBVNCSERVER_HAVE_LIBXDAMAGE
if
(
xdamage
)
{
X_LOCK
;
XDamageDestroy
(
dpy
,
xdamage
);
X_UNLOCK
;
rfbLog
(
"destroyed xdamage object: 0x%lx
\n
"
,
xdamage
);
xdamage
=
0
;
}
#endif
}
void
check_xdamage_state
(
void
)
{
if
(
!
using_xdamage
||
!
xdamage_present
)
{
return
;
}
/*
* Create or destroy the Damage object as needed, we don't want
* one if no clients are connected.
*/
if
(
client_count
)
{
create_xdamage
();
}
else
{
destroy_xdamage
();
}
}
/* -- cursor.c -- */
/*
* Here begins a bit of a mess to experiment with multiple cursors
...
...
@@ -9525,7 +10136,7 @@ static cursor_info_t cur_arrow = {NULL, NULL, 18, 18, 0, 0, 0, NULL};
/*
* It turns out we can at least detect mouse is on the root window so
* show it (under -cursor
X or -
X) with this familiar cursor...
* show it (under -cursor
X) with this familiar cursor...
*/
static
char
*
curs_root_data
=
" "
...
...
@@ -10868,8 +11479,8 @@ void check_x11_pointer(void) {
}
/* offset subtracted since XQueryPointer relative to rootwin */
x
=
root_x
-
off_x
;
y
=
root_y
-
off_y
;
x
=
root_x
-
off_x
-
coff_x
;
y
=
root_y
-
off_y
-
coff_y
;
/* record the cursor position in the rfb screen */
cursor_position
(
x
,
y
);
...
...
@@ -11286,8 +11897,8 @@ XImage *initialize_xdisplay_fb(void) {
if
(
!
subwin
)
{
/* full screen */
window
=
rootwin
;
dpy_x
=
DisplayWidth
(
dpy
,
scr
);
dpy_y
=
DisplayHeight
(
dpy
,
scr
);
dpy_x
=
wdpy_x
=
DisplayWidth
(
dpy
,
scr
);
dpy_y
=
wdpy_y
=
DisplayHeight
(
dpy
,
scr
);
off_x
=
0
;
off_y
=
0
;
/* this may be overridden via visual_id below */
...
...
@@ -11302,8 +11913,8 @@ XImage *initialize_xdisplay_fb(void) {
X_UNLOCK
;
clean_up_exit
(
1
);
}
dpy_x
=
attr
.
width
;
dpy_y
=
attr
.
height
;
dpy_x
=
wdpy_x
=
attr
.
width
;
dpy_y
=
wdpy_y
=
attr
.
height
;
subwin_bs
=
attr
.
backing_store
;
...
...
@@ -11315,6 +11926,46 @@ XImage *initialize_xdisplay_fb(void) {
X_LOCK
;
}
clipshift
=
0
;
cdpy_x
=
cdpy_y
=
coff_x
=
coff_y
=
0
;
if
(
clip_str
)
{
int
w
,
h
,
x
,
y
,
bad
=
0
;
if
(
parse_geom
(
clip_str
,
&
w
,
&
h
,
&
x
,
&
y
,
wdpy_x
,
wdpy_y
))
{
if
(
x
<
0
)
{
x
=
0
;
}
if
(
y
<
0
)
{
y
=
0
;
}
if
(
x
+
w
>
wdpy_x
)
{
w
=
wdpy_x
-
x
;
}
if
(
y
+
h
>
wdpy_y
)
{
h
=
wdpy_y
-
y
;
}
if
(
w
<=
0
||
h
<=
0
)
{
bad
=
1
;
}
}
else
{
bad
=
1
;
}
if
(
bad
)
{
rfbLog
(
"skipping invalid -clip WxH+X+Y: %s
\n
"
,
clip_str
);
}
else
{
/* OK, change geom behind everyone's back... */
cdpy_x
=
w
;
cdpy_y
=
h
;
coff_x
=
x
;
coff_y
=
y
;
clipshift
=
1
;
dpy_x
=
cdpy_x
;
dpy_y
=
cdpy_y
;
}
}
/* initialize depth to reasonable value, visual_id may override */
depth
=
DefaultDepth
(
dpy
,
scr
);
...
...
@@ -11379,13 +12030,13 @@ XImage *initialize_xdisplay_fb(void) {
/* subwins can be a dicey if they are changing size... */
XTranslateCoordinates
(
dpy
,
window
,
rootwin
,
0
,
0
,
&
subwin_x
,
&
subwin_y
,
&
twin
);
if
(
subwin_x
+
dpy_x
>
disp_x
)
{
if
(
subwin_x
+
w
dpy_x
>
disp_x
)
{
shift
=
1
;
subwin_x
=
disp_x
-
dpy_x
-
3
;
subwin_x
=
disp_x
-
w
dpy_x
-
3
;
}
if
(
subwin_y
+
dpy_y
>
disp_y
)
{
if
(
subwin_y
+
w
dpy_y
>
disp_y
)
{
shift
=
1
;
subwin_y
=
disp_y
-
dpy_y
-
3
;
subwin_y
=
disp_y
-
w
dpy_y
-
3
;
}
if
(
subwin_x
<
0
)
{
shift
=
1
;
...
...
@@ -11435,17 +12086,17 @@ XImage *initialize_xdisplay_fb(void) {
if
(
subwin
)
{
XSetErrorHandler
(
old_handler
);
if
(
trapped_xerror
)
{
rfbLog
(
"trapped GetImage at SUBWIN creation.
\n
"
);
if
(
try
<
subwin_tries
)
{
usleep
(
250
*
1000
);
if
(
!
get_window_size
(
window
,
&
dpy_x
,
&
dpy_y
))
{
rfbLog
(
"could not get size of subwin "
"0x%lx
\n
"
,
subwin
);
X_UNLOCK
;
clean_up_exit
(
1
);
}
goto
again
;
rfbLog
(
"trapped GetImage at SUBWIN creation.
\n
"
);
if
(
try
<
subwin_tries
)
{
usleep
(
250
*
1000
);
if
(
!
get_window_size
(
window
,
&
wdpy_x
,
&
wdpy_y
))
{
rfbLog
(
"could not get size of subwin "
"0x%lx
\n
"
,
subwin
);
X_UNLOCK
;
clean_up_exit
(
1
);
}
goto
again
;
}
}
trapped_xerror
=
0
;
...
...
@@ -11727,7 +12378,7 @@ void initialize_screen(int *argc, char **argv, XImage *fb) {
screen
->
serverFormat
.
greenMax
=
0
;
screen
->
serverFormat
.
blueMax
=
0
;
/* these main_* formats are use
elsewhere by us
. */
/* these main_* formats are use
d generally
. */
main_red_shift
=
0
;
main_green_shift
=
0
;
main_blue_shift
=
0
;
...
...
@@ -12080,12 +12731,12 @@ void solid_root(char *color) {
swa
.
background_pixmap
=
None
;
visual
.
visualid
=
CopyFromParent
;
mask
=
(
CWOverrideRedirect
|
CWBackingStore
|
CWSaveUnder
|
CWBackPixmap
);
expose
=
XCreateWindow
(
dpy
,
window
,
0
,
0
,
dpy_x
,
dpy_y
,
0
,
depth
,
expose
=
XCreateWindow
(
dpy
,
window
,
0
,
0
,
wdpy_x
,
w
dpy_y
,
0
,
depth
,
InputOutput
,
&
visual
,
mask
,
&
swa
);
if
(
!
color
)
{
/* restore the root window from the XImage snapshot */
pixmap
=
XCreatePixmap
(
dpy
,
window
,
dpy_x
,
dpy_y
,
depth
);
pixmap
=
XCreatePixmap
(
dpy
,
window
,
wdpy_x
,
w
dpy_y
,
depth
);
if
(
!
image
)
{
/* whoops */
...
...
@@ -12100,7 +12751,7 @@ void solid_root(char *color) {
gcv
.
plane_mask
=
AllPlanes
;
gc
=
XCreateGC
(
dpy
,
window
,
GCFunction
|
GCPlaneMask
,
&
gcv
);
XPutImage
(
dpy
,
pixmap
,
gc
,
image
,
0
,
0
,
0
,
0
,
dpy_x
,
dpy_y
);
XPutImage
(
dpy
,
pixmap
,
gc
,
image
,
0
,
0
,
0
,
0
,
wdpy_x
,
w
dpy_y
);
gcv
.
foreground
=
gcv
.
background
=
BlackPixel
(
dpy
,
scr
);
gc
=
XCreateGC
(
dpy
,
window
,
GCForeground
|
GCBackground
,
&
gcv
);
...
...
@@ -12130,14 +12781,14 @@ void solid_root(char *color) {
iswa
.
save_under
=
False
;
iswa
.
background_pixmap
=
ParentRelative
;
iwin
=
XCreateWindow
(
dpy
,
window
,
0
,
0
,
dpy_x
,
dpy_y
,
0
,
depth
,
InputOutput
,
&
visual
,
mask
,
&
iswa
);
iwin
=
XCreateWindow
(
dpy
,
window
,
0
,
0
,
wdpy_x
,
wdpy_y
,
0
,
depth
,
InputOutput
,
&
visual
,
mask
,
&
iswa
);
rfbLog
(
"snapshotting background...
\n
"
);
XMapWindow
(
dpy
,
iwin
);
XSync
(
dpy
,
False
);
image
=
XGetImage
(
dpy
,
iwin
,
0
,
0
,
dpy_x
,
dpy_y
,
AllPlanes
,
image
=
XGetImage
(
dpy
,
iwin
,
0
,
0
,
wdpy_x
,
w
dpy_y
,
AllPlanes
,
ZPixmap
);
XSync
(
dpy
,
False
);
XDestroyWindow
(
dpy
,
iwin
);
...
...
@@ -12191,7 +12842,7 @@ void solid_cde(char *color) {
swa
.
background_pixmap
=
None
;
visual
.
visualid
=
CopyFromParent
;
mask
=
(
CWOverrideRedirect
|
CWBackingStore
|
CWSaveUnder
|
CWBackPixmap
);
expose
=
XCreateWindow
(
dpy
,
window
,
0
,
0
,
dpy_x
,
dpy_y
,
0
,
depth
,
expose
=
XCreateWindow
(
dpy
,
window
,
0
,
0
,
wdpy_x
,
w
dpy_y
,
0
,
depth
,
InputOutput
,
&
visual
,
mask
,
&
swa
);
if
(
!
color
)
{
...
...
@@ -12212,7 +12863,8 @@ void solid_cde(char *color) {
continue
;
}
pixmap
=
XCreatePixmap
(
dpy
,
twin
,
dpy_x
,
dpy_y
,
depth
);
pixmap
=
XCreatePixmap
(
dpy
,
twin
,
wdpy_x
,
wdpy_y
,
depth
);
/* draw the image to a pixmap: */
gcv
.
function
=
GXcopy
;
...
...
@@ -12220,7 +12872,7 @@ void solid_cde(char *color) {
gc
=
XCreateGC
(
dpy
,
twin
,
GCFunction
|
GCPlaneMask
,
&
gcv
);
XPutImage
(
dpy
,
pixmap
,
gc
,
image
[
n
],
0
,
0
,
0
,
0
,
dpy_x
,
dpy_y
);
wdpy_x
,
w
dpy_y
);
gcv
.
foreground
=
gcv
.
background
=
BlackPixel
(
dpy
,
scr
);
gc
=
XCreateGC
(
dpy
,
twin
,
GCForeground
|
GCBackground
,
...
...
@@ -12366,7 +13018,7 @@ void solid_cde(char *color) {
iswa
.
background_pixmap
=
ParentRelative
;
visual
.
visualid
=
CopyFromParent
;
iwin
=
XCreateWindow
(
dpy
,
twin
,
0
,
0
,
dpy_x
,
dpy_y
,
iwin
=
XCreateWindow
(
dpy
,
twin
,
0
,
0
,
wdpy_x
,
w
dpy_y
,
0
,
depth
,
InputOutput
,
&
visual
,
mask
,
&
iswa
);
rfbLog
(
"snapshotting CDE backdrop ws%d 0x%lx -> "
...
...
@@ -12374,7 +13026,7 @@ void solid_cde(char *color) {
XMapWindow
(
dpy
,
iwin
);
XSync
(
dpy
,
False
);
image
[
n
]
=
XGetImage
(
dpy
,
iwin
,
0
,
0
,
dpy_x
,
dpy_y
,
image
[
n
]
=
XGetImage
(
dpy
,
iwin
,
0
,
0
,
wdpy_x
,
w
dpy_y
,
AllPlanes
,
ZPixmap
);
XSync
(
dpy
,
False
);
XDestroyWindow
(
dpy
,
iwin
);
...
...
@@ -12673,21 +13325,7 @@ void initialize_blackouts(char *list) {
p
=
strtok
(
blist
,
",
\t
"
);
while
(
p
)
{
/* handle +/-x and +/-y */
if
(
sscanf
(
p
,
"%dx%d+%d+%d"
,
&
w
,
&
h
,
&
x
,
&
y
)
==
4
)
{
;
}
else
if
(
sscanf
(
p
,
"%dx%d-%d+%d"
,
&
w
,
&
h
,
&
x
,
&
y
)
==
4
)
{
w
=
nabs
(
w
);
x
=
dpy_x
-
x
-
w
;
}
else
if
(
sscanf
(
p
,
"%dx%d+%d-%d"
,
&
w
,
&
h
,
&
x
,
&
y
)
==
4
)
{
h
=
nabs
(
h
);
y
=
dpy_y
-
y
-
h
;
}
else
if
(
sscanf
(
p
,
"%dx%d-%d-%d"
,
&
w
,
&
h
,
&
x
,
&
y
)
==
4
)
{
w
=
nabs
(
w
);
h
=
nabs
(
h
);
x
=
dpy_x
-
x
-
w
;
y
=
dpy_y
-
y
-
h
;
}
else
{
if
(
!
parse_geom
(
p
,
&
w
,
&
h
,
&
x
,
&
y
,
dpy_x
,
dpy_y
))
{
if
(
*
p
!=
'\0'
)
{
rfbLog
(
"skipping invalid geometry: %s
\n
"
,
p
);
}
...
...
@@ -13055,15 +13693,6 @@ static int nap_ok = 0, nap_diff_count = 0;
static
int
scan_count
=
0
;
/* indicates which scan pattern we are on */
static
int
scan_in_progress
=
0
;
/* scan pattern jitter from x0rfbserver */
#define NSCAN 32
static
int
scanlines
[
NSCAN
]
=
{
0
,
16
,
8
,
24
,
4
,
20
,
12
,
28
,
10
,
26
,
18
,
2
,
22
,
6
,
30
,
14
,
1
,
17
,
9
,
25
,
7
,
23
,
15
,
31
,
19
,
3
,
27
,
11
,
29
,
13
,
5
,
21
};
typedef
struct
tile_change_region
{
/* start and end lines, along y, of the changed area inside a tile. */
unsigned
short
first_line
,
last_line
;
...
...
@@ -13087,6 +13716,10 @@ void initialize_tiles(void) {
tile_has_diff
=
(
unsigned
char
*
)
malloc
((
size_t
)
(
ntiles
*
sizeof
(
unsigned
char
)));
tile_has_xdamage_diff
=
(
unsigned
char
*
)
malloc
((
size_t
)
(
ntiles
*
sizeof
(
unsigned
char
)));
tile_row_has_xdamage_diff
=
(
unsigned
char
*
)
malloc
((
size_t
)
(
ntiles_y
*
sizeof
(
unsigned
char
)));
tile_tried
=
(
unsigned
char
*
)
malloc
((
size_t
)
(
ntiles
*
sizeof
(
unsigned
char
)));
tile_copied
=
(
unsigned
char
*
)
...
...
@@ -13109,6 +13742,14 @@ void free_tiles(void) {
free
(
tile_has_diff
);
tile_has_diff
=
NULL
;
}
if
(
tile_has_xdamage_diff
)
{
free
(
tile_has_xdamage_diff
);
tile_has_xdamage_diff
=
NULL
;
}
if
(
tile_row_has_xdamage_diff
)
{
free
(
tile_row_has_xdamage_diff
);
tile_row_has_xdamage_diff
=
NULL
;
}
if
(
tile_tried
)
{
free
(
tile_tried
);
tile_tried
=
NULL
;
...
...
@@ -15032,7 +15673,7 @@ static int scan_display(int ystart, int rescan) {
int
pixelsize
=
bpp
/
8
;
int
x
,
y
,
w
,
n
;
int
tile_count
=
0
;
int
whole_line
=
1
,
nodiffs
=
0
;
int
nodiffs
=
0
,
diff_hint
;
y
=
ystart
;
...
...
@@ -15043,6 +15684,15 @@ static int scan_display(int ystart, int rescan) {
while
(
y
<
dpy_y
)
{
if
(
using_xdamage
)
{
XD_tot
++
;
if
(
xdamage_hint_skip
(
y
))
{
XD_skip
++
;
y
+=
NSCAN
;
continue
;
}
}
/* grab the horizontal scanline from the display: */
X_LOCK
;
XRANDR_SET_TRAP_RET
(
-
1
,
"scan_display-set"
);
...
...
@@ -15054,7 +15704,7 @@ static int scan_display(int ystart, int rescan) {
src
=
scanline
->
data
;
dst
=
main_fb
+
y
*
main_bytes_per_line
;
if
(
whole_line
&&
!
memcmp
(
dst
,
src
,
main_bytes_per_line
))
{
if
(
!
memcmp
(
dst
,
src
,
main_bytes_per_line
))
{
/* no changes anywhere in scan line */
nodiffs
=
1
;
if
(
!
rescan
)
{
...
...
@@ -15066,6 +15716,7 @@ static int scan_display(int ystart, int rescan) {
x
=
0
;
while
(
x
<
dpy_x
)
{
n
=
(
x
/
tile_x
)
+
(
y
/
tile_y
)
*
ntiles_x
;
diff_hint
=
0
;
if
(
blackouts
)
{
if
(
blackout_line_skip
(
n
,
x
,
y
,
rescan
,
...
...
@@ -15081,6 +15732,10 @@ static int scan_display(int ystart, int rescan) {
x
+=
NSCAN
;
continue
;
}
}
else
if
(
xdamage_tile_count
&&
tile_has_xdamage_diff
[
n
])
{
tile_has_xdamage_diff
[
n
]
=
2
;
diff_hint
=
1
;
}
/* set ptrs to correspond to the x offset: */
...
...
@@ -15094,7 +15749,7 @@ static int scan_display(int ystart, int rescan) {
w
=
NSCAN
;
}
if
(
memcmp
(
dst
,
src
,
w
*
pixelsize
))
{
if
(
diff_hint
||
memcmp
(
dst
,
src
,
w
*
pixelsize
))
{
/* found a difference, record it: */
if
(
!
blackouts
)
{
tile_has_diff
[
n
]
=
1
;
...
...
@@ -15129,9 +15784,15 @@ int scan_for_updates(int count_only) {
double
frac3
=
0
.
02
;
/* do scan_display() again after copy_tiles() */
for
(
i
=
0
;
i
<
ntiles
;
i
++
)
{
tile_has_diff
[
i
]
=
0
;
tile_has_xdamage_diff
[
i
]
=
0
;
tile_tried
[
i
]
=
0
;
tile_copied
[
i
]
=
0
;
}
for
(
i
=
0
;
i
<
ntiles_y
;
i
++
)
{
/* could be useful, currently not used */
tile_row_has_xdamage_diff
[
i
]
=
0
;
}
xdamage_tile_count
=
0
;
/*
* n.b. this program has only been tested so far with
...
...
@@ -15142,15 +15803,16 @@ int scan_for_updates(int count_only) {
scan_count
++
;
scan_count
%=
NSCAN
;
if
(
scan_count
%
(
NSCAN
/
4
)
==
0
)
{
/* some periodic maintenance */
if
(
subwin
)
{
set_offset
();
/* follow the subwindow */
}
if
(
indexed_color
)
{
/* check for changed colormap */
set_colormap
(
0
);
}
/* some periodic maintenance */
if
(
subwin
)
{
set_offset
();
/* follow the subwindow */
}
if
(
indexed_color
&&
scan_count
%
4
==
0
)
{
/* check for changed colormap */
set_colormap
(
0
);
}
if
(
using_xdamage
)
{
collect_xdamage
(
scan_count
);
}
}
...
...
@@ -15172,6 +15834,20 @@ int scan_for_updates(int count_only) {
return
tile_count
;
}
if
(
xdamage_tile_count
)
{
/* pick up "known" damaged tiles we missed in scan_display() */
for
(
i
=
0
;
i
<
ntiles
;
i
++
)
{
if
(
tile_has_diff
[
i
])
{
continue
;
}
if
(
tile_has_xdamage_diff
[
i
]
==
1
)
{
tile_has_xdamage_diff
[
i
]
=
2
;
tile_has_diff
[
i
]
=
1
;
tile_count
++
;
}
}
}
nap_set
(
tile_count
);
if
(
fs_factor
&&
frac1
>=
fs_frac
)
{
...
...
@@ -15349,9 +16025,11 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui,
exit
(
0
);
}
if
(
getenv
(
"DISPLAY"
)
!=
NULL
)
{
/* worst case */
x11vnc_xdisplay
=
strdup
(
getenv
(
"DISPLAY"
));
}
if
(
use_dpy
)
{
/* better */
x11vnc_xdisplay
=
strdup
(
use_dpy
);
}
if
(
connect_to_x11vnc
)
{
...
...
@@ -15360,6 +16038,8 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui,
if
(
!
client_connect_file
)
{
if
(
getenv
(
"XAUTHORITY"
)
!=
NULL
)
{
old_xauth
=
strdup
(
getenv
(
"XAUTHORITY"
));
}
else
{
old_xauth
=
strdup
(
""
);
}
dpy
=
XOpenDisplay
(
x11vnc_xdisplay
);
if
(
!
dpy
&&
auth_file
)
{
...
...
@@ -15367,11 +16047,12 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui,
dpy
=
XOpenDisplay
(
x11vnc_xdisplay
);
}
if
(
!
dpy
&&
!
x11vnc_xdisplay
)
{
/* worstest case */
x11vnc_xdisplay
=
strdup
(
":0"
);
dpy
=
XOpenDisplay
(
x11vnc_xdisplay
);
}
if
(
!
dpy
)
{
fprintf
(
stderr
,
"gui: could not open "
fprintf
(
stderr
,
"gui: could not open
x11vnc
"
"display: %s
\n
"
,
NONUL
(
x11vnc_xdisplay
));
exit
(
1
);
}
...
...
@@ -15398,7 +16079,7 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui,
}
set_env
(
"X11VNC_XDISPLAY"
,
x11vnc_xdisplay
);
if
(
getenv
(
"XAUTHORITY"
)
!=
NULL
)
{
set_env
(
"X11VNC_
XAUTHORITY
"
,
getenv
(
"XAUTHORITY"
));
set_env
(
"X11VNC_
AUTH_FILE
"
,
getenv
(
"XAUTHORITY"
));
}
if
(
rc
==
0
)
{
fprintf
(
stderr
,
"gui: ping succeeded.
\n
"
);
...
...
@@ -15411,7 +16092,15 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui,
XCloseDisplay
(
dpy
);
}
if
(
old_xauth
)
{
set_env
(
"XAUTHORITY"
,
old_xauth
);
if
(
*
old_xauth
==
'\0'
)
{
/* wasn't set, hack it out if it is now */
char
*
xauth
=
getenv
(
"XAUTHORITY"
);
if
(
xauth
)
{
*
(
xauth
-
2
)
=
'_'
;
/* yow */
}
}
else
{
set_env
(
"XAUTHORITY"
,
old_xauth
);
}
free
(
old_xauth
);
}
}
...
...
@@ -15459,8 +16148,6 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui,
if
(
simple_gui
)
{
set_env
(
"X11VNC_SIMPLE_GUI"
,
"1"
);
}
if
(
auth_file
)
{
}
sprintf
(
cmd
,
"%s -"
,
wish
);
tmpf
=
tmpfile
();
...
...
@@ -16509,6 +17196,10 @@ static void watch_loop(void) {
continue
;
}
if
(
using_xdamage
)
{
check_xdamage_state
();
}
if
(
watch_bell
)
{
/* n.b. assumes -nofb folks do not want bell... */
check_bell_event
();
...
...
@@ -16618,6 +17309,12 @@ static void print_help(int mode) {
" shifts a root view to it: this shows SaveUnders menus,
\n
"
" etc, although they will be clipped if they extend beyond
\n
"
" the window.
\n
"
"-clip WxH+X+Y Only show the sub-region of the full display that
\n
"
" corresponds to the rectangle with size WxH and offset
\n
"
" +X+Y. The VNC display has size WxH (i.e. smaller than
\n
"
" the full display). This also works for -id/-sid mode
\n
"
" where the offset is relative to the upper left corner
\n
"
" of the selected window.
\n
"
"-flashcmap In 8bpp indexed color, let the installed colormap flash
\n
"
" as the pointer moves from window to window (slow).
\n
"
"-notruecolor For 8bpp displays, force indexed color (i.e. a colormap)
\n
"
...
...
@@ -17292,6 +17989,36 @@ static void print_help(int mode) {
" to really throttle down the screen polls (i.e. sleep
\n
"
" for about 1.5 secs). Use 0 to disable. Default: %d
\n
"
"
\n
"
"-noxdamage Do not use the X DAMAGE extension to detect framebuffer
\n
"
" changes even if it is available.
\n
"
"
\n
"
" x11vnc's use of the DAMAGE extension: 1) significantly
\n
"
" reduces the load when the screen is not changing much,
\n
"
" and 2) detects changed areas (small ones by default)
\n
"
" more quickly.
\n
"
"
\n
"
" Currently the DAMAGE extension is overly conservative
\n
"
" and often reports large areas (e.g. a whole terminal
\n
"
" or browser window) as damaged even though the actual
\n
"
" changed region is much smaller (sometimes just a few
\n
"
" pixels). So heuristics were introduced to skip large
\n
"
" areas and use the damage rectangles only as
\"
hints
\"\n
"
" for the traditional scanline polling. The following
\n
"
" tuning parameters are introduced to adjust this
\n
"
" behavior:
\n
"
"
\n
"
"-xd_area A Set the largest DAMAGE rectangle area
\"
A
\"
(in
\n
"
" pixels: width * height) to trust as truly damaged:
\n
"
" the rectangle will be copied from the framebuffer
\n
"
" (slow) no matter what. Set to zero to trust *all*
\n
"
" rectangles. Default: %d
\n
"
"-xd_mem f Set how long DAMAGE rectangles should be
\"
remembered
\"
,
\n
"
"
\"
f
\"
is a floating point number and is in units of the
\n
"
" scanline repeat cycle time (%d iterations). The default
\n
"
" (%.1f) should give no painting problems. Increase it if
\n
"
" there are problems or decrease it to live on the edge
\n
"
" (perhaps useful on a slow machine).
\n
"
"
\n
"
"-sigpipe string Broken pipe (SIGPIPE) handling.
\"
string
\"
can be
\n
"
"
\"
ignore
\"
or
\"
exit
\"
. For
\"
ignore
\"
libvncserver
\n
"
" will handle the abrupt loss of a client and continue,
\n
"
...
...
@@ -17417,6 +18144,7 @@ static void print_help(int mode) {
" id:windowid set -id window to
\"
windowid
\"
. empty
\n
"
" or
\"
root
\"
to go back to root window
\n
"
" sid:windowid set -sid window to
\"
windowid
\"\n
"
" clip:WxH+X+Y set -clip mode to
\"
WxH+X+Y
\"\n
"
" flashcmap enable -flashcmap mode.
\n
"
" noflashcmap disable -flashcmap mode.
\n
"
" notruecolor enable -notruecolor mode.
\n
"
...
...
@@ -17559,6 +18287,10 @@ static void print_help(int mode) {
" nap enable -nap mode.
\n
"
" nonap disable -nap mode.
\n
"
" sb:n set -sb to n s, same as screen_blank:n
\n
"
" xdamage enable xdamage polling hints.
\n
"
" noxdamage disable xdamage polling hints.
\n
"
" xd_area:A set -xd_area max pixel area to
\"
A
\"\n
"
" xd_mem:f set -xd_mem remembrance to
\"
f
\"\n
"
" fs:frac set -fs fraction to
\"
frac
\"
, e.g. 0.5
\n
"
" gaps:n set -gaps to n.
\n
"
" grow:n set -grow to n.
\n
"
...
...
@@ -17625,7 +18357,7 @@ static void print_help(int mode) {
" variables correspond to the presence of X extensions):
\n
"
"
\n
"
" ans= stop quit exit shutdown ping blacken zero
\n
"
" refresh reset close disconnect id sid waitmapped
\n
"
" refresh reset close disconnect id sid
clip
waitmapped
\n
"
" nowaitmapped flashcmap noflashcmap truecolor notruecolor
\n
"
" overlay nooverlay overlay_cursor overlay_yescursor
\n
"
" nooverlay_nocursor nooverlay_cursor nooverlay_yescursor
\n
"
...
...
@@ -17641,9 +18373,10 @@ static void print_help(int mode) {
" remap repeat norepeat fb nofb bell nobell sel
\n
"
" nosel primary noprimary cursorshape nocursorshape
\n
"
" cursorpos nocursorpos cursor show_cursor noshow_cursor
\n
"
" nocursor xfixes noxfixes alphacut alphafrac alpharemove
\n
"
" noalpharemove alphablend noalphablend xwarp xwarppointer
\n
"
" noxwarp noxwarppointer buttonmap dragging nodragging
\n
"
" nocursor xfixes noxfixes xdamage noxdamage xd_area
\n
"
" xd_mem alphacut alphafrac alpharemove noalpharemove
\n
"
" alphablend noalphablend xwarp xwarppointer noxwarp
\n
"
" noxwarppointer buttonmap dragging nodragging
\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
"
...
...
@@ -17654,15 +18387,16 @@ static void print_help(int mode) {
" nodontdisconnect desktop noremote
\n
"
"
\n
"
" aro= display vncdisplay desktopname http_url auth
\n
"
" users rootshift scale_str scaled_x scaled_y scale_numer
\n
"
" scale_denom scale_fac scaling_noblend scaling_nomult4
\n
"
" scaling_pad scaling_interpolate inetd safer unsafe
\n
"
" passwdfile using_shm logfile o rc norc h help V version
\n
"
" lastmod bg sigpipe threads clients client_count pid
\n
"
" ext_xtest ext_xkb ext_xshm ext_xinerama ext_overlay
\n
"
" ext_xfixes ext_xdamage ext_xrandr rootwin num_buttons
\n
"
" button_mask mouse_x mouse_y bpp depth indexed_color
\n
"
" dpy_x dpy_y rfbauth passwd
\n
"
" users rootshift clipshift scale_str scaled_x scaled_y
\n
"
" scale_numer scale_denom scale_fac scaling_noblend
\n
"
" scaling_nomult4 scaling_pad scaling_interpolate inetd
\n
"
" safer unsafe passwdfile using_shm logfile o rc norc
\n
"
" h help V version lastmod bg sigpipe threads clients
\n
"
" client_count pid ext_xtest ext_xkb ext_xshm ext_xinerama
\n
"
" ext_overlay ext_xfixes ext_xdamage ext_xrandr rootwin
\n
"
" num_buttons button_mask mouse_x mouse_y bpp depth
\n
"
" indexed_color dpy_x dpy_y wdpy_x wdpy_y off_x off_y
\n
"
" cdpy_x cdpy_y coff_x coff_y rfbauth passwd
\n
"
"
\n
"
"-sync By default -remote commands are run asynchronously, that
\n
"
" is, the request is posted and the program immediately
\n
"
...
...
@@ -17761,6 +18495,7 @@ static void print_help(int mode) {
waitms
,
take_naps
?
"take naps"
:
"no naps"
,
screen_blank
,
xdamage_max_area
,
NSCAN
,
xdamage_memory
,
use_threads
?
"-threads"
:
"-nothreads"
,
fs_frac
,
gaps_fill
,
...
...
@@ -18126,6 +18861,7 @@ int main(int argc, char* argv[]) {
int
vpw_loc
=
-
1
;
int
dt
=
0
,
bg
=
0
;
int
got_rfbwait
=
0
,
got_deferupdate
=
0
,
got_defer
=
0
;
int
got_noxdamage
=
0
;
/* used to pass args we do not know about to rfbGetScreen(): */
int
argc_vnc
=
1
;
char
*
argv_vnc
[
128
];
...
...
@@ -18211,6 +18947,9 @@ int main(int argc, char* argv[]) {
}
}
else
if
(
!
strcmp
(
arg
,
"-waitmapped"
))
{
subwin_wait_mapped
=
1
;
}
else
if
(
!
strcmp
(
arg
,
"-clip"
))
{
CHECK_ARGC
clip_str
=
strdup
(
argv
[
++
i
]);
}
else
if
(
!
strcmp
(
arg
,
"-flashcmap"
))
{
flash_cmap
=
1
;
}
else
if
(
!
strcmp
(
arg
,
"-notruecolor"
))
{
...
...
@@ -18468,6 +19207,24 @@ int main(int argc, char* argv[]) {
}
else
if
(
!
strcmp
(
arg
,
"-sb"
))
{
CHECK_ARGC
screen_blank
=
atoi
(
argv
[
++
i
]);
}
else
if
(
!
strcmp
(
arg
,
"-noxdamage"
))
{
using_xdamage
=
0
;
use_xdamage_hints
=
0
;
got_noxdamage
=
1
;
}
else
if
(
!
strcmp
(
arg
,
"-xd_area"
))
{
int
tn
;
CHECK_ARGC
tn
=
atoi
(
argv
[
++
i
]);
if
(
tn
>=
0
)
{
xdamage_max_area
=
tn
;
}
}
else
if
(
!
strcmp
(
arg
,
"-xd_mem"
))
{
double
f
;
CHECK_ARGC
f
=
atof
(
argv
[
++
i
]);
if
(
f
>=
0
.
0
)
{
xdamage_memory
=
f
;
}
}
else
if
(
!
strcmp
(
arg
,
"-sigpipe"
))
{
CHECK_ARGC
if
(
known_sigpipe_mode
(
argv
[
++
i
]))
{
...
...
@@ -18820,6 +19577,8 @@ int main(int argc, char* argv[]) {
:
"null"
);
fprintf
(
stderr
,
" subwin: 0x%lx
\n
"
,
subwin
);
fprintf
(
stderr
,
" -sid mode: %d
\n
"
,
rootshift
);
fprintf
(
stderr
,
" clip: %s
\n
"
,
clip_str
?
clip_str
:
"null"
);
fprintf
(
stderr
,
" flashcmap: %d
\n
"
,
flash_cmap
);
fprintf
(
stderr
,
" force_idx: %d
\n
"
,
force_indexed_color
);
fprintf
(
stderr
,
" visual: %s
\n
"
,
visual_str
?
visual_str
...
...
@@ -18909,6 +19668,9 @@ int main(int argc, char* argv[]) {
fprintf
(
stderr
,
" waitms: %d
\n
"
,
waitms
);
fprintf
(
stderr
,
" take_naps: %d
\n
"
,
take_naps
);
fprintf
(
stderr
,
" sb: %d
\n
"
,
screen_blank
);
fprintf
(
stderr
,
" xdamage: %d
\n
"
,
!
got_noxdamage
);
fprintf
(
stderr
,
" xd_area: %d
\n
"
,
xdamage_max_area
);
fprintf
(
stderr
,
" xd_mem: %.3f
\n
"
,
xdamage_memory
);
fprintf
(
stderr
,
" sigpipe: %s
\n
"
,
sigpipe
?
sigpipe
:
"null"
);
fprintf
(
stderr
,
" threads: %d
\n
"
,
use_threads
);
...
...
@@ -19050,6 +19812,12 @@ int main(int argc, char* argv[]) {
xdamage_present
=
1
;
}
#endif
if
(
!
quiet
&&
xdamage_present
&&
!
got_noxdamage
)
{
rfbLog
(
"X DAMAGE available on display, using it for"
" polling hints
\n
"
);
rfbLog
(
" to disable this behavior use: "
"'-noxdamage'
\n
"
);
}
overlay_present
=
0
;
#ifdef SOLARIS_OVERLAY
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment