Commit 5c13bd0c authored by runge's avatar runge

x11vnc: -users lurk=, -solid for cde, -gui ez,.. beginner mode.

parent 86ccf267
2005-02-14 Karl Runge <runge@karlrunge.com>
* x11vnc: -users lurk=, -solid for cde, -gui ez,.. beginner mode.
2005-02-10 Karl Runge <runge@karlrunge.com> 2005-02-10 Karl Runge <runge@karlrunge.com>
* x11vnc: -input option to fine tune allowed client input, * x11vnc: -input option to fine tune allowed client input,
additions to remote control and gui for this. additions to remote control and gui for this.
......
2005-02-14 Karl Runge <runge@karlrunge.com>
* cleanup -users stuff, add "lurk=" mode
* support cde in -solid
* simple gui mode for beginners, -gui ez,...
2005-02-10 Karl Runge <runge@karlrunge.com> 2005-02-10 Karl Runge <runge@karlrunge.com>
* Add -input to fine tune client input (keystroke, mouse motion, * Add -input to fine tune client input (keystroke, mouse motion,
and button presses). Allow per-client setting via remote cntl. and button presses). Allow per-client setting via remote cntl.
......
x11vnc README file Date: Thu Feb 10 23:33:03 EST 2005 x11vnc README file Date: Mon Feb 14 14:23:56 EST 2005
The following information is taken from these URLs: The following information is taken from these URLs:
...@@ -889,7 +889,9 @@ ls -l ./x11vnc/x11vnc ...@@ -889,7 +889,9 @@ ls -l ./x11vnc/x11vnc
the person sitting at the X session types "xhost +localhost" then one the person sitting at the X session types "xhost +localhost" then one
should be able to attach x11vnc to the session (from the same should be able to attach x11vnc to the session (from the same
machine). The person could then type "xhost -localhost" after x11vnc machine). The person could then type "xhost -localhost" after x11vnc
has connected to go back to the default permissions. has connected to go back to the default permissions. Also, for some
situations the -users lurk= option may be of use (please read the
documentation on the -users option).
Some Linux distributions or display managers may set XAUTHORITY to a Some Linux distributions or display managers may set XAUTHORITY to a
random local filename. You need to dig out where they have hidden the random local filename. You need to dig out where they have hidden the
...@@ -2246,9 +2248,9 @@ ied) ...@@ -2246,9 +2248,9 @@ ied)
has been added to allow exact extraction of the mouse cursor shape. has been added to allow exact extraction of the mouse cursor shape.
The only issue is the handling of alpha channel transparency in The only issue is the handling of alpha channel transparency in
cursors (they must be approximated). XFIXES is available on recent cursors (they must be approximated). XFIXES is available on recent
Linux Xorg based distros and Solaris 10 express (on Solaris you will Linux Xorg based distros and Solaris 10 (on Solaris you will need to
need to add "-L /usr/openwin/sfw/lib -R /usr/openwin/sfw/lib" to add "-L /usr/openwin/sfw/lib -R /usr/openwin/sfw/lib" to LDFLAGS for
LDFLAGS for configure to enable it). configure to enable it).
Q-43: When using XFIXES cursorshape mode, some of the cursors look Q-43: When using XFIXES cursorshape mode, some of the cursors look
really bad with extra black borders around the cursor and other cruft. really bad with extra black borders around the cursor and other cruft.
...@@ -3088,8 +3090,8 @@ x11vnc: a VNC server for real X displays ...@@ -3088,8 +3090,8 @@ x11vnc: a VNC server for real X displays
Here are all of x11vnc command line options: Here are all of x11vnc command line options:
% x11vnc -opts (see below for -help long descriptions) % x11vnc -opts (see below for -help long descriptions)
x11vnc: allow VNC connections to real X11 displays. 0.7.1pre lastmod: 2005-02-0 x11vnc: allow VNC connections to real X11 displays. 0.7.1pre lastmod: 2005-02-1
5 4
x11vnc options: x11vnc options:
-display disp -auth file -display disp -auth file
...@@ -3102,9 +3104,10 @@ x11vnc options: ...@@ -3102,9 +3104,10 @@ x11vnc options:
-timeout n -inetd -timeout n -inetd
-connect string -vncconnect -connect string -vncconnect
-novncconnect -allow host1[,host2..] -novncconnect -allow host1[,host2..]
-localhost -viewpasswd string -localhost -input string
-passwdfile filename -storepasswd pass file -viewpasswd string -passwdfile filename
-accept string -gone string -storepasswd pass file -accept string
-gone string -users list
-noshm -flipbyteorder -noshm -flipbyteorder
-onetile -solid [color] -onetile -solid [color]
-blackout string -xinerama -blackout string -xinerama
...@@ -3163,8 +3166,8 @@ libvncserver options: ...@@ -3163,8 +3166,8 @@ libvncserver options:
% x11vnc -help % x11vnc -help
x11vnc: allow VNC connections to real X11 displays. 0.7.1pre lastmod: 2005-02-0 x11vnc: allow VNC connections to real X11 displays. 0.7.1pre lastmod: 2005-02-1
5 4
Typical usage is: Typical usage is:
...@@ -3210,7 +3213,8 @@ Options: ...@@ -3210,7 +3213,8 @@ Options:
environment variable to "disp". environment variable to "disp".
-auth file Set the X authority file to be "file", equivalent to -auth file Set the X authority file to be "file", equivalent to
setting the XAUTHORITY environment variable to "file" setting the XAUTHORITY environment variable to "file"
before startup. See Xsecurity(7), xauth(1) man pages. before startup. Same as -xauth file. See Xsecurity(7),
xauth(1) man pages for more info.
-id windowid Show the window corresponding to "windowid" not -id windowid Show the window corresponding to "windowid" not
the entire display. New windows like popup menus, the entire display. New windows like popup menus,
...@@ -3307,10 +3311,13 @@ Options: ...@@ -3307,10 +3311,13 @@ Options:
-connect string For use with "vncviewer -listen" reverse connections. -connect string For use with "vncviewer -listen" reverse connections.
If "string" has the form "host" or "host:port" If "string" has the form "host" or "host:port"
the connection is made once at startup. Use commas the connection is made once at startup. Use commas
for a list of host's and host:port's. If "string" for a list of host's and host:port's.
contains "/" it is instead interpreted as a file to
periodically check for new hosts. The first line is If "string" contains "/" it is instead interpreted
read and then the file is truncated. as a file to periodically check for new hosts.
The first line is read and then the file is truncated.
Be careful for this usage mode if x11vnc is running as
root (e.g. via inetd(1) or gdm(1)).
-vncconnect Monitor the VNC_CONNECT X property set by the standard -vncconnect Monitor the VNC_CONNECT X property set by the standard
-novncconnect VNC program vncconnect(1). When the property is -novncconnect VNC program vncconnect(1). When the property is
set to "host" or "host:port" establish a reverse set to "host" or "host:port" establish a reverse
...@@ -3327,14 +3334,30 @@ Options: ...@@ -3327,14 +3334,30 @@ Options:
each time a new client connects. Lines can be commented each time a new client connects. Lines can be commented
out with the "#" character in the usual way. out with the "#" character in the usual way.
-localhost Same as -allow 127.0.0.1 -localhost Same as -allow 127.0.0.1
-input string Fine tuning of allowed user input. If "string" does
not contain a comma "," the tuning applies only to
normal clients. Otherwise the part before "," is
for normal clients and the part after for view-only
clients. "K" is for Keystroke input, "M" for
Mouse-motion input, and "B" for Button-click input.
Their presence in the string enables that type of input.
E.g. "-input M" means normal users can only move
the mouse and "-input KMB,M" lets normal users do
anything and enables view-only users to move the mouse.
This option is ignored when a global -viewonly is in
effect (all input is discarded).
-viewpasswd string Supply a 2nd password for view-only logins. The -passwd -viewpasswd string Supply a 2nd password for view-only logins. The -passwd
(full-access) password must also be supplied. (full-access) password must also be supplied.
-passwdfile filename Specify libvncserver -passwd via the first line of -passwdfile filename Specify libvncserver -passwd via the first line of
the file "filename" instead of via command line. the file "filename" instead of via command line.
If a second non blank line exists in the file it is If a second non blank line exists in the file it
taken as a view-only password (i.e. -viewpasswd) Note: is taken as a view-only password (i.e. -viewpasswd)
this is a simple plaintext passwd, see also -rfbauth To supply an empty password for either field use the
and -storepasswd below for obfuscated passwords. string "__EMPTY__". Note: -passwdfile is a simple
plaintext passwd, see also -rfbauth and -storepasswd
below for obfuscated passwords. Neither should be
readable by others.
-storepasswd pass file Store password "pass" as the VNC password in the -storepasswd pass file Store password "pass" as the VNC password in the
file "file". Once the password is stored the file "file". Once the password is stored the
program exits. Use the password via "-rfbauth file" program exits. Use the password via "-rfbauth file"
...@@ -3348,6 +3371,11 @@ Options: ...@@ -3348,6 +3371,11 @@ Options:
otherwise the client is rejected. See below for an otherwise the client is rejected. See below for an
extension to accept a client view-only. extension to accept a client view-only.
If x11vnc is running as root (say from inetd(1) or from
display managers xdm(1), gdm(1), etc), think about the
security implications carefully before supplying this
option (likewise for the -gone option).
Environment: The RFB_CLIENT_IP environment variable will Environment: The RFB_CLIENT_IP environment variable will
be set to the incoming client IP number and the port be set to the incoming client IP number and the port
in RFB_CLIENT_PORT (or -1 if unavailable). Similarly, in RFB_CLIENT_PORT (or -1 if unavailable). Similarly,
...@@ -3399,6 +3427,74 @@ Options: ...@@ -3399,6 +3427,74 @@ Options:
in -accept. Unlike -accept, the command return code in -accept. Unlike -accept, the command return code
is not interpreted by x11vnc. Example: -gone 'xlock &' is not interpreted by x11vnc. Example: -gone 'xlock &'
-users list If x11vnc is started as root (say from inetd(1) or
from display managers xdm(1), gdm(1), etc), then as
soon as possible after connections to the display are
established try to switch to one of the users in the
comma separated "list". If x11vnc is not running as
root this option is ignored.
Why use this option? In general it is not needed
since x11vnc is already connected to the display and
can perform its primary functions. The option was
added to make some of the *external* utility commands
x11vnc occasionally runs work properly. In particular
under GNOME and KDE to implement the "-solid color"
feature external commands (gconftool-2 and dcop) must be
run as the user owning the desktop session. Since this
option switches userid it also affects the userid used
to run the processes for the -accept and -gone options.
It also affects the ability to read files for options
such as -connect, -allow, and -remap. Note that the
-connect file is also sometimes written to.
So be careful with this option since in many situations
its use can decrease security.
The switch to a user will only take place if the
display can still be successfully opened as that user
(this is primarily to try to guess the actual owner
of the session). Example: "-users fred,wilma,betty".
Note that a malicious user "barney" by quickly using
"xhost +" when logging in may get x11vnc to switch
to user "fred". What happens next?
Under display managers it may be a long time before
the switch succeeds (i.e. a user logs in). To make
it switch immediately regardless if the display
can be reopened prefix the username with the +
character. E.g. "-users +bob" or "-users +nobody".
The latter (i.e. switching immediately to user
"nobody") is probably the only use of this option
that increases security.
To immediately switch to a user *before* connections to
the display are made or any files opened use the "="
character: "-users =bob". That user needs to be able
to open the display of course.
The special user "guess=" means to examine the utmpx
database (see who(1)) looking for a user attached to
the display number (from DISPLAY or -display option)
and try him/her. To limit the list of guesses, use:
"-users guess=bob,betty".
Even more sinister is the special user "lurk=" that
means to try to guess the DISPLAY from the utmpx login
database as well. So it "lurks" waiting for anyone
to log into an X session and then connects to it.
Specify a list of users after the = to limit which
users will be tried. If the first user in the list
is something like ":0" or ":0-2" that indicates a
range of DISPLAY numbers that will be tried (regardless
of whether they are in the utmpx database) for all
users that are logged in. Examples: "-users lurk="
and "-users lurk=:0-1,bob,mary"
Be especially careful using the "guess=" and "lurk="
modes. They are not recommended for use on machines
with untrustworthy local users.
-noshm Do not use the MIT-SHM extension for the polling. -noshm Do not use the MIT-SHM extension for the polling.
Remote displays can be polled this way: be careful this Remote displays can be polled this way: be careful this
can use large amounts of network bandwidth. This is can use large amounts of network bandwidth. This is
...@@ -3414,15 +3510,18 @@ Options: ...@@ -3414,15 +3510,18 @@ Options:
try to change the desktop background to a solid color. try to change the desktop background to a solid color.
The [color] is optional: the default color is "cyan4". The [color] is optional: the default color is "cyan4".
For a different one specify the X color (rgb.txt name, For a different one specify the X color (rgb.txt name,
e.g. "darkblue" or numerical "#RRGGBB"). Currently e.g. "darkblue" or numerical "#RRGGBB").
this option only works on GNOME, KDE, and classic X
(i.e. with the background image on the root window). Currently this option only works on GNOME, KDE, CDE,
The "gconftool-2" and "dcop" external commands are and classic X (i.e. with the background image on the
run for GNOME and KDE respectively. Other desktops root window). The "gconftool-2" and "dcop" external
won't work, e.g. XFCE (send us the corresponding commands are run for GNOME and KDE respectively.
commands if you find them). If x11vnc guesses your Other desktops won't work, e.g. XFCE (send us the
desktop incorrectly, you can force it by prefixing corresponding commands if you find them). If x11vnc is
color with "gnome:", "kde:", or "root:". running as root (inetd(1) or gdm(1)), the -users option
may be needed for GNOME and KDE. If x11vnc guesses
your desktop incorrectly, you can force it by prefixing
color with "gnome:", "kde:", "cde:" or "root:".
-blackout string Black out rectangles on the screen. "string" is a -blackout string Black out rectangles on the screen. "string" is a
comma separated list of WxH+X+Y type geometries for comma separated list of WxH+X+Y type geometries for
each rectangle. each rectangle.
...@@ -3818,9 +3917,11 @@ Options: ...@@ -3818,9 +3917,11 @@ Options:
up on the X display in the environment variable DISPLAY. up on the X display in the environment variable DISPLAY.
"gui-opts" can be a comma separated list of items. "gui-opts" can be a comma separated list of items.
Currently there are only two types of items: 1) a gui Currently there are these types of items: 1) a gui mode,
mode and 2) the X display the gui should display on. a 2) gui "simplicity", and 3) the X display the gui
The gui mode can be "start", "conn", or "wait" should display on.
1) The gui mode can be "start", "conn", or "wait"
"start" is the default mode above and is not required. "start" is the default mode above and is not required.
"conn" means do not automatically start up x11vnc, "conn" means do not automatically start up x11vnc,
but instead just try to connect to an existing x11vnc but instead just try to connect to an existing x11vnc
...@@ -3828,16 +3929,22 @@ Options: ...@@ -3828,16 +3929,22 @@ Options:
else (you will later instruct the gui to start x11vnc else (you will later instruct the gui to start x11vnc
or connect to an existing one.) or connect to an existing one.)
Note the possible confusion regarding the potentially 2) The gui simplicity is off by default (a power-user
gui with all options is presented) To start with
something less daunting supply the string "simple"
("ez" is an alias for this). Once the gui is
started you can toggle between the two with "Misc ->
simple_gui".
3) Note the possible confusion regarding the potentially
two different X displays: x11vnc polls one, but you two different X displays: x11vnc polls one, but you
may want the gui to appear on another. For example, if may want the gui to appear on another. For example, if
you ssh in and x11vnc is not running yet you may want you ssh in and x11vnc is not running yet you may want
the gui to come back to you via your ssh redirected X the gui to come back to you via your ssh redirected X
display (e.g. localhost:10). display (e.g. localhost:10).
Examples: "x11vnc -gui", "x11vnc -gui localhost:10", Examples: "x11vnc -gui", "x11vnc -gui ez"
"x11vnc -gui :10", "x11vnc -gui wait,:10", "x11vnc -gui localhost:10", "x11vnc -gui conn,host:0"
"x11vnc -gui <x11vnc-opts...>"
If you do not specify a gui X display in "gui-opts" If you do not specify a gui X display in "gui-opts"
then the DISPLAY environment variable and -display then the DISPLAY environment variable and -display
...@@ -3935,6 +4042,11 @@ Options: ...@@ -3935,6 +4042,11 @@ Options:
use "-host" to delete a single host use "-host" to delete a single host
localhost enable -localhost mode localhost enable -localhost mode
nolocalhost disable -localhost mode nolocalhost disable -localhost mode
input:str set -input to "str", empty to disable.
client_input:str set the K, M, B -input on a per-client
basis. select which client as for
disconnect, e.g. client_input:host:MB
or client_input:0x2:K
accept:cmd set -accept "cmd" (empty to disable). accept:cmd set -accept "cmd" (empty to disable).
gone:cmd set -gone "cmd" (empty to disable). gone:cmd set -gone "cmd" (empty to disable).
noshm enable -noshm mode. noshm enable -noshm mode.
...@@ -4103,13 +4215,13 @@ Options: ...@@ -4103,13 +4215,13 @@ Options:
xrandr_mode padgeom quiet q noquiet modtweak nomodtweak xrandr_mode padgeom quiet q noquiet modtweak nomodtweak
xkb noxkb skip_keycodes add_keysyms noadd_keysyms xkb noxkb skip_keycodes add_keysyms noadd_keysyms
clear_mods noclear_mods clear_keys noclear_keys clear_mods noclear_mods clear_keys noclear_keys
remap repeat norepeat fb nofb bell nobell sel nosel remap repeat norepeat fb nofb bell nobell sel
primary noprimary cursorshape nocursorshape cursorpos nosel primary noprimary cursorshape nocursorshape
nocursorpos cursor show_cursor noshow_cursor cursorpos nocursorpos cursor show_cursor noshow_cursor
nocursor xfixes noxfixes alphacut alphafrac nocursor xfixes noxfixes alphacut alphafrac alpharemove
alpharemove noalpharemove alphablend noalphablend noalpharemove alphablend noalphablend xwarp xwarppointer
xwarp xwarppointer noxwarp noxwarppointer buttonmap noxwarp noxwarppointer buttonmap dragging nodragging
dragging nodragging pointer_mode pm input_skip speeds pointer_mode pm input_skip input client_input speeds
debug_pointer dp nodebug_pointer nodp debug_keyboard dk debug_pointer dp nodebug_pointer nodp debug_keyboard dk
nodebug_keyboard nodk deferupdate defer wait rfbwait nodebug_keyboard nodk deferupdate defer wait rfbwait
nap nonap sb screen_blank fs gaps grow fuzz snapfb nap nonap sb screen_blank fs gaps grow fuzz snapfb
...@@ -4119,7 +4231,7 @@ Options: ...@@ -4119,7 +4231,7 @@ Options:
nodontdisconnect desktop noremote nodontdisconnect desktop noremote
aro= display vncdisplay desktopname http_url auth aro= display vncdisplay desktopname http_url auth
rootshift scale_str scaled_x scaled_y scale_numer users rootshift scale_str scaled_x scaled_y scale_numer
scale_denom scale_fac scaling_noblend scaling_nomult4 scale_denom scale_fac scaling_noblend scaling_nomult4
scaling_pad scaling_interpolate inetd safer unsafe scaling_pad scaling_interpolate inetd safer unsafe
passwdfile using_shm logfile o rc norc h help V version passwdfile using_shm logfile o rc norc h help V version
...@@ -4157,10 +4269,13 @@ Options: ...@@ -4157,10 +4269,13 @@ Options:
Note that if they can modify VNC_CONNECT, they could Note that if they can modify VNC_CONNECT, they could
also run their own x11vnc and have complete control also run their own x11vnc and have complete control
of the desktop. If the "-connect /path/to/file" of the desktop. If the "-connect /path/to/file"
channel is being used, obviously anyone who can write channel is being used, obviously anyone who can
to /path/to/file can remotely control x11vnc. So be write to /path/to/file can remotely control x11vnc.
sure to protect the X display and that file's write So be sure to protect the X display and that file's
permissions. write permissions.
To disable the VNC_CONNECT property channel completely
use -novncconnect.
-unsafe If x11vnc is running as root (e.g. inetd or Xsetup for -unsafe If x11vnc is running as root (e.g. inetd or Xsetup for
a display manager) a few remote commands are disabled a display manager) a few remote commands are disabled
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
exec wish "$0" "$@" exec wish "$0" "$@"
catch {rename send {}} catch {rename send {}}
# #
# Copyright (c) 2004 Karl J. Runge <runge@karlrunge.com> # Copyright (c) 2004-2005 Karl J. Runge <runge@karlrunge.com>
# All rights reserved. # All rights reserved.
# #
# This is free software; you can redistribute it and/or modify # This is free software; you can redistribute it and/or modify
...@@ -45,9 +45,6 @@ catch {rename send {}} ...@@ -45,9 +45,6 @@ catch {rename send {}}
# R means it is an action only valid in remote mode. # R means it is an action only valid in remote mode.
# S means it is an action only valid in startup mode. # S means it is an action only valid in startup mode.
# Q means it is an action worth querying after running. # Q means it is an action worth querying after running.
# D means it is a good idea to delay a little before querying
# (i.e. perhaps it causes x11vnc to do a lot of work, new fb)
# No longer used, -sync is used instead.
# P means the string can be +/- appended/deleted (string may not # P means the string can be +/- appended/deleted (string may not
# be the same after the remote command) # be the same after the remote command)
# G means gui internal item # G means gui internal item
...@@ -66,61 +63,61 @@ Row: Displays Screen Tuning Debugging Misc ...@@ -66,61 +63,61 @@ Row: Displays Screen Tuning Debugging Misc
Actions Actions
=SA start =SA start
=RA stop =RA stop
=GA attach =DGA attach
=RA detach =DRA detach
-- --
=RA ping =RA ping
=RA update-all =RA update-all
=GA clear-all =GA clear-all
-- -- D
=RA stop+quit =DRA stop+quit
=GA Quit =DGA Quit
Help Help
=GA gui =DGA gui
=GA all =GA all
Clients Clients
=RQA current: =DRQA current:
=F connect: =DF connect:
=RQA disconnect: =DRQA disconnect:
-- --
accept: accept:
gone: gone:
vncconnect vncconnect
-- -- D
http =D http
=F httpdir: =F httpdir:
httpport: httpport:
enablehttpproxy enablehttpproxy
Displays Displays
display: =D display:
=F auth: =F auth:
desktop: =D desktop:
rfbport: =D rfbport:
=0 gui: =0 gui:
Screen Screen
=DRA refresh =DRA refresh
=DRA reset =RA reset
=DRA blacken =DRA blacken
-- -- D
=D id: id:
=D sid: sid:
=D scale: =D scale:
-- --
=D overlay overlay
overlay_nocursor overlay_nocursor
-- --
=D visual: visual:
flashcmap flashcmap
notruecolor notruecolor
-- --
=DP blackout: =P blackout:
=D xinerama xinerama
-- --
solid =D solid
solid_color: solid_color:
-- --
= xrandr = xrandr
...@@ -128,8 +125,8 @@ Screen ...@@ -128,8 +125,8 @@ Screen
padgeom: padgeom:
Keyboard Keyboard
norepeat =D norepeat
add_keysyms =D add_keysyms
skip_keycodes: skip_keycodes:
modtweak modtweak
xkb xkb
...@@ -140,25 +137,27 @@ Keyboard ...@@ -140,25 +137,27 @@ Keyboard
clear_keys clear_keys
Pointer Pointer
=-C:none,arrow,X,some,most cursor: =D-C:none,arrow,X,some,most cursor:
noxfixes noxfixes
noalphablend noalphablend
-- --
cursorpos cursorpos
nocursorshape =D nocursorshape
-- --
buttonmap: buttonmap:
-- --
xwarppointer xwarppointer
Misc Misc
=GD simple-gui
-- D
=F rc: =F rc:
norc norc
-- --
nofb nofb
-- --
nobell =D nobell
nosel =D nosel
noprimary noprimary
-- --
bg bg
...@@ -178,29 +177,29 @@ Debugging ...@@ -178,29 +177,29 @@ Debugging
quiet quiet
-- --
=GA show-start-cmd =GA show-start-cmd
=G debug_gui =DG debug_gui
Permissions Permissions
=RQA lock =DRQA lock
=RQA unlock =DRQA unlock
=SQA deny_all =SQA deny_all
-- --
=FP allow: =DFP allow:
localhost localhost
=RA allowonce: =RA allowonce:
-- -- D
=RA noremote =RA noremote
-- --
viewonly =D viewonly
shared =D shared
forever =D forever
timeout: timeout:
-- --
input: input:
-- --
=SA alwaysshared =S alwaysshared
=SA nevershared =S nevershared
=SA dontdisconnect =S dontdisconnect
-- --
viewpasswd: viewpasswd:
=F passwdfile: =F passwdfile:
...@@ -212,22 +211,22 @@ Permissions ...@@ -212,22 +211,22 @@ Permissions
unsafe unsafe
Tuning Tuning
=-C:0,1,2,3,4 pointer_mode: =D-C:0,1,2,3,4 pointer_mode:
input_skip: input_skip:
nodragging =D nodragging
-- --
=D noshm noshm
flipbyteorder flipbyteorder
onetile onetile
-- --
alphacut: alphacut:
alphafrac: alphafrac:
alpharemove alpharemove
-- -- D
speeds: speeds:
wait: =D wait:
defer: defer:
nap =D nap
screen_blank: screen_blank:
-- --
fs: fs:
...@@ -357,6 +356,12 @@ Set the -solid color value. ...@@ -357,6 +356,12 @@ Set the -solid color value.
set helptext(xrandr_mode) " set helptext(xrandr_mode) "
Set the -xrandr mode value. Set the -xrandr mode value.
"
set helptext(simple-gui) "
Toggle between menu items corresponding the most basic ones
and all possible settings. I.e. toggle between a simple gui
and one for power users.
" "
set helptext(all) $helpall set helptext(all) $helpall
...@@ -366,7 +371,7 @@ tkx11vnc is a simple frontend to x11vnc. Nothing fancy, it merely ...@@ -366,7 +371,7 @@ tkx11vnc is a simple frontend to x11vnc. Nothing fancy, it merely
provides an interface to each of the many x11vnc command line options and provides an interface to each of the many x11vnc command line options and
remote control commands. See \"Help -> all\" for much info about x11vnc. remote control commands. See \"Help -> all\" for much info about x11vnc.
Most menu items have a (?) button one can click on to get more information All menu items have a (?) button one can click on to get more information
about the option or command. about the option or command.
There are two states tkx11vnc can be in: There are two states tkx11vnc can be in:
...@@ -465,9 +470,60 @@ is feasible). These options overlap with the x11vnc options -shared ...@@ -465,9 +470,60 @@ is feasible). These options overlap with the x11vnc options -shared
and -forever which are hopefully enough for most usage. They may be and -forever which are hopefully enough for most usage. They may be
specified for x11vnc startup if desired. specified for x11vnc startup if desired.
"
global beginner_mode
if {$beginner_mode} {
set helptext(gui) "
tkx11vnc is a simple frontend to x11vnc. It is currently running in
\"ez\" or \"simple\" mode. For many more options run it in normal
mode buy toggling \"Misc -> simple_gui\".
All menu items have a (?) button one can click on to get more information
about the option or command.
GUI components:
--- ----------
1) At the top of the gui is a info text label where information will
be posted, e.g. when traversing menu items text indicating how to get
help on the item and its current value will be displayed.
2) Below the info label is the area where the menu buttons, Actions,
Clients, etc., are presented. If a menu item has a checkbox,
it corresponds to a boolean on/off variable. Otherwise it is
either a string variable, or an action not associated with a
variable (for the most part).
3) Below the menu button area is a text label indicating the current x11vnc
X display being polled and the corresponding VNC display name. Both
will be \"(*none*)\" when there is no connection established.
4) Below the x11 and vnc displays text label is a text area there scrolling
information about actions being taken and commands being run is displayed.
To scroll click in the area and use PageUp/PageDown or the arrow keys.
5) At the bottom is an entry area. When one selects a menu item that
requires supplying a string value, the label will be set to the
parameter name and one types in the new value. Then one presses the
\"OK\" button or presses \"Enter\" to set the value. Or you can press
\"Skip\" or \"Escape\" to avoid changing the variable. Some variables
are boolean toggles (for example, \"Permissions -> viewonly\") or Radio
button selections. Selecting these menu items will not activate the
entry area but rather toggle the variable directly.
Cascades Bug: There is a bug not yet worked around for the cascade menus
where the (?) help button gets in the way. To get the mouse over to
the cascade menu click and release mouse to activate the cascade, then
you can click on its items. Dragging with a mouse button held down will
not work (sorry!).
" "
} }
}
proc center_win {w} { proc center_win {w} {
wm withdraw $w wm withdraw $w
set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2]; set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2];
...@@ -555,6 +611,10 @@ proc active_when_connected {item} { ...@@ -555,6 +611,10 @@ proc active_when_connected {item} {
if {[opt_match G $item]} { if {[opt_match G $item]} {
return 1 return 1
} elseif {[opt_match R $item]} {
return 1
} elseif {[opt_match S $item]} {
return 0
} elseif {[is_action $item]} { } elseif {[is_action $item]} {
if {[opt_match R $item]} { if {[opt_match R $item]} {
return 1 return 1
...@@ -569,10 +629,27 @@ proc active_when_connected {item} { ...@@ -569,10 +629,27 @@ proc active_when_connected {item} {
} }
proc active_when_starting {item} { proc active_when_starting {item} {
global helpremote helptext global helpremote helptext beginner_mode
if {$beginner_mode} {
if {[opt_match G $item]} {
return 1
}
if {$item == "display"} {
return 1
}
if {$item == "debug_gui"} {
return 1
}
return 0
}
if {[opt_match G $item]} { if {[opt_match G $item]} {
return 1 return 1
} elseif {[opt_match S $item]} {
return 1
} elseif {[opt_match R $item]} {
return 0
} elseif {[is_action $item]} { } elseif {[is_action $item]} {
if {[opt_match S $item]} { if {[opt_match S $item]} {
return 1 return 1
...@@ -1120,8 +1197,10 @@ proc insert_input_window {} { ...@@ -1120,8 +1197,10 @@ proc insert_input_window {} {
global vl_bk vl_bm vl_bb vr_bk vr_bm vr_bb global vl_bk vl_bm vl_bb vr_bk vr_bm vr_bb
append_text "\nUse these checkboxes to set the input permissions, " append_text "\nUse these checkboxes to set the input permissions, "
append_text "or type in the \"KMB...\"\n-input string manually. " append_text "or type in the \"KMB...\"\n"
append_text "Then press \"OK\" or \"Skip\".\n\n" append_text "-input string manually. Then press \"OK\" or \"Skip\".\n"
append_text "(note: an empty setting means use the default behavior, "
append_text "see viewonly)\n\n"
set w "$text_area.wk_f" set w "$text_area.wk_f"
catch {destroy $w} catch {destroy $w}
frame $w -bd 1 -relief ridge -cursor {top_left_arrow} frame $w -bd 1 -relief ridge -cursor {top_left_arrow}
...@@ -1136,7 +1215,7 @@ proc insert_input_window {} { ...@@ -1136,7 +1215,7 @@ proc insert_input_window {} {
-pady 1 -command set_kmb_str -text "Mouse Motion" -pady 1 -command set_kmb_str -text "Mouse Motion"
checkbutton $fl.bb -font $ffont -anchor w -variable vl_bb \ checkbutton $fl.bb -font $ffont -anchor w -variable vl_bb \
-pady 1 -command set_kmb_str -text "Button Clicks" -pady 1 -command set_kmb_str -text "Button Clicks"
label $fr.l -pady 1 -font $ffont -text "View-only clients:" label $fr.l -pady 1 -font $ffont -text "View-Only clients:"
checkbutton $fr.bk -font $ffont -anchor w -variable vr_bk \ checkbutton $fr.bk -font $ffont -anchor w -variable vr_bk \
-pady 1 -command set_kmb_str -text "Keystrokes" -pady 1 -command set_kmb_str -text "Keystrokes"
checkbutton $fr.bm -font $ffont -anchor w -variable vr_bm \ checkbutton $fr.bm -font $ffont -anchor w -variable vr_bm \
...@@ -1623,7 +1702,7 @@ proc tail_logfile {} { ...@@ -1623,7 +1702,7 @@ proc tail_logfile {} {
set xterm_cmd "xterm -sb -fn $ffont -geometry 80x45 -title x11vnc-logfile -e" set xterm_cmd "xterm -sb -fn $ffont -geometry 80x45 -title x11vnc-logfile -e"
set cmd [split $xterm_cmd] set cmd [split $xterm_cmd]
lappend cmd "tail" lappend cmd "tail"
lappend cmd "+1f" lappend cmd "-3000f"
lappend cmd $logfile lappend cmd $logfile
lappend cmd "&" lappend cmd "&"
catch {[eval exec $cmd]} catch {[eval exec $cmd]}
...@@ -1665,14 +1744,18 @@ proc detach_from_display {} { ...@@ -1665,14 +1744,18 @@ proc detach_from_display {} {
# Menu item is an action: # Menu item is an action:
proc do_action {item} { proc do_action {item} {
global menu_var connected_to_x11vnc global menu_var connected_to_x11vnc beginner_mode
if {[in_debug_mode]} { if {[in_debug_mode]} {
append_text "action: \"$item\"\n" append_text "action: \"$item\"\n"
} }
if {$item == "ping"} { if {$item == "ping"} {
if {$beginner_mode} {
try_connect_and_query_all
} else {
try_connect try_connect
}
return return
} elseif {$item == "start"} { } elseif {$item == "start"} {
start_x11vnc start_x11vnc
...@@ -1976,7 +2059,7 @@ proc client_dialog {client} { ...@@ -1976,7 +2059,7 @@ proc client_dialog {client} {
set sm 0 set sm 0
set sb 0 set sb 0
if {[regexp -nocase {K} $input]} { if {[regexp -nocase {K} $input]} {
append_text "Keystroke" append_text "Keystrokes"
set sk 1 set sk 1
} }
if {[regexp -nocase {M} $input]} { if {[regexp -nocase {M} $input]} {
...@@ -1990,7 +2073,7 @@ proc client_dialog {client} { ...@@ -1990,7 +2073,7 @@ proc client_dialog {client} {
if {$sk || $sm} { if {$sk || $sm} {
append_text ", " append_text ", "
} }
append_text "Button-Click" append_text "Button-Clicks"
set sb 1 set sb 1
} }
if {! $sk && ! $sm && ! $sb} { if {! $sk && ! $sm && ! $sb} {
...@@ -2020,7 +2103,7 @@ proc client_dialog {client} { ...@@ -2020,7 +2103,7 @@ proc client_dialog {client} {
} elseif {[regexp -nocase {(disconnect|close)} $val]} { } elseif {[regexp -nocase {(disconnect|close)} $val]} {
disconnect_dialog $client disconnect_dialog $client
} else { } else {
regsub -all -nocase {[^KMB]} $val "" regsub -all -nocase {[^KMB]} $val "" val
set item_bool(client_input) 0 set item_bool(client_input) 0
push_new_value "client_input" "client_input" "$cid:$val" 0 push_new_value "client_input" "client_input" "$cid:$val" 0
} }
...@@ -2119,6 +2202,10 @@ proc set_widgets {} { ...@@ -2119,6 +2202,10 @@ proc set_widgets {} {
set case $item_case($item) set case $item_case($item)
set menu $menu_m($case) set menu $menu_m($case)
set entry $item_entry($item) set entry $item_entry($item)
if {$entry < 0} {
# skip case under beginner_mode
continue
}
set type [$menu type $entry] set type [$menu type $entry]
if {$type == "separator" || $type == "tearoff"} { if {$type == "separator" || $type == "tearoff"} {
continue continue
...@@ -2126,102 +2213,52 @@ proc set_widgets {} { ...@@ -2126,102 +2213,52 @@ proc set_widgets {} {
if {$connected_to_x11vnc} { if {$connected_to_x11vnc} {
if {[active_when_connected $item]} { if {[active_when_connected $item]} {
$menu entryconfigure $entry -state normal $menu entryconfigure $entry -state normal
#puts "n-1 $case / $item / $entry"
} else { } else {
$menu entryconfigure $entry -state disabled $menu entryconfigure $entry -state disabled
#puts "I-1 $case / $item / $entry"
} }
} else { } else {
if {[active_when_starting $item]} { if {[active_when_starting $item]} {
$menu entryconfigure $entry -state normal $menu entryconfigure $entry -state normal
#puts "n-2 $case / $item / $entry"
} else { } else {
$menu entryconfigure $entry -state disabled $menu entryconfigure $entry -state disabled
#puts "I-2 $case / $item / $entry"
} }
} }
} }
} }
proc make_widgets {} { proc toggle_simple_gui {} {
global template global beginner_mode simple_gui_created
global menu_b menu_m global connected_to_x11vnc
global item_opts item_bool item_case item_entry menu_var unset_str
global item_cascade
global info_label info_str x11_display vnc_display
global text_area
global entry_box entry_str entry_set entry_label entry_ok entry_browse
global entry_help entry_skip
global bfont ffont
global helptext helpremote helplabel
set label_width 80
set info_label .info
label $info_label -textvariable info_str -bd 2 -relief groove \
-anchor w -width $label_width -font $ffont
pack $info_label -side top -fill x -expand 0
# Extract the Rows:
set row 0;
set colmax 0;
foreach line [split $template "\n"] {
if {[regexp {^Row: (.*)} $line rest]} {
set col 0
foreach case [split $rest] {
if {$case == "" || $case == "Row:"} {
continue
}
set menu_row($case) $row
set menu_col($case) $col
set menu_count($case) 0
lappend cases($col) $case; if {$beginner_mode} {
set len [string length $case] append_text "\nSwitching to simple-gui mode.\n"
if {[info exists max_len($col)]} {
if {$len > $max_len($col)} {
set max_len($col) $len
}
} else { } else {
set max_len($col) $len append_text "\nSwitching to power-user gui mode.\n"
}
incr col
if {$col > $colmax} {
set colmax $col
}
}
incr row;
}
} }
# Make frames for the rows and make the menu buttons. set simple_gui_created 1
set f ".menuframe" make_menu_items
frame $f set_widgets
for {set c 0} {$c < $colmax} {incr c} { set_internal_help
set colf "$f.menuframe$c" if {$connected_to_x11vnc} {
frame $colf query_all
pack $colf -side left -fill y
set fbg [$colf cget -background]
foreach case $cases($c) {
set menub "$colf.menu$case";
set menu "$colf.menu$case.menu";
set menu_b($case) $menub
set menu_m($case) $menu
set ul 0
foreach char [split $case ""] {
set char [string tolower $char]
if {![info exists underlined($char)]} {
set underlined($char) 1
break
}
incr ul
}
menubutton $menub -text "$case" -underline $ul \
-anchor w -menu $menu -background $fbg \
-font $bfont
pack $menub -side top -fill x
menu $menu -tearoff 0
}
} }
pack $f -side top -fill x append_text "\n"
}
proc make_menu_items {} {
global template
global menu_b menu_m menu_count
global item_opts item_bool item_case item_entry menu_var unset_str
global item_cascade
global bfont ffont beginner_mode simple_gui_created
global helptext helpremote helplabel
# Now extract the menu items: # Extract the menu items:
set case ""; set case "";
foreach line [split $template "\n"] { foreach line [split $template "\n"] {
if {[regexp {^Row:} $line]} { if {[regexp {^Row:} $line]} {
...@@ -2229,8 +2266,19 @@ proc make_widgets {} { ...@@ -2229,8 +2266,19 @@ proc make_widgets {} {
} }
if {[regexp {^[A-z]} $line]} { if {[regexp {^[A-z]} $line]} {
set case [string trim $line] set case [string trim $line]
if {$simple_gui_created} {
set i0 0
if {$case == "Misc"} {
# kludge for simple_gui
set i0 1
}
catch {$menu_m($case) delete $i0 end}
}
set menu_count($case) 0
continue; continue;
} }
set item [string trim $line] set item [string trim $line]
regsub -all { *} $item " " item regsub -all { *} $item " " item
if {$item == ""} { if {$item == ""} {
...@@ -2252,17 +2300,29 @@ proc make_widgets {} { ...@@ -2252,17 +2300,29 @@ proc make_widgets {} {
} }
regsub {:$} $item {} item regsub {:$} $item {} item
if {$item == "-- D"} {
set beginner_sep 1
set item "--"
} else {
set beginner_sep 0
}
set item_opts($item) $opts set item_opts($item) $opts
set item_case($item) $case set item_case($item) $case
set item_bool($item) $bool set item_bool($item) $bool
set item_cascade($item) "" set item_cascade($item) ""
set item_entry($item) $menu_count($case) set item_entry($item) $menu_count($case)
if {0} { puts "ITEM: $item - $opts - $case - $bool - $menu_count($case)" }
set mvar 0 set mvar 0
set m $menu_m($case) set m $menu_m($case)
if {$beginner_mode && ! $beginner_sep && ![opt_match D $item]} {
set item_entry($item) "-1"
continue;
}
if {0} { puts "ITEM: $item\t- $opts\t- $case\t- $bool\t- $menu_count($case)" }
# Create the menu items, its variables, etc., etc. # Create the menu items, its variables, etc., etc.
if {$item == "--"} { if {$item == "--"} {
...@@ -2282,7 +2342,8 @@ proc make_widgets {} { ...@@ -2282,7 +2342,8 @@ proc make_widgets {} {
} elseif {$item == "current"} { } elseif {$item == "current"} {
# Current clients cascade # Current clients cascade
set subm $m.cascade$menu_count($case) set subm $m.current_cascade
catch {destroy $subm}
set item_cascade($item) $subm set item_cascade($item) $subm
update_clients_menu "" update_clients_menu ""
$m add cascade -label "$item" \ $m add cascade -label "$item" \
...@@ -2300,7 +2361,8 @@ proc make_widgets {} { ...@@ -2300,7 +2361,8 @@ proc make_widgets {} {
# String # String
if {[regexp -- {-C:(.*)} $item_opts($item) m0 m1]} { if {[regexp -- {-C:(.*)} $item_opts($item) m0 m1]} {
# Radiobutton select # Radiobutton select
set subm $m.cascade$menu_count($case) set subm $m.radio_cascade$menu_count($case)
catch {destroy $subm}
menu $subm -tearoff 0 -font $ffont menu $subm -tearoff 0 -font $ffont
foreach val [split $m1 ","] { foreach val [split $m1 ","] {
$subm add radiobutton -label "$val" \ $subm add radiobutton -label "$val" \
...@@ -2321,6 +2383,13 @@ proc make_widgets {} { ...@@ -2321,6 +2383,13 @@ proc make_widgets {} {
} }
set mvar 1 set mvar 1
} elseif {$item == "simple-gui"} {
if {! $simple_gui_created} {
$m add checkbutton -label "$item" \
-command "toggle_simple_gui" \
-font $ffont \
-variable beginner_mode
}
} else { } else {
# Boolean # Boolean
$m add checkbutton -label "$item" \ $m add checkbutton -label "$item" \
...@@ -2331,12 +2400,13 @@ proc make_widgets {} { ...@@ -2331,12 +2400,13 @@ proc make_widgets {} {
} }
incr menu_count($case) incr menu_count($case)
if {$mvar} { if {$mvar} {
set menu_var($item) $unset_str set menu_var($item) $unset_str
} }
} }
# Now make the litte "(?)" help buttons # Now make the little "(?)" help buttons
foreach case [array names menu_m] { foreach case [array names menu_m] {
if {$case == "Help"} { if {$case == "Help"} {
continue; continue;
...@@ -2376,6 +2446,90 @@ proc make_widgets {} { ...@@ -2376,6 +2446,90 @@ proc make_widgets {} {
} }
} }
} }
}
proc make_widgets {} {
global template
global menu_b menu_m menu_count
global item_opts item_bool item_case item_entry menu_var unset_str
global item_cascade
global info_label info_str x11_display vnc_display
global text_area
global entry_box entry_str entry_set entry_label entry_ok entry_browse
global entry_help entry_skip
global bfont ffont beginner_mode
global helptext helpremote helplabel
# Make the top label
set label_width 80
set info_label .info
label $info_label -textvariable info_str -bd 2 -relief groove \
-anchor w -width $label_width -font $ffont
pack $info_label -side top -fill x -expand 0
# Extract the Rows:
set row 0;
set colmax 0;
foreach line [split $template "\n"] {
if {[regexp {^Row: (.*)} $line rest]} {
set col 0
foreach case [split $rest] {
if {$case == "" || $case == "Row:"} {
continue
}
set menu_row($case) $row
set menu_col($case) $col
lappend cases($col) $case;
set len [string length $case]
if {[info exists max_len($col)]} {
if {$len > $max_len($col)} {
set max_len($col) $len
}
} else {
set max_len($col) $len
}
incr col
if {$col > $colmax} {
set colmax $col
}
}
incr row;
}
}
# Make frames for the rows and make the menu buttons.
set f ".menuframe"
frame $f
for {set c 0} {$c < $colmax} {incr c} {
set colf "$f.menuframe$c"
frame $colf
pack $colf -side left -fill y
set fbg [$colf cget -background]
foreach case $cases($c) {
set menub "$colf.menu$case";
set menu "$colf.menu$case.menu";
set menu_b($case) $menub
set menu_m($case) $menu
set ul 0
foreach char [split $case ""] {
set char [string tolower $char]
if {![info exists underlined($char)]} {
set underlined($char) 1
break
}
incr ul
}
menubutton $menub -text "$case" -underline $ul \
-anchor w -menu $menu -background $fbg \
-font $bfont
pack $menub -side top -fill x
menu $menu -tearoff 0
}
}
pack $f -side top -fill x
make_menu_items
# Make the x11 and vnc display label bar: # Make the x11 and vnc display label bar:
set df .displayframe set df .displayframe
...@@ -2552,9 +2706,9 @@ proc stop_watch {onoff} { ...@@ -2552,9 +2706,9 @@ proc stop_watch {onoff} {
proc double_check_noremote {} { proc double_check_noremote {} {
set msg "\n\n" set msg "\n\n"
append msg "WARNING: setting \"noremote\" will disable ALL remote control commands\n" append msg "*** WARNING: setting \"noremote\" will disable ALL remote control commands (i.e.\n"
append msg "WARNING: (i.e. this gui will be locked out) Do you really want to do this?\n" append msg "*** WARNING: *this* gui will be locked out). Do you really want to do this?\n"
append msg "WARNING: If so, press \"OK\", otherwise press \"Skip\"\n" append msg "*** WARNING: If so, press \"OK\", otherwise press \"Skip\"\n"
append msg "\n" append msg "\n"
bell bell
return [warning_dialog $msg "noremote"] return [warning_dialog $msg "noremote"]
...@@ -2829,7 +2983,7 @@ proc try_connect {} { ...@@ -2829,7 +2983,7 @@ proc try_connect {} {
# main: # main:
global env x11vnc_prog x11vnc_cmdline x11vnc_xdisplay x11vnc_connect; global env x11vnc_prog x11vnc_cmdline x11vnc_xdisplay x11vnc_connect;
global x11vnc_auth_file global x11vnc_auth_file beginner_mode simple_gui_created
global helpall helptext helpremote helplabel hostname; global helpall helptext helpremote helplabel hostname;
global all_settings reply_xdisplay always_update global all_settings reply_xdisplay always_update
global max_text_height max_text_width global max_text_height max_text_width
...@@ -2920,6 +3074,13 @@ if {[info exists env(X11VNC_AUTH_FILE)]} { ...@@ -2920,6 +3074,13 @@ if {[info exists env(X11VNC_AUTH_FILE)]} {
set x11vnc_auth_file "" set x11vnc_auth_file ""
} }
set simple_gui_created 0
if {[info exists env(X11VNC_SIMPLE_GUI)]} {
set beginner_mode 1
} else {
set beginner_mode 0
}
set hostname [exec uname -n] set hostname [exec uname -n]
#puts [exec env] #puts [exec env]
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
"exec wish \"$0\" \"$@\"\n" "exec wish \"$0\" \"$@\"\n"
"catch {rename send {}}\n" "catch {rename send {}}\n"
"#\n" "#\n"
"# Copyright (c) 2004 Karl J. Runge <runge@karlrunge.com>\n" "# Copyright (c) 2004-2005 Karl J. Runge <runge@karlrunge.com>\n"
"# All rights reserved.\n" "# All rights reserved.\n"
"#\n" "#\n"
"# This is free software; you can redistribute it and/or modify\n" "# This is free software; you can redistribute it and/or modify\n"
...@@ -51,9 +51,6 @@ ...@@ -51,9 +51,6 @@
"# R means it is an action only valid in remote mode.\n" "# R means it is an action only valid in remote mode.\n"
"# S means it is an action only valid in startup mode.\n" "# S means it is an action only valid in startup mode.\n"
"# Q means it is an action worth querying after running.\n" "# Q means it is an action worth querying after running.\n"
"# D means it is a good idea to delay a little before querying \n"
"# (i.e. perhaps it causes x11vnc to do a lot of work, new fb)\n"
"# No longer used, -sync is used instead.\n"
"# P means the string can be +/- appended/deleted (string may not\n" "# P means the string can be +/- appended/deleted (string may not\n"
"# be the same after the remote command)\n" "# be the same after the remote command)\n"
"# G means gui internal item\n" "# G means gui internal item\n"
...@@ -72,61 +69,61 @@ ...@@ -72,61 +69,61 @@
"Actions\n" "Actions\n"
" =SA start\n" " =SA start\n"
" =RA stop\n" " =RA stop\n"
" =GA attach\n" " =DGA attach\n"
" =RA detach\n" " =DRA detach\n"
" --\n" " --\n"
" =RA ping\n" " =RA ping\n"
" =RA update-all\n" " =RA update-all\n"
" =GA clear-all\n" " =GA clear-all\n"
" --\n" " -- D\n"
" =RA stop+quit \n" " =DRA stop+quit \n"
" =GA Quit \n" " =DGA Quit \n"
"\n" "\n"
"Help\n" "Help\n"
" =GA gui\n" " =DGA gui\n"
" =GA all\n" " =GA all\n"
"\n" "\n"
"Clients\n" "Clients\n"
" =RQA current:\n" " =DRQA current:\n"
" =F connect:\n" " =DF connect:\n"
" =RQA disconnect:\n" " =DRQA disconnect:\n"
" --\n" " --\n"
" accept:\n" " accept:\n"
" gone:\n" " gone:\n"
" vncconnect\n" " vncconnect\n"
" --\n" " -- D\n"
" http\n" " =D http\n"
" =F httpdir:\n" " =F httpdir:\n"
" httpport:\n" " httpport:\n"
" enablehttpproxy\n" " enablehttpproxy\n"
"\n" "\n"
"Displays\n" "Displays\n"
" display:\n" " =D display:\n"
" =F auth:\n" " =F auth:\n"
" desktop:\n" " =D desktop:\n"
" rfbport:\n" " =D rfbport:\n"
" =0 gui:\n" " =0 gui:\n"
"\n" "\n"
"Screen\n" "Screen\n"
" =DRA refresh\n" " =DRA refresh\n"
" =DRA reset\n" " =RA reset\n"
" =DRA blacken\n" " =DRA blacken\n"
" --\n" " -- D\n"
" =D id:\n" " id:\n"
" =D sid:\n" " sid:\n"
" =D scale:\n" " =D scale:\n"
" --\n" " --\n"
" =D overlay\n" " overlay\n"
" overlay_nocursor\n" " overlay_nocursor\n"
" --\n" " --\n"
" =D visual:\n" " visual:\n"
" flashcmap\n" " flashcmap\n"
" notruecolor\n" " notruecolor\n"
" --\n" " --\n"
" =DP blackout:\n" " =P blackout:\n"
" =D xinerama\n" " xinerama\n"
" --\n" " --\n"
" solid\n" " =D solid\n"
" solid_color:\n" " solid_color:\n"
" --\n" " --\n"
" = xrandr\n" " = xrandr\n"
...@@ -134,8 +131,8 @@ ...@@ -134,8 +131,8 @@
" padgeom:\n" " padgeom:\n"
"\n" "\n"
"Keyboard\n" "Keyboard\n"
" norepeat\n" " =D norepeat\n"
" add_keysyms\n" " =D add_keysyms\n"
" skip_keycodes:\n" " skip_keycodes:\n"
" modtweak\n" " modtweak\n"
" xkb\n" " xkb\n"
...@@ -146,25 +143,27 @@ ...@@ -146,25 +143,27 @@
" clear_keys\n" " clear_keys\n"
"\n" "\n"
"Pointer\n" "Pointer\n"
" =-C:none,arrow,X,some,most cursor:\n" " =D-C:none,arrow,X,some,most cursor:\n"
" noxfixes\n" " noxfixes\n"
" noalphablend\n" " noalphablend\n"
" --\n" " --\n"
" cursorpos\n" " cursorpos\n"
" nocursorshape\n" " =D nocursorshape\n"
" --\n" " --\n"
" buttonmap:\n" " buttonmap:\n"
" --\n" " --\n"
" xwarppointer\n" " xwarppointer\n"
"\n" "\n"
"Misc\n" "Misc\n"
" =GD simple-gui\n"
" -- D\n"
" =F rc:\n" " =F rc:\n"
" norc\n" " norc\n"
" --\n" " --\n"
" nofb\n" " nofb\n"
" --\n" " --\n"
" nobell\n" " =D nobell\n"
" nosel\n" " =D nosel\n"
" noprimary\n" " noprimary\n"
" --\n" " --\n"
" bg\n" " bg\n"
...@@ -184,29 +183,29 @@ ...@@ -184,29 +183,29 @@
" quiet\n" " quiet\n"
" --\n" " --\n"
" =GA show-start-cmd\n" " =GA show-start-cmd\n"
" =G debug_gui\n" " =DG debug_gui\n"
"\n" "\n"
"Permissions\n" "Permissions\n"
" =RQA lock\n" " =DRQA lock\n"
" =RQA unlock\n" " =DRQA unlock\n"
" =SQA deny_all\n" " =SQA deny_all\n"
" --\n" " --\n"
" =FP allow:\n" " =DFP allow:\n"
" localhost\n" " localhost\n"
" =RA allowonce:\n" " =RA allowonce:\n"
" --\n" " -- D\n"
" =RA noremote\n" " =RA noremote\n"
" --\n" " --\n"
" viewonly\n" " =D viewonly\n"
" shared\n" " =D shared\n"
" forever\n" " =D forever\n"
" timeout:\n" " timeout:\n"
" --\n" " --\n"
" input:\n" " input:\n"
" --\n" " --\n"
" =SA alwaysshared\n" " =S alwaysshared\n"
" =SA nevershared\n" " =S nevershared\n"
" =SA dontdisconnect\n" " =S dontdisconnect\n"
" --\n" " --\n"
" viewpasswd:\n" " viewpasswd:\n"
" =F passwdfile:\n" " =F passwdfile:\n"
...@@ -218,22 +217,22 @@ ...@@ -218,22 +217,22 @@
" unsafe\n" " unsafe\n"
"\n" "\n"
"Tuning\n" "Tuning\n"
" =-C:0,1,2,3,4 pointer_mode:\n" " =D-C:0,1,2,3,4 pointer_mode:\n"
" input_skip:\n" " input_skip:\n"
" nodragging\n" " =D nodragging\n"
" --\n" " --\n"
" =D noshm\n" " noshm\n"
" flipbyteorder\n" " flipbyteorder\n"
" onetile\n" " onetile\n"
" --\n" " --\n"
" alphacut:\n" " alphacut:\n"
" alphafrac:\n" " alphafrac:\n"
" alpharemove\n" " alpharemove\n"
" --\n" " -- D\n"
" speeds:\n" " speeds:\n"
" wait:\n" " =D wait:\n"
" defer:\n" " defer:\n"
" nap\n" " =D nap\n"
" screen_blank:\n" " screen_blank:\n"
" --\n" " --\n"
" fs:\n" " fs:\n"
...@@ -365,6 +364,12 @@ ...@@ -365,6 +364,12 @@
"Set the -xrandr mode value.\n" "Set the -xrandr mode value.\n"
"\"\n" "\"\n"
"\n" "\n"
" set helptext(simple-gui) \"\n"
"Toggle between menu items corresponding the most basic ones\n"
"and all possible settings. I.e. toggle between a simple gui\n"
"and one for power users.\n"
"\"\n"
"\n"
" set helptext(all) $helpall\n" " set helptext(all) $helpall\n"
"\n" "\n"
" set helptext(gui) \"\n" " set helptext(gui) \"\n"
...@@ -372,7 +377,7 @@ ...@@ -372,7 +377,7 @@
"provides an interface to each of the many x11vnc command line options and\n" "provides an interface to each of the many x11vnc command line options and\n"
"remote control commands. See \\\"Help -> all\\\" for much info about x11vnc.\n" "remote control commands. See \\\"Help -> all\\\" for much info about x11vnc.\n"
"\n" "\n"
"Most menu items have a (?) button one can click on to get more information\n" "All menu items have a (?) button one can click on to get more information\n"
"about the option or command.\n" "about the option or command.\n"
"\n" "\n"
"There are two states tkx11vnc can be in:\n" "There are two states tkx11vnc can be in:\n"
...@@ -472,6 +477,57 @@ ...@@ -472,6 +477,57 @@
"specified for x11vnc startup if desired.\n" "specified for x11vnc startup if desired.\n"
"\n" "\n"
"\"\n" "\"\n"
"\n"
"global beginner_mode\n"
"if {$beginner_mode} {\n"
" set helptext(gui) \"\n"
"tkx11vnc is a simple frontend to x11vnc. It is currently running in\n"
"\\\"ez\\\" or \\\"simple\\\" mode. For many more options run it in normal\n"
"mode buy toggling \\\"Misc -> simple_gui\\\".\n"
"\n"
"All menu items have a (?) button one can click on to get more information\n"
"about the option or command.\n"
"\n"
"GUI components: \n"
"--- ----------\n"
"\n"
"1) At the top of the gui is a info text label where information will\n"
"be posted, e.g. when traversing menu items text indicating how to get\n"
"help on the item and its current value will be displayed.\n"
"\n"
"2) Below the info label is the area where the menu buttons, Actions,\n"
"Clients, etc., are presented. If a menu item has a checkbox,\n"
"it corresponds to a boolean on/off variable. Otherwise it is\n"
"either a string variable, or an action not associated with a\n"
"variable (for the most part).\n"
"\n"
"3) Below the menu button area is a text label indicating the current x11vnc\n"
"X display being polled and the corresponding VNC display name. Both\n"
"will be \\\"(*none*)\\\" when there is no connection established.\n"
"\n"
"4) Below the x11 and vnc displays text label is a text area there scrolling\n"
"information about actions being taken and commands being run is displayed.\n"
"To scroll click in the area and use PageUp/PageDown or the arrow keys.\n"
"\n"
"5) At the bottom is an entry area. When one selects a menu item that\n"
"requires supplying a string value, the label will be set to the\n"
"parameter name and one types in the new value. Then one presses the\n"
"\\\"OK\\\" button or presses \\\"Enter\\\" to set the value. Or you can press\n"
"\\\"Skip\\\" or \\\"Escape\\\" to avoid changing the variable. Some variables\n"
"are boolean toggles (for example, \\\"Permissions -> viewonly\\\") or Radio\n"
"button selections. Selecting these menu items will not activate the\n"
"entry area but rather toggle the variable directly.\n"
"\n"
"\n"
"Cascades Bug: There is a bug not yet worked around for the cascade menus\n"
"where the (?) help button gets in the way. To get the mouse over to\n"
"the cascade menu click and release mouse to activate the cascade, then\n"
"you can click on its items. Dragging with a mouse button held down will\n"
"not work (sorry!).\n"
"\n"
"\"\n"
"}\n"
"\n"
"}\n" "}\n"
"\n" "\n"
"proc center_win {w} {\n" "proc center_win {w} {\n"
...@@ -561,6 +617,10 @@ ...@@ -561,6 +617,10 @@
"\n" "\n"
" if {[opt_match G $item]} {\n" " if {[opt_match G $item]} {\n"
" return 1\n" " return 1\n"
" } elseif {[opt_match R $item]} {\n"
" return 1\n"
" } elseif {[opt_match S $item]} {\n"
" return 0\n"
" } elseif {[is_action $item]} {\n" " } elseif {[is_action $item]} {\n"
" if {[opt_match R $item]} {\n" " if {[opt_match R $item]} {\n"
" return 1\n" " return 1\n"
...@@ -575,10 +635,27 @@ ...@@ -575,10 +635,27 @@
"}\n" "}\n"
"\n" "\n"
"proc active_when_starting {item} {\n" "proc active_when_starting {item} {\n"
" global helpremote helptext\n" " global helpremote helptext beginner_mode\n"
"\n"
" if {$beginner_mode} {\n"
" if {[opt_match G $item]} {\n"
" return 1\n"
" }\n"
" if {$item == \"display\"} {\n"
" return 1\n"
" }\n"
" if {$item == \"debug_gui\"} {\n"
" return 1\n"
" }\n"
" return 0\n"
" }\n"
"\n" "\n"
" if {[opt_match G $item]} {\n" " if {[opt_match G $item]} {\n"
" return 1\n" " return 1\n"
" } elseif {[opt_match S $item]} {\n"
" return 1\n"
" } elseif {[opt_match R $item]} {\n"
" return 0\n"
" } elseif {[is_action $item]} {\n" " } elseif {[is_action $item]} {\n"
" if {[opt_match S $item]} {\n" " if {[opt_match S $item]} {\n"
" return 1\n" " return 1\n"
...@@ -1126,8 +1203,10 @@ ...@@ -1126,8 +1203,10 @@
" global vl_bk vl_bm vl_bb vr_bk vr_bm vr_bb \n" " global vl_bk vl_bm vl_bb vr_bk vr_bm vr_bb \n"
"\n" "\n"
" append_text \"\\nUse these checkboxes to set the input permissions, \"\n" " append_text \"\\nUse these checkboxes to set the input permissions, \"\n"
" append_text \"or type in the \\\"KMB...\\\"\\n-input string manually. \"\n" " append_text \"or type in the \\\"KMB...\\\"\\n\"\n"
" append_text \"Then press \\\"OK\\\" or \\\"Skip\\\".\\n\\n\"\n" " append_text \"-input string manually. Then press \\\"OK\\\" or \\\"Skip\\\".\\n\"\n"
" append_text \"(note: an empty setting means use the default behavior, \"\n"
" append_text \"see viewonly)\\n\\n\"\n"
" set w \"$text_area.wk_f\"\n" " set w \"$text_area.wk_f\"\n"
" catch {destroy $w}\n" " catch {destroy $w}\n"
" frame $w -bd 1 -relief ridge -cursor {top_left_arrow}\n" " frame $w -bd 1 -relief ridge -cursor {top_left_arrow}\n"
...@@ -1142,7 +1221,7 @@ ...@@ -1142,7 +1221,7 @@
" -pady 1 -command set_kmb_str -text \"Mouse Motion\" \n" " -pady 1 -command set_kmb_str -text \"Mouse Motion\" \n"
" checkbutton $fl.bb -font $ffont -anchor w -variable vl_bb \\\n" " checkbutton $fl.bb -font $ffont -anchor w -variable vl_bb \\\n"
" -pady 1 -command set_kmb_str -text \"Button Clicks\"\n" " -pady 1 -command set_kmb_str -text \"Button Clicks\"\n"
" label $fr.l -pady 1 -font $ffont -text \"View-only clients:\"\n" " label $fr.l -pady 1 -font $ffont -text \"View-Only clients:\"\n"
" checkbutton $fr.bk -font $ffont -anchor w -variable vr_bk \\\n" " checkbutton $fr.bk -font $ffont -anchor w -variable vr_bk \\\n"
" -pady 1 -command set_kmb_str -text \"Keystrokes\" \n" " -pady 1 -command set_kmb_str -text \"Keystrokes\" \n"
" checkbutton $fr.bm -font $ffont -anchor w -variable vr_bm \\\n" " checkbutton $fr.bm -font $ffont -anchor w -variable vr_bm \\\n"
...@@ -1629,7 +1708,7 @@ ...@@ -1629,7 +1708,7 @@
" set xterm_cmd \"xterm -sb -fn $ffont -geometry 80x45 -title x11vnc-logfile -e\"\n" " set xterm_cmd \"xterm -sb -fn $ffont -geometry 80x45 -title x11vnc-logfile -e\"\n"
" set cmd [split $xterm_cmd]\n" " set cmd [split $xterm_cmd]\n"
" lappend cmd \"tail\"\n" " lappend cmd \"tail\"\n"
" lappend cmd \"+1f\"\n" " lappend cmd \"-3000f\"\n"
" lappend cmd $logfile\n" " lappend cmd $logfile\n"
" lappend cmd \"&\"\n" " lappend cmd \"&\"\n"
" catch {[eval exec $cmd]}\n" " catch {[eval exec $cmd]}\n"
...@@ -1671,14 +1750,18 @@ ...@@ -1671,14 +1750,18 @@
"\n" "\n"
"# Menu item is an action:\n" "# Menu item is an action:\n"
"proc do_action {item} {\n" "proc do_action {item} {\n"
" global menu_var connected_to_x11vnc\n" " global menu_var connected_to_x11vnc beginner_mode\n"
"\n" "\n"
" if {[in_debug_mode]} {\n" " if {[in_debug_mode]} {\n"
" append_text \"action: \\\"$item\\\"\\n\"\n" " append_text \"action: \\\"$item\\\"\\n\"\n"
" }\n" " }\n"
"\n" "\n"
" if {$item == \"ping\"} {\n" " if {$item == \"ping\"} {\n"
" if {$beginner_mode} {\n"
" try_connect_and_query_all\n"
" } else {\n"
" try_connect\n" " try_connect\n"
" }\n"
" return\n" " return\n"
" } elseif {$item == \"start\"} {\n" " } elseif {$item == \"start\"} {\n"
" start_x11vnc\n" " start_x11vnc\n"
...@@ -1982,7 +2065,7 @@ ...@@ -1982,7 +2065,7 @@
" set sm 0\n" " set sm 0\n"
" set sb 0\n" " set sb 0\n"
" if {[regexp -nocase {K} $input]} {\n" " if {[regexp -nocase {K} $input]} {\n"
" append_text \"Keystroke\"\n" " append_text \"Keystrokes\"\n"
" set sk 1\n" " set sk 1\n"
" }\n" " }\n"
" if {[regexp -nocase {M} $input]} {\n" " if {[regexp -nocase {M} $input]} {\n"
...@@ -1996,7 +2079,7 @@ ...@@ -1996,7 +2079,7 @@
" if {$sk || $sm} {\n" " if {$sk || $sm} {\n"
" append_text \", \"\n" " append_text \", \"\n"
" }\n" " }\n"
" append_text \"Button-Click\"\n" " append_text \"Button-Clicks\"\n"
" set sb 1\n" " set sb 1\n"
" }\n" " }\n"
" if {! $sk && ! $sm && ! $sb} {\n" " if {! $sk && ! $sm && ! $sb} {\n"
...@@ -2026,7 +2109,7 @@ ...@@ -2026,7 +2109,7 @@
" } elseif {[regexp -nocase {(disconnect|close)} $val]} {\n" " } elseif {[regexp -nocase {(disconnect|close)} $val]} {\n"
" disconnect_dialog $client\n" " disconnect_dialog $client\n"
" } else {\n" " } else {\n"
" regsub -all -nocase {[^KMB]} $val \"\" \n" " regsub -all -nocase {[^KMB]} $val \"\" val\n"
" set item_bool(client_input) 0\n" " set item_bool(client_input) 0\n"
" push_new_value \"client_input\" \"client_input\" \"$cid:$val\" 0\n" " push_new_value \"client_input\" \"client_input\" \"$cid:$val\" 0\n"
" }\n" " }\n"
...@@ -2125,6 +2208,10 @@ ...@@ -2125,6 +2208,10 @@
" set case $item_case($item)\n" " set case $item_case($item)\n"
" set menu $menu_m($case)\n" " set menu $menu_m($case)\n"
" set entry $item_entry($item)\n" " set entry $item_entry($item)\n"
" if {$entry < 0} {\n"
" # skip case under beginner_mode \n"
" continue\n"
" }\n"
" set type [$menu type $entry]\n" " set type [$menu type $entry]\n"
" if {$type == \"separator\" || $type == \"tearoff\"} {\n" " if {$type == \"separator\" || $type == \"tearoff\"} {\n"
" continue\n" " continue\n"
...@@ -2132,102 +2219,52 @@ ...@@ -2132,102 +2219,52 @@
" if {$connected_to_x11vnc} {\n" " if {$connected_to_x11vnc} {\n"
" if {[active_when_connected $item]} {\n" " if {[active_when_connected $item]} {\n"
" $menu entryconfigure $entry -state normal\n" " $menu entryconfigure $entry -state normal\n"
"#puts \"n-1 $case / $item / $entry\"\n"
" } else {\n" " } else {\n"
" $menu entryconfigure $entry -state disabled\n" " $menu entryconfigure $entry -state disabled\n"
"#puts \"I-1 $case / $item / $entry\"\n"
" }\n" " }\n"
" } else {\n" " } else {\n"
" if {[active_when_starting $item]} {\n" " if {[active_when_starting $item]} {\n"
" $menu entryconfigure $entry -state normal\n" " $menu entryconfigure $entry -state normal\n"
"#puts \"n-2 $case / $item / $entry\"\n"
" } else {\n" " } else {\n"
" $menu entryconfigure $entry -state disabled\n" " $menu entryconfigure $entry -state disabled\n"
"#puts \"I-2 $case / $item / $entry\"\n"
" }\n" " }\n"
" }\n" " }\n"
" }\n" " }\n"
"}\n" "}\n"
"\n" "\n"
"proc make_widgets {} {\n" "proc toggle_simple_gui {} {\n"
" global template \n" " global beginner_mode simple_gui_created\n"
" global menu_b menu_m\n" " global connected_to_x11vnc\n"
" global item_opts item_bool item_case item_entry menu_var unset_str\n"
" global item_cascade\n"
" global info_label info_str x11_display vnc_display\n"
" global text_area\n"
" global entry_box entry_str entry_set entry_label entry_ok entry_browse\n"
" global entry_help entry_skip\n"
" global bfont ffont\n"
" global helptext helpremote helplabel\n"
"\n"
" set label_width 80\n"
"\n"
" set info_label .info\n"
" label $info_label -textvariable info_str -bd 2 -relief groove \\\n"
" -anchor w -width $label_width -font $ffont\n"
" pack $info_label -side top -fill x -expand 0\n"
"\n"
" # Extract the Rows:\n"
" set row 0;\n"
" set colmax 0;\n"
" foreach line [split $template \"\\n\"] {\n"
" if {[regexp {^Row: (.*)} $line rest]} {\n"
" set col 0\n"
" foreach case [split $rest] {\n"
" if {$case == \"\" || $case == \"Row:\"} {\n"
" continue\n"
" }\n"
" set menu_row($case) $row\n"
" set menu_col($case) $col\n"
" set menu_count($case) 0\n"
"\n" "\n"
" lappend cases($col) $case;\n" " if {$beginner_mode} {\n"
" set len [string length $case]\n" " append_text \"\\nSwitching to simple-gui mode.\\n\"\n"
" if {[info exists max_len($col)]} {\n"
" if {$len > $max_len($col)} {\n"
" set max_len($col) $len\n"
" }\n"
" } else {\n" " } else {\n"
" set max_len($col) $len\n" " append_text \"\\nSwitching to power-user gui mode.\\n\"\n"
" }\n"
" incr col\n"
" if {$col > $colmax} {\n"
" set colmax $col\n"
" }\n"
" }\n"
" incr row;\n"
" }\n"
" }\n" " }\n"
"\n" "\n"
" # Make frames for the rows and make the menu buttons.\n" " set simple_gui_created 1\n"
" set f \".menuframe\"\n" " make_menu_items\n"
" frame $f\n" " set_widgets\n"
" for {set c 0} {$c < $colmax} {incr c} {\n" " set_internal_help\n"
" set colf \"$f.menuframe$c\"\n" " if {$connected_to_x11vnc} {\n"
" frame $colf\n" " query_all\n"
" pack $colf -side left -fill y\n"
" set fbg [$colf cget -background]\n"
" foreach case $cases($c) {\n"
" set menub \"$colf.menu$case\";\n"
" set menu \"$colf.menu$case.menu\";\n"
" set menu_b($case) $menub\n"
" set menu_m($case) $menu\n"
" set ul 0\n"
" foreach char [split $case \"\"] {\n"
" set char [string tolower $char]\n"
" if {![info exists underlined($char)]} {\n"
" set underlined($char) 1\n"
" break\n"
" }\n"
" incr ul\n"
" }\n"
" menubutton $menub -text \"$case\" -underline $ul \\\n"
" -anchor w -menu $menu -background $fbg \\\n"
" -font $bfont\n"
" pack $menub -side top -fill x\n"
" menu $menu -tearoff 0\n"
" }\n"
" }\n" " }\n"
" pack $f -side top -fill x\n" " append_text \"\\n\"\n"
"}\n"
"\n"
"proc make_menu_items {} {\n"
" global template \n"
" global menu_b menu_m menu_count\n"
" global item_opts item_bool item_case item_entry menu_var unset_str\n"
" global item_cascade\n"
" global bfont ffont beginner_mode simple_gui_created\n"
" global helptext helpremote helplabel\n"
"\n" "\n"
" # Now extract the menu items:\n" " # Extract the menu items:\n"
" set case \"\";\n" " set case \"\";\n"
" foreach line [split $template \"\\n\"] {\n" " foreach line [split $template \"\\n\"] {\n"
" if {[regexp {^Row:} $line]} {\n" " if {[regexp {^Row:} $line]} {\n"
...@@ -2235,8 +2272,19 @@ ...@@ -2235,8 +2272,19 @@
" }\n" " }\n"
" if {[regexp {^[A-z]} $line]} {\n" " if {[regexp {^[A-z]} $line]} {\n"
" set case [string trim $line]\n" " set case [string trim $line]\n"
"\n"
" if {$simple_gui_created} {\n"
" set i0 0\n"
" if {$case == \"Misc\"} {\n"
" # kludge for simple_gui\n"
" set i0 1\n"
" }\n"
" catch {$menu_m($case) delete $i0 end}\n"
" }\n"
" set menu_count($case) 0\n"
" continue;\n" " continue;\n"
" }\n" " }\n"
"\n"
" set item [string trim $line]\n" " set item [string trim $line]\n"
" regsub -all { *} $item \" \" item\n" " regsub -all { *} $item \" \" item\n"
" if {$item == \"\"} {\n" " if {$item == \"\"} {\n"
...@@ -2258,17 +2306,29 @@ ...@@ -2258,17 +2306,29 @@
" }\n" " }\n"
" regsub {:$} $item {} item\n" " regsub {:$} $item {} item\n"
"\n" "\n"
" if {$item == \"-- D\"} {\n"
" set beginner_sep 1\n"
" set item \"--\"\n"
" } else {\n"
" set beginner_sep 0\n"
" }\n"
"\n"
" set item_opts($item) $opts\n" " set item_opts($item) $opts\n"
" set item_case($item) $case\n" " set item_case($item) $case\n"
" set item_bool($item) $bool\n" " set item_bool($item) $bool\n"
" set item_cascade($item) \"\"\n" " set item_cascade($item) \"\"\n"
" set item_entry($item) $menu_count($case)\n" " set item_entry($item) $menu_count($case)\n"
"\n" "\n"
" if {0} { puts \"ITEM: $item - $opts - $case - $bool - $menu_count($case)\" }\n"
"\n"
" set mvar 0 \n" " set mvar 0 \n"
" set m $menu_m($case)\n" " set m $menu_m($case)\n"
"\n" "\n"
" if {$beginner_mode && ! $beginner_sep && ![opt_match D $item]} {\n"
" set item_entry($item) \"-1\"\n"
" continue;\n"
" }\n"
"\n"
" if {0} { puts \"ITEM: $item\\t- $opts\\t- $case\\t- $bool\\t- $menu_count($case)\" }\n"
"\n"
" # Create the menu items, its variables, etc., etc.\n" " # Create the menu items, its variables, etc., etc.\n"
"\n" "\n"
" if {$item == \"--\"} {\n" " if {$item == \"--\"} {\n"
...@@ -2288,7 +2348,8 @@ ...@@ -2288,7 +2348,8 @@
"\n" "\n"
" } elseif {$item == \"current\"} {\n" " } elseif {$item == \"current\"} {\n"
" # Current clients cascade\n" " # Current clients cascade\n"
" set subm $m.cascade$menu_count($case)\n" " set subm $m.current_cascade\n"
" catch {destroy $subm}\n"
" set item_cascade($item) $subm\n" " set item_cascade($item) $subm\n"
" update_clients_menu \"\"\n" " update_clients_menu \"\"\n"
" $m add cascade -label \"$item\" \\\n" " $m add cascade -label \"$item\" \\\n"
...@@ -2306,7 +2367,8 @@ ...@@ -2306,7 +2367,8 @@
" # String\n" " # String\n"
" if {[regexp -- {-C:(.*)} $item_opts($item) m0 m1]} {\n" " if {[regexp -- {-C:(.*)} $item_opts($item) m0 m1]} {\n"
" # Radiobutton select\n" " # Radiobutton select\n"
" set subm $m.cascade$menu_count($case)\n" " set subm $m.radio_cascade$menu_count($case)\n"
" catch {destroy $subm}\n"
" menu $subm -tearoff 0 -font $ffont\n" " menu $subm -tearoff 0 -font $ffont\n"
" foreach val [split $m1 \",\"] {\n" " foreach val [split $m1 \",\"] {\n"
" $subm add radiobutton -label \"$val\" \\\n" " $subm add radiobutton -label \"$val\" \\\n"
...@@ -2327,6 +2389,13 @@ ...@@ -2327,6 +2389,13 @@
" }\n" " }\n"
" set mvar 1\n" " set mvar 1\n"
"\n" "\n"
" } elseif {$item == \"simple-gui\"} {\n"
" if {! $simple_gui_created} {\n"
" $m add checkbutton -label \"$item\" \\\n"
" -command \"toggle_simple_gui\" \\\n"
" -font $ffont \\\n"
" -variable beginner_mode\n"
" }\n"
" } else {\n" " } else {\n"
" # Boolean\n" " # Boolean\n"
" $m add checkbutton -label \"$item\" \\\n" " $m add checkbutton -label \"$item\" \\\n"
...@@ -2337,12 +2406,13 @@ ...@@ -2337,12 +2406,13 @@
" }\n" " }\n"
"\n" "\n"
" incr menu_count($case)\n" " incr menu_count($case)\n"
"\n"
" if {$mvar} {\n" " if {$mvar} {\n"
" set menu_var($item) $unset_str\n" " set menu_var($item) $unset_str\n"
" }\n" " }\n"
" }\n" " }\n"
"\n" "\n"
" # Now make the litte \"(?)\" help buttons\n" " # Now make the little \"(?)\" help buttons\n"
" foreach case [array names menu_m] {\n" " foreach case [array names menu_m] {\n"
" if {$case == \"Help\"} {\n" " if {$case == \"Help\"} {\n"
" continue;\n" " continue;\n"
...@@ -2382,6 +2452,90 @@ ...@@ -2382,6 +2452,90 @@
" }\n" " }\n"
" }\n" " }\n"
" }\n" " }\n"
"}\n"
"\n"
"proc make_widgets {} {\n"
" global template \n"
" global menu_b menu_m menu_count\n"
" global item_opts item_bool item_case item_entry menu_var unset_str\n"
" global item_cascade\n"
" global info_label info_str x11_display vnc_display\n"
" global text_area\n"
" global entry_box entry_str entry_set entry_label entry_ok entry_browse\n"
" global entry_help entry_skip\n"
" global bfont ffont beginner_mode\n"
" global helptext helpremote helplabel\n"
"\n"
" # Make the top label\n"
" set label_width 80\n"
" set info_label .info\n"
" label $info_label -textvariable info_str -bd 2 -relief groove \\\n"
" -anchor w -width $label_width -font $ffont\n"
" pack $info_label -side top -fill x -expand 0\n"
"\n"
" # Extract the Rows:\n"
" set row 0;\n"
" set colmax 0;\n"
" foreach line [split $template \"\\n\"] {\n"
" if {[regexp {^Row: (.*)} $line rest]} {\n"
" set col 0\n"
" foreach case [split $rest] {\n"
" if {$case == \"\" || $case == \"Row:\"} {\n"
" continue\n"
" }\n"
" set menu_row($case) $row\n"
" set menu_col($case) $col\n"
"\n"
" lappend cases($col) $case;\n"
" set len [string length $case]\n"
" if {[info exists max_len($col)]} {\n"
" if {$len > $max_len($col)} {\n"
" set max_len($col) $len\n"
" }\n"
" } else {\n"
" set max_len($col) $len\n"
" }\n"
" incr col\n"
" if {$col > $colmax} {\n"
" set colmax $col\n"
" }\n"
" }\n"
" incr row;\n"
" }\n"
" }\n"
"\n"
" # Make frames for the rows and make the menu buttons.\n"
" set f \".menuframe\"\n"
" frame $f\n"
" for {set c 0} {$c < $colmax} {incr c} {\n"
" set colf \"$f.menuframe$c\"\n"
" frame $colf\n"
" pack $colf -side left -fill y\n"
" set fbg [$colf cget -background]\n"
" foreach case $cases($c) {\n"
" set menub \"$colf.menu$case\";\n"
" set menu \"$colf.menu$case.menu\";\n"
" set menu_b($case) $menub\n"
" set menu_m($case) $menu\n"
" set ul 0\n"
" foreach char [split $case \"\"] {\n"
" set char [string tolower $char]\n"
" if {![info exists underlined($char)]} {\n"
" set underlined($char) 1\n"
" break\n"
" }\n"
" incr ul\n"
" }\n"
" menubutton $menub -text \"$case\" -underline $ul \\\n"
" -anchor w -menu $menu -background $fbg \\\n"
" -font $bfont\n"
" pack $menub -side top -fill x\n"
" menu $menu -tearoff 0\n"
" }\n"
" }\n"
" pack $f -side top -fill x\n"
"\n"
" make_menu_items\n"
"\n" "\n"
" # Make the x11 and vnc display label bar:\n" " # Make the x11 and vnc display label bar:\n"
" set df .displayframe\n" " set df .displayframe\n"
...@@ -2558,9 +2712,9 @@ ...@@ -2558,9 +2712,9 @@
"\n" "\n"
"proc double_check_noremote {} {\n" "proc double_check_noremote {} {\n"
" set msg \"\\n\\n\"\n" " set msg \"\\n\\n\"\n"
" append msg \"WARNING: setting \\\"noremote\\\" will disable ALL remote control commands\\n\"\n" " append msg \"*** WARNING: setting \\\"noremote\\\" will disable ALL remote control commands (i.e.\\n\"\n"
" append msg \"WARNING: (i.e. this gui will be locked out) Do you really want to do this?\\n\"\n" " append msg \"*** WARNING: *this* gui will be locked out). Do you really want to do this?\\n\"\n"
" append msg \"WARNING: If so, press \\\"OK\\\", otherwise press \\\"Skip\\\"\\n\"\n" " append msg \"*** WARNING: If so, press \\\"OK\\\", otherwise press \\\"Skip\\\"\\n\"\n"
" append msg \"\\n\"\n" " append msg \"\\n\"\n"
" bell\n" " bell\n"
" return [warning_dialog $msg \"noremote\"]\n" " return [warning_dialog $msg \"noremote\"]\n"
...@@ -2835,7 +2989,7 @@ ...@@ -2835,7 +2989,7 @@
"# main:\n" "# main:\n"
"\n" "\n"
"global env x11vnc_prog x11vnc_cmdline x11vnc_xdisplay x11vnc_connect;\n" "global env x11vnc_prog x11vnc_cmdline x11vnc_xdisplay x11vnc_connect;\n"
"global x11vnc_auth_file\n" "global x11vnc_auth_file beginner_mode simple_gui_created\n"
"global helpall helptext helpremote helplabel hostname;\n" "global helpall helptext helpremote helplabel hostname;\n"
"global all_settings reply_xdisplay always_update\n" "global all_settings reply_xdisplay always_update\n"
"global max_text_height max_text_width\n" "global max_text_height max_text_width\n"
...@@ -2926,6 +3080,13 @@ ...@@ -2926,6 +3080,13 @@
" set x11vnc_auth_file \"\"\n" " set x11vnc_auth_file \"\"\n"
"}\n" "}\n"
"\n" "\n"
"set simple_gui_created 0\n"
"if {[info exists env(X11VNC_SIMPLE_GUI)]} {\n"
" set beginner_mode 1\n"
"} else {\n"
" set beginner_mode 0\n"
"}\n"
"\n"
"\n" "\n"
"set hostname [exec uname -n]\n" "set hostname [exec uname -n]\n"
"#puts [exec env]\n" "#puts [exec env]\n"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.TH X11VNC "1" "February 2005" "x11vnc " "User Commands" .TH X11VNC "1" "February 2005" "x11vnc " "User Commands"
.SH NAME .SH NAME
x11vnc - allow VNC connections to real X11 displays x11vnc - allow VNC connections to real X11 displays
version: 0.7.1pre, lastmod: 2005-02-10 version: 0.7.1pre, lastmod: 2005-02-14
.SH SYNOPSIS .SH SYNOPSIS
.B x11vnc .B x11vnc
[OPTION]... [OPTION]...
...@@ -274,10 +274,13 @@ Supply a 2nd password for view-only logins. The \fB-passwd\fR ...@@ -274,10 +274,13 @@ Supply a 2nd password for view-only logins. The \fB-passwd\fR
.IP .IP
Specify libvncserver \fB-passwd\fR via the first line of Specify libvncserver \fB-passwd\fR via the first line of
the file \fIfilename\fR instead of via command line. the file \fIfilename\fR instead of via command line.
If a second non blank line exists in the file it is If a second non blank line exists in the file it
taken as a view-only password (i.e. \fB-viewpasswd)\fR Note: is taken as a view-only password (i.e. \fB-viewpasswd)\fR
this is a simple plaintext passwd, see also \fB-rfbauth\fR To supply an empty password for either field use the
and \fB-storepasswd\fR below for obfuscated passwords. string "__EMPTY__". Note: \fB-passwdfile\fR is a simple
plaintext passwd, see also \fB-rfbauth\fR and \fB-storepasswd\fR
below for obfuscated passwords. Neither should be
readable by others.
.PP .PP
\fB-storepasswd\fR \fIpass\fR \fIfile\fR \fB-storepasswd\fR \fIpass\fR \fIfile\fR
.IP .IP
...@@ -382,44 +385,66 @@ root this option is ignored. ...@@ -382,44 +385,66 @@ root this option is ignored.
.IP .IP
Why use this option? In general it is not needed Why use this option? In general it is not needed
since x11vnc is already connected to the display and since x11vnc is already connected to the display and
can perform its primary functions. It was added to can perform its primary functions. The option was
make some of the *external* utility commands x11vnc added to make some of the *external* utility commands
occasionally runs work properly. In particular under x11vnc occasionally runs work properly. In particular
GNOME and KDE to implement the "\fB-solid\fR \fIcolor\fR" feature under GNOME and KDE to implement the "\fB-solid\fR \fIcolor\fR"
external commands (gconftool-2 and dcop) must be run as feature external commands (gconftool-2 and dcop) must be
the user owning the desktop session. This option also run as the user owning the desktop session. Since this
affects the userid used to run the processes for the option switches userid it also affects the userid used
\fB-accept\fR and \fB-gone\fR options. It also affects the ability to run the processes for the \fB-accept\fR and \fB-gone\fR options.
to read files for options such as \fB-connect,\fR \fB-allow,\fR and It also affects the ability to read files for options
\fB-remap.\fR Note that the \fB-connect\fR file is also written to. such as \fB-connect,\fR \fB-allow,\fR and \fB-remap.\fR Note that the
\fB-connect\fR file is also sometimes written to.
.IP .IP
So be careful with this option since in many situations So be careful with this option since in many situations
its use can decrease security. its use can decrease security.
.IP .IP
The switch to a user will only take place if the display The switch to a user will only take place if the
can still be opened as that user (this is primarily to display can still be successfully opened as that user
try to guess the actual owner of the session). Example: (this is primarily to try to guess the actual owner
"\fB-users\fR \fIfred,wilma,betty\fR". Note that a malicious of the session). Example: "\fB-users\fR \fIfred,wilma,betty\fR".
user "barney" by quickly using "xhost +" when Note that a malicious user "barney" by quickly using
logging in can get x11vnc to switch to user "fred". "xhost +" when logging in may get x11vnc to switch
What happens next? to user "fred". What happens next?
.IP .IP
Under display managers it may be a long time before Under display managers it may be a long time before
the switch succeeds (i.e. a user logs in). To make the switch succeeds (i.e. a user logs in). To make
it switch immediately regardless if the display can it switch immediately regardless if the display
be reopened or not prefix the username with the + can be reopened prefix the username with the +
character. E.g. "\fB-users\fR \fI+bob\fR" or "\fB-users\fR \fI+nobody\fR". character. E.g. "\fB-users\fR \fI+bob\fR" or "\fB-users\fR \fI+nobody\fR".
The latter (i.e. switching immediately to user The latter (i.e. switching immediately to user
"nobody") is probably the only use of this option "nobody") is probably the only use of this option
that increases security. To switch to a user *before* that increases security.
connections to the display are made or any files opened .IP
use the "=" character: "\fB-users\fR \fI=username\fR". To immediately switch to a user *before* connections to
.IP the display are made or any files opened use the "="
The special user "guess" means to examine the utmpx character: "\fB-users\fR \fI=bob\fR". That user needs to be able
database looking for a user attached to the display to open the display of course.
number and try him/her. To limit the list of guesses, .IP
use: "\fB-users\fR \fIguess=bob,betty\fR". Be especially careful The special user "guess=" means to examine the utmpx
using this mode. database (see
.IR who (1)
) looking for a user attached to
the display number (from DISPLAY or \fB-display\fR option)
and try him/her. To limit the list of guesses, use:
"\fB-users\fR \fIguess=bob,betty\fR".
.IP
Even more sinister is the special user "lurk=" that
means to try to guess the DISPLAY from the utmpx login
database as well. So it "lurks" waiting for anyone
to log into an X session and then connects to it.
Specify a list of users after the = to limit which
users will be tried. If the first user in the list
is something like ":0" or ":0-2" that indicates a
range of DISPLAY numbers that will be tried (regardless
of whether they are in the utmpx database) for all
users that are logged in. Examples: "\fB-users\fR \fIlurk=\fR"
and "\fB-users\fR \fIlurk=:0-1,bob,mary\fR"
.IP
Be especially careful using the "guess=" and "lurk="
modes. They are not recommended for use on machines
with untrustworthy local users.
.PP .PP
\fB-noshm\fR \fB-noshm\fR
.IP .IP
...@@ -448,20 +473,20 @@ The [color] is optional: the default color is "cyan4". ...@@ -448,20 +473,20 @@ The [color] is optional: the default color is "cyan4".
For a different one specify the X color (rgb.txt name, For a different one specify the X color (rgb.txt name,
e.g. "darkblue" or numerical "#RRGGBB"). e.g. "darkblue" or numerical "#RRGGBB").
.IP .IP
Currently this option only works on GNOME, KDE, and Currently this option only works on GNOME, KDE, CDE,
classic X (i.e. with the background image on the root and classic X (i.e. with the background image on the
window). The "gconftool-2" and "dcop" external root window). The "gconftool-2" and "dcop" external
commands are run for GNOME and KDE respectively. commands are run for GNOME and KDE respectively.
Other desktops won't work, e.g. XFCE (send us the Other desktops won't work, e.g. XFCE (send us the
corresponding commands if you find them). If x11vnc corresponding commands if you find them). If x11vnc is
is running as root ( running as root (
.IR inetd (1) .IR inetd (1)
or or
.IR gdm (1) .IR gdm (1)
), the \fB-users\fR ), the \fB-users\fR option
option may be needed for GNOME and KDE. If x11vnc may be needed for GNOME and KDE. If x11vnc guesses
guesses your desktop incorrectly, you can force it by your desktop incorrectly, you can force it by prefixing
prefixing color with "gnome:", "kde:", or "root:". color with "gnome:", "kde:", "cde:" or "root:".
.PP .PP
\fB-blackout\fR \fIstring\fR \fB-blackout\fR \fIstring\fR
.IP .IP
...@@ -1008,9 +1033,11 @@ to start up both the gui and x11vnc with the gui showing ...@@ -1008,9 +1033,11 @@ to start up both the gui and x11vnc with the gui showing
up on the X display in the environment variable DISPLAY. up on the X display in the environment variable DISPLAY.
.IP .IP
"gui-opts" can be a comma separated list of items. "gui-opts" can be a comma separated list of items.
Currently there are only two types of items: 1) a gui Currently there are these types of items: 1) a gui mode,
mode and 2) the X display the gui should display on. a 2) gui "simplicity", and 3) the X display the gui
The gui mode can be "start", "conn", or "wait" should display on.
.IP
1) The gui mode can be "start", "conn", or "wait"
"start" is the default mode above and is not required. "start" is the default mode above and is not required.
"conn" means do not automatically start up x11vnc, "conn" means do not automatically start up x11vnc,
but instead just try to connect to an existing x11vnc but instead just try to connect to an existing x11vnc
...@@ -1018,15 +1045,22 @@ process. "wait" means just start the gui and nothing ...@@ -1018,15 +1045,22 @@ process. "wait" means just start the gui and nothing
else (you will later instruct the gui to start x11vnc else (you will later instruct the gui to start x11vnc
or connect to an existing one.) or connect to an existing one.)
.IP .IP
Note the possible confusion regarding the potentially 2) The gui simplicity is off by default (a power-user
gui with all options is presented) To start with
something less daunting supply the string "simple"
("ez" is an alias for this). Once the gui is
started you can toggle between the two with "Misc ->
simple_gui".
.IP
3) Note the possible confusion regarding the potentially
two different X displays: x11vnc polls one, but you two different X displays: x11vnc polls one, but you
may want the gui to appear on another. For example, if may want the gui to appear on another. For example, if
you ssh in and x11vnc is not running yet you may want you ssh in and x11vnc is not running yet you may want
the gui to come back to you via your ssh redirected X the gui to come back to you via your ssh redirected X
display (e.g. localhost:10). display (e.g. localhost:10).
.IP .IP
Examples: "x11vnc \fB-gui",\fR "x11vnc \fB-gui\fR localhost:10", Examples: "x11vnc \fB-gui",\fR "x11vnc \fB-gui\fR ez"
"x11vnc \fB-gui\fR :10", "x11vnc \fB-gui\fR conn,host:10", "x11vnc \fB-gui\fR localhost:10", "x11vnc \fB-gui\fR conn,host:0"
.IP .IP
If you do not specify a gui X display in "gui-opts" If you do not specify a gui X display in "gui-opts"
then the DISPLAY environment variable and \fB-display\fR then the DISPLAY environment variable and \fB-display\fR
......
...@@ -290,7 +290,7 @@ static int xdamage_base_event_type; ...@@ -290,7 +290,7 @@ static int xdamage_base_event_type;
#endif #endif
/* date +'lastmod: %Y-%m-%d' */ /* date +'lastmod: %Y-%m-%d' */
char lastmod[] = "0.7.1pre lastmod: 2005-02-10"; char lastmod[] = "0.7.1pre lastmod: 2005-02-14";
/* X display info */ /* X display info */
...@@ -977,14 +977,18 @@ char *get_shell(void) { ...@@ -977,14 +977,18 @@ char *get_shell(void) {
} }
} }
int switch_user(char *); /* -- user.c -- */
int switch_user(char *, int);
int switch_user_env(uid_t, char*, char *, int);
void try_to_switch_users(void); void try_to_switch_users(void);
char *guess_desktop(void); char *guess_desktop(void);
void switch_user_dummy(void) { /* tasks for after we switch */
void switch_user_task_dummy(void) {
; /* dummy does nothing */ ; /* dummy does nothing */
} }
void switch_user_solid_bg(void) { void switch_user_task_solid_bg(void) {
/* we have switched users, some things to do. */ /* we have switched users, some things to do. */
if (use_solid_bg && client_count) { if (use_solid_bg && client_count) {
solid_bg(0); solid_bg(0);
...@@ -1017,7 +1021,7 @@ void check_switched_user (void) { ...@@ -1017,7 +1021,7 @@ void check_switched_user (void) {
} }
if (! did_dummy) { if (! did_dummy) {
switch_user_dummy(); switch_user_task_dummy();
did_dummy = 1; did_dummy = 1;
} }
if (! did_solid) { if (! did_solid) {
...@@ -1034,7 +1038,7 @@ void check_switched_user (void) { ...@@ -1034,7 +1038,7 @@ void check_switched_user (void) {
doit = 1; doit = 1;
} }
if (doit) { if (doit) {
switch_user_solid_bg(); switch_user_task_solid_bg();
did_solid = 1; did_solid = 1;
} }
} }
...@@ -1044,169 +1048,462 @@ void check_switched_user (void) { ...@@ -1044,169 +1048,462 @@ void check_switched_user (void) {
} }
} }
int guess_user_and_switch(char *str) { /* utilities for switching users */
char *get_login_list(int with_display) {
char *out;
#if LIBVNCSERVER_HAVE_UTMPX_H #if LIBVNCSERVER_HAVE_UTMPX_H
char *q, *dstr, *d = DisplayString(dpy); int i, cnt, max = 200, ut_namesize = 32;
char *allowed = NULL; int dpymax = 1000, sawdpy[1000];
int i, ret = 0, max = 300; struct utmpx *utx;
if (strstr(str, "guess=") == str) { /* size based on "username:999," * max */
char *allowed = strchr(str, '='); out = (char *) malloc(max * (ut_namesize+1+3+1) + 1);
allowed++; out[0] = '\0';
}
/* pick out ":N" */ for (i=0; i<dpymax; i++) {
dstr = strchr(d, ':'); sawdpy[i] = 0;
if (! dstr) {
return 0;
}
q = strchr(dstr, '.');
if (q) {
*q = '\0';
} }
/* look over the utmpx entries looking for this display */
setutxent(); setutxent();
for (i=0; i<max; i++) { cnt = 0;
char *str; while (1) {
struct utmpx *utx = getutxent(); char *user, *line, *host, *id;
char tmp[10];
int d = -1;
utx = getutxent();
if (! utx) { if (! utx) {
break; break;
} }
if (utx->ut_type != USER_PROCESS) {
str = lblanks(utx->ut_user); continue;
if (*str == '\0') {
continue; /* blank user */
} }
if (allowed) { user = lblanks(utx->ut_user);
char *p, *t = strdup(allowed); if (*user == '\0') {
int ok = 0; continue;
p = strtok(t, ",");
while (p) {
if (!strcmp(p, utx->ut_user)) {
ok = 1;
} }
p = strtok(NULL, ","); if (strchr(user, ',')) {
continue; /* unlikely, but comma is our sep. */
} }
free(t);
if (! ok) { line = lblanks(utx->ut_line);
host = lblanks(utx->ut_host);
id = lblanks(utx->ut_id);
if (with_display) {
if (0 && line[0] != ':' && strcmp(line, "dtlocal")) {
/* XXX useful? */
continue; continue;
} }
if (line[0] == ':') {
if (sscanf(line, ":%d", &d) != 1) {
d = -1;
}
}
if (d < 0 && host[0] == ':') {
if (sscanf(host, ":%d", &d) != 1) {
d = -1;
}
}
if (d < 0 && id[0] == ':') {
if (sscanf(id, ":%d", &d) != 1) {
d = -1;
} }
if (!strcmp(utx->ut_user, "guess")) {
continue; /* never... */
} }
/* try the line for leading :N */ if (d < 0 || d >= dpymax || sawdpy[d]) {
str = lblanks(utx->ut_line);
if (strstr(str, dstr) == str) {
int n = strlen(dstr);
if (isdigit(*(str+n))) {
continue; /* :1 vs. :10 */
} else if (switch_user(utx->ut_user)) {
rfbLog("switched to guessed user: %s\n",
utx->ut_user);
ret = 1;
break;
} else {
continue; continue;
} }
} sawdpy[d] = 1;
sprintf(tmp, ":%d", d);
} else {
/* try to eliminate repeats */
int repeat = 0;
char *q;
/* try the host for leading :N */ q = out;
str = lblanks(utx->ut_host); while ((q = strstr(q, user)) != NULL) {
if (strstr(str, dstr) == str) { char *p = q + strlen(user) + strlen(":DPY");
int n = strlen(dstr); if (q == out || *(q-1) == ',') {
if (isdigit(*(str+n))) { /* bounded on left. */
continue; /* :1 vs. :10 */ if (*p == ',' || *p == '\0') {
} else if (switch_user(utx->ut_user)) { /* bounded on right. */
rfbLog("switched to guessed user: %s\n", repeat = 1;
utx->ut_user);
ret = 1;
break; break;
} else { }
}
q = p;
}
if (repeat) {
continue; continue;
} }
sprintf(tmp, ":DPY");
} }
if (*out) {
strcat(out, ",");
} }
endutxent(); strcat(out, user);
strcat(out, tmp);
return ret; cnt++;
if (cnt >= max) {
break;
}
}
endutxent();
#else #else
return 0; out = strdup("");
#endif #endif
return out;
} }
int switch_user(char *user) { char **user_list(char *user_str) {
int force = 0, numerical = 1; int n, i;
uid_t uid = 0; char *p, **list;
char *q, *name, *home;
if (*user == '+') { p = user_str;
force = 1; n = 1;
user++; while (*p++) {
if (*p == ',') {
n++;
} }
}
list = (char **) malloc((n+1)*(sizeof(char *)));
if (!strcmp(user, "guess") || strstr(user, "guess=") == user) { p = strtok(user_str, ",");
return guess_user_and_switch(user); i = 0;
while (p) {
list[i++] = p;
p = strtok(NULL, ",");
} }
list[i] = NULL;
return list;
}
void user2uid(char *user, uid_t *uid, char **name, char **home) {
int numerical = 1;
char *q;
*uid = (uid_t) -1;
*name = NULL;
*home = NULL;
q = user; q = user;
while (*q) { while (*q) {
if (! isdigit(*q)) { if (! isdigit(*q++)) {
numerical = 0; numerical = 0;
break; break;
} }
q++;
} }
#if LIBVNCSERVER_HAVE_PWD_H
if (numerical) { if (numerical) {
int u = atoi(user); int u = atoi(user);
struct passwd *pw;
if (u > 0) { if (u < 0) {
uid = (uid_t) u; return;
}
*uid = (uid_t) u;
}
#if LIBVNCSERVER_HAVE_PWD_H
if (1) {
struct passwd *pw;
if (numerical) {
pw = getpwuid(*uid);
} else { } else {
return 0; pw = getpwnam(user);
} }
pw = getpwuid(uid);
if (pw) { if (pw) {
name = pw->pw_name; *uid = pw->pw_uid;
home = pw->pw_dir; *name = pw->pw_name; /* n.b. use immediately */
} else { *home = pw->pw_dir;
return 0;
} }
}
#endif
}
int try_user_and_display(uid_t, char*);
int lurk(char **users) {
uid_t uid;
int success = 0, dmin = -1, dmax = -1;
char *p, *logins, **u;
if ((u = users) != NULL && *u != NULL && *(*u) == ':') {
int len;
char *tmp;
/* extract min and max display numbers */
tmp = *u;
if (strchr(tmp, '-')) {
if (sscanf(tmp, ":%d-%d", &dmin, &dmax) != 2) {
dmin = -1;
dmax = -1;
}
}
if (dmin < 0) {
if (sscanf(tmp, ":%d", &dmin) != 1) {
dmin = -1;
dmax = -1;
} else { } else {
struct passwd *pw = getpwnam(user); dmax = dmin;
if (pw) { }
uid = pw->pw_uid; }
name = pw->pw_name; if ((dmin < 0 || dmax < 0) || dmin > dmax || dmax > 10000) {
home = pw->pw_dir; dmin = -1;
dmax = -1;
}
/* get user logins regardless of having a display: */
logins = get_login_list(0);
/*
* now we append the list in users (might as well try
* them) this will probably allow weird ways of starting
* xservers to work.
*/
len = strlen(logins);
u++;
while (*u != NULL) {
len += strlen(*u) + strlen(":DPY,");
u++;
}
tmp = (char *) malloc(len+1);
strcpy(tmp, logins);
/* now concatenate them: */
u = users+1;
while (*u != NULL) {
char *q, chk[100];
snprintf(chk, 100, "%s:DPY", *u);
q = strstr(tmp, chk);
if (q) {
char *p = q + strlen(chk);
if (q == tmp || *(q-1) == ',') {
/* bounded on left. */
if (*p == ',' || *p == '\0') {
/* bounded on right. */
u++;
continue;
}
}
}
if (*tmp) {
strcat(tmp, ",");
}
strcat(tmp, *u);
strcat(tmp, ":DPY");
u++;
}
free(logins);
logins = tmp;
} else { } else {
return 0; logins = get_login_list(1);
} }
p = strtok(logins, ",");
while (p) {
char *user, *name, *home, dpystr[10];
char *q, *t;
int ok = 1, dn;
t = strdup(p); /* bob:0 */
q = strchr(t, ':');
if (! q) {
free(t);
break;
} }
#else *q = '\0';
return 0; user = t;
#endif snprintf(dpystr, 10, ":%s", q+1);
if (users) {
u = users;
ok = 0;
while (*u != NULL) {
if (*(*u) == ':') {
u++;
continue;
}
if (!strcmp(user, *u++)) {
ok = 1;
break;
}
}
}
user2uid(user, &uid, &name, &home);
free(t);
if (! uid) { if (! uid) {
ok = 0;
}
if (! ok) {
p = strtok(NULL, ",");
continue;
}
for (dn = dmin; dn <= dmax; dn++) {
if (dn >= 0) {
sprintf(dpystr, ":%d", dn);
}
if (try_user_and_display(uid, dpystr)) {
if (switch_user_env(uid, name, home, 0)) {
rfbLog("lurk: now user: %s @ %s\n",
name, dpystr);
started_as_root = 2;
success = 1;
}
set_env("DISPLAY", dpystr);
break;
}
}
if (success) {
break;
}
p = strtok(NULL, ",");
}
free(logins);
return success;
}
void lurk_loop(char *str) {
char *tstr = NULL, **users = NULL;
if (strstr(str, "lurk=") != str) {
exit(1);
}
rfbLog("lurking for logins using: '%s'\n", str);
if (strlen(str) > strlen("lurk=")) {
char *q = strchr(str, '=');
tstr = strdup(q+1);
users = user_list(tstr);
}
while (1) {
if (lurk(users)) {
break;
}
sleep(3);
}
if (tstr) {
free(tstr);
}
if (users) {
free(users);
}
}
int guess_user_and_switch(char *str, int fb_mode) {
char *dstr, *d = DisplayString(dpy);
char *p, *tstr = NULL, *allowed = NULL, *logins, **users = NULL;
int dpy1, ret = 0;
/* pick out ":N" */
dstr = strchr(d, ':');
if (! dstr) {
return 0;
}
if (sscanf(dstr, ":%d", &dpy1) != 1) {
return 0;
}
if (dpy1 < 0) {
return 0; return 0;
} }
if (! force) { if (strstr(str, "guess=") == str && strlen(str) > strlen("guess=")) {
#if LIBVNCSERVER_HAVE_FORK && LIBVNCSERVER_HAVE_SYS_WAIT_H allowed = strchr(str, '=');
allowed++;
tstr = strdup(allowed);
users = user_list(tstr);
}
/* loop over the utmpx entries looking for this display */
logins = get_login_list(1);
p = strtok(logins, ",");
while (p) {
char *user, *q, *t;
int dpy2, ok = 1;
t = strdup(p);
q = strchr(t, ':');
if (! q) {
free(t);
break;
}
*q = '\0';
user = t;
dpy2 = atoi(q+1);
if (users) {
char **u = users;
ok = 0;
while (*u != NULL) {
if (!strcmp(user, *u++)) {
ok = 1;
break;
}
}
}
if (dpy1 != dpy2) {
ok = 0;
}
if (! ok) {
free(t);
p = strtok(NULL, ",");
continue;
}
if (switch_user(user, fb_mode)) {
rfbLog("switched to guessed user: %s\n", user);
free(t);
ret = 1;
break;
}
p = strtok(NULL, ",");
}
if (tstr) {
free(tstr);
}
if (users) {
free(users);
}
if (logins) {
free(logins);
}
return ret;
}
int try_user_and_display(uid_t uid, char *dpystr) {
/* NO strtoks */
#if LIBVNCSERVER_HAVE_FORK && LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_PWD_H
pid_t pid, pidw; pid_t pid, pidw;
char *home, *name;
int st; int st;
struct passwd *pw;
pw = getpwuid(uid);
if (pw) {
name = pw->pw_name;
home = pw->pw_dir;
} else {
return 0;
}
/* /*
* We fork here and try to open the display again as the * We fork here and try to open the display again as the
* new user. Unreadable XAUTHORITY could be a problem... * new user. Unreadable XAUTHORITY could be a problem...
* This is not really needed since we have DISPLAY open * This is not really needed since we have DISPLAY open but:
* but: 1) is a good indicator this user owns the session * 1) is a good indicator this user owns the session and 2)
* and 2) some activities do spawn new X apps, e.g. * some activities do spawn new X apps, e.g. xmessage(1), etc.
* xmessage(1), etc.
*/ */
if ((pid = fork()) > 0) { if ((pid = fork()) > 0) {
; ;
...@@ -1215,23 +1512,17 @@ int switch_user(char *user) { ...@@ -1215,23 +1512,17 @@ int switch_user(char *user) {
rfbLogPerror("fork"); rfbLogPerror("fork");
return 0; return 0;
} else { } else {
/* child */
Display *dpy2 = 0; Display *dpy2 = 0;
char *xauth = getenv("XAUTHORITY"); int rc;
#if LIBVNCSERVER_HAVE_SETUID
if (setuid(uid) != 0) { rc = switch_user_env(uid, name, home, 0);
exit(1); /* fail */ if (! rc) {
}
#else
exit(1); exit(1);
#endif
if (xauth && access(xauth, R_OK) != 0) {
*(xauth-2) = '_'; /* yow */
} }
set_env("USER", name);
set_env("LOGNAME", name);
set_env("HOME", home);
fclose(stderr); fclose(stderr);
dpy2 = XOpenDisplay(DisplayString(dpy)); dpy2 = XOpenDisplay(dpystr);
if (dpy2) { if (dpy2) {
XCloseDisplay(dpy2); XCloseDisplay(dpy2);
exit(0); /* success */ exit(0); /* success */
...@@ -1240,52 +1531,91 @@ int switch_user(char *user) { ...@@ -1240,52 +1531,91 @@ int switch_user(char *user) {
} }
} }
/* see what the child says: */ /* see what the child says: */
pidw = waitpid(pid, &st, 0); pidw = waitpid(pid, &st, 0);
if (pidw == pid && WIFEXITED(st) && WEXITSTATUS(st) == 0) { if (pidw == pid && WIFEXITED(st) && WEXITSTATUS(st) == 0) {
force = 1; return 1;
}
#endif /* LIBVNCSERVER_HAVE_FORK ... */
return 0;
}
int switch_user(char *user, int fb_mode) {
/* NO strtoks */
int doit = 0;
uid_t uid = 0;
char *name, *home;
if (*user == '+') {
doit = 1;
user++;
}
if (strstr(user, "guess=") == user) {
return guess_user_and_switch(user, fb_mode);
}
user2uid(user, &uid, &name, &home);
if (uid == -1 || uid == 0) {
return 0;
}
if (! doit && dpy) {
/* see if this display works: */
char *dstr = DisplayString(dpy);
doit = try_user_and_display(uid, dstr);
}
if (doit) {
int rc = switch_user_env(uid, name, home, fb_mode);
if (rc) {
started_as_root = 2;
} }
#else return rc;
force = 1; } else {
#endif return 0;
} }
}
if (force) { int switch_user_env(uid_t uid, char *name, char *home, int fb_mode) {
char *xauth = getenv("XAUTHORITY"); /* NO strtoks */
char *xauth;
int reset_fb = 0;
#if !LIBVNCSERVER_HAVE_SETUID
return 0;
#else
/* /*
* OK tricky here, we need to free the shm... otherwise * OK tricky here, we need to free the shm... otherwise
* we won't be able to delete it as the other user... * we won't be able to delete it as the other user...
*/ */
#if !LIBVNCSERVER_HAVE_SETUID if (fb_mode == 1 && using_shm) {
return 0; reset_fb = 1;
#else
if (using_shm) {
clean_shm(0); clean_shm(0);
free_tiles(); free_tiles();
} }
if (setuid(uid) != 0) { if (setuid(uid) != 0) {
if (using_shm) { if (reset_fb) {
/* 2 means we did clean_shm and free_tiles */ /* 2 means we did clean_shm and free_tiles */
do_new_fb(2); do_new_fb(2);
} }
return 0; return 0;
} }
#endif #endif
if (using_shm) { if (reset_fb) {
do_new_fb(2); do_new_fb(2);
} }
xauth = getenv("XAUTHORITY");
if (xauth && access(xauth, R_OK) != 0) { if (xauth && access(xauth, R_OK) != 0) {
*(xauth-2) = '_'; /* yow */ *(xauth-2) = '_'; /* yow */
} }
set_env("USER", name); set_env("USER", name);
set_env("LOGNAME", name); set_env("LOGNAME", name);
set_env("HOME", home); set_env("HOME", home);
return 1; return 1;
} else {
return 0;
}
} }
void try_to_switch_users(void) { void try_to_switch_users(void) {
...@@ -1309,9 +1639,8 @@ void try_to_switch_users(void) { ...@@ -1309,9 +1639,8 @@ void try_to_switch_users(void) {
users = strdup(users_list); users = strdup(users_list);
if (strstr(users, "guess=") == users) { if (strstr(users, "guess=") == users) {
if (switch_user(users)) { if (switch_user(users, 1)) {
started_as_root = 2; started_as_root = 2;
rfbLog("try_to_switch_users: now %s\n", p);
} }
free(users); free(users);
return; return;
...@@ -1319,7 +1648,7 @@ void try_to_switch_users(void) { ...@@ -1319,7 +1648,7 @@ void try_to_switch_users(void) {
p = strtok(users, ","); p = strtok(users, ",");
while (p) { while (p) {
if (switch_user(p)) { if (switch_user(p, 1)) {
started_as_root = 2; started_as_root = 2;
rfbLog("try_to_switch_users: now %s\n", p); rfbLog("try_to_switch_users: now %s\n", p);
break; break;
...@@ -6316,6 +6645,17 @@ void check_xevents(void) { ...@@ -6316,6 +6645,17 @@ void check_xevents(void) {
} }
sent_some_sel = 1; sent_some_sel = 1;
} }
if (! have_clients) {
/*
* If we don't have clients we can miss the X server
* going away until a client connects.
*/
static time_t last_X_ping = 0;
if (now > last_X_ping + 5) {
last_X_ping = now;
XGetSelectionOwner(dpy, XA_PRIMARY);
}
}
if (XCheckTypedEvent(dpy, MappingNotify, &xev)) { if (XCheckTypedEvent(dpy, MappingNotify, &xev)) {
XRefreshKeyboardMapping((XMappingEvent *) &xev); XRefreshKeyboardMapping((XMappingEvent *) &xev);
...@@ -11566,7 +11906,7 @@ void solid_root(char *color) { ...@@ -11566,7 +11906,7 @@ void solid_root(char *color) {
Colormap cmap; Colormap cmap;
if (subwin || window != rootwin) { if (subwin || window != rootwin) {
rfbLog("cannot set subwin to solid color, must be root\n"); rfbLog("cannot set subwin to solid color, must be rootwin\n");
return; return;
} }
...@@ -11625,7 +11965,6 @@ void solid_root(char *color) { ...@@ -11625,7 +11965,6 @@ void solid_root(char *color) {
iswa.override_redirect = True; iswa.override_redirect = True;
iswa.backing_store = NotUseful; iswa.backing_store = NotUseful;
iswa.save_under = False; iswa.save_under = False;
iswa.background_pixmap = None;
iswa.background_pixmap = ParentRelative; iswa.background_pixmap = ParentRelative;
iwin = XCreateWindow(dpy, window, 0, 0, dpy_x, dpy_y, 0, depth, iwin = XCreateWindow(dpy, window, 0, 0, dpy_x, dpy_y, 0, depth,
...@@ -11660,6 +11999,263 @@ void solid_root(char *color) { ...@@ -11660,6 +11999,263 @@ void solid_root(char *color) {
XDestroyWindow(dpy, expose); XDestroyWindow(dpy, expose);
} }
void solid_cde(char *color) {
int wsmax = 16;
static XImage *image[16];
static Window ws_wins[16];
static int nws = -1;
Window expose;
Pixmap pixmap;
XGCValues gcv;
GC gc;
XSetWindowAttributes swa;
Visual visual;
unsigned long mask, pixel;
XColor cdef;
Colormap cmap;
int n;
if (subwin || window != rootwin) {
rfbLog("cannot set subwin to solid color, must be rootwin\n");
return;
}
/* create the "clear" window just for generating exposures */
swa.override_redirect = True;
swa.backing_store = NotUseful;
swa.save_under = False;
swa.background_pixmap = None;
visual.visualid = CopyFromParent;
mask = (CWOverrideRedirect|CWBackingStore|CWSaveUnder|CWBackPixmap);
expose = XCreateWindow(dpy, window, 0, 0, dpy_x, dpy_y, 0, depth,
InputOutput, &visual, mask, &swa);
if (! color) {
/* restore the backdrop windows from the XImage snapshots */
for (n=0; n < nws; n++) {
Window twin;
if (! image[n]) {
continue;
}
twin = ws_wins[n];
if (! twin) {
twin = rootwin;
}
if (! valid_window(twin)) {
continue;
}
pixmap = XCreatePixmap(dpy, twin, dpy_x, dpy_y, depth);
/* draw the image to a pixmap: */
gcv.function = GXcopy;
gcv.plane_mask = AllPlanes;
gc = XCreateGC(dpy, twin, GCFunction|GCPlaneMask, &gcv);
XPutImage(dpy, pixmap, gc, image[n], 0, 0, 0, 0,
dpy_x, dpy_y);
gcv.foreground = gcv.background = BlackPixel(dpy, scr);
gc = XCreateGC(dpy, twin, GCForeground|GCBackground,
&gcv);
rfbLog("restoring CDE ws%d snapshot to 0x%lx\n",
n, twin);
/* set the pixmap as the bg: */
XSetWindowBackgroundPixmap(dpy, twin, pixmap);
XFreePixmap(dpy, pixmap);
XClearWindow(dpy, twin);
XFlush(dpy);
}
/* generate exposures */
XMapWindow(dpy, expose);
XSync(dpy, False);
XDestroyWindow(dpy, expose);
return;
}
if (nws < 0) {
/* need to retrieve snapshots of the ws backgrounds: */
Window iwin, wm_win;
XSetWindowAttributes iswa;
Atom dt_list, wm_info, type;
int format;
unsigned long length, after;
unsigned char *data;
unsigned int * dp;
nws = 0;
/* extract the hidden wm properties about backdrops: */
wm_info = XInternAtom(dpy, "_MOTIF_WM_INFO", True);
if (wm_info == None) {
return;
}
XGetWindowProperty(dpy, rootwin, wm_info, 0L, 10L, False,
AnyPropertyType, &type, &format, &length, &after, &data);
/*
* xprop -notype -root _MOTIF_WM_INFO
* _MOTIF_WM_INFO = 0x2, 0x580028
*/
if (length < 2 || format != 32 || after != 0) {
return;
}
dp = (unsigned int *) data;
wm_win = (Window) *(dp+1); /* 2nd item. */
dt_list = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True);
if (dt_list == None) {
return;
}
XGetWindowProperty(dpy, wm_win, dt_list, 0L, 10L, False,
AnyPropertyType, &type, &format, &length, &after, &data);
nws = length;
if (nws > wsmax) {
nws = wsmax;
}
if (nws < 0) {
nws = 0;
}
rfbLog("special CDE win: 0x%lx, %d workspaces\n", wm_win, nws);
if (nws == 0) {
return;
}
for (n=0; n<nws; n++) {
Atom ws_atom;
char tmp[32];
Window twin;
XWindowAttributes attr;
int i, cnt;
image[n] = NULL;
ws_wins[n] = 0x0;
sprintf(tmp, "_DT_WORKSPACE_INFO_ws%d", n);
ws_atom = XInternAtom(dpy, tmp, False);
if (ws_atom == None) {
continue;
}
XGetWindowProperty(dpy, wm_win, ws_atom, 0L, 100L,
False, AnyPropertyType, &type, &format, &length,
&after, &data);
if (format != 8 || after != 0) {
continue;
}
/*
* xprop -notype -id wm_win
* _DT_WORKSPACE_INFO_ws0 = "One", "3", "0x2f2f4a",
* "0x63639c", "0x103", "1", "0x58044e"
*/
cnt = 0;
twin = 0x0;
for (i=0; i<length; i++) {
if (*(data+i) != '\0') {
continue;
}
cnt++; /* count nulls to indicate field */
if (cnt == 6) {
/* one past the null: */
char *q = (char *) (data+i+1);
unsigned long in;
if (sscanf(q, "0x%lx", &in) == 1) {
twin = (Window) in;
break;
}
}
}
ws_wins[n] = twin;
if (! twin) {
twin = rootwin;
}
XGetWindowAttributes(dpy, twin, &attr);
if (twin != rootwin) {
if (attr.map_state != IsViewable) {
XMapWindow(dpy, twin);
}
XRaiseWindow(dpy, twin);
}
XSync(dpy, False);
/* create image window: */
iswa.override_redirect = True;
iswa.backing_store = NotUseful;
iswa.save_under = False;
iswa.background_pixmap = ParentRelative;
visual.visualid = CopyFromParent;
iwin = XCreateWindow(dpy, twin, 0, 0, dpy_x, dpy_y,
0, depth, InputOutput, &visual, mask, &iswa);
rfbLog("snapshotting CDE backdrop ws%d 0x%lx -> "
"0x%lx ...\n", n, twin, iwin);
XMapWindow(dpy, iwin);
XSync(dpy, False);
image[n] = XGetImage(dpy, iwin, 0, 0, dpy_x, dpy_y,
AllPlanes, ZPixmap);
XSync(dpy, False);
XDestroyWindow(dpy, iwin);
if (twin != rootwin) {
XLowerWindow(dpy, twin);
if (attr.map_state != IsViewable) {
XUnmapWindow(dpy, twin);
}
}
}
}
if (nws == 0) {
return;
}
/* use black for low colors or failure */
pixel = BlackPixel(dpy, scr);
if (depth > 8 || strcmp(color, solid_default)) {
cmap = DefaultColormap (dpy, scr);
if (XParseColor(dpy, cmap, color, &cdef) &&
XAllocColor(dpy, cmap, &cdef)) {
pixel = cdef.pixel;
} else {
rfbLog("error parsing/allocing color: %s\n", color);
}
}
rfbLog("setting solid backgrounds...\n");
for (n=0; n < nws; n++) {
Window twin = ws_wins[n];
if (image[n] == NULL) {
continue;
}
if (! twin) {
twin = rootwin;
}
XSetWindowBackground(dpy, twin, pixel);
}
XMapWindow(dpy, expose);
XSync(dpy, False);
XDestroyWindow(dpy, expose);
}
void solid_gnome(char *color) { void solid_gnome(char *color) {
char get_color[] = "gconftool-2 --get " char get_color[] = "gconftool-2 --get "
"/desktop/gnome/background/primary_color"; "/desktop/gnome/background/primary_color";
...@@ -11793,6 +12389,13 @@ char *guess_desktop() { ...@@ -11793,6 +12389,13 @@ char *guess_desktop() {
if (prop != None) { if (prop != None) {
return "gnome"; return "gnome";
} }
prop = XInternAtom(dpy, "_MOTIF_WM_INFO", True);
if (prop != None) {
prop = XInternAtom(dpy, "_DT_WORKSPACE_LIST", True);
if (prop != None) {
return "cde";
}
}
return "root"; return "root";
} }
...@@ -11817,6 +12420,8 @@ void solid_bg(int restore) { ...@@ -11817,6 +12420,8 @@ void solid_bg(int restore) {
solid_gnome(NULL); solid_gnome(NULL);
} else if (desktop == 2) { } else if (desktop == 2) {
solid_kde(NULL); solid_kde(NULL);
} else if (desktop == 3) {
solid_cde(NULL);
} }
solid_on = 0; solid_on = 0;
return; return;
...@@ -11836,6 +12441,8 @@ void solid_bg(int restore) { ...@@ -11836,6 +12441,8 @@ void solid_bg(int restore) {
dtname = "gnome"; dtname = "gnome";
} else if (strstr(solid_str, "kde:") == solid_str) { } else if (strstr(solid_str, "kde:") == solid_str) {
dtname = "kde"; dtname = "kde";
} else if (strstr(solid_str, "cde:") == solid_str) {
dtname = "cde";
} else { } else {
dtname = "root"; dtname = "root";
} }
...@@ -11856,6 +12463,9 @@ void solid_bg(int restore) { ...@@ -11856,6 +12463,9 @@ void solid_bg(int restore) {
} else if (!strcmp(dtname, "kde")) { } else if (!strcmp(dtname, "kde")) {
desktop = 2; desktop = 2;
solid_kde(color); solid_kde(color);
} else if (!strcmp(dtname, "cde")) {
desktop = 3;
solid_cde(color);
} else { } else {
desktop = 0; desktop = 0;
solid_root(color); solid_root(color);
...@@ -14559,7 +15169,8 @@ char gui_code[] = ""; ...@@ -14559,7 +15169,8 @@ char gui_code[] = "";
#include "tkx11vnc.h" #include "tkx11vnc.h"
#endif #endif
void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { void run_gui(char *gui_xdisplay, int connect_to_x11vnc, int simple_gui,
pid_t parent) {
char *x11vnc_xdisplay = NULL; char *x11vnc_xdisplay = NULL;
char extra_path[] = ":/usr/local/bin:/usr/bin/X11:/usr/sfw/bin" char extra_path[] = ":/usr/local/bin:/usr/bin/X11:/usr/sfw/bin"
":/usr/X11R6/bin:/usr/openwin/bin:/usr/dt/bin"; ":/usr/X11R6/bin:/usr/openwin/bin:/usr/dt/bin";
...@@ -14682,8 +15293,10 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) { ...@@ -14682,8 +15293,10 @@ void run_gui(char *gui_xdisplay, int connect_to_x11vnc, pid_t parent) {
set_env("DISPLAY", gui_xdisplay); set_env("DISPLAY", gui_xdisplay);
set_env("X11VNC_PROG", program_name); set_env("X11VNC_PROG", program_name);
set_env("X11VNC_CMDLINE", program_cmdline); set_env("X11VNC_CMDLINE", program_cmdline);
if (simple_gui) {
set_env("X11VNC_SIMPLE_GUI", "1");
}
if (auth_file) { if (auth_file) {
set_env("X11VNC_AUTH_FILE", auth_file);
} }
sprintf(cmd, "%s -", wish); sprintf(cmd, "%s -", wish);
...@@ -14722,6 +15335,7 @@ void do_gui(char *opts) { ...@@ -14722,6 +15335,7 @@ void do_gui(char *opts) {
char *gui_xdisplay = NULL; char *gui_xdisplay = NULL;
int start_x11vnc = 1; int start_x11vnc = 1;
int connect_to_x11vnc = 0; int connect_to_x11vnc = 0;
int simple_gui = 0;
Display *test_dpy; Display *test_dpy;
if (opts) { if (opts) {
...@@ -14754,6 +15368,8 @@ void do_gui(char *opts) { ...@@ -14754,6 +15368,8 @@ void do_gui(char *opts) {
} else if (!strcmp(p, "conn") || !strcmp(p, "connect")) { } else if (!strcmp(p, "conn") || !strcmp(p, "connect")) {
start_x11vnc = 0; start_x11vnc = 0;
connect_to_x11vnc = 1; connect_to_x11vnc = 1;
} else if (!strcmp(p, "ez") || !strcmp(p, "simple")) {
simple_gui = 1;
} else { } else {
fprintf(stderr, "unrecognized gui opt: %s\n", p); fprintf(stderr, "unrecognized gui opt: %s\n", p);
} }
...@@ -14804,7 +15420,8 @@ void do_gui(char *opts) { ...@@ -14804,7 +15420,8 @@ void do_gui(char *opts) {
perror("fork"); perror("fork");
clean_up_exit(1); clean_up_exit(1);
} else { } else {
run_gui(gui_xdisplay, connect_to_x11vnc, parent); run_gui(gui_xdisplay, connect_to_x11vnc, simple_gui,
parent);
exit(1); exit(1);
} }
#else #else
...@@ -14814,7 +15431,7 @@ void do_gui(char *opts) { ...@@ -14814,7 +15431,7 @@ void do_gui(char *opts) {
#endif #endif
} }
if (!start_x11vnc) { if (!start_x11vnc) {
run_gui(gui_xdisplay, connect_to_x11vnc, 0); run_gui(gui_xdisplay, connect_to_x11vnc, simple_gui, 0);
exit(1); exit(1);
} }
if (old_xauth) { if (old_xauth) {
...@@ -15957,10 +16574,13 @@ static void print_help(int mode) { ...@@ -15957,10 +16574,13 @@ static void print_help(int mode) {
" (full-access) password must also be supplied.\n" " (full-access) password must also be supplied.\n"
"-passwdfile filename Specify libvncserver -passwd via the first line of\n" "-passwdfile filename Specify libvncserver -passwd via the first line of\n"
" the file \"filename\" instead of via command line.\n" " the file \"filename\" instead of via command line.\n"
" If a second non blank line exists in the file it is\n" " If a second non blank line exists in the file it\n"
" taken as a view-only password (i.e. -viewpasswd) Note:\n" " is taken as a view-only password (i.e. -viewpasswd)\n"
" this is a simple plaintext passwd, see also -rfbauth\n" " To supply an empty password for either field use the\n"
" and -storepasswd below for obfuscated passwords.\n" " string \"__EMPTY__\". Note: -passwdfile is a simple\n"
" plaintext passwd, see also -rfbauth and -storepasswd\n"
" below for obfuscated passwords. Neither should be\n"
" readable by others.\n"
"-storepasswd pass file Store password \"pass\" as the VNC password in the\n" "-storepasswd pass file Store password \"pass\" as the VNC password in the\n"
" file \"file\". Once the password is stored the\n" " file \"file\". Once the password is stored the\n"
" program exits. Use the password via \"-rfbauth file\"\n" " program exits. Use the password via \"-rfbauth file\"\n"
...@@ -16039,44 +16659,64 @@ static void print_help(int mode) { ...@@ -16039,44 +16659,64 @@ static void print_help(int mode) {
" \n" " \n"
" Why use this option? In general it is not needed\n" " Why use this option? In general it is not needed\n"
" since x11vnc is already connected to the display and\n" " since x11vnc is already connected to the display and\n"
" can perform its primary functions. It was added to\n" " can perform its primary functions. The option was\n"
" make some of the *external* utility commands x11vnc\n" " added to make some of the *external* utility commands\n"
" occasionally runs work properly. In particular under\n" " x11vnc occasionally runs work properly. In particular\n"
" GNOME and KDE to implement the \"-solid color\" feature\n" " under GNOME and KDE to implement the \"-solid color\"\n"
" external commands (gconftool-2 and dcop) must be run as\n" " feature external commands (gconftool-2 and dcop) must be\n"
" the user owning the desktop session. This option also\n" " run as the user owning the desktop session. Since this\n"
" affects the userid used to run the processes for the\n" " option switches userid it also affects the userid used\n"
" -accept and -gone options. It also affects the ability\n" " to run the processes for the -accept and -gone options.\n"
" to read files for options such as -connect, -allow, and\n" " It also affects the ability to read files for options\n"
" -remap. Note that the -connect file is also written to.\n" " such as -connect, -allow, and -remap. Note that the\n"
" -connect file is also sometimes written to.\n"
" \n" " \n"
" So be careful with this option since in many situations\n" " So be careful with this option since in many situations\n"
" its use can decrease security.\n" " its use can decrease security.\n"
" \n" " \n"
" The switch to a user will only take place if the display\n" " The switch to a user will only take place if the\n"
" can still be opened as that user (this is primarily to\n" " display can still be successfully opened as that user\n"
" try to guess the actual owner of the session). Example:\n" " (this is primarily to try to guess the actual owner\n"
" \"-users fred,wilma,betty\". Note that a malicious\n" " of the session). Example: \"-users fred,wilma,betty\".\n"
" user \"barney\" by quickly using \"xhost +\" when\n" " Note that a malicious user \"barney\" by quickly using\n"
" logging in can get x11vnc to switch to user \"fred\".\n" " \"xhost +\" when logging in may get x11vnc to switch\n"
" What happens next?\n" " to user \"fred\". What happens next?\n"
" \n" " \n"
" Under display managers it may be a long time before\n" " Under display managers it may be a long time before\n"
" the switch succeeds (i.e. a user logs in). To make\n" " the switch succeeds (i.e. a user logs in). To make\n"
" it switch immediately regardless if the display can\n" " it switch immediately regardless if the display\n"
" be reopened or not prefix the username with the +\n" " can be reopened prefix the username with the +\n"
" character. E.g. \"-users +bob\" or \"-users +nobody\".\n" " character. E.g. \"-users +bob\" or \"-users +nobody\".\n"
" The latter (i.e. switching immediately to user\n" " The latter (i.e. switching immediately to user\n"
" \"nobody\") is probably the only use of this option\n" " \"nobody\") is probably the only use of this option\n"
" that increases security. To switch to a user *before*\n" " that increases security.\n"
" connections to the display are made or any files opened\n" " \n"
" use the \"=\" character: \"-users =username\".\n" " To immediately switch to a user *before* connections to\n"
" the display are made or any files opened use the \"=\"\n"
" character: \"-users =bob\". That user needs to be able\n"
" to open the display of course.\n"
" \n" " \n"
" The special user \"guess\" means to examine the utmpx\n" " The special user \"guess=\" means to examine the utmpx\n"
" database looking for a user attached to the display\n" " database (see who(1)) looking for a user attached to\n"
" number and try him/her. To limit the list of guesses,\n" " the display number (from DISPLAY or -display option)\n"
" use: \"-users guess=bob,betty\". Be especially careful\n" " and try him/her. To limit the list of guesses, use:\n"
" using this mode.\n" " \"-users guess=bob,betty\".\n"
" \n"
" Even more sinister is the special user \"lurk=\" that\n"
" means to try to guess the DISPLAY from the utmpx login\n"
" database as well. So it \"lurks\" waiting for anyone\n"
" to log into an X session and then connects to it.\n"
" Specify a list of users after the = to limit which\n"
" users will be tried. If the first user in the list\n"
" is something like \":0\" or \":0-2\" that indicates a\n"
" range of DISPLAY numbers that will be tried (regardless\n"
" of whether they are in the utmpx database) for all\n"
" users that are logged in. Examples: \"-users lurk=\"\n"
" and \"-users lurk=:0-1,bob,mary\"\n"
" \n"
" Be especially careful using the \"guess=\" and \"lurk=\"\n"
" modes. They are not recommended for use on machines\n"
" with untrustworthy local users.\n"
" \n" " \n"
"-noshm Do not use the MIT-SHM extension for the polling.\n" "-noshm Do not use the MIT-SHM extension for the polling.\n"
" Remote displays can be polled this way: be careful this\n" " Remote displays can be polled this way: be careful this\n"
...@@ -16095,16 +16735,16 @@ static void print_help(int mode) { ...@@ -16095,16 +16735,16 @@ static void print_help(int mode) {
" For a different one specify the X color (rgb.txt name,\n" " For a different one specify the X color (rgb.txt name,\n"
" e.g. \"darkblue\" or numerical \"#RRGGBB\").\n" " e.g. \"darkblue\" or numerical \"#RRGGBB\").\n"
"\n" "\n"
" Currently this option only works on GNOME, KDE, and\n" " Currently this option only works on GNOME, KDE, CDE,\n"
" classic X (i.e. with the background image on the root\n" " and classic X (i.e. with the background image on the\n"
" window). The \"gconftool-2\" and \"dcop\" external\n" " root window). The \"gconftool-2\" and \"dcop\" external\n"
" commands are run for GNOME and KDE respectively.\n" " commands are run for GNOME and KDE respectively.\n"
" Other desktops won't work, e.g. XFCE (send us the\n" " Other desktops won't work, e.g. XFCE (send us the\n"
" corresponding commands if you find them). If x11vnc\n" " corresponding commands if you find them). If x11vnc is\n"
" is running as root (inetd(1) or gdm(1)), the -users\n" " running as root (inetd(1) or gdm(1)), the -users option\n"
" option may be needed for GNOME and KDE. If x11vnc\n" " may be needed for GNOME and KDE. If x11vnc guesses\n"
" guesses your desktop incorrectly, you can force it by\n" " your desktop incorrectly, you can force it by prefixing\n"
" prefixing color with \"gnome:\", \"kde:\", or \"root:\".\n" " color with \"gnome:\", \"kde:\", \"cde:\" or \"root:\".\n"
"-blackout string Black out rectangles on the screen. \"string\" is a\n" "-blackout string Black out rectangles on the screen. \"string\" is a\n"
" comma separated list of WxH+X+Y type geometries for\n" " comma separated list of WxH+X+Y type geometries for\n"
" each rectangle.\n" " each rectangle.\n"
...@@ -16512,9 +17152,11 @@ static void print_help(int mode) { ...@@ -16512,9 +17152,11 @@ static void print_help(int mode) {
" up on the X display in the environment variable DISPLAY.\n" " up on the X display in the environment variable DISPLAY.\n"
"\n" "\n"
" \"gui-opts\" can be a comma separated list of items.\n" " \"gui-opts\" can be a comma separated list of items.\n"
" Currently there are only two types of items: 1) a gui\n" " Currently there are these types of items: 1) a gui mode,\n"
" mode and 2) the X display the gui should display on.\n" " a 2) gui \"simplicity\", and 3) the X display the gui\n"
" The gui mode can be \"start\", \"conn\", or \"wait\"\n" " should display on.\n"
"\n"
" 1) The gui mode can be \"start\", \"conn\", or \"wait\"\n"
" \"start\" is the default mode above and is not required.\n" " \"start\" is the default mode above and is not required.\n"
" \"conn\" means do not automatically start up x11vnc,\n" " \"conn\" means do not automatically start up x11vnc,\n"
" but instead just try to connect to an existing x11vnc\n" " but instead just try to connect to an existing x11vnc\n"
...@@ -16522,15 +17164,22 @@ static void print_help(int mode) { ...@@ -16522,15 +17164,22 @@ static void print_help(int mode) {
" else (you will later instruct the gui to start x11vnc\n" " else (you will later instruct the gui to start x11vnc\n"
" or connect to an existing one.)\n" " or connect to an existing one.)\n"
"\n" "\n"
" Note the possible confusion regarding the potentially\n" " 2) The gui simplicity is off by default (a power-user\n"
" gui with all options is presented) To start with\n"
" something less daunting supply the string \"simple\"\n"
" (\"ez\" is an alias for this). Once the gui is\n"
" started you can toggle between the two with \"Misc ->\n"
" simple_gui\".\n"
"\n"
" 3) Note the possible confusion regarding the potentially\n"
" two different X displays: x11vnc polls one, but you\n" " two different X displays: x11vnc polls one, but you\n"
" may want the gui to appear on another. For example, if\n" " may want the gui to appear on another. For example, if\n"
" you ssh in and x11vnc is not running yet you may want\n" " you ssh in and x11vnc is not running yet you may want\n"
" the gui to come back to you via your ssh redirected X\n" " the gui to come back to you via your ssh redirected X\n"
" display (e.g. localhost:10).\n" " display (e.g. localhost:10).\n"
"\n" "\n"
" Examples: \"x11vnc -gui\", \"x11vnc -gui localhost:10\",\n" " Examples: \"x11vnc -gui\", \"x11vnc -gui ez\"\n"
" \"x11vnc -gui :10\", \"x11vnc -gui conn,host:10\",\n" " \"x11vnc -gui localhost:10\", \"x11vnc -gui conn,host:0\"\n"
"\n" "\n"
" If you do not specify a gui X display in \"gui-opts\"\n" " If you do not specify a gui X display in \"gui-opts\"\n"
" then the DISPLAY environment variable and -display\n" " then the DISPLAY environment variable and -display\n"
...@@ -17244,65 +17893,65 @@ static void check_rcfile(int argc, char **argv) { ...@@ -17244,65 +17893,65 @@ static void check_rcfile(int argc, char **argv) {
} }
} }
int main(int argc, char* argv[]) { void immediate_switch_user(int argc, char* argv[]) {
int i;
int i, len;
int ev, er, maj, min;
char *arg;
int remote_sync = 0;
char *remote_cmd = NULL;
char *query_cmd = NULL;
char *gui_str = NULL;
int pw_loc = -1;
int vpw_loc = -1;
int dt = 0, bg = 0;
int got_rfbwait = 0, got_deferupdate = 0, got_defer = 0;
/* used to pass args we do not know about to rfbGetScreen(): */
int argc_vnc = 1; char *argv_vnc[128];
/* if we are root limit some remote commands: */
if (!getuid() || !geteuid()) {
safe_remote_only = 1;
started_as_root = 1;
/* check for '-users =fred' */
for (i=1; i < argc; i++) { for (i=1; i < argc; i++) {
char *u; char *u;
int saved;
if (strcmp(argv[i], "-users")) { if (strcmp(argv[i], "-users")) {
continue; continue;
} }
if (i == argc - 1) { if (i == argc - 1) {
fprintf(stderr, "not enough arguments for: " fprintf(stderr, "not enough arguments for: -users\n");
"-users\n");
exit(1); exit(1);
} }
if (*(argv[i+1]) != '=') { if (*(argv[i+1]) != '=') {
break; break;
} }
/* wants an immediate switch: =bob */
u = strdup(argv[i+1]); u = strdup(argv[i+1]);
*u = '+'; *u = '+';
if (strstr(u, "+guess") == u) { if (strstr(u, "+guess") == u) {
fprintf(stderr, "invalid user: %s\n", u); fprintf(stderr, "invalid user: %s\n", u+1);
exit(1); exit(1);
} }
/* kludge... */ if (!switch_user(u, 0)) {
saved = using_shm; fprintf(stderr, "Could not switch to user: %s\n", u+1);
using_shm = 0;
if (!switch_user(u)) {
fprintf(stderr, "Could not switch to user: "
"%s\n", u+1);
exit(1); exit(1);
} else { } else {
fprintf(stderr, "Switched to user: %s\n", u+1); fprintf(stderr, "Switched to user: %s\n", u+1);
started_as_root = 2; started_as_root = 2;
} }
using_shm = saved;
free(u); free(u);
break; break;
} }
}
int main(int argc, char* argv[]) {
int i, len;
int ev, er, maj, min;
char *arg;
int remote_sync = 0;
char *remote_cmd = NULL;
char *query_cmd = NULL;
char *gui_str = NULL;
int pw_loc = -1;
int vpw_loc = -1;
int dt = 0, bg = 0;
int got_rfbwait = 0, got_deferupdate = 0, got_defer = 0;
/* used to pass args we do not know about to rfbGetScreen(): */
int argc_vnc = 1; char *argv_vnc[128];
/* if we are root limit some remote commands, etc: */
if (!getuid() || !geteuid()) {
safe_remote_only = 1;
started_as_root = 1;
/* check for '-users =bob' */
immediate_switch_user(argc, argv);
} }
argv_vnc[0] = strdup(argv[0]); argv_vnc[0] = strdup(argv[0]);
...@@ -17797,12 +18446,20 @@ int main(int argc, char* argv[]) { ...@@ -17797,12 +18446,20 @@ int main(int argc, char* argv[]) {
exit(1); exit(1);
} }
if (fgets(line, 1024, in) != NULL) { if (fgets(line, 1024, in) != NULL) {
char *q;
int len = strlen(line); int len = strlen(line);
if (len > 0 && line[len-1] == '\n') { if (len > 0 && line[len-1] == '\n') {
line[len-1] = '\0'; line[len-1] = '\0';
} }
argv_vnc[argc_vnc++] = strdup("-passwd"); argv_vnc[argc_vnc++] = strdup("-passwd");
if (!strcmp(line, "__EMPTY__")) {
argv_vnc[argc_vnc++] = strdup("");
} else if ((q = strstr(line, "__ENDPASSWD__")) !=NULL) {
*q = '\0';
argv_vnc[argc_vnc++] = strdup(line);
} else {
argv_vnc[argc_vnc++] = strdup(line); argv_vnc[argc_vnc++] = strdup(line);
}
pw_loc = 100; /* just for pw_loc check below */ pw_loc = 100; /* just for pw_loc check below */
if (fgets(line, 1024, in) != NULL) { if (fgets(line, 1024, in) != NULL) {
/* try to read viewonly passwd from file */ /* try to read viewonly passwd from file */
...@@ -17822,7 +18479,15 @@ int main(int argc, char* argv[]) { ...@@ -17822,7 +18479,15 @@ int main(int argc, char* argv[]) {
} }
} }
if (ok) { if (ok) {
if (!strcmp(line, "__EMPTY__")) {
viewonly_passwd = strdup("");
} else if ((q = strstr(line,
"__ENDPASSWD__")) != NULL) {
*q = '\0';
viewonly_passwd = strdup(line);
} else {
viewonly_passwd = strdup(line); viewonly_passwd = strdup(line);
}
} else { } else {
rfbLog("*** not setting" rfbLog("*** not setting"
" viewonly password to the 2nd" " viewonly password to the 2nd"
...@@ -17972,6 +18637,8 @@ int main(int argc, char* argv[]) { ...@@ -17972,6 +18637,8 @@ int main(int argc, char* argv[]) {
fprintf(stderr, " vnc_conn: %d\n", vnc_connect); fprintf(stderr, " vnc_conn: %d\n", vnc_connect);
fprintf(stderr, " allow: %s\n", allow_list ? allow_list fprintf(stderr, " allow: %s\n", allow_list ? allow_list
: "null"); : "null");
fprintf(stderr, " input: %s\n", allowed_input_str
? allowed_input_str : "null");
fprintf(stderr, " passfile: %s\n", passwdfile ? passwdfile fprintf(stderr, " passfile: %s\n", passwdfile ? passwdfile
: "null"); : "null");
fprintf(stderr, " accept: %s\n", accept_cmd ? accept_cmd fprintf(stderr, " accept: %s\n", accept_cmd ? accept_cmd
...@@ -18093,6 +18760,14 @@ int main(int argc, char* argv[]) { ...@@ -18093,6 +18760,14 @@ int main(int argc, char* argv[]) {
use_xkb_modtweak = 0; use_xkb_modtweak = 0;
#endif #endif
if (users_list && strstr(users_list, "lurk=")) {
if (use_dpy) {
rfbLog("warning: -display does not make sense in "
"\"lurk=\" mode...\n");
}
lurk_loop(users_list);
}
if (use_dpy) { if (use_dpy) {
dpy = XOpenDisplay(use_dpy); dpy = XOpenDisplay(use_dpy);
} else if ( (use_dpy = getenv("DISPLAY")) ) { } else if ( (use_dpy = getenv("DISPLAY")) ) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment