Artifact Content
Not logged in

Artifact 4cd95e6bb9d81ef3d5cde7e1cc8b48a605ddad0c:


# Simple VNC viewer using the tkvnc widget from http://www.ch-werner.de/tkvnc
#############################################################################
#
# To scroll the VNC widget use three finger wipe. To zoom the VNC widget
# use two finger pinch gesture. To bring up the on-screen keyboard tap
# with two fingers in the area where you want to type in.
#
# The "Back" key brings up the option dialog with entries for host,
# password etc. and buttons to exit and connect/disconnect.

package require vnc
wm attributes . -fullscreen 1
. configure -bg black
sdltk touchtranslate 13 ;# RMB, pan/zoom, fingers translated

catch {borg screenorientation landscape}

array set VNCEV {fbits 0 trigger 0}
array set VNCSET {host {} pwd {} shared 0 viewonly 0 lifecycle 0}
array set VNC {host {} lastconnect 1}

# callback on connection status change from VNC widget

proc vnc_info {data} {
    global VNCSET VNC
    if {$VNCSET(lifecycle)} {
	return
    }
    array set VNC $data
    if {$VNC(lastconnect) && !$VNC(connected)} {
	vnc_settings
    } elseif {!$VNC(lastconnect) && $VNC(connected)} {
	if {[catch {sdltk root $VNC(width) $VNC(height)}]} {
	    .vnc disconnect
	    return
	}
	set VNCSET(host) $VNC(host)
	set VNCSET(shared) $VNC(shared)
	set VNCSET(viewonly) $VNC(viewonly)
	if {![catch {open ~/vnc.set w} f]} {
	    puts $f [array get VNCSET]
	    close $f
	}
    }
    set VNC(lastconnect) $VNC(connected)
}

# settings dialog

proc vnc_settings {} {
    global VNCSET VNC
    set w .settings
    if {[winfo exists $w]} {
	if {$VNC(connected)} {
	    $w.buttons.a configure -text "Disconnect" \
		-command [list vnc_command $w disc]
	} else {
	    $w.buttons.a configure -text "Connect" \
		-command [list vnc_command $w conn]
	}
	return
    }
    toplevel $w
    wm title $w "VNC Options"
    wm protocol $w WM_DELETE_WINDOW [list $w.buttons.c invoke]
    wm transient $w .
    ttk::label $w.lh -text "Host:"
    grid $w.lh -row 0 -column 0 -sticky e -padx 5 -pady 5
    ttk::entry $w.host -textvariable VNCSET(host)
    grid $w.host -row 0 -column 1 -sticky ew -padx 5 -pady 5
    ttk::label $w.lp -text "Password:"
    grid $w.lp -row 1 -column 0 -sticky e -padx 5 -pady 5
    ttk::entry $w.pwd -textvariable VNCSET(pwd) -show "*"
    grid $w.pwd -row 1 -column 1 -sticky ew -padx 5 -pady 5
    ttk::checkbutton $w.shared -variable VNCSET(shared) -text "Shared"
    grid $w.shared -row 2 -column 1 -sticky w -padx 5 -pady 5
    ttk::checkbutton $w.vonly -variable VNCSET(viewonly) -text "View Only"
    grid $w.vonly -row 3 -column 1 -sticky w -padx 5 -pady 5
    ttk::frame $w.buttons
    grid $w.buttons -row 4 -column 0 -columnspan 2 -sticky ew -padx 5 -pady 5
    if {$VNC(connected)} {
	ttk::button $w.buttons.a -text "Disconnect" -width 12 \
	    -command [list vnc_command $w disc]
    } else {
	if {![catch {open ~/vnc.set} f]} {
	    catch {array set VNCSET [read $f 1024]}
	    close $f
	}
	ttk::button $w.buttons.a -text "Connect" -width 12 \
	    -command [list vnc_command $w conn]
    }
    ttk::button $w.buttons.x -text "Exit" -width 12 -command {exit 0}
    ttk::button $w.buttons.c -text "Cancel" -width 12 \
	-command [subst {
	    grab release $w
	    focus .vnc
	    destroy $w
	}]
    pack $w.buttons.a $w.buttons.x $w.buttons.c -side left -expand 1 -padx 5
    bind $w <Break> [list $w.buttons.c invoke]
    ::tk::PlaceWindow $w
    grab $w
}

# perform connect to VNC server

