~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Open Mash Cross Reference
mash/tcl/rvic/ui-remote.tcl

Component: ~ [ mash ] ~ [ apps ] ~ [ gsm ] ~ [ lib ] ~ [ otcl ] ~ [ srm ] ~ [ tcl8.3 ] ~ [ tclcl ] ~ [ tk8.3 ] ~ [ tutorials ] ~

  1 # ui-remote.tcl --
  2 #
  3 #       FIXME: This file needs a description here.
  4 #
  5 # Copyright (c) 1993-2002 The Regents of the University of California.
  6 # All rights reserved.
  7 #
  8 # Redistribution and use in source and binary forms, with or without
  9 # modification, are permitted provided that the following conditions are met:
 10 #
 11 # A. Redistributions of source code must retain the above copyright notice,
 12 #    this list of conditions and the following disclaimer.
 13 # B. Redistributions in binary form must reproduce the above copyright notice,
 14 #    this list of conditions and the following disclaimer in the documentation
 15 #    and/or other materials provided with the distribution.
 16 # C. Neither the names of the copyright holders nor the names of its
 17 #    contributors may be used to endorse or promote products derived from this
 18 #    software without specific prior written permission.
 19 #
 20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 21 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 23 # ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 24 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 25 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 26 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 28 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 30 #
 31 # @(#) $Header: /usr/mash/src/repository/mash/mash-1/tcl/rvic/ui-remote.tcl,v 1.33 2002/05/21 21:19:12 chema Exp $
 32 
 33 
 34 #--------------#--------------#--------------#--------------
 35 # ::TODO::
 36 #  - cl/srvr rendezvous
 37 #  - MUA agent
 38 #  -X use regex's for switching
 39 #  -X use cname if name not unique across members
 40 #  -X fix format change causing decoders to get confused
 41 #  -X fix race condition with shared memory writes to switching window
 42 #--------------#--------------#--------------#--------------
 43 # ::instance variable type info::
 44 #  - window_() ==> video widgets
 45 #  - attached_src_() ==> Source/RTPs
 46 #  - active_()  ==> keys = Source/RTP ; attribs = SimpleActiveSource
 47 #  - allsrcs_ ==> Source/RTP
 48 #--------------#--------------#--------------#--------------
 49 
 50 import RTPAgent AudioMonitor VisualFrame Rendezvous RvicLayout
 51 
 52 
 53 #-------------#-------------#-------------#-------------
 54 # Class SimpleActiveSource
 55 #-------------#-------------#-------------#-------------
 56 
 57 # SimpleActiveSource has no tile thumbnails, no cmenu,
 58 # and useHW hard-coded to 0.
 59 Class SimpleActiveSource
 60 
 61 #
 62 SimpleActiveSource instproc init { parent src } {
 63         $self next
 64         $self instvar src_ parent_
 65         set src_ $src
 66         set parent_ $parent
 67 }
 68 
 69 # bind a source to a window so that the video stream from
 70 # source $src appears in window $vw
 71 SimpleActiveSource instproc attach-window vw {
 72         $self instvar src_ parent_
 73         # useHW set to 0 FIXME
 74         $vw attach-decoder $src_ [[$parent_ set vframe_] set colorModel_] 0
 75 }
 76 
 77 #
 78 SimpleActiveSource instproc detach-window vw {
 79         $self instvar src_
 80         $vw detach-decoder $src_
 81 }
 82 
 83 #
 84 SimpleActiveSource instproc detach-windows {} {
 85         #puts "SimpleActiveSource($self)::detach-windows"
 86         $self instvar parent_
 87 
 88         for {set i 1} {$i < 10} {incr i} {
 89                 set rtps [$parent_ attached_source $i]
 90                 set ss [$parent_ rtp2simple $rtps]
 91                 if {$ss == $self} {
 92                         set w [$parent_ window $i]
 93                         $self detach-window $w
 94                         #puts "detaching window [$parent_ window $i]"
 95                         $parent_ attached_source $i ""
 96                 }
 97         }
 98 }
 99 
