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}
13 proc pagew {page} { return ".picture-$page" }
14 proc debug {m} { puts $m }
22 proc widgets-dgram {} {
24 if {[string length $geometry]} { wm geometry . $geometry }
25 set sizes [exec ./gui-plan-$cpage --sizes]
27 frame $w -background {} \
28 -width [lindex $sizes 0] \
29 -height [lindex $sizes 1]
33 proc widgets-fullscreen-nowm {} {
34 pack propagate . false
35 foreach wh {width height} {
36 . configure -$wh [winfo screen$wh .]
41 global errorCode errorInfo
43 puts stderr "UNEXPECTED BACKGROUND ERROR\n"
44 puts stderr "$errorCode\n$errorInfo\n$emsg"
49 #---------- train set event registraton ----------
51 set event_dispatch_body {
54 set event_selections {}
56 proc register-event {selections args re body} {
57 global event_dispatch_body event_selections
59 eval lappend event_selections $selections
61 foreach selection $selections {
62 if {[regexp {^\w} $selection]} {
63 error "selection $selection lacks context char"
66 if {[regexp {^\^\w} $re]} {
67 error "re $re never matches context char"
70 regsub -all {\W+} $re - proc
71 set proc "event/$proc/[join $args -]"
74 while {![catch { info args $proc$suffix }]} { set suffix [incr number] }
77 proc $proc [concat l $args] $body
79 foreach a $args { append al " \$$a" }
81 append event_dispatch_body \
82 " if {\[regexp [list $re] \$l dummy $args]} {\n" \
83 " debug \"$proc$al\"\n" \
84 " eval [list $proc] \[list \$l$al]\n" \
89 #---------- handling of commands we issue ----------
91 proc scmd {onresult ctrlr commandstr args} {
93 # eval [list $onresult-ok|nak|error $ackornakmessage] $args
94 global commands_queued
96 lappend commands_queued [list $ctrlr $onresult $args]
99 proc scmd_result {oknakerr message reporterrmsg} {
100 global commands_queued
101 manyset [lindex $commands_queued 0] ctrlr onresult args
102 set commands_queued [lrange $commands_queued 1 end]
103 if {[string length $reporterrmsg]} {
104 report-problem "$ctrlr: $reporterrmsg"
106 set proc "$onresult-$oknakerr"
107 if {![string compare $oknakerr nak] && [catch { info args $proc }]} {
108 set proc "$onresult-err"
110 eval [list $proc $message] $args
113 register-event {} {} {^\+ack \S+ ok } { scmd_result ok $l "" }
114 register-event {} {train segment error} \
115 {^\+ack \S+ SignallingPredictedProblem (\S+) (\S+) \: (.*) $} {
117 if {[string compare - $segment]} { append m " @$segment" }
119 scmd_result err $l $m
121 register-event {} {} {^\+ack } { scmd_result err $l $l }
122 register-event {} {} {^\+nack \S+ } { scmd_result nak $l $l }
124 proc routinecmd-nak {m args} { error "got nak to routine command: $m" }
125 proc routinecmd-err {m args} { }
126 proc routinecmd-ok {m args} { }
128 proc mustsucceed-err {m args} { error "unexpected error: $m" }
129 proc mustsucceed-ok {m args} { }
131 proc report-problem-report-stderr {m} { puts stderr "*** $m" }
132 set report_problem_report report-problem-report-stderr
134 proc report-problem {message} {
135 global report_problem_report
136 eval $report_problem_report [list $message]
139 proc widget-problem-report {} {
140 global problem_reports report_problem_report
141 set problem_reports "\n\n\n\n\n\n"
142 label .problem-report -anchor w -justify left -takefocus 0 \
143 -border 2 -relief sunken -width 80 -textvariable problem_reports
144 pack .problem-report -side top
145 set report_problem_report report-problem-report-widget
148 proc report-problem-report-widget {m} {
149 global problem_reports
150 set problem_reports [join [concat \
151 [lrange [split $problem_reports "\n"] 1 end] \
156 #---------- movpos (overlay buttons, keybindings, execution) ----------
158 proc movpos-button-gvars {mid} {
159 upvar #0 mp_details($mid) details
160 if {![info exists details]} { return 0 }
161 uplevel 1 [list manyset $details cpage key seg feat poslocs]
162 uplevel 1 { set w [pagew $cpage].movpos-$mid }
163 upvar #0 mp_state($mid) state
164 uplevel 1 [list manyset $state posn]
168 proc movpos-button-sstate {mid} {
169 upvar #0 mp_state($mid) state
170 set state [uplevel 1 { list $posn } ]
173 proc widgets-movpos {} {
175 foreach mid [array names mp_details] {
176 upvar #0 mp_state($mid) state
178 movpos-button-gvars $mid
179 set w [pagew $cpage].movpos-$mid
180 button $w -text $key -padx 0 -pady 0 -borderwidth 0 \
181 -command [list movpos-invoked $mid "plan $cpage"]
182 movpos-button-setdisplay $mid
186 proc movpos-button-setdisplay {mid} {
187 # we want to display as much of these as possible:
188 # position known ? (actual position is done by button location)
190 # whether a train's plan includes a different position
192 movpos-button-gvars $mid
195 $w configure -background $bg -foreground $fg \
196 -activebackground $bg -activeforeground $fg
197 switch -exact $posn {
198 ? { manyset [lindex $poslocs 2] x y }
199 default { manyset [lindex $poslocs $posn] x y }
201 place $w -anchor center -x $x -y $y
204 proc movpos-invoked {mid ctrlr} {
205 global movfeatcommand
206 movpos-button-gvars $mid
207 switch -exact $posn {
209 default { set new_posn 0 }
211 scmd routinecmd $ctrlr "$movfeatcommand $seg $feat $new_posn"
214 register-event ?movpos_*_feat {seg feat posn_new} \
215 {^.movpos (\w+) feat (\w+) ([01]|\?) } {
217 if {![movpos-button-gvars $mid]} return
219 movpos-button-sstate $mid
220 movpos-button-setdisplay $mid
223 proc movpos-bindkey-1 {cpage key seg feat} {
225 manyset [subseg-end-get-centroid $cpage $seg $feat {}] mx my
228 manyset [subseg-end-get-centroid $cpage $seg $feat $posn] x y
229 set dx [expr {$x-$mx}]; set dy [expr {$y-$my}]
230 set d [expr {sqrt($dx*$dx + $dy*$dy)}]
231 set mul [expr {$posdeviation / ($d + 1e-6)}]
232 set x [expr {$mx + $mul*$dx}]
233 set y [expr {$my + $mul*$dy}]
234 lappend poslocs [list $x $y]
236 lappend poslocs [list $mx $my]
237 upvar #0 mp_details($mid) details
238 set details [list $cpage $key $seg $feat $poslocs]
240 bind . <Key-[string tolower $key]> [list movpos-invoked $mid "keyboard"]
243 #---------- computation of movpos button locations
245 proc layout-subseg-end {seg feat posn x y} {
247 upvar #0 ld_sse/${cpage}($seg/$feat$posn) sse
248 if {![info exists sse]} { set sse {0 0 0} }
251 set sx [expr {$sx + $x}]
252 set sy [expr {$sy + $y}]
253 set sse [list $n $sx $sy]
254 if {[string length $posn]} { layout-subseg-end $seg $feat {} $x $y }
257 proc subseg-end-get-centroid {cpage seg feat posn} {
258 upvar #0 ld_sse/${cpage}($seg/$feat$posn) sse
259 if {![info exists sse]} {
260 puts "skipping binding of unknown $seg/$feat$posn"
264 return [list [expr {$sx * 1.0 / $n}] [expr {$sy * 1.0 / $n}]]
267 proc layout-data {} {
269 upvar #0 ld_sse/$cpage sse
271 set f ../layout/ours.dgram-$cpage.overlay-info
274 upvar #0 movpos_bindings($cpage) bindings
275 if {![info exists bindings]} {
276 puts "no movpos bindings for $cpage"
279 foreach binding $bindings {
280 if {[regexp {^([A-Z])\=(\w+)/([A-Z]+)$} $binding dummy key seg feat]} {
281 movpos-bindkey-1 $cpage $key $seg $feat
282 } elseif {[regexp {^[A-Z]$} $binding] || [regexp {~} $binding]} {
284 error "incomprehensible binding $binding on page $cpage"
289 #---------- speed ----------
292 # $train_commanded($train) $speed_step
293 # $train_direction($train) forwards|backwards or unset
294 # $speedws [list $w ...]
296 # speed/${w}(...) aka s(...):
297 # $s(ctrlr) controller
298 # $s(train) train selected, or something not \w+
299 # $s(optionmenu) optionmenu widget name
300 # $s(kind) abs or rel
301 # $s(commanding) step of command we have scmd'd, or unset
302 # $s(queued) step of command we would like to queue
304 # $s(inhibit) 0 all is well, can command any speed
305 # 1 train newly selected, only rel can command higher speed
306 # 2 can only command same or lower speed
308 # We don't worry too much about races: in particular, we don't mind
309 # racing with other things trying to command the speed, and losing
310 # the odd increment/decrement. But since we thread the requested
311 # speed via realtime, we do queue up our own increments/decrements
312 # while we're executing a speed command, to avoid loss of steps during
315 # Interfaces for concrete controllers:
316 # speedw-new $w $ctrlr
317 # speedw-setstate $w disabled|normal controller appears/disappears
318 # speedw-userinput-abs $w $step
319 # speedw-userinput-rel $w $stepmap
321 # eval {stepmap} [list $oldstep] => $newstep
325 proc speedws-forall {command args} {
327 foreach w $speedws { eval [list $command $w] $args }
330 proc speedws-fortrain {train command args} {
334 if {[string compare $s(train) $train]} continue
335 eval [list $command $w] $args
339 proc speedw-new {w ctrlr} {
347 frame $w -relief sunken -border 2
348 label $w.ctrlr -state disabled -text $s(ctrlr)
349 set s(optionmenu) [tk_optionMenu $w.train speed/${w}(train) {}]
350 $w.train configure -textvariable {} -width 15
351 label $w.speed -state disabled -width 4 \
352 -font -*-courier-bold-r-*-*-20-*-*-*-*-*-*-* \
353 -background black -foreground white
354 pack $w.ctrlr $w.train $w.speed -side left
356 speedw-notrains $w "(starting)"
358 proc speedw-notrains {w whystr} {
359 $w.train configure -state disabled
360 speedw-train-noneselected $w $whystr
362 proc speedw-train-noneselected {w whystr} {
365 $w.train configure -text $whystr
366 $w.speed configure -text -
370 proc speedw-inhibit {w} {
373 $w.speed configure -foreground red
375 proc speedw-uninhibit {w max} {
378 if {$r>$max} { return -1 }
380 $w.speed configure -foreground white
384 proc speedw-setstate {w disnorm} {
385 $w.ctrlr configure -state $disnorm
386 $w.speed configure -state $disnorm
389 proc speedw-train-selectnext {w} {
391 set max [$s(optionmenu) index end]
392 for {set ix 0} {$ix <= $max} {incr ix} {
393 set v [$s(optionmenu) entrycget $ix -value]
394 if {![string compare $v $s(train)]} break
396 set activate [expr {($ix+1) % ($max+1)}]
397 $s(optionmenu) invoke $activate
400 proc speedw-train-selected {w t} {
402 $w.train configure -text $t
405 $w.speed configure -foreground white
406 speedw-redisplay-speed $w
409 proc speedw-redisplay-speed {w} {
411 upvar #0 train_commanded($s(train)) gcommanded
412 upvar #0 train_direction($s(train)) gdirection
414 if {[info exists gdirection]} {
415 switch -exact $gdirection {
416 forwards { set t "$t>" }
417 backwards { set t "<$t" }
420 $w.speed configure -text $t
423 proc speedw-train-direction {w dirchange} {
425 if {![string length $s(train)]} return
426 scmd routinecmd $s(ctrlr) "direction $s(train) $dirchange"
429 proc speedw-trains-available {w l} {
431 if {![llength $l]} { speedw-train-noneselected $w "(no trains)"; return }
432 $s(optionmenu) delete 0 end
433 $s(optionmenu) add radiobutton -label "(none)" -value {} \
434 -command [list speedw-train-noneselected $w "(no train selected)"]
437 $s(optionmenu) add radiobutton -label $t -value $t \
438 -command [list speedw-train-selected $w $t]
440 $w.train configure -state normal
441 if {[llength $l]==1} {
442 $s(optionmenu) invoke 1
443 } elseif {[set ix [lsearch -exact $l $s(train)]] >= 0} {
444 $s(optionmenu) invoke [expr {$ix+1}]
445 } elseif {![string length $s(train)]} {
446 $s(optionmenu) invoke 0
448 $w.train configure -text "$s(train) (not present)"
452 proc speedw-userinput-abs {w speed} {
454 if {![string length $s(train)]} return
459 proc speedw-check {w} {
461 if {![string length $s(train)]} return
462 upvar #0 train_commanded($s(train)) gcommanded
463 upvar #0 train_direction($s(train)) gdirection
464 if {[info exists s(commanding)]} return
465 if {![info exists s(queued)]} return
466 set newspeed $s(queued)
469 if {$newspeed > $gcommanded} return
470 speedw-uninhibit $w 2
472 set s(commanding) $newspeed
473 scmd speedw-commanded $s(ctrlr) "speed $s(train) $newspeed $gdirection" $w
476 proc speedw-commanded-nak {m args} { error "got nak from speed: $m" }
477 proc speedw-commanded-ok {m w} {
482 proc speedw-commanded-err {m w} {
489 proc speedw-userinput-rel {w stepmap} {
491 if {![string length $s(train)]} return
492 upvar #0 train_commanded($s(train)) gcommanded
493 if {[info exists s(queued)]} {
494 set oldspeed $s(queued)
495 } elseif {[info exists s(commanding)]} {
496 set oldspeed $s(commanding)
498 set oldspeed $gcommanded
500 set newspeed [eval $stepmap [list $oldspeed]]
501 speedw-userinput-abs $w $newspeed
504 proc speedw-userinput-rel-steps {w delta steplist} {
506 if {[speedw-uninhibit $w 2]>1} { incr delta 1 }
509 speedw-uninhibit $w 1
511 speedw-userinput-rel $w [list speedw-stepmap-fromlist $steplist $delta]
514 proc speedws-train-problem {train} {
515 speedws-fortrain $train speedw-inhibit
518 register-event ?train_*_at {train direction} \
519 {^.train (\w+) at \S+ (forwards|backwards) } {
520 upvar #0 train_direction($train) dirn
522 speedws-fortrain $train speedw-redisplay-speed
525 register-event ?train_*_speed_commanding {train speed} \
526 {^.train (\w+) speed commanding (\d+) } {
527 upvar #0 train_commanded($train) cmd
529 speedws-fortrain $train speedw-redisplay-speed
532 proc speedws-stastate-hook {} {
533 global train_commanded stastate
534 switch -exact -- $stastate {
536 set trains [array names train_commanded]
537 speedws-forall speedw-trains-available $trains
540 speedws-forall speedw-notrains "($stastate)"
543 catch { unset train_commanded }
544 speedws-forall speedw-notrains "($stastate)"
549 register-event &train_*_signalling-problem {train problem} \
550 {^\&train (\w+) signalling-problem (.*) $} {
552 regsub {^(\S+) (\S+) \: } $problem {\1 @\2: } problem
553 report-problem "event: $problem"
554 speedws-train-problem $train
557 proc speedw-new-cooked {wunique desc} {
558 set w .inputs.$wunique
560 pack $w -side left -padx 10
564 proc speedw-stepmap-fromlist {speedlist offset oldspeed} {
565 if {![llength $speedlist]} {
567 upvar #0 default_speedstep_list speedlist
570 foreach entry $speedlist {
571 if {$entry==$oldspeed} { set ixbelow $ixabove; break }
572 if {$entry>$oldspeed} break
576 set ix [expr {($offset>0 ? $ixbelow : $ixabove) + $offset}]
577 if {$ix<0} { return 0 }
578 if {$ix>=[llength $speedlist]} { return [lindex $speedlist end] }
579 return [lindex $speedlist $ix]
582 #---------- concrete input bindings ----------
584 proc ib-suppressions {args} {
587 set l [concat $l --redaction $supp --suppress]
592 proc ib-speedw-new {devid wunique desc} {
593 upvar #0 input/$devid in
594 set in(speedw) [speedw-new-cooked $wunique $desc]
599 proc ib-ev/wheelmouse/EV_REL/REL_WHEEL {devid value} {
600 upvar #0 input/$devid in
601 speedw-userinput-rel-steps $in(speedw) $value {}
604 proc ib-selectnext {devid value} {
605 if {$value!=1} return
606 upvar #0 input/$devid in
607 speedw-train-selectnext $in(speedw)
609 proc ib-changedirection {devid value} {
610 upvar #0 input/$devid in
612 speedw-train-direction $in(speedw) change
615 proc ib-ev/wheelmouse/EV_KEY/BTN_LEFT {devid value} {
616 ib-selectnext $devid $value
618 proc ib-ev/wheelmouse/EV_KEY/BTN_RIGHT {devid value} {
619 ib-changedirection $devid $value
622 proc ib-create/wheelmouse {devid wunique desc} {
623 ib-speedw-new $devid $wunique $desc
626 proc ib-wheelmouse-redactions {} {
627 return [ib-suppressions \
632 proc ib-evcmd/wheelmouse {devid target} {
633 return [ib-evcmd-construct $devid $target [concat \
634 [list --grab] [ib-wheelmouse-redactions]]]
637 #----- ebuyer wireless keyboard
639 proc ib-create/ebwikeb {devid wunique} {
640 upvar #0 input/$devid in
641 set in(desc) "main keyboard"
642 ib-create/wheelmouse $devid $wunique $in(desc)
646 proc ib-evcmd/ebwikeb {devid target} {
648 [list ./evdev-manip --redact --stdin-monitor] \
649 [ib-wheelmouse-redactions] \
654 {0xffbc 88 0xffbc 00}] \
655 [list --evdev /dev/input/event2 \
656 --evdev /dev/input/event3 \
657 --hiddev /dev/hiddev0]]
660 proc ib-ev/ebwikeb/EV_REL/REL_WHEEL {devid value} {
661 ib-ev/wheelmouse/EV_REL/REL_WHEEL $devid $value
663 proc ib-ev/ebwikeb/EV_KEY/BTN_RIGHT {devid value} {
664 ib-changedirection $devid $value
667 proc ib-ebwikeb-modifier {devid value bitval} {
668 upvar #0 input/${devid}(modifiers) mod
670 set mod [expr {$mod | $bitval}]
672 set mod [expr {$mod & ~$bitval}]
675 proc ib-ev/ebwikeb/EV_KEY/KEY_LEFTSHIFT {devid value} {
676 ib-ebwikeb-modifier $devid $value 0x0001
678 proc ib-ev/ebwikeb/EV_KEY/KEY_RIGHTSHIFT {devid value} {
679 ib-ebwikeb-modifier $devid $value 0x0002
681 proc ib-ev/ebwikeb/EV_KEY/KEY_LEFTCTRL {devid value} {
682 ib-ebwikeb-modifier $devid $value 0x0100
684 proc ib-ev/ebwikeb/EV_KEY/KEY_RIGHTCTRL {devid value} {
685 ib-ebwikeb-modifier $devid $value 0x0200
687 proc ib-ev/ebwikeb/EV_KEY/KEY_CAPSLOCK {devid value} {
688 ib-ebwikeb-modifier $devid $value 0x0400
691 proc ib-ev/ebwikeb/0xffbc_88/0xffbc_0d {devid value} {
692 ib-selectnext $devid $value
695 proc ib-ev/ebwikeb/EV_KEY/KEY_BOOKMARKS {devid value} {
696 upvar #0 input/$devid in
698 if {!($in(modifiers) & 0xff00)} return
699 if {$in(modifiers) & 0x00ff} {
704 scmd routinecmd $in(desc) "!realtime $how"
707 #----- static keybindings speed `controller'
709 proc bind-keyboard-speed {kslow kfast kseltrain kreverse desc} {
710 set wunique [get-unique keyboardspeed]
711 set w [speedw-new-cooked $wunique $desc]
712 foreach delta {-1 +1} sf {slow fast} {
713 bind . <Key-[set k$sf]> [list speedw-userinput-rel-steps $w $delta {}]
715 bind . <Key-$kseltrain> [list speedw-train-selectnext $w]
716 bind . <Key-$kreverse> [list speedw-train-direction $w change]
717 speedw-setstate $w normal
720 #---------- input device evdev binding ----------
722 proc ib-evcmd-construct {devid target xargs} {
723 upvar #0 input/$devid in
724 if {[llength $target] > 1} {
725 debug "ib $devid - multiple devices, not supported"
728 manyset [lindex $target 0] ev sysfs
729 if {[regexp { } $ev]} { error "event device `$ev' contains space" }
731 [list ./evdev-manip --redact] $xargs \
732 [list --stdin-monitor \
733 --expect-sysfs /sys$sysfs/$ev/dev \
737 proc bind-input {bus vendor product version concrete args} {
738 global input_bindings
739 set devid $bus:$vendor:$product:$version
740 lappend input_bindings [list $devid $concrete $args]
743 proc bind-input-static {event sysfs concrete args} {
744 global input_bindings input_statics
745 set devid [get-unique static]
746 lappend input_statics [list $devid $event $sysfs]
747 lappend input_bindings [list $devid $concrete $args]
750 proc widgets-input-bindings {} {
751 global input_bindings
752 foreach binding $input_bindings {
753 manyset $binding devid concrete xa
754 set cid [get-unique $concrete]
755 upvar #0 input/$devid in
757 set in(concrete) $concrete
758 eval [list ib-create/$concrete $devid $cid] $xa
760 pack .inputs -side top -fill x
763 # input/$bus:$vendor:$product:$version becomes `in' via upvar
764 # $in(chan) channel open onto evdev-manip; unset if none
765 # $in(laststart) last start time, [clock seconds]
766 # at every event we set this the current time
767 # but we insist on adding at least 5s
768 # and if that would make it > current time +15s
770 # $in(speedw) optional, may be set by ib-create
772 proc scan-input-bindings {} {
773 global errorInfo errorCode unmatched_notified
774 global input_bindings inputretryadd inputretrymax scaninputinterval
777 set f [open /proc/bus/input/devices]
779 if {[string match {POSIX ENOENT *} $errorCode]} return
780 error $emsg $errorInfo $errorCode
785 if {[info exists v(devid)] &&
786 [info exists v(sysfs)] &&
787 [info exists v(event)]} {
788 lappend target($v(devid)) [list $v(event) $v(sysfs)]
797 {^I: Bus=(\w+) Vendor=(\w+) Product=(\w+) Version=(\w+)\s} \
798 $l dummy bus vendor product version]} {
799 set v(devid) $bus:$vendor:$product:$version
800 } elseif {[regexp {^S: Sysfs=(\S+)\s} $l dummy sysfs]} {
802 } elseif {[regexp {^H: Handlers=(?:.*\s)?(event\d+)\s} $l dummy ev]} {
809 foreach static $input_statics {
810 manyset $static devid event sysfs
811 lappend target($devid) [list $event $sysfs]
813 foreach devid [array names target] {
814 upvar #0 input/$devid in
815 if {![info exists in(concrete)]} {
816 if {![info exists unmatched_notified($devid)]} {
817 debug "ib $devid unmatched, ignored"
818 set unmatched_notified($devid) 1
822 if {[info exists in(chan)]} continue
823 set now [clock seconds]
824 set newlast [expr {$in(laststart) + $inputretryadd}]
825 if {$newlast > $now + $inputretrymax} continue
826 if {$newlast < $now} { set newlast $now }
827 set cmdl [ib-evcmd/$in(concrete) $devid $target($devid)]
828 if {![llength $cmdl]} {
832 lappend cmdl 2>@ stderr
833 set in(laststart) $newlast
834 catch-for-input-binding $devid {
835 debug "ib $devid running $cmdl"
836 set in(chan) [open |$cmdl r+]
837 fconfigure $in(chan) -blocking 0 -buffering line
838 fileevent $in(chan) readable [list catch-for-input-binding $devid \
839 [list readable input-binding $in(chan) $devid]]
842 foreach binding $input_bindings {
843 manyset $binding devid concrete ctrlr
844 upvar #0 input/$devid in
845 if {![info exists in(concrete)]} continue
846 if {[info exists target($devid)]} continue
847 input-binding-notpresent $devid absent
849 after $scaninputinterval scan-input-bindings
852 proc input-binding-notpresent {devid why} {
853 upvar #0 input/$devid in
854 if {[info exists in(speedw)]} {
855 speedw-setstate $in(speedw) disabled
857 if {![catch { info args ib-absent/$in(concret) }]} {
858 ib-absent/$in(concrete) $devid $why
862 proc input-binding-eof {chan devid} {
863 upvar #0 input/$devid in
864 fconfigure $in(chan) -blocking 1
866 error "evdev-manip exited" {} {CHILDSTATUS ? 0}
869 proc input-binding-inputline {chan l devid} {
871 upvar #0 input/$devid in
872 if {![catch { info args ib-inputline/$in(concrete) }]} {
873 # give the input binding first dibs
874 if {[ib-inputline/$in(concrete) $devid $l]} return
876 regsub {^[^ ]+ } $l {} lr
877 switch -glob -- $lr {
879 debug "ib $devid start << $l"
880 if {[info exists in(speedw)]} {
881 speedw-setstate $in(speedw) normal
885 manyset [split $lr] value kindl kindr codel coder
886 set proc ib-ev/$in(concrete)/${kindl}_${kindr}/${codel}_${coder}
887 if {[catch { info args $proc }]} {
889 debug "ib $devid unbound $proc << $l"
896 debug "ib $devid ignored << $l"
901 proc catch-for-input-binding {devid body} {
902 upvar #0 input/$devid in
903 global errorInfo errorCode
904 set r [catch { uplevel 1 $body } rv]
905 if {$r!=1} { return -code $r $rv }
906 switch -glob $errorCode {
907 {CHILDSTATUS *} { set m "exited with status [lindex $errorCode 2]" }
908 {CHILDKILLED *} { set m "killed by signal [lindex $errorCode 3]" }
909 {POSIX *} { set m "communication error: [lindex $errorCode 1]" }
910 * { error $rv $errorInfo $errorCode }
912 debug "ib $devid died $m"
913 catch { close $in(chan) }
914 catch { unset in(chan) }
916 input-binding-notpresent $devid "died $m"
919 proc engage-input-bindings {} {
923 #---------- plan background (gui-plan subprocess) ----------
925 proc gui-pipe-readable {args} {
927 while {[gets $gui_pipe l] >= 0} {
930 if {[eof $gui_pipe]} {
932 error "gui-plan crashed"
936 #---------- train set events of general interest, and setup ----------
938 proc train-event-eof {args} {
939 error "lost connection to train set"
942 register-event ?stastate {ctxch state} {^(.)stastate (\w+|\-) } {
943 global ctrain trains stastate
945 report-problem "stastate: $state"
946 if {[string compare $ctxch |]} speedws-stastate-hook
949 register-event ?resolution {message} \
950 {^.resolution (\S+ .*)$} {
951 if {[string match "problems *" $message]} return
952 report-problem "resolution: $message"
955 register-event ?warning {message} {^.warning (\S+ .*)$} {
956 report-problem "warning: $message"
959 register-event {} {} {^=connected } {
960 global pages gui_pipe server port event_selections
962 scmd replayed {} "select-replay [concat $event_selections]"
964 foreach page $pages {
968 set cmdl [list ./gui-plan-$page [winfo id $w] @$server,$port]
969 lappend cmdl 2>@ stderr
970 set gui_pipe [open |$cmdl r]
972 puts stderr "running $cmdl"
973 fconfigure $gui_pipe -blocking no
974 fileevent $gui_pipe readable gui-pipe-readable
977 proc replayed-err {m args} { error "replay failed: $m" }
978 proc replayed-ok {m args} {
979 speedws-stastate-hook
982 register-event {} {} {^=failed } { error "multiplexer failed: $l" }
983 register-event {} {} {^=denied } { error "multiplexer denied us: $l" }
984 register-event {} {} {^\+nack } { error "multiplexer does not understand" }
986 #---------- main program ----------
988 append event_dispatch_body {
991 proc train-event-inputline {sconn l} $event_dispatch_body
992 proc register-event {args} { error "too late!" }
994 proc engage-server {} {
995 global server port sconn
997 set sconn [socket $server $port]
998 fconfig-trainproto $sconn
999 fileevent $sconn readable {readable train-event $sconn}
1003 global pages cpage configfile input_bindings input_statics
1004 setting server railway {[[0-9a-z:].*}
1005 setting geometry {} {[-+]\d+[-+]\d+}
1006 setting posdeviation 10 {\d+}
1007 setting movfeatcommand {movfeat+} {(?:!movfeat|movfeat\+?\+?)}
1008 setting problemdisplayms 1000 {\d+}
1009 setting inputretryadd 5 {\d+}
1010 setting inputretrymax 15 {\d+}
1011 setting scaninputinterval 500 {\d+}
1012 setting showunbound 0 {[01]}
1014 set hostname [lindex [split [info hostname] .] 0]
1015 setting configfile gui-$hostname.config {.+}
1019 if {![info exists input_bindings]} { set input_bindings {} }
1020 if {![info exists input_statics]} { set input_statics {} }
1022 uplevel #0 source gui-layout.config
1023 uplevel #0 source $configfile
1024 foreach cpage $pages {
1030 widgets-input-bindings
1031 widget-problem-report
1033 engage-input-bindings