proc vnc_connect {} {
    global VNCSET
    set args $VNCSET(host)
    lappend args $VNCSET(pwd)
    if {$VNCSET(shared)} {
	lappend args "-shared"
    }
    if {$VNCSET(viewonly)} {
	lappend args "-viewonly"
    }
    if {[catch {.vnc connect {*}$args} err]} {
	tk_messageBox -title "Error" -message $err \
	    -type ok -icon error
	after idle vnc_settings
    }
}

# execute connect or disconnect to/from VNC server

proc vnc_command {w what} {
    switch -glob -- $what {
	disc* {
	    after idle {
		catch {.vnc disconnect}
	    }
	}
	conn* {
	    after idle vnc_connect
	}
    }
    grab release $w
    focus .vnc
    destroy $w
}

# handle finger events and viewport changes

proc vnc_vp_finger {w op rootx rooty dx dy state} {
    global VNCEV
    switch $op {
	v {
	    # viewport changed, withdraw trigger
	    set VNCEV(trigger) 0
	}
	d {
	    set VNCEV(x$state) \
		[expr round(($rootx * [winfo screenwidth .]) / 10000)]
	    set VNCEV(y$state) \
		[expr round(($rooty * [winfo screenheight .]) / 10000)]
	    set VNCEV(fbits) [expr $VNCEV(fbits) | (1 << $state)]
	    if {($VNCEV(fbits) & 6) == 6} {
		set VNCEV(trigger) 1
	    }
	}
	m {
	    if {($dx < -15) || ($dx > 15) || ($dy < -15) || ($dy > 15)} {
		# motion delta, withdraw trigger
		set VNCEV(trigger) 0
	    }
	}
	u {
	    set VNCEV(x$state) \
		[expr round(($rootx * [winfo screenwidth .]) / 10000)]
	    set VNCEV(y$state) \
		[expr round(($rooty * [winfo screenheight .]) / 10000)]
	    set VNCEV(fbits) [expr $VNCEV(fbits) & ~(1 << $state)]
	    set doit 0
	    if {$VNCEV(fbits) == 0} {
		if {$VNCEV(trigger)} {
		    set doit 1
		}
		set VNCEV(trigger) 0
	    }
	    if {$doit} {
		set dx [expr {$VNCEV(x2) - $VNCEV(x1)}]
		set dy [expr {$VNCEV(y2) - $VNCEV(y1)}]
		set px [expr {$VNCEV(x1) + $dx / 2}]
		set py [expr {$VNCEV(y1) + $dy / 2}]
		sdltk textinput 1 $px $py
	    }
	}
    }
}

# handle app lifecycle events

proc vnc_lifecycle {suspend} {
    global VNCSET VNCEV
    if {$suspend} {
	set VNCEV(fbits) 0
	set VNCEV(trigger) 0
	set VNCSET(lastinfo) [.vnc info]
	set VNCSET(lifecycle) 1
	catch {.vnc disconnect}
    } else {
	set VNCSET(lifecycle) 0
	array set info $VNCSET(lastinfo)
	if {$info(connected)} {
	    set args $info(host)
	    lappend args $VNCSET(pwd)
	    if {$info(shared)} {
		lappend args "-shared"
	    }
	    if {$info(viewonly)} {
		lappend args "-viewonly"
	    }
	    if {[catch {.vnc connect {*}$args} err]} {
		after idle vnc_settings
	    }
	}
    }
}

vnc .vnc -bd 0 -relief flat -highlightthickness 0 -bg black \
    -infocommand vnc_info
pack .vnc -side top -fill both -expand 1
focus .vnc

wm withdraw .

bind . <<WillEnterBackground>> {vnc_lifecycle 1}
bind . <<WillEnterForeground>> {vnc_lifecycle 0}
bind . <<ViewportUpdate>> {vnc_vp_finger %W v %x %y %X %Y %s}
bind . <<FingerDown>> {vnc_vp_finger %W d %x %y %X %Y %s}
bind . <<FingerMotion>> {vnc_vp_finger %W m %x %y %X %Y %s}
bind . <<FingerUp>> {vnc_vp_finger %W u %x %y %X %Y %s}

bind VNC <Control-Tab> {}
bind VNC <Break> {vnc_settings ; break}

# initially bring up settings dialog

after idle {
    wm deiconify .
    event generate .vnc <Key-Break>
}