3 #---------- general utilities ----------
7 tk_setPalette background black foreground white
11 set default_speedstep_list {0 1 10 20 35 50 65 80 95 110 126}
16 proc pagew {page} { return ".picture-$page" }
17 proc debug {m} { puts $m }
25 proc widgets-dgram {} {
26 global cpage geometry picturepadx picturepady
27 if {[string length $geometry]} { wm geometry . $geometry }
28 set sizes [exec ./gui-plan-$cpage --sizes]
31 frame $w.picture -background {} \
32 -width [lindex $sizes 0] \
33 -height [lindex $sizes 1]
34 pack $w.picture -padx $picturepadx -pady $picturepady
38 proc widgets-fullscreen-nowm {} {
39 pack propagate . false
40 foreach wh {width height} {
41 . configure -$wh [winfo screen$wh .]
46 global errorCode errorInfo
48 puts stderr "UNEXPECTED BACKGROUND ERROR\n"
49 puts stderr "$errorCode\n$errorInfo\n$emsg"
54 #---------- train set event registraton ----------
56 set event_dispatch_body {
59 set event_selections {}
61 proc register-event {selections args re body} {
62 global event_dispatch_body event_selections
64 eval lappend event_selections $selections
66 foreach selection $selections {
67 if {[regexp {^\w} $selection]} {
68 error "selection $selection lacks context char"
71 if {[regexp {^\^\w} $re]} {
72 error "re $re never matches context char"
75 regsub -all {\W+} $re - proc
76 set proc "event/$proc/[join $args -]"
79 while {![catch { info args $proc$suffix }]} { set suffix [incr number] }
82 proc $proc [concat l $args] $body
84 foreach a $args { append al " \$$a" }
86 append event_dispatch_body \
87 " if {\[regexp [list $re] \$l dummy $args]} {\n" \
88 " debug \"$proc$al\"\n" \
89 " eval [list $proc] \[list \$l$al]\n" \
94 #---------- handling of commands we issue ----------
96 proc scmd {onresult ctrlr commandstr args} {
98 # eval [list $onresult-ok|nak|error $ackornakmessage] $args
99 global commands_queued
101 lappend commands_queued [list $ctrlr $onresult $args]
104 proc scmd_result {oknakerr message reporterrmsg} {
105 global commands_queued
106 manyset [lindex $commands_queued 0] ctrlr onresult args
107 set commands_queued [lrange $commands_queued 1 end]
108 if {[string length $reporterrmsg]} {
109 report-problem "$ctrlr: $reporterrmsg"
111 set proc "$onresult-$oknakerr"
112 if {![string compare $oknakerr nak] && [catch { info args $proc }]} {
113 set proc "$onresult-err"
115 eval [list $proc $message] $args
118 register-event {} {} {^\+ack \S+ ok } { scmd_result ok $l "" }
119 register-event {} {train segment error} \
120 {^\+ack \S+ SignallingPredictedProblem (\S+) (\S+) \: (.*) $} {
122 if {[string compare - $segment]} { append m " @$segment" }
124 scmd_result err $l $m
126 register-event {} {} {^\+ack } { scmd_result err $l $l }
127 register-event {} {} {^\+nack \S+ } { scmd_result nak $l $l }
129 proc routinecmd-nak {m args} { error "got nak to routine command: $m" }
130 proc routinecmd-err {m args} { }
131 proc routinecmd-ok {m args} { }
133 proc mustsucceed-err {m args} { error "unexpected error: $m" }
134 proc mustsucceed-ok {m args} { }
136 proc report-problem-report-stderr {m} { puts stderr "*** $m" }
137 set report_problem_report report-problem-report-stderr
139 proc report-problem {message} {
140 global report_problem_report
141 eval $report_problem_report [list $message]
144 proc widget-problem-report {} {
145 global problem_reports report_problem_report
146 set problem_reports "\n\n\n\n\n"
147 label .problem-report -anchor w -justify left -takefocus 0 \
148 -border 2 -relief sunken -width 80 -textvariable problem_reports
149 pack .problem-report -side top
150 set report_problem_report report-problem-report-widget
153 proc report-problem-report-widget {m} {
154 global problem_reports
155 set problem_reports [join [concat \
156 [lrange [split $problem_reports "\n"] 1 end] \
161 #---------- movpos (overlay buttons, keybindings, execution) ----------
163 proc movpos-button-gvars {mid} {
164 upvar #0 mp_details($mid) details
165 if {![info exists details]} { return 0 }
166 uplevel 1 [list manyset $details cpage key seg feat poslocs]
167 uplevel 1 { set w [pagew $cpage].movpos-$mid }
168 upvar #0 mp_state($mid) state
169 uplevel 1 [list manyset $state posn]
173 proc movpos-button-sstate {mid posn} {
174 upvar #0 mp_state($mid) state
176 movpos-button-setdisplay $mid
179 proc movpos-all-unknown {} {
181 foreach mid [array names mp_details] {
182 movpos-button-sstate $mid ?
186 proc widgets-movpos {} {
188 foreach mid [array names mp_details] {
189 upvar #0 mp_state($mid) state
191 movpos-button-gvars $mid
192 set w [pagew $cpage].movpos-$mid
193 button $w -text $key -padx 0 -pady 0 -borderwidth 0 \
194 -command [list movpos-invoked $mid "plan $cpage"]
195 movpos-button-setdisplay $mid
199 proc movpos-button-setdisplay {mid} {
200 # we want to display as much of these as possible:
201 # position known ? (actual position is done by button location)
203 # whether a train's plan includes a different position
205 movpos-button-gvars $mid
206 switch -exact $posn {
207 ? { set poslocn 2; set fg black; set bg white }
208 default { set poslocn $posn; set fg white; set bg black }
210 $w configure -background $bg -foreground $fg \
211 -activebackground $bg -activeforeground $fg
212 manyset [lindex $poslocs $poslocn] x y
213 place $w -anchor center -x $x -y $y
216 proc movpos-invoked {mid ctrlr} {
217 global movfeatcommand
218 movpos-button-gvars $mid
219 switch -exact $posn {
221 default { set new_posn 0 }
223 scmd routinecmd $ctrlr "$movfeatcommand $seg $feat $new_posn"
226 register-event ?movpos_*_feat {seg feat posn_new} \
227 {^.movpos (\w+) feat (\w+) ([01]|\?) } {
229 if {![movpos-button-gvars $mid]} return
231 movpos-button-sstate $mid $posn
234 proc movpos-bindkey-1 {cpage key seg feat adj0x adj0y adj1x adj1y} {
235 global posdeviation picturepadx picturepady
236 manyset [subseg-end-get-centroid $cpage $seg $feat {}] mx my
239 manyset [subseg-end-get-centroid $cpage $seg $feat $posn] x y
240 set dx [expr {$x-$mx}]; set dy [expr {$y-$my}]
241 set d [expr {sqrt($dx*$dx + $dy*$dy)}]
242 set mul [expr {$posdeviation / ($d + 1e-6)}]
243 set x [expr {$mx + $mul*$dx + $picturepadx + [set adj${posn}x]}]
244 set y [expr {$my + $mul*$dy + $picturepady + [set adj${posn}y]}]
245 lappend poslocs [list $x $y]
247 lappend poslocs [list [expr {$mx + $picturepadx + ($adj0x+$adj1x)*0.5}] \
248 [expr {$my + $picturepady + ($adj0y+$adj1y)*0.5}]]
249 upvar #0 mp_details($mid) details
250 set details [list $cpage $key $seg $feat $poslocs]
252 bind . <Key-[string tolower $key]> [list movpos-invoked $mid "keyboard"]
255 #---------- computation of movpos button locations
257 proc layout-subseg-featmap {seg concfeatpos args} {
259 set subsegfeatmap($concfeatpos) $args
262 proc layout-subseg-end {seg feat posn x y} {
264 upvar #0 subsegfeatmap($feat$posn) mapped
265 if {[info exists mapped]} {
266 foreach {feat posn} $mapped {
267 layout-subseg-end $seg $feat $posn $x $y
271 upvar #0 ld_sse/${cpage}($seg/$feat$posn) sse
272 if {![info exists sse]} { set sse {{} {}} }
276 set sse [list $lx $ly]
277 if {[string length $posn]} { layout-subseg-end $seg $feat {} $x $y }
280 proc subseg-end-get-centroid {cpage seg feat posn} {
281 upvar #0 ld_sse/${cpage}($seg/$feat$posn) sse
282 if {![info exists sse]} {
283 puts "skipping binding of unknown $seg/$feat$posn"
287 set sx 0; set sy 0; set n [llength $lx]
288 foreach x $lx y $ly { addexpr sx {$x}; addexpr sy {$y} }
289 return [list [expr {$sx * 1.0 / $n}] [expr {$sy * 1.0 / $n}]]
292 proc layout-data {} {
294 upvar #0 ld_sse/$cpage sse
296 source ../layout/ours.dgram.segmap-info
297 source ../layout/ours.dgram-$cpage.overlay-info
299 upvar #0 movpos_bindings($cpage) bindings
300 if {![info exists bindings]} {
301 puts "no movpos bindings for $cpage"
304 foreach binding $bindings {
306 {^([A-Z])([-+]\d*)?([-+]\d*)?([-+]\d*)?([-+]\d*)?\=(\w+)/([A-Z]+)$} \
307 $binding dummy key adj0x adj0y adj1x adj1y seg feat]} {
308 if {![string length $adj1x]} { set adj1x $adj0x; set adj1y $adj0y }
309 movpos-bindkey-1 $cpage $key $seg $feat \
310 $adj0x.0 $adj0y.0 $adj1x.0 $adj1y.0
311 } elseif {[regexp {^[A-Z]$} $binding] || [regexp {~} $binding]} {
313 error "incomprehensible binding $binding on page $cpage"
318 #---------- speed ----------
321 # $train_commanded($train) $speed_step
322 # $train_direction($train) forwards|backwards or unset
323 # $speedws [list $w ...]
325 # speed/${w}(...) aka s(...):
326 # $s(ctrlr) controller
327 # $s(train) train selected, or something not \w+
328 # $s(optionmenu) optionmenu widget name
329 # $s(kind) abs or rel
330 # $s(commanding) step of command we have scmd'd, or unset
331 # $s(queued) step of command we would like to queue
333 # $s(inhibit) 0 all is well, can command any speed
334 # 1 train newly selected, only rel can command higher speed
335 # 2 can only command same or lower speed
337 # We don't worry too much about races: in particular, we don't mind
338 # racing with other things trying to command the speed, and losing
339 # the odd increment/decrement. But since we thread the requested
340 # speed via realtime, we do queue up our own increments/decrements
341 # while we're executing a speed command, to avoid loss of steps during
344 # Interfaces for concrete controllers:
345 # speedw-new $w $ctrlr
346 # speedw-setstate $w disabled|normal controller appears/disappears
347 # speedw-userinput-abs $w $step
348 # speedw-userinput-rel $w $stepmap
350 # eval {stepmap} [list $oldstep] => $newstep
354 proc speedws-forall {command args} {
356 foreach w $speedws { eval [list $command $w] $args }
359 proc speedws-fortrain {train command args} {
363 if {[string compare $s(train) $train]} continue
364 eval [list $command $w] $args
368 proc speedw-new {w ctrlr} {
376 frame $w -relief sunken -border 2
377 label $w.ctrlr -state disabled -text $s(ctrlr)
378 set s(optionmenu) [tk_optionMenu $w.train speed/${w}(train) {}]
379 $w.train configure -textvariable {} -width 15
380 label $w.speed -state disabled -width 4 \
381 -font -*-courier-bold-r-*-*-20-*-*-*-*-*-*-* \
382 -background black -foreground white
383 pack $w.ctrlr $w.train $w.speed -side left
385 speedw-notrains $w "(starting)"
387 proc speedw-notrains {w whystr} {
388 $w.train configure -state disabled
389 speedw-train-noneselected $w $whystr
391 proc speedw-train-noneselected {w whystr} {
394 $w.train configure -text $whystr
395 $w.speed configure -text -
399 proc speedw-inhibit {w} {
402 $w.speed configure -foreground red
404 proc speedw-uninhibit {w max} {
407 if {$r>$max} { return -1 }
409 $w.speed configure -foreground white
413 proc speedw-setstate {w disnorm} {
414 $w.ctrlr configure -state $disnorm
415 $w.speed configure -state $disnorm
418 proc speedw-train-selectnext {w} {
420 set max [$s(optionmenu) index end]
421 for {set ix 0} {$ix <= $max} {incr ix} {
422 set v [$s(optionmenu) entrycget $ix -value]
423 if {![string compare $v $s(train)]} break
425 set activate [expr {($ix+1) % ($max+1)}]
426 $s(optionmenu) invoke $activate
429 proc speedw-train-selected {w t} {
431 $w.train configure -text $t
434 $w.speed configure -foreground white
435 speedw-redisplay-speed $w
438 proc speedw-redisplay-speed {w} {
440 upvar #0 train_commanded($s(train)) gcommanded
441 upvar #0 train_direction($s(train)) gdirection
443 if {[info exists gdirection]} {
444 switch -exact $gdirection {
445 forwards { set t "$t>" }
446 backwards { set t "<$t" }
449 $w.speed configure -text $t
452 proc speedw-train-direction {w dirchange} {
454 if {![string length $s(train)]} return
455 scmd routinecmd $s(ctrlr) "direction $s(train) $dirchange"
458 proc speedw-trains-available {w l} {
460 if {![llength $l]} { speedw-train-noneselected $w "(no trains)"; return }
461 $s(optionmenu) delete 0 end
462 $s(optionmenu) add radiobutton -label "(none)" -value {} \
463 -command [list speedw-train-noneselected $w "(no train selected)"]
466 $s(optionmenu) add radiobutton -label $t -value $t \
467 -command [list speedw-train-selected $w $t]
469 $w.train configure -state normal
470 if {[llength $l]==1} {
471 $s(optionmenu) invoke 1
472 } elseif {[set ix [lsearch -exact $l $s(train)]] >= 0} {
473 $s(optionmenu) invoke [expr {$ix+1}]
474 } elseif {![string length $s(train)]} {
475 $s(optionmenu) invoke 0
477 $w.train configure -text "$s(train) (not present)"
481 proc speedw-userinput-abs {w speed} {
483 if {![string length $s(train)]} return
488 proc speedw-check {w} {
490 if {![string length $s(train)]} return
491 upvar #0 train_commanded($s(train)) gcommanded
492 upvar #0 train_direction($s(train)) gdirection
493 if {[info exists s(commanding)]} return
494 if {![info exists s(queued)]} return
495 set newspeed $s(queued)
498 if {$newspeed > $gcommanded} return
499 speedw-uninhibit $w 2
501 set s(commanding) $newspeed
502 scmd speedw-commanded $s(ctrlr) "speed $s(train) $newspeed $gdirection" $w
505 proc speedw-commanded-nak {m args} { error "got nak from speed: $m" }
506 proc speedw-commanded-ok {m w} {
511 proc speedw-commanded-err {m w} {
518 proc speedw-userinput-rel {w stepmap} {
520 if {![string length $s(train)]} return
521 upvar #0 train_commanded($s(train)) gcommanded
522 if {[info exists s(queued)]} {
523 set oldspeed $s(queued)
524 } elseif {[info exists s(commanding)]} {
525 set oldspeed $s(commanding)
527 set oldspeed $gcommanded
529 set newspeed [eval $stepmap [list $oldspeed]]
530 speedw-userinput-abs $w $newspeed
533 proc speedw-userinput-rel-steps {w delta steplist} {
535 if {[speedw-uninhibit $w 2]>1} { incr delta 1 }
538 speedw-uninhibit $w 1
540 speedw-userinput-rel $w [list speedw-stepmap-fromlist $steplist $delta]
543 proc speedws-train-problem {train} {
544 speedws-fortrain $train speedw-inhibit
547 register-event ?train_*_at {train direction} \
548 {^.train (\w+) at \S+ (forwards|backwards) } {
549 upvar #0 train_direction($train) dirn
551 speedws-fortrain $train speedw-redisplay-speed
554 register-event ?train_*_speed_commanding {train speed} \
555 {^.train (\w+) speed commanding (\d+) } {
556 upvar #0 train_commanded($train) cmd
558 speedws-fortrain $train speedw-redisplay-speed
561 proc speedws-stastate-hook {} {
562 global train_direction stastate
563 switch -exact -- $stastate {
565 set trains [array names train_direction]
566 speedws-forall speedw-trains-available $trains
572 speedws-forall speedw-notrains "($stastate)"
575 catch { unset train_commanded }
576 speedws-forall speedw-notrains "($stastate)"
581 register-event &train_*_signalling-problem {train problem} \
582 {^\&train (\w+) signalling-problem (.*) $} {
584 regsub {^(\S+) (\S+) \: } $problem {\1 @\2: } problem
585 report-problem "event: $problem"
586 speedws-train-problem $train
589 proc speedw-new-cooked {wunique desc} {
590 set w .inputs.$wunique
592 pack $w -side left -padx 10
596 proc speedw-stepmap-fromlist {speedlist offset oldspeed} {
597 if {![llength $speedlist]} {
599 upvar #0 default_speedstep_list speedlist
602 foreach entry $speedlist {
603 if {$entry==$oldspeed} { set ixbelow $ixabove; break }
604 if {$entry>$oldspeed} break
608 set ix [expr {($offset>0 ? $ixbelow : $ixabove) + $offset}]
609 if {$ix<0} { return 0 }
610 if {$ix>=[llength $speedlist]} { return [lindex $speedlist end] }
611 return [lindex $speedlist $ix]
614 #---------- concrete input bindings ----------
616 proc ib-suppressions {args} {
619 set l [concat $l --redaction $supp --suppress]
624 proc ib-speedw-new {devid wunique desc} {
625 upvar #0 input/$devid in
626 set in(speedw) [speedw-new-cooked $wunique $desc]
631 proc ib-ev/wheelmouse/EV_REL/REL_WHEEL {devid value} {
632 upvar #0 input/$devid in
633 speedw-userinput-rel-steps $in(speedw) [expr {-$value}] {}
636 proc ib-selectnext {devid value} {
637 if {$value!=1} return
638 upvar #0 input/$devid in
639 speedw-train-selectnext $in(speedw)
641 proc ib-changedirection {devid value} {
642 upvar #0 input/$devid in
644 speedw-train-direction $in(speedw) change
647 proc ib-ev/wheelmouse/EV_KEY/BTN_LEFT {devid value} {
648 ib-selectnext $devid $value
650 proc ib-ev/wheelmouse/EV_KEY/BTN_RIGHT {devid value} {
651 ib-changedirection $devid $value
654 proc ib-create/wheelmouse {devid wunique desc} {
655 ib-speedw-new $devid $wunique $desc
658 proc ib-wheelmouse-redactions {} {
659 return [ib-suppressions \
664 proc ib-evcmd/wheelmouse {devid target} {
665 return [ib-evcmd-construct $devid $target [concat \
666 [list --grab] [ib-wheelmouse-redactions]]]
669 #----- ebuyer wireless keyboard
671 proc ib-create/ebwikeb {devid wunique} {
672 upvar #0 input/$devid in
673 set in(desc) "main keyboard"
674 ib-create/wheelmouse $devid $wunique $in(desc)
678 proc ib-evcmd/ebwikeb {devid target} {
680 [list ./evdev-manip-ebwikeb --redact --stdin-monitor] \
681 [ib-wheelmouse-redactions] \
686 {0xffbc 88 0xffbc 00}]]
689 proc ib-ev/ebwikeb/EV_REL/REL_WHEEL {devid value} {
690 ib-ev/wheelmouse/EV_REL/REL_WHEEL $devid $value
693 proc ib-ebwikeb-modifier {devid value bitval} {
694 upvar #0 input/${devid}(modifiers) mod
696 set mod [expr {$mod | $bitval}]
698 set mod [expr {$mod & ~$bitval}]
701 proc ib-ev/ebwikeb/EV_KEY/KEY_LEFTSHIFT {devid value} {
702 ib-ebwikeb-modifier $devid $value 0x0001
704 proc ib-ev/ebwikeb/EV_KEY/KEY_RIGHTSHIFT {devid value} {
705 ib-ebwikeb-modifier $devid $value 0x0002
707 proc ib-ev/ebwikeb/EV_KEY/KEY_LEFTCTRL {devid value} {
708 ib-ebwikeb-modifier $devid $value 0x0100
710 proc ib-ev/ebwikeb/EV_KEY/KEY_RIGHTCTRL {devid value} {
711 ib-ebwikeb-modifier $devid $value 0x0200
713 proc ib-ev/ebwikeb/EV_KEY/KEY_CAPSLOCK {devid value} {
714 ib-ebwikeb-modifier $devid $value 0x0400
717 proc ib-ev/ebwikeb/0xffbc_88/0xffbc_0d {devid value} {
718 upvar #0 input/$devid in
720 if {$in(modifiers) & 0x00ff} {
721 ib-selectnext $devid $value
723 ib-changedirection $devid $value
727 proc ib-ev/ebwikeb/EV_KEY/KEY_BOOKMARKS {devid value} {
728 upvar #0 input/$devid in
730 if {!($in(modifiers) & 0xff00)} return
731 if {$in(modifiers) & 0x00ff} {
736 scmd routinecmd $in(desc) "!realtime $how"
739 #----- static keybindings speed `controller'
741 proc bind-keyboard-speed {kslow kfast kseltrain kreverse desc} {
742 set wunique [get-unique keyboardspeed]
743 set w [speedw-new-cooked $wunique $desc]
744 foreach delta {-1 +1} sf {slow fast} {
745 bind . <Key-[set k$sf]> [list speedw-userinput-rel-steps $w $delta {}]
747 bind . <Key-$kseltrain> [list speedw-train-selectnext $w]
748 bind . <Key-$kreverse> [list speedw-train-direction $w change]
749 speedw-setstate $w normal
752 #---------- input device evdev binding ----------
754 proc ib-evcmd-construct {devid target xargs} {
755 upvar #0 input/$devid in
756 if {[llength $target] > 1} {
757 debug "ib $devid - multiple devices, not supported"
760 manyset [lindex $target 0] ev sysfs
761 if {[regexp { } $ev]} { error "event device `$ev' contains space" }
763 [list ./evdev-manip --redact] $xargs \
764 [list --stdin-monitor \
765 --expect-sysfs /sys$sysfs/$ev/dev \
769 proc bind-input {bus vendor product version concrete args} {
770 global input_bindings
771 set devid $bus:$vendor:$product:$version
772 lappend input_bindings [list $devid $concrete $args]
775 proc bind-input-static {event sysfs concrete args} {
776 global input_bindings input_statics
777 set devid [get-unique static]
778 lappend input_statics [list $devid $event $sysfs]
779 lappend input_bindings [list $devid $concrete $args]
782 proc widgets-input-bindings {} {
783 global input_bindings
784 foreach binding $input_bindings {
785 manyset $binding devid concrete xa
786 set cid [get-unique $concrete]
787 upvar #0 input/$devid in
789 set in(concrete) $concrete
790 eval [list ib-create/$concrete $devid $cid] $xa
792 pack .inputs -side top -fill x
795 # input/$bus:$vendor:$product:$version becomes `in' via upvar
796 # $in(chan) channel open onto evdev-manip; unset if none
797 # $in(laststart) last start time, [clock seconds]
798 # at every event we set this the current time
799 # but we insist on adding at least 5s
800 # and if that would make it > current time +15s
802 # $in(speedw) optional, may be set by ib-create
804 proc scan-input-bindings {} {
805 global errorInfo errorCode unmatched_notified
806 global input_bindings inputretryadd inputretrymax scaninputinterval
809 set f [open /proc/bus/input/devices]
811 if {[string match {POSIX ENOENT *} $errorCode]} return
812 error $emsg $errorInfo $errorCode
817 if {[info exists v(devid)] &&
818 [info exists v(sysfs)] &&
819 [info exists v(event)]} {
820 lappend target($v(devid)) [list $v(event) $v(sysfs)]
829 {^I: Bus=(\w+) Vendor=(\w+) Product=(\w+) Version=(\w+)\s} \
830 $l dummy bus vendor product version]} {
831 set v(devid) $bus:$vendor:$product:$version
832 } elseif {[regexp {^S: Sysfs=(\S+)\s} $l dummy sysfs]} {
834 } elseif {[regexp {^H: Handlers=(?:.*\s)?(event\d+)\s} $l dummy ev]} {
841 foreach static $input_statics {
842 manyset $static devid event sysfs
843 lappend target($devid) [list $event $sysfs]
845 foreach devid [array names target] {
846 upvar #0 input/$devid in
847 if {![info exists in(concrete)]} {
848 if {![info exists unmatched_notified($devid)]} {
849 debug "ib $devid unmatched, ignored"
850 set unmatched_notified($devid) 1
854 if {[info exists in(chan)]} continue
855 set now [clock seconds]
856 set newlast [expr {$in(laststart) + $inputretryadd}]
857 if {$newlast > $now + $inputretrymax} continue
858 if {$newlast < $now} { set newlast $now }
859 set cmdl [ib-evcmd/$in(concrete) $devid $target($devid)]
860 if {![llength $cmdl]} {
864 lappend cmdl 2>@ stderr
865 set in(laststart) $newlast
866 catch-for-input-binding $devid {
867 debug "ib $devid running $cmdl"
868 set in(chan) [open |$cmdl r+]
869 fconfigure $in(chan) -blocking 0 -buffering line
870 fileevent $in(chan) readable [list catch-for-input-binding $devid \
871 [list readable input-binding $in(chan) $devid]]
874 foreach binding $input_bindings {
875 manyset $binding devid concrete ctrlr
876 upvar #0 input/$devid in
877 if {![info exists in(concrete)]} continue
878 if {[info exists target($devid)]} continue
879 input-binding-notpresent $devid absent
881 after $scaninputinterval scan-input-bindings
884 proc input-binding-notpresent {devid why} {
885 upvar #0 input/$devid in
886 if {[info exists in(speedw)]} {
887 speedw-setstate $in(speedw) disabled
889 if {![catch { info args ib-absent/$in(concret) }]} {
890 ib-absent/$in(concrete) $devid $why
894 proc input-binding-eof {chan devid} {
895 upvar #0 input/$devid in
896 fconfigure $in(chan) -blocking 1
898 error "evdev-manip exited" {} {CHILDSTATUS ? 0}
901 proc input-binding-inputline {chan l devid} {
903 upvar #0 input/$devid in
904 if {![catch { info args ib-inputline/$in(concrete) }]} {
905 # give the input binding first dibs
906 if {[ib-inputline/$in(concrete) $devid $l]} return
908 regsub {^[^ ]+ } $l {} lr
909 switch -glob -- $lr {
911 debug "ib $devid start << $l"
912 if {[info exists in(speedw)]} {
913 speedw-setstate $in(speedw) normal
917 manyset [split $lr] value kindl kindr codel coder
918 set proc ib-ev/$in(concrete)/${kindl}_${kindr}/${codel}_${coder}
919 if {[catch { info args $proc }]} {
921 debug "ib $devid unbound $proc << $l"
928 debug "ib $devid ignored << $l"
933 proc catch-for-input-binding {devid body} {
934 upvar #0 input/$devid in
935 global errorInfo errorCode
936 set r [catch { uplevel 1 $body } rv]
937 if {$r!=1} { return -code $r $rv }
938 switch -glob $errorCode {
939 {CHILDSTATUS *} { set m "exited with status [lindex $errorCode 2]" }
940 {CHILDKILLED *} { set m "killed by signal [lindex $errorCode 3]" }
941 {POSIX *} { set m "communication error: [lindex $errorCode 1]" }
942 * { error $rv $errorInfo $errorCode }
944 debug "ib $devid died $m"
945 catch { close $in(chan) }
946 catch { unset in(chan) }
948 input-binding-notpresent $devid "died $m"
951 proc engage-input-bindings {} {
955 #---------- plan background (gui-plan subprocess) ----------
957 proc gui-pipe-readable {args} {
959 while {[gets $gui_pipe l] >= 0} {
962 if {[eof $gui_pipe]} {
964 error "gui-plan crashed"
968 #---------- train set events of general interest, and setup ----------
970 proc train-event-eof {args} {
971 error "lost connection to train set"
974 register-event ?stastate {ctxch state} {^(.)stastate (\w+|\-) } {
975 global ctrain trains stastate
977 report-problem "stastate: $state"
978 if {[string compare $ctxch |]} speedws-stastate-hook
981 register-event ?resolution {message} \
982 {^.resolution (\S+ .*)$} {
983 if {[string match "problems *" $message]} return
984 report-problem "resolution: $message"
987 register-event ?warning {message} {^.warning (\S+ .*)$} {
988 report-problem "warning: $message"
991 register-event {} {} {^=connected } {
992 global pages gui_pipe server port event_selections
994 scmd replayed {} "select-replay [concat $event_selections]"
996 foreach page $pages {
998 tkwait visibility $w.picture
1000 set cmdl [list ./gui-plan-$page [winfo id $w.picture] @$server,$port]
1001 lappend cmdl 2>@ stderr
1002 set gui_pipe [open |$cmdl r]
1004 puts stderr "running $cmdl"
1005 fconfigure $gui_pipe -blocking no
1006 fileevent $gui_pipe readable gui-pipe-readable
1009 proc replayed-err {m args} { error "replay failed: $m" }
1010 proc replayed-ok {m args} {
1011 speedws-stastate-hook
1014 register-event {} {} {^=failed } { error "multiplexer failed: $l" }
1015 register-event {} {} {^=denied } { error "multiplexer denied us: $l" }
1016 register-event {} {} {^\+nack } { error "multiplexer does not understand" }
1018 #---------- main program ----------
1020 append event_dispatch_body {
1023 proc train-event-inputline {sconn l} $event_dispatch_body
1024 proc register-event {args} { error "too late!" }
1026 proc engage-server {} {
1027 global server port sconn
1029 set sconn [socket $server $port]
1030 fconfig-trainproto $sconn
1031 fileevent $sconn readable {readable train-event $sconn}
1035 global pages cpage configfile input_bindings input_statics
1036 setting server railway {[[0-9a-z:].*}
1037 setting geometry {} {[-+]\d+[-+]\d+}
1038 setting posdeviation 5 {\d+}
1039 setting movfeatcommand {movfeat+} {(?:!movfeat|movfeat\+?\+?)}
1040 setting problemdisplayms 1000 {\d+}
1041 setting inputretryadd 5 {\d+}
1042 setting inputretrymax 15 {\d+}
1043 setting scaninputinterval 500 {\d+}
1044 setting showunbound 0 {[01]}
1046 set hostname [lindex [split [info hostname] .] 0]
1047 setting configfile gui-$hostname.config {.+}
1051 if {![info exists input_bindings]} { set input_bindings {} }
1052 if {![info exists input_statics]} { set input_statics {} }
1054 uplevel #0 source gui-layout.config
1055 uplevel #0 source $configfile
1056 foreach cpage $pages {
1062 widgets-input-bindings
1063 widget-problem-report
1065 engage-input-bindings