100 
101 
102 #-------------#-------------#-------------#-------------
103 # Class RemoteVicUI
104 #-------------#-------------#-------------#-------------
105 
106 
107 import VicUI
108 
109 #
110 # The main user interface for the rvic application.
111 #
112 Class RemoteVicUI -superclass VicUI
113 
114 # returns SimpleActiveSource object for a given Source/RTP object
115 RemoteVicUI public rtp2simple {src} {
116         $self instvar active_
117         if {[info exists active_($src)]} {
118                 return $active_($src)
119         } else {
120                 return ""
121         }
122 }
123 
124 # get or set attached_src_ instvar
125 RemoteVicUI public attached_source {i args} {
126         #puts "RemoteVicUI::attached_source $i $args"
127         $self instvar attached_src_
128 
129         if {[llength $args] > 0} {
130                 switch $args {
131                         ""      {unset attached_src_($i)}
132                         "{}"    {unset attached_src_($i)}
133                         default {set attached_src_($i) $args}
134                 }
135         } else {
136                 if {[info exists attached_src_($i)]} {
137                         return $attached_src_($i)
138                 } else {
139                         return ""
140                 }
141         }
142 }
143 
144 # window_ instvar accessor
145 #
146 RemoteVicUI public window {i} {
147         $self instvar window_
148         return $window_($i)
149 }
150 
151 # resize & move window number `i' according to to `geom'
152 # <br> if `geom' == 0, delete (actually just hide) the window
153 #
154 RemoteVicUI public window_geom {i geom} {
155         $self instvar active_ path_
156         #puts "RemoteVicUI::window_geom $i => $geom"
157 
158         if {$geom == 0} {
159                 foreach {at val} "width 1 height 1 x 0 y 0" {
160                         set $at $val
161                 }
162         } else {
163                 set gl [split $geom "x+"]
164                 set width [expr int([lindex $gl 0]*640)]
165                 set height [expr int([lindex $gl 1]*480)]
166                 set x [lindex $gl 2]
167                 set y [lindex $gl 3]
168         }
169         #puts "RemoteVicUI::window_geom $i => $width-by-$height at +$x+$y"
170 
171         set w [$self window $i]
172         [$w window] resize $width $height
173         place configure $path_.top.vw$i -relx $x -rely $y
174 
175         set att [$self attached_source $i]
176         if {$att != ""} {
177                 if [info exists active_($att)] {
178                         set as $active_($att)
179                         $as detach-window $w
180                         $as attach-window $w
181                 }
182         }
183         after idle $w redraw
184 }
185 
186 # toggle transmitting
187 RemoteVicUI public toggle_transmit {} {
188         $self instvar controlMenu_ xmit_
189         if $xmit_ {set xmit_ 0} else {set xmit_ 1}
190         #puts "RemoteVicUI::toggle_transmit (xmit_= $xmit_)"
191         $controlMenu_ invoke_transmit
192 }
193 
194 
195 #
196 # Build the "user-interface."
197 #
198 RemoteVicUI instproc build_gui { w } {
199         $self instvar videoAgent_ grid_ label_ controlMenu_ path_ vpipe_ \
200                         vframe_ allsrcs_ window_ al_ voice_switched_ voice_sw_regex_ \
201                         timer_switched_ timer_sw_regex_ layout_switched_ currLayout_ \
202                         xmit_
203         set path_ $w
204         set vframe_ [new VisualFrame $w.top]
205         set allsrcs_ ""
206         set voice_switched_ "1" ; # defaults to vw 1
207         set voice_sw_regex_(1) "*"
208         set timer_switched_ "2" ; # defaults to vw 2
209         set timer_sw_regex_(2) "*"
210         set layout_switched_ 0
211         set xmit_ [$self yesno transmitOnStartup]
212 
213         $self init_layouts
214 
215         wm geometry . 640x480+0+0
216         place $w.top -x -5 -y -5 -width 640 -height 480
217         label $w.top.l -text "Waiting to finish configuring video windows..."
218         place $w.top.l -relx 0.5 -rely 0.5 -anchor c
219         set currLayout_ "none"
220         after idle "$self reconfig FocusPlusContext"
221         after idle "wm title . {rvic on [localaddr]:\
222                         [$self get_option conferenceName]}"
223         after idle "$self match_layout_to_srcs"
224         after idle "$self start_timer_switching"
225 
226         bind . <s> "$self print-active-sources"
227         bind . <q> "exit"
228         bind . <a> "$self switch_src all"
229         bind . <t> "$self toggle_transmit"
230         foreach {key configname} {y FocusPlusContext u Single i BradyBunch2x2 \
231                         o BradyBunch3x3 p PictureInPicture} {
232                 bind . <$key> "$self reconfig $configname"
233         }
234 
235         foreach i {1 2 3 4 5 6 7 8 9} {
236                 set p $w.top.vw$i
237                 set window_($i) [new VideoWidget $p 320 240]
238                 bind all <Key-$i> "$self switch_src $i"
239         }
240 
241         # Note: sending {} for the ActiveSourceManager parameter
242         set controlMenu_ [new ControlMenu $self $videoAgent_ \
243                         $vpipe_ $vframe_ {} [new UISrcListWindow $w $videoAgent_]]
244         $controlMenu_ build_window ;# never shows up
245 
246         # start remote control interface
247         set ctrlAddr [$self get_option rvicCtrlSpec]
248         set al_ [new RvicCtrl $ctrlAddr $self]
249 
250         # monitor the audio session for voice-switching
251         set aspec [$self get_option audioMonitorSpec]
252         if { $aspec != "" } {
253                 set ab [new AddressBlock $aspec]
254                 set monitor [new AudioMonitor $ab $self]
255                 # audio doesn't want traffic looped back
256                 #$audioAgent_ set-loopback 0
257                 # send back-to-back packets spaced out at 128kb/s
258                 #$audioAgent_ set-bandwidth 128
259         }
260         after 1000 "$self periodic_update"
261 }
262 
263 
264 # initialize to the set of "standard layouts", using the same syntax
265 # as messages on the wire
266 RemoteVicUI private init_layouts {} {
267         $self instvar layouts_
268         set layouts_ ""
269         lappend layouts_ [new RvicLayout Single \
270                         "Set VideoWindowGeom\n\
271                         1 1.0x1.0+0.0+0.0\n\
272                         Set VoiceSwitchList\n\
273                         1 *"]
274         lappend layouts_ [new RvicLayout PictureInPicture \
275                         "Set VideoWindowGeom\n\
276                         1 1.0x1.0+0.0+0.0\n\
277                         2 0.25x0.25+0.7+0.05\n\
278                         Set VoiceSwitchList\n\
279                         1 *\n\
280                         Set TimerSwitchList\n\
281                         2 *"]
282         lappend layouts_ [new RvicLayout FocusPlusContext \
283                         "Set VideoWindowGeom\n\
284                         1 0.5x0.5+0.09375+0.09375\n\
285                         2 0.25x0.25+0.6875+0.0625\n\
286                         3 0.25x0.25+0.6875+0.3750\n\
287                         4 0.25x0.25+0.0625+0.6875\n\
288                         5 0.25x0.25+0.3750+0.6875\n\
289                         6 0.25x0.25+0.6875+0.6875\n\
290                         Set VoiceSwitchList\n\
291                         1 *\n\
292                         Set TimerSwitchList\n\
293                         2 *"]
294         lappend layouts_ [new RvicLayout BradyBunch2x2 \
295                         "Set VideoWindowGeom\n\
296                         1 0.5x0.5+0.0+0.0\n\
297                         2 0.5x0.5+0.5+0.0\n\
298                         3 0.5x0.5+0.0+0.5\n\
299                         4 0.5x0.5+0.5+0.5\n\
300                         Set VoiceSwitchList\n\
301                         1 *\n\
302                         Set TimerSwitchList\n\
303                         2 *"]
304         lappend layouts_ [new RvicLayout BradyBunch3x3 \
305                         "Set VideoWindowGeom\n\
306                         1 0.25x0.25+0.0625+0.0625\n\
307                         2 0.25x0.25+0.375+0.0625\n\
308                         3 0.25x0.25+0.6875+0.0625\n\
309                         4 0.25x0.25+0.0625+0.375\n\
310                         5 0.25x0.25+0.375+0.375\n\
311                         6 0.25x0.25+0.6875+0.375\n\
312                         7 0.25x0.25+0.0625+0.6875\n\
313                         8 0.25x0.25+0.375+0.6875\n\
314                         9 0.25x0.25+0.6875+0.6875\n\
315                         Set VoiceSwitchList\n\
316                         1 *\n\
317                         Set TimerSwitchList\n\
318                         2 *"]
319 }
320 
321 
322 #
323 # This reconfigures the video windows. <i>name</i> should be
324 # a string matching an RvicLayout::name of any of the existing
325 # RvicLayout objects listed in layouts_
326 #
327 RemoteVicUI instproc reconfig {name} {
328         $self instvar currLayout_ allsrcs_ path_ layouts_
329 
330         # check for no sources
331         if {[llength $allsrcs_] == 0} {
332                 if {[info commands $path_.top.l] == ""} {
333                         label $path_.top.l -text "Waiting for video..."
334                         place $path_.top.l -relx 0.5 -rely 0.5 -anchor c
335                 } else {
336                         $path_.top.l configure -text "Waiting for video..."
337                 }
338                 if {$currLayout_ == "none"} {after 6000 $self reconfig $name}
339                 foreach i {1 2 3 4 5 6 7 8 9} {
340                         $self window_geom $i 0
341                 }
342                 return
343         } else {
344                 destroy $path_.top.l
345         }
346 
347         set doneSwitch 0
348         # check `name' against each layout
349         foreach l $layouts_ {
350                 if {$name == [$l name]} {
351                         set doneSwitch 1
352                         set n [$l num_windows]
353                         for {set i 1} {$i <= $n} {incr i} {
354                                 $self window_geom $i [[$l get_window $i] geom]
355                         }
356                         for {set i [expr $n+1]} {$i <= 9} {incr i} {
357                                 $self window_geom $i 0
358                         }
359                 }
360         }
361         if !$doneSwitch {
362                 puts "reconfig:: Bad layout: $name"
363                 return
364         }
365         set currLayout_ $name
366 }
367 
368 
369 # Set up video widgets so that as many sources as possible are shown
370 # (a so-called "default view")
371 RemoteVicUI private default_vw_views {} {
372         $self instvar active_ allsrcs_ layout_switched_
373 
374         set l [llength $allsrcs_]
375         if {$l == 0} {
376                 after 5000 "$self default_vw_views"
377                 return
378         }
379 
380         foreach i {1 2 3 4 5 6 7 8 9} {
381                 set idx [expr ($i-1) % $l]
382                 set s [lindex $allsrcs_ $idx]
383                 $self switch_src_to $i $s
384         }
385 }
386 
387 # finds best match of configuration of windows
388 # to the number of sources by count of the number of windows.
389 # `Best' is defined as:
390 # <br><i> the minimum numwins s.t. numwins >= numsrcs </i> .
391 # <br> if <i> numsrcs > numwins </i> for all layouts, the layout
392 # with maximum numwins is used
393 #
394 RemoteVicUI public match_layout_to_srcs {} {
395         #puts "RemoteVicUI::match_layout_to_srcs"
396         $self instvar allsrcs_ layout_switched_ layouts_
397 
398         if !$layout_switched_ {return}
399 
400         set nsrcs [llength $allsrcs_]
401         set bestN 9999
402         set bestL ""
403         set maxN 0
404         set maxL ""
405         foreach l $layouts_ {
406                 set nwins [$l num_windows]
407                 if {$nwins > $maxN} {
408                         set maxN $nwins
409                         set maxL $l
410                 }
411                 #puts "$nsrcs <= $nwins && $nwins <= $bestN"
412                 if {$nsrcs <= $nwins && $nwins < $bestN} {
413                         set bestL $l
414                         set bestN $nwins
415                 }
416         }
417         if {$bestL == ""} {set bestL $maxL}
418         #puts "best layout: [$bestL name] for $nsrcs srcs"
419         $self reconfig [$bestL name]
420         $self default_vw_views
421 }
422 
423 # Return a list of the active simpleactivesources.
424 RemoteVicUI instproc active-sources {} {
425         $self instvar active_
426         return [array names active_]
427 }
428 
429 # prints active simpleactivesources.
430 RemoteVicUI instproc print-active-sources {} {
431         foreach i [$self active-sources] {puts "$i: [$i sdes cname] / [$i getid]"}
432 }
433 
434 
435 #
436 # Add the SimpleActiveSource, <i>ss</i>, to the <i>active_</i>
437 # array (indexed by Source/RTP, <i>src</i>).
438 # Also add the Source/RTP to the list, <i>allsrcs_</i>.
439 RemoteVicUI instproc add_active { ss src } {
440         $self instvar active_ allsrcs_
441         set active_($src) $ss
442         lappend allsrcs_ $src
443         $self match_layout_to_srcs
444 }
445 
446 #
447 # Remove the Source, <i>src</i>, from the <i>active_</i> array and
448 # the <i>allsrcs_</i> list.
449 #
450 RemoteVicUI instproc rm_active src {
451         $self instvar active_ allsrcs_
452         unset active_($src)
453         set i [lsearch $allsrcs_ $src]
454         if {$i != -1} {
455                 set allsrcs_ [lreplace $allsrcs_ $i $i]
456         }
457         $self match_layout_to_srcs
458 }
459 
460 #
461 # Update the source descprtion by updating the element of the global
462 # arrays src_info(), src_nickname(), and src_name() indexed by the
463 # specified <i>src</i>.  If the src_name() is changed, then this can
464 # be reflected via change_name
465 #
466 RemoteVicUI instproc trigger_sdes src {
467         global src_info src_nickname src_name
468         # Figure out best presentation from available information.
469         set name [$src sdes name]
470         set cname [$src sdes cname]
471         set addr [$src addr]
472         if { $name == "" } {
473                 if { $cname == "" } {
474                         set src_nickname($src) $addr
475                         set info $addr/[$src format_name]
476 
477                 } else {
478                         set src_nickname($src) $cname
479                         set info "$addr/[$src format_name]"
480                 }
481         } elseif [cname_redundant $name $cname] {
482                 set src_nickname($src) $name
483                 set info $addr/[$src format_name]
484         } else {
485                 set src_nickname($src) $name
486                 set info $cname/[$src format_name]
487         }
488         set src_info($src) $cname/[$src format_name]
489 
490         set msg [$src sdes note]
491         if { $msg != "" } {
492                 set info $msg
493         }
494         set src_info($src) $info
495 
496         # only call change_name when name really changes
497         if { ![info exists src_name($src)] || "$src_name($src)" != "$name" } {
498                 set src_name($src) $name
499                 $self change_name $src
500         }
501 }
502 
503 # noop
504 RemoteVicUI private change_name src {
505         #puts "RemoteVicUI::change_name $src ([$src sdes name]/[$src sdes cname])"
506         #Switcher rebuild_switch_list_menu
507 }
508 
509 
510 # Add a src to the active senders list.  creates a
511 # SimpleActiveSource rather than an ActiveSource:
512 # i.e., no postage stamp window
513 RemoteVicUI instproc activate src {
514         #
515         # give the VideoAgent a chance to create and install
516         # a decoder and for that decoder to see a packet so it can
517         # determine the output geometry and color decimation.
518         # we shouldn't have to do this (e.g., resize will
519         # take care of a geometry change), but currently
520         # decoders can't trigger a renderer realloation
521         # when the decimation changes.FIXME fix this
522         #
523         after idle "$self really_activate $src"
524 }
525 
526 #
527 RemoteVicUI instproc really_activate src {
528         set as [new SimpleActiveSource $self $src]
529         $self add_active $as $src
530         $self instvar allsrcs_ active_
531         # if any windows are empty, allocate this stream to them
532         foreach i {1 2 3 4 5 6 7 8 9} {
533                 if {[$self attached_source $i] == ""} {
534                         $as attach-window [$self window $i]
535                         $self attached_source $i $src
536                 }
537         }
538 }
539 
540 # Remove a src from the active senders list.
541 #
542 RemoteVicUI instproc deactivate src {
543         #puts "RemoteVicUI::deactivate: $src"
544         $self instvar active_
545         if [info exists active_($src)] {
546                 set as $active_($src)
547                 $as detach-windows
548                 $self rm_active $src
549         }
550 
551         $src handler ""
552 }
553 
554 
555 # deal with format change  <br>
556 # (only marginally tested: doesn't crash but the decoders can get confused)
557 RemoteVicUI instproc trigger_format src {
558         #puts "RemoteVicUI::trigger_format [$src getid]"
559         $self instvar active_ videoAgent_
560         if ![info exists active_($src)] {
561                 return
562         }
563         set as $active_($src)
564         set L ""
565         for {set i 1} {$i < 10} {incr i} {
566                 if {[$self attached_source $i] == $src} {
567                         lappend L $i
568                 }
569         }
570         $as detach-windows
571         #FIXME
572         set extoutList [extout_detach_src $src]
573 
574         set d [$videoAgent_ reactivate $src]
575         ###set d [$videoAgent_ create_decoder $src]
576         $self update_decoder $src
577 
578         foreach i $L {
579                 set w [$self window $i]
580                 $as attach-window $w
581                 $w redraw
582         }
583         #FIXME
584         extout_attach_src $src $extoutList
585 }
586 
587 #
588 # Called when the video stream state changes in a way that would
589 # affect the choice of renderer.  For example, when a jpeg stream
590 # changes from type-0 to type-1 we might have to revert from
591 # hardware to software decoding, or we might have to reallocate
592 # a 422 renderer as a 420 renderer.  This never needs to happen
593 # for most stream types (i.e., because the decimation factor is fixed).
594 # <br> FIXME - copied from hvic -- NEVER TESTED
595 RemoteVicUI instproc decoder_changed src {
596         $self instvar active_
597         if ![info exists active_($src)] {
598                 return
599         }
600         #FIXME redundant with trigger_format
601         set as $active_($src)
602         $as detach-windows
603         #FIXME
604         set extoutList [extout_detach_src $src]
605         foreach w $iw {
606                 $as attach-window $uw
607                 $w redraw
608         }
609         #FIXME
610         extout_attach_src $src $extoutList
611         return
612 }
613 
614 
615 
616 #-------------#-------------#-------------#-------------
617 # methods for dealing with switching window sources
618 #-------------#-------------#-------------#-------------
619 
620 
621 #
622 RemoteVicUI instproc switch_src_by_name {int name} {
623         $self instvar allsrcs_ active_
624 
625         #puts "trying to switch $int to $name"
626         if { [llength $allsrcs_] > 0 } {
627                 if {$name == ""} {
628                         $self switch_src $int
629                         return
630                 }
631                 set target ""
632                 foreach s [array names active_] {
633                         #puts "$name ?=? [$s getid]"
634                         if {[string first $name [$s getid]] != -1} {
635                                 set target $s
636                         }
637                 }
638                 if { $target == "" } {
639                         foreach s [array names active_] {
640                                 if {[string first $name [$s sdes cname]] != -1} {
641                                         set target $s
642                                 }
643                         }
644                 }
645                 if { $target == "" } {
646                         foreach s [array names active_] {
647                                 if {[string first $name [$s sdes name]] != -1} {
648                                         set target $s
649                                 }
650                         }
651                 }
652                 if { $target == "" } {
653                         foreach s [array names active_] {
654                                 if {[string first $name [$s sdes note]] != -1} {
655                                         set target $s
656                                 }
657                         }
658                 }
659                 if { $target == "" } {
660                         puts stderr "no target - \"$name\""
661                         if {[llength $name] > 1} {
662                                 puts "heuristic: trying [lindex $name 0]"
663                                 if {[regexp -nocase [lindex $name 0] [$s getid]]} {
664                                         set target $s
665                                 }
666                         }
667                         if { $target == "" } {
668                                 $self switch_src $int
669                                 return
670                         }
671                 }
672                 $self switch_src_to $int $target
673         }
674 }
675 
676 # for timer- or iterative- switching window `num': <br>
677 # returns the "next" source in the list of sources
678 # or "" if there are no sources
679 RemoteVicUI public next_src {num} {
680         $self instvar allsrcs_
681 
682         set len [llength $allsrcs_]
683         if { $len > 0 } {
684                 set curr_src [$self attached_source $num]
685                 if {$curr_src != ""} {
686                         set j [lsearch $allsrcs_ $curr_src]
687                         set j [expr ($j+1) % $len]
688                         return [lindex $allsrcs_ $j]
689                 } else {
690                         return [lindex $allsrcs_ 0]
691                 }
692         } else {
693                 return ""
694         }
695 }
696 
697 # switch to next src in window `num' <br>
698 # if num == "all", switch all the windows
699 RemoteVicUI instproc switch_src {num} {
700         if {$num == "all"} {
701                 for {set i 1} {$i < 10} {incr i} {
702                         $self switch_src $i
703                 }
704                 return
705         }
706 
707         $self instvar allsrcs_
708         set len [llength $allsrcs_]
709         if { $len > 0 } {
710                 set nxt [$self next_src $num]
711                 if {$nxt != ""} {
712                         $self switch_src_to $num $nxt
713                 }
714         }
715 }
716 
717 # switch window number `num' to source object `src'
718 RemoteVicUI public switch_src_to {num src} {
719         $self instvar active_
720 
721         set w [$self window $num]
722         if {[info exists active_($src)]} {
723                 # detach old source
724                 set curr_src [$self attached_source $num]
725                 if {$curr_src != ""} {
726                         if {[info exists active_($curr_src)]} {
727                                 set olds $active_($curr_src)
728                                 $olds detach-window $w
729                         }
730                 }
731                 # attach new source
732                 set as $active_($src)
733                 $as attach-window $w
734                 $self attached_source $num $src
735                 $w redraw
736         }
737 }
738 
739 
740 #
741 # <i>audio_src</i> is an audio src. <br>
742 # <i>allsrcs_</i> are video sources <br>
743 # only voice-switches windows with indicies indicated
744 # in the list variable `voice_switched_'
745 #
746 RemoteVicUI instproc switch_to audio_src {
747         $self instvar allsrcs_ active_ voice_switched_
748 
749         if { [string match *CoLab* [$audio_src getid]] } {
750                 puts "Warning: ignoring CoLab audio sources..."
751                 return
752         }
753         if { [llength $allsrcs_] > 0 } {
754                 set target ""
755                 foreach s $allsrcs_ {
756                         if { "[$audio_src sdes cname]" == "[$s sdes cname]" } {
757                                 set target $s
758                         }
759                 }
760                 if { $target == "" } {
761                         puts stderr "no target: [$audio_src sdes cname]"
762                         return
763                 }
764                 foreach i $voice_switched_ {
765                         $self switch_src_to $i $target
766                 }
767         }
768 }
769 
770 #-----------------#-----------------#
771 # Timer switching
772 #-----------------#-----------------#
773 
774 
775 # initialize or re-initialize window timer switching
776 RemoteVicUI private start_timer_switching {} {
777         $self instvar timer_switched_ timer_sw_int_
778 
779         # set default switch time
780         foreach i {1 2 3 4 5 6 7 8 9} {
781                 set timer_sw_int_($i) 5
782         }
783         foreach i $timer_switched_ {
784                 $self sched_timer $i
785         }
786 }
787 
788 # schedule the next timer switching event for window `num'
789 # if it should be switched, canceling any existing scheduled switch
790 RemoteVicUI private sched_timer {num} {
791         $self instvar timer_switched_ timer_sw_regex_ timers_ timer_sw_int_
792         if [info exists timers_($num)] {
793                 after cancel $timers_($num)
794         }
795 
796         # check for reasons to stop switching this window
797         set idx [lsearch -exact $timer_switched_ $num]
798         if {$idx == -1} {return}
799         if {$timer_sw_int_($num) == 0} {
800                 set timer_switched_ [lreplace $timer_switched_ $idx $idx]
801                 return
802         }
803 
804         set ms [expr $timer_sw_int_($num)*1000]
805         set timers_($num) [after $ms "$self timer_switch $num"]
806 }
807 
808 # timer-switch window `num', and then schedule the next switch
809 RemoteVicUI private timer_switch {num} {
810         $self instvar timer_sw_int_ timer_sw_regex_ timers_
811         # FIXME ignoring regex for now...
812         $self switch_src $num
813         unset timers_($num)
814         $self sched_timer $num
815 }
816 
817 
818 #---------------#---------------#---------------#---------------
819 # RemoteVicUI methods that deal with messages of the form
820 #  Set MethodName  or  Get MethodName
821 #---------------#---------------#---------------#---------------
822 
823 #
824 RemoteVicUI private SourceList {data} {
825         $self instvar al_
826         set reply "SourceList"
827         foreach i [$self active-sources] {
828                 append reply "\n" [$i getid]
829                 #append reply "\n" [$i sdes cname]
830         }
831         $al_ announce $reply
832 }
833 
834 #
835 RemoteVicUI private MonitorStyleList {data} {
836         $self instvar al_ layouts_
837         set reply "MonitorStyleList\n"
838         foreach layout $layouts_  {
839                 append reply "[$layout name] {"
840                 for {set i 1} {$i <= [$layout num_windows]} {incr i} {
841                         append reply "[[$layout get_window $i] geom] "
842                 }
843                         append reply "}\n"
844         }
845         $al_ announce $reply
846 }
847 
848 #
849 RemoteVicUI private MonitorStyle {data} {
850         $self instvar al_ currLayout_
851         if {[lrange $data 2 end] == ""} {
852                 $al_ announce "MonitorStyle $currLayout_"
853         } else {
854                 $self reconfig [lrange $data 2 end]
855         }
856 }
857 
858 #
859 RemoteVicUI private VideoWindow {data} {
860         $self instvar al_
861         if {[llength $data] > 3} {
862                 $self switch_src_by_name [lindex $data 2] [lrange $data 3 end]
863         } elseif {[llength $data] == 3} {
864                 $self switch_src [lindex $data 2]
865         } else {
866                 # blank argument: return update of vw-->cname mapping
867                 set reply "VideoWindow\n"
868                 append reply [$self build_sources_list]
869                 $al_ announce $reply
870         }
871 }
872 
873 #
874 RemoteVicUI private VideoWindowGeom {data} {
875         $self instvar al_
876         if {[llength $data] == 2} {
877                 # FIXME respond with the geoms of the windows
878                 #
879                 #
880                 return
881         }
882         set lines [split $data \n]
883         set lines [lrange $lines 1 end]
884         foreach line $lines {
885                 if {$line == ""} {continue}
886                 set idx [lindex $line 0]
887                 set val [lrange $line 1 end]
888                 $self window_geom $idx $val
889         }
890 }
891 
892 #
893 RemoteVicUI private Autoswitchlayout {data} {
894         $self instvar al_ layout_switched_
895         switch [llength $data] {
896                 2 {
897                         if $layout_switched_ {set ls on} else {set ls off}
898                         $al_ announce "Autoswitchlayout $ls"
899                 }
900                 3 {
901                         set onoff [lindex $data 2]
902                         $self set_autoswitching $onoff
903                 }
904                 default {
905                         puts "Bad Autoswitchlayout msg: $data"
906                 }
907         }
908 }
909 
910 #
911 RemoteVicUI private VoiceSwitchList {data} {
912         $self instvar voice_switched_ voice_sw_regex_ al_
913         if {[llength $data] == 2} {
914                 set reply [$self build_vswlist]
915                 $al_ announce $reply
916                 return
917         }
918         set lines [split $data \n]
919         set lines [lrange $lines 1 end]
920         foreach l $lines {
921                 set idx [lindex $l 0]
922                 set val [lrange $l 1 end]
923                 if {$val == ""} {
924                         # disable voice-switching
925                         set lidx [lsearch -exact $voice_switched_ $idx]
926                         if {$lidx != -1} {
927                                 set voice_switched_ \
928                                                 [lreplace $voice_switched_ $lidx $lidx]
929                         }
930                         return
931                 }
932                 if {[lsearch -exact $voice_switched_ $idx] == -1} {
933                         lappend voice_switched_ $idx
934                         set voice_sw_regex_($idx) $val
935                 }
936         }
937 }
938 
939 #
940 RemoteVicUI private TimerSwitchList {data} {
941         $self instvar timer_switched_ timer_sw_regex_ al_
942         if {[llength $data] == 2} {
943                 set reply [$self build_tswlist]
944                 $al_ announce $reply
945                 return
946         }
947         set lines [split $data \n]
948         set lines [lrange $lines 1 end]
949         foreach l $lines {
950                 set idx [lindex $l 0]
951                 set val [lrange $l 1 end]
952                 if {$val == ""} {
953                         # disable timer-switching
954                         set lidx [lsearch -exact $timer_switched_ $idx]
955                         if {$lidx != -1} {
956                                 set timer_switched_ \
957                                                 [lreplace $timer_switched_ $lidx $lidx]
958                         }
959                         return
960                 }
961                 if {[lsearch -exact $timer_switched_ $idx] == -1} {
962                         lappend timer_switched_ $idx
963                         set timer_sw_regex_($idx) $val
964                         $self sched_timer $idx
965                 }
966         }
967 }
968 
969 #
970 RemoteVicUI private SwitchInterval {data} {
971         $self instvar timer_sw_int_
972         set lines [split $data \n]
973         set lines [lrange $lines 1 end]
974         foreach l $lines {
975                 set idx [lindex $l 0]
976                 set val [lrange $l 1 end]
977                 set timer_sw_int_($idx) $val
978                 $self sched_timer $idx
979         }
980 }
981 
982 # Set or Get TransmitVideo state
983 RemoteVicUI private TransmitVideo {data} {
984         $self instvar xmit_ al_
985         switch [llength $data] {
986                 2 {}
987                 3 {
988                         set onoff [lindex $data 2]
989                         if {$xmit_ != $onoff} {$self toggle_transmit}
990                 }
991                 default {puts "Bad TransmitVideo msg: $data"}
992         }
993         $al_ announce "TransmitVideo $xmit_"
994 }
995 
996 #
997 # answer with the current session IP address (one line per 
998 # layer). If the command is Set, set the session address
999 #
1000 RemoteVicUI private SessionAddress {data} {
1001         $self instvar al_
1002         $self instvar videoAgent_
1003 
1004 
1005         if {[lindex $data 0] == "Set"} {
1006                 # set the new session address
1007                 set spec [lindex $data 2]
1008                 $videoAgent_ reset_spec $spec
1009         }
1010 
1011         # answer with the current (or new) session address
1012         set reply "SessionAddress"
1013 
1014         # get the network manager object from the VideoAgent
1015         set networkManager [$videoAgent_ set network_]
1016 
1017         for {set i 0} {$i < [$networkManager set nchan_]} {incr i} {
1018                 # get the ith network layer
1019                 set networkLayer [$networkManager set net_($i)]
1020                 # get the ith network layer's address and source
1021                 append reply "\n" "[$networkLayer set addr_]/[$networkLayer set port_]"
1022         }
1023 
1024         $al_ announce $reply
1025 }
1026 
1027 
1028 #-------------#-------------
1029 # helpers for message handlers
1030 #-------------#-------------
1031 
1032 #
1033 RemoteVicUI private build_vswlist {} {
1034         $self instvar voice_switched_ voice_sw_regex_
1035         set reply "VoiceSwitchList"
1036         foreach i $voice_switched_ {
1037                 append reply "\n$i $voice_sw_regex_($i)"
1038         }
1039         return $reply
1040 }
1041 
1042 #
1043 RemoteVicUI private build_tswlist {} {
1044         $self instvar timer_switched_ timer_sw_regex_
1045         set reply "TimerSwitchList"
1046         foreach i $timer_switched_ {
1047                 append reply "\n$i $timer_sw_regex_($i)"
1048         }
1049         return $reply
1050 }
1051 
1052 # set whether to automatically match layout to the number of srcs
1053 RemoteVicUI private set_autoswitching {onoff} {
1054         $self instvar layout_switched_
1055         if {$onoff == "on"} {set onoff 1} else {set onoff 0}
1056         set layout_switched_ $onoff
1057         $self match_layout_to_srcs
1058 }
1059 
1060 # return a textual list of the current window sources
1061 # (indexed, newline-separated lines)
1062 #
1063 # (TODO: also append sdes name if different, handle the two @ client)
1064 RemoteVicUI private build_sources_list {} {
1065         #puts "RemoteVicUI::build_sources_list"
1066         set reply ""
1067         foreach i {1 2 3 4 5 6 7 8 9} {
1068                 #puts "$i: [$self attached_source $i]"
1069                 append reply "$i "
1070                 set as [$self attached_source $i]
1071                 if {![string match *_o* $as] && ![string match "" $as]} {
1072                         puts "Error: attached_source returned `$as'"
1073                 }
1074                 if {$as != ""} {
1075                         append reply [$as getid]
1076                 } else {
1077                         append reply "(empty)"
1078                 }
1079                 append reply "\n"
1080         }
1081         ##puts " ... done."
1082         return [string trim $reply]
1083 }
1084 
1085 
1086 
1087 #-------------#-------------#-------------#-------------
1088 # Class RvcCtrl
1089 #-------------#-------------#-------------#-------------
1090 
1091 import UDPServer
1092 
1093 # The remote-control interface to RVIC
1094 Class RvicCtrl -superclass UDPServer
1095 
1096 #
1097 RvicCtrl public init {spec parent} {
1098         eval [list $self] next $spec
1099         $self instvar parent_
1100         set parent_ $parent
1101