.nr dT 4.nr P1 .2i.de EX.nr x \\$1v\\!h0c n \\nx 0...nr Pd \n(PD.de FG \" start figure caption: .FG filename.ps verticalsize.KF.BP \\$1 \\$2.sp .5v.EX \\$2v.ps -1.vs -1...de fg \" end figure caption (yes, it is clumsy).ps.vs.br.KE...de Tk \" Tk option name and option.CW \\$1.I \\$2 \\$3...de Tc \" Tk command string.IP "\\f(CW\\$1\\fP \\$2" \w'\f(CWburble\fP'u.br...de Kp \" Keypress.IP "\\f(CW\\$1\\fP" \w'\f(CWKeypress\fP'u.br...de d0 \" no gap between paragraphs.nr PD 0...de d1 \" restore gap.nr PD \n(Pd...ds Op "\fR[\fP\fIoptions ...\fP\fR]\fP.TLAn Overview of Limbo/Tk.AU.I "Lucent Technologies Inc.".brRevised June 2000 by Vita Nuova.SHOverview.LPLimbo/Tk is a concise and powerful way to construct graphical user interfaces without directly using the Draw module primitives.Standard interfaces can quickly be created from collections of menus, buttons, and other widgets that are part of Limbo/Tk's visual toolkit.It is modelled on Ousterhout's Tk 4.0 [1,2], commonly deployedwith the scripting language Tcl as `Tcl/Tk'.Although inspired by Tk 4.0, Inferno's Tk implementation is new, and unrelated to Ousterhout's.It is intended to be used with the new programming language Limbo, not Tcl.Limbo/Tk applications makeextensive use of Limbo's concurrent programming constructions and data structures,and that is reflected in the interface.Section 9 of Volume 1 of the.I "Inferno Programmer's Manual"provides a detailed reference for Limbo/Tk.This paper provides an overview of its use in some simple staged examples, andconcludes witha summary of the differences between the Limbo/Tk implementation and Tk 4.0.In the rest of this paper, `Tk' refers to Limbo/Tk, and `Tk 4.0' refers to Ousterhout'soriginal implementation..NH 1The Limbo/Tk environment.LPLimbo applications access Tk by means of a built-in module,.CW "$Tk" .The standard distribution also includes the window manager.CW "wm"and the Limbo module.CW Wmlib .Unlike.CW Tk ,.CW Wmlibis not built-in but implemented in Limbo by.CW /appl/lib/wmlib.b .It simplifies and standardises the construction of windowed applications;it also contains some graphical devices such as tabbed notebooks not provideddirectly by Limbo/Tk.The essentials of both.CW Tkand.CW Wmlibare discussed here..LPProgrammers usually need only three functions from the.CW Tkmodule:.IP •.CW toplevel.brCreates a graphical window \- a Tk `top level' widget \-that can be used to build a Limbo/Tk application.The function returns a reference to an adt.CW "\%Tk->Toplevel"adt that represents the window in subsequent Tk operations..IP •.CW cmd.brCreates and arranges graphic objects within the toplevel window by processing Limbo/Tk command strings.The interface to Tk is primarily the passing of strings betweenthe application and Tkof the toolkit using.CW cmd .Each call to.CW cmdreturns a string representing the result of the Tk command;a string starting with.CW ! ' `diagnoses an error..IP •.CW namechan.brGives a name within Tk (in the scope of a given window) to a Limbo.CW "chan of string"that Tk commands can use to send data to a Limbo program..LPOther functions in the module have more specialised uses that will not be discussed here.For instance,.CW "mouse"and.CW "keyboard"are used by a window manager to send mouse and keyboard eventsto the Tk implementation for distribution to applications..LPEven.CW toplevelis not commonly used in the window manager environment:a function.CW Wmlib->titlebarprovides the usual interface to.CW toplevel .The low-level interface will be described first, for completeness,then the normal case using.CW titlebar ..NH 1Basic Limbo/Tk.LPThis section shows a simple Tk application that uses only the fundamental Tk functions..NH 2Preliminaries.LPThe example assumes that the.CW "Tk"module is loaded as.CW "tk" :.P1include "tk.m"; tk: Tk; ...init(ctxt: ref Draw->Context, nil: list of string){ tk = load Tk Tk->PATH; ...}.P2.NH 2Creating a toplevel.LPThe following fragment makes the Limbo identifier.CW "top"refer to a new.CW "ref Tk->Toplevel"for use in later Tk commands:.P1 top := tk->toplevel(ctxt.screen, "-x 150 -y 150");.P2The upper left corner of this window will be at point (150, 150), where (0,0) is the upper left corner of the screen;.I xcoordinates increase from left to right, and.I ycoordinates increase from top to bottom..LPIn general,.CW "Tk->toplevel"takes a screen argument and a string containing further options, and it returns a reference to a top-level Limbo/Tk widget on the given screen.The options argument contains.Tk -option valuepairs, such as.CW "-relief raised" .As well as the generic options,.CW "toplevel"accepts the options.Tk -x intand.Tk -y intto specify the upper left corner of the toplevel widget, where (0,0) is the top left corner of the screen,and.Tk -debug boolto cause a trace of all Tk commands to be printed,if the boolean value is true..NH 2Creating a named channel to Tk.LPThe following fragment creates a.CW "chan of string"called.CW "c" ,then associates the name.CW "cmdchan"within Tk with the Limbo channel.CW "c" :.P1 c := chan of string; tk->namechan(top, c, "cmdchan");.P2The named channel.CW "cmdchan"can now be used in a special Tk.CW sendcommandto send strings to be processed by a Limbo program, typically notifying it of an event.Note that the Limbo identifer name need not match the name given to Tk,although it is invariably easier to follow the code if the two are the same..NH 2Defining and positioning widgets.LPThe following fragment uses.CW "tk->cmd"to define four widgets: two buttons, a label, and an entry widget. The widgets are positioned in their parent window (in this case the toplevel window.CW "top" )using the Tk command.CW "pack" :.P1.ps -1.vs -1 \fI# define widgets\fP tk->cmd(top, "button .b1 -text Exit -command {send cmdchan exit}"); tk->cmd(top, "button .b2 -text Send -command {send cmdchan send}"); tk->cmd(top, "label .l -text {Name: }"); tk->cmd(top, "entry .e"); \fI# bind newline character in entry widget to command\fP tk->cmd(top, "bind .e <Key-\en> {send cmdchan send}"); \fI# pack widgets\fP tk->cmd(top, "pack .b1 .b2 .l .e -side left; update");.ps +1.vs +1.P2This particular pack command packs the widgets named.CW ".b1" ,.CW ".b2" ,.CW ".l" ,and.CW ".e"into the.CW "top"window beginning at the left side. The.CW "update"command forces Tk to update the screen right away. The result is shown in Figure 1..FG "f1.ps" 0.21i.ce.I "Figure 1. Two buttons, a label and an entry widget.".fg.LPEntering a newline (`return' or `enter' key)\-the character.CW \en ' `in Limbo\-in the entry box results in the execution of the Tk command.CW "{send cmdchan send}" ,because of the binding set by.CW "bind .e <Key-\en>"previously executed by.CW tk->cmd .The.CW "bind"command is often used to bind specific widget events (including key presses, mouse button presses, and mouse motion) to Tk.CW sendcommands..NH 2Processing widget events.LPThis next fragment defines what will happen when a user selects either the.CW "Exit"or the.CW "Send"buttons. The.CW "Exit"behaviour is simple: the program ends. If a user touches.CW Send ,the program executes.CW "tk->cmd"to get whatever text is in the entry widget.CW ".e"then prints it to standard output..P1 for(;;) { s := <- c; case s { "exit" => return; "send" => sys->print("name was: %s\en", tk->cmd(top, ".e get")); } }.P2.NH 1Example - using Tk and Wmlib.LPThis section uses both Tk and Wmlib to create a simple window manager application with a titlebar, including resize and exit buttons.This is the usual way to create new windows..NH 1Preamble.LPThe example assumes that the.CW "Tk"module is loaded as before, as module variable.CW "tk" ,but furthermore that the.CW "Wmlib"module is also loaded, as.CW "wmlib" :.P1include "tk.m"; tk: Tk;include "wmlib.m"; wmlib: Wmlib; ... tk = load Tk Tk->PATH; wmlib = load Wmlib Wmlib->PATH; wmlib->init();.P2Note that.CW wmlib->initis called once to initialise the.CW wmlibmodule just loaded, before any other functions are called..LPIn window manager applications the.CW Tk->toplevelfunction is not normally used directly.Instead, a window manager interface is used to create boththe top level widget and a channel to receive events from the window manager.The.CW titlebarfunction has the signature:.ps -1.vs -1.P1 titlebar(scr: Draw->Screen, tkargs: string, title: string, buts: int): (ref Tk->Toplevel, chan of string);.P2.vs +1.ps +1The.CW Screenis the one on which the window is to be created, normally theone passed in the.CW Contextparameter to a program's.CW initfunction.The.I tkargsparameter can control the position and appearance of the window,but is best left nil (or the empty string) to use the window manager'sdefaults (see.I wmlib (2)for details otherwise),including automatic placement.The.I titlestring gives the title that appears in the title bar.Finally,.I butsis a bit set that selects the buttons to appear.The value.CW Wmlib->Applgives the usual resize and hide buttons;the exit (delete) button always appears.The following is used in the example:.P1 (top, titlechan) := wmlib->titlebar(ctxt.screen, nil, "Text Browser", Wmlib->Appl);.P2Note that.CW titlebarreturns a tuple.The first element is a reference to the Tk top level widgetfor use in later Tk commands.The second element of the tupleis a Limbo channel of type.CW "chan of string"that passes window manager events to the application..LPThe channel.CW "titlechan"is used by.CW "wmlib"to send messages, but it is normally necessaryto create a channel to Tk to receive events from widgets the application creates:.P1 cmdchan := chan of string; tk->namechan(top, cmdchan, "cmdchan");.P2.NH 2Defining and positioning widgets.LPThe function.CW "Wmlib->tkcmds"takes two arguments, a.CW "ref Tk->Toplevel"that identifies a top level window,and an.CW "array of string" .Each element of the array is a Tk command acceptable to.CW "Tk->cmd" ;.CW Wmlib->tkcmdssimply applies itto each element of the array..LPMost of the following fragment consists of Tk command strings that are members of the array of strings.CW "tk_config" .The comments describe the widgets being created. Not all widgets and menu items in this example are functional.The last line executes the array of commands using.CW "wmlib->tkcmds" :.nr dP +1.nr dV +1p.P1 tk_config := array[] of { \fI# define menubar frame, widget frame, text frame\fP "frame .mbar -relief groove -bd 2", "frame .w", "frame .text", \fI# define and pack menus\fP "menubutton .file -text File -menu .file.m", "menubutton .edit -text Edit -menu .edit.m", "menubutton .help -text Help -menu .help.m", "menu .file.m", ".file.m add command -label Send -command {send cmdchan send}", ".file.m add command -label Exit -command {send cmdchan exit}", "menu .edit.m", ".edit.m add command -label Cut", "menu .help.m", ".help.m add command -label Index -underline 0", "pack .file .edit -side left -in .mbar; update", "pack .help -side right -in .mbar",.P3 \fI# define and pack buttons and text entry box (for file name)\fP "button .b1 -text Send -command {send cmdchan send}", "button .b2 -text Open -command {send cmdchan open}", "label .l -text {Name: }", "entry .e", "bind .e <Key-\en> {send cmdchan open}", "pack .b1 .b2 .l .e -side left -in .w",.P3 \fI# define and pack text panel and its scrollbar\fP "text .t -yscrollcommand {.scroll set} -bg white", "scrollbar .scroll -command {.t yview}", "pack .scroll -side left -fill y -in .text", "pack .t -side right -in .text -expand 1 -fill both", \fI# pack frames\fP "pack .text -side bottom -fill both -expand 1", "pack .mbar .w -fill x; update", "pack propagate . 0", }; \fI# run the Tk commands\fP wmlib->tkcmds(top, tk_config);.P2.nr dP -1.nr dV -1pThe result of executing these Tk commands is shown in Figure 2..LPThe arrays defining Tk widgets are sometimes made global to a modulewhen they can sensibly be used by several functions.It is also common to use small Limbo functions to replicatesimilar widgets by building Tk commands from the value of parameters,using Limbo string concatenationor.CW sys->sprint ..FG "f2.ps" 2.16i.ce.I "Figure 2. A Wm application with a menu bar, a tool bar, and a text window.".fg.LP.NH 2Processing widget events.LPThis fragment uses an.CW "alt"block to wait for strings to arrive from either.CW "titlechan"orLines 1–500 of 1248. Load more
.nr dT 4 .nr P1 .2i .de EX .nr x \\$1v \\!h0c n \\nx 0 .. .nr Pd \n(PD .de FG \" start figure caption: .FG filename.ps verticalsize .KF .BP \\$1 \\$2 .sp .5v .EX \\$2v .ps -1 .vs -1 .. .de fg \" end figure caption (yes, it is clumsy) .ps .vs .br .KE .. .de Tk \" Tk option name and option .CW \\$1 .I \\$2 \\$3 .. .de Tc \" Tk command string .IP "\\f(CW\\$1\\fP \\$2" \w'\f(CWburble\fP'u .br .. .de Kp \" Keypress .IP "\\f(CW\\$1\\fP" \w'\f(CWKeypress\fP'u .br .. .de d0 \" no gap between paragraphs .nr PD 0 .. .de d1 \" restore gap .nr PD \n(Pd .. .ds Op "\fR[\fP\fIoptions ...\fP\fR]\fP .TL An Overview of Limbo/Tk .AU .I "Lucent Technologies Inc." .br Revised June 2000 by Vita Nuova .SH Overview .LP Limbo/Tk is a concise and powerful way to construct graphical user interfaces without directly using the Draw module primitives. Standard interfaces can quickly be created from collections of menus, buttons, and other widgets that are part of Limbo/Tk's visual toolkit. It is modelled on Ousterhout's Tk 4.0 [1,2], commonly deployed with the scripting language Tcl as `Tcl/Tk'. Although inspired by Tk 4.0, Inferno's Tk implementation is new, and unrelated to Ousterhout's. It is intended to be used with the new programming language Limbo, not Tcl. Limbo/Tk applications make extensive use of Limbo's concurrent programming constructions and data structures, and that is reflected in the interface. Section 9 of Volume 1 of the .I "Inferno Programmer's Manual" provides a detailed reference for Limbo/Tk. This paper provides an overview of its use in some simple staged examples, and concludes with a summary of the differences between the Limbo/Tk implementation and Tk 4.0. In the rest of this paper, `Tk' refers to Limbo/Tk, and `Tk 4.0' refers to Ousterhout's original implementation. .NH 1 The Limbo/Tk environment .LP Limbo applications access Tk by means of a built-in module, .CW "$Tk" . The standard distribution also includes the window manager .CW "wm" and the Limbo module .CW Wmlib . Unlike .CW Tk , .CW Wmlib is not built-in but implemented in Limbo by .CW /appl/lib/wmlib.b . It simplifies and standardises the construction of windowed applications; it also contains some graphical devices such as tabbed notebooks not provided directly by Limbo/Tk. The essentials of both .CW Tk and .CW Wmlib are discussed here. .LP Programmers usually need only three functions from the .CW Tk module: .IP • .CW toplevel .br Creates a graphical window \- a Tk `top level' widget \- that can be used to build a Limbo/Tk application. The function returns a reference to an adt .CW "\%Tk->Toplevel" adt that represents the window in subsequent Tk operations. .IP • .CW cmd .br Creates and arranges graphic objects within the toplevel window by processing Limbo/Tk command strings. The interface to Tk is primarily the passing of strings between the application and Tk of the toolkit using .CW cmd . Each call to .CW cmd returns a string representing the result of the Tk command; a string starting with .CW ! ' ` diagnoses an error. .IP • .CW namechan .br Gives a name within Tk (in the scope of a given window) to a Limbo .CW "chan of string" that Tk commands can use to send data to a Limbo program. .LP Other functions in the module have more specialised uses that will not be discussed here. For instance, .CW "mouse" and .CW "keyboard" are used by a window manager to send mouse and keyboard events to the Tk implementation for distribution to applications. .LP Even .CW toplevel is not commonly used in the window manager environment: a function .CW Wmlib->titlebar provides the usual interface to .CW toplevel . The low-level interface will be described first, for completeness, then the normal case using .CW titlebar . .NH 1 Basic Limbo/Tk .LP This section shows a simple Tk application that uses only the fundamental Tk functions. .NH 2 Preliminaries .LP The example assumes that the .CW "Tk" module is loaded as .CW "tk" : .P1 include "tk.m"; tk: Tk; ... init(ctxt: ref Draw->Context, nil: list of string) { tk = load Tk Tk->PATH; ... } .P2 .NH 2 Creating a toplevel .LP The following fragment makes the Limbo identifier .CW "top" refer to a new .CW "ref Tk->Toplevel" for use in later Tk commands: .P1 top := tk->toplevel(ctxt.screen, "-x 150 -y 150"); .P2 The upper left corner of this window will be at point (150, 150), where (0,0) is the upper left corner of the screen; .I x coordinates increase from left to right, and .I y coordinates increase from top to bottom. .LP In general, .CW "Tk->toplevel" takes a screen argument and a string containing further options, and it returns a reference to a top-level Limbo/Tk widget on the given screen. The options argument contains .Tk -option value pairs, such as .CW "-relief raised" . As well as the generic options, .CW "toplevel" accepts the options .Tk -x int and .Tk -y int to specify the upper left corner of the toplevel widget, where (0,0) is the top left corner of the screen, and .Tk -debug bool to cause a trace of all Tk commands to be printed, if the boolean value is true. .NH 2 Creating a named channel to Tk .LP The following fragment creates a .CW "chan of string" called .CW "c" , then associates the name .CW "cmdchan" within Tk with the Limbo channel .CW "c" : .P1 c := chan of string; tk->namechan(top, c, "cmdchan"); .P2 The named channel .CW "cmdchan" can now be used in a special Tk .CW send command to send strings to be processed by a Limbo program, typically notifying it of an event. Note that the Limbo identifer name need not match the name given to Tk, although it is invariably easier to follow the code if the two are the same. .NH 2 Defining and positioning widgets .LP The following fragment uses .CW "tk->cmd" to define four widgets: two buttons, a label, and an entry widget. The widgets are positioned in their parent window (in this case the toplevel window .CW "top" ) using the Tk command .CW "pack" : .P1 .ps -1 .vs -1 \fI# define widgets\fP tk->cmd(top, "button .b1 -text Exit -command {send cmdchan exit}"); tk->cmd(top, "button .b2 -text Send -command {send cmdchan send}"); tk->cmd(top, "label .l -text {Name: }"); tk->cmd(top, "entry .e"); \fI# bind newline character in entry widget to command\fP tk->cmd(top, "bind .e <Key-\en> {send cmdchan send}"); \fI# pack widgets\fP tk->cmd(top, "pack .b1 .b2 .l .e -side left; update"); .ps +1 .vs +1 .P2 This particular pack command packs the widgets named .CW ".b1" , .CW ".b2" , .CW ".l" , and .CW ".e" into the .CW "top" window beginning at the left side. The .CW "update" command forces Tk to update the screen right away. The result is shown in Figure 1. .FG "f1.ps" 0.21i .ce .I "Figure 1. Two buttons, a label and an entry widget." .fg .LP Entering a newline (`return' or `enter' key)\-the character .CW \en ' ` in Limbo\- in the entry box results in the execution of the Tk command .CW "{send cmdchan send}" , because of the binding set by .CW "bind .e <Key-\en>" previously executed by .CW tk->cmd . The .CW "bind" command is often used to bind specific widget events (including key presses, mouse button presses, and mouse motion) to Tk .CW send commands. .NH 2 Processing widget events .LP This next fragment defines what will happen when a user selects either the .CW "Exit" or the .CW "Send" buttons. The .CW "Exit" behaviour is simple: the program ends. If a user touches .CW Send , the program executes .CW "tk->cmd" to get whatever text is in the entry widget .CW ".e" then prints it to standard output. .P1 for(;;) { s := <- c; case s { "exit" => return; "send" => sys->print("name was: %s\en", tk->cmd(top, ".e get")); } } .P2 .NH 1 Example - using Tk and Wmlib .LP This section uses both Tk and Wmlib to create a simple window manager application with a titlebar, including resize and exit buttons. This is the usual way to create new windows. .NH 1 Preamble .LP The example assumes that the .CW "Tk" module is loaded as before, as module variable .CW "tk" , but furthermore that the .CW "Wmlib" module is also loaded, as .CW "wmlib" : .P1 include "tk.m"; tk: Tk; include "wmlib.m"; wmlib: Wmlib; ... tk = load Tk Tk->PATH; wmlib = load Wmlib Wmlib->PATH; wmlib->init(); .P2 Note that .CW wmlib->init is called once to initialise the .CW wmlib module just loaded, before any other functions are called. .LP In window manager applications the .CW Tk->toplevel function is not normally used directly. Instead, a window manager interface is used to create both the top level widget and a channel to receive events from the window manager. The .CW titlebar function has the signature: .ps -1 .vs -1 .P1 titlebar(scr: Draw->Screen, tkargs: string, title: string, buts: int): (ref Tk->Toplevel, chan of string); .P2 .vs +1 .ps +1 The .CW Screen is the one on which the window is to be created, normally the one passed in the .CW Context parameter to a program's .CW init function. The .I tkargs parameter can control the position and appearance of the window, but is best left nil (or the empty string) to use the window manager's defaults (see .I wmlib (2) for details otherwise), including automatic placement. The .I title string gives the title that appears in the title bar. Finally, .I buts is a bit set that selects the buttons to appear. The value .CW Wmlib->Appl gives the usual resize and hide buttons; the exit (delete) button always appears. The following is used in the example: .P1 (top, titlechan) := wmlib->titlebar(ctxt.screen, nil, "Text Browser", Wmlib->Appl); .P2 Note that .CW titlebar returns a tuple. The first element is a reference to the Tk top level widget for use in later Tk commands. The second element of the tuple is a Limbo channel of type .CW "chan of string" that passes window manager events to the application. .LP The channel .CW "titlechan" is used by .CW "wmlib" to send messages, but it is normally necessary to create a channel to Tk to receive events from widgets the application creates: .P1 cmdchan := chan of string; tk->namechan(top, cmdchan, "cmdchan"); .P2 .NH 2 Defining and positioning widgets .LP The function .CW "Wmlib->tkcmds" takes two arguments, a .CW "ref Tk->Toplevel" that identifies a top level window, and an .CW "array of string" . Each element of the array is a Tk command acceptable to .CW "Tk->cmd" ; .CW Wmlib->tkcmds simply applies it to each element of the array. .LP Most of the following fragment consists of Tk command strings that are members of the array of strings .CW "tk_config" . The comments describe the widgets being created. Not all widgets and menu items in this example are functional. The last line executes the array of commands using .CW "wmlib->tkcmds" : .nr dP +1 .nr dV +1p .P1 tk_config := array[] of { \fI# define menubar frame, widget frame, text frame\fP "frame .mbar -relief groove -bd 2", "frame .w", "frame .text", \fI# define and pack menus\fP "menubutton .file -text File -menu .file.m", "menubutton .edit -text Edit -menu .edit.m", "menubutton .help -text Help -menu .help.m", "menu .file.m", ".file.m add command -label Send -command {send cmdchan send}", ".file.m add command -label Exit -command {send cmdchan exit}", "menu .edit.m", ".edit.m add command -label Cut", "menu .help.m", ".help.m add command -label Index -underline 0", "pack .file .edit -side left -in .mbar; update", "pack .help -side right -in .mbar", .P3 \fI# define and pack buttons and text entry box (for file name)\fP "button .b1 -text Send -command {send cmdchan send}", "button .b2 -text Open -command {send cmdchan open}", "label .l -text {Name: }", "entry .e", "bind .e <Key-\en> {send cmdchan open}", "pack .b1 .b2 .l .e -side left -in .w", .P3 \fI# define and pack text panel and its scrollbar\fP "text .t -yscrollcommand {.scroll set} -bg white", "scrollbar .scroll -command {.t yview}", "pack .scroll -side left -fill y -in .text", "pack .t -side right -in .text -expand 1 -fill both", \fI# pack frames\fP "pack .text -side bottom -fill both -expand 1", "pack .mbar .w -fill x; update", "pack propagate . 0", }; \fI# run the Tk commands\fP wmlib->tkcmds(top, tk_config); .P2 .nr dP -1 .nr dV -1p The result of executing these Tk commands is shown in Figure 2. .LP The arrays defining Tk widgets are sometimes made global to a module when they can sensibly be used by several functions. It is also common to use small Limbo functions to replicate similar widgets by building Tk commands from the value of parameters, using Limbo string concatenation or .CW sys->sprint . .FG "f2.ps" 2.16i .ce .I "Figure 2. A Wm application with a menu bar, a tool bar, and a text window." .fg .LP .NH 2 Processing widget events .LP This fragment uses an .CW "alt" block to wait for strings to arrive from either .CW "titlechan" or