Chapter 8

Application Examples

This chapter contains small examples showing varying aspects and usages of the viola language and toolkit.


8.1 Geometry Management Examples

See chapter 2.


8.2 A Text Field List

Application listing (txtLs.v):

\name {txtLs} \class {vpane} \width {200} \height {150} \children {txtLs.top txtLs.view} \ \name {txtLs.top} \class {txtLsabel} \parent {txtLs} \label {This is a list} \maxHeight {20} \ \name {txtLs.view} \class {hpane} \parent {txtLs} \children {txtLs.view.sb txtLs.view.tf} \ \name {txtLs.view.sb} \class {slider} \parent {txtLs.view} \shownNotify {txtLs.view.tf} \maxWidth {20} \ \name {txtLs.view.tf} \parent {txtLs.view} \class {txtEdit} \shownDepend {txtLs.view.sb} \content {Line one Line two Line three Line four Line five Line six } \font {normal} \verbatim {1} \

Objects parent/children relationship:

Windows:

The windows after geometry management/formatting:


8.3 A RGB Color Setter

A little app that changes the X root window color to either red, green, or blue.

Application listing (rgb.v):

\class {vpane} \name {rgb} \children {rgb.label rgb.switches} \width {200} \height {40} \ \class {hpane} \name {rgb.label} \parent {rgb} \children {rgb.label.r rgb.label.g rgb.label.b} \ \class {txtDisp} \name {rgb.label.r} \parent {rgb.label} \content {Red} \ \class {txtDisp} \name {rgb.label.g} \parent {rgb.label} \content {Green} \ \class {txtDisp} \name {rgb.label.b} \parent {rgb.label} \content {Blue} \ \class {hpane} \name {rgb.switches} \parent {rgb} \children {rgb.switches.r rgb.switches.g rgb.switches.b} \script { switch (arg[0]) { case "justMe": objectListSend("children", "toggleTo", 0); send(arg[1], "toggleTo", 1); return; break; } usual(); } \ \class {radio} \name {rgb.switches.r} \parent {rgb.switches} \content {Red} \script { switch (arg[0]) { case "toggleTo": if (arg[1] == 1) system("xsetroot -solid red"); break; } usual(); } \ \class {radio} \name {rgb.switches.g} \parent {rgb.switches} \content {Green} \script { switch (arg[0]) { case "toggleTo": if (arg[1] == 1) system("xsetroot -solid green"); break; } usual(); } \ \class {radio} \name {rgb.switches.b} \parent {rgb.switches} \content {Blue} \script { switch (arg[0]) { case "toggleTo": if (arg[1] == 1) system("xsetroot -solid blue"); break; } usual(); } \

Objects parent/children relationship:

The windows:

And after geometry formatting, you'd get the GUI shown above.


8.4 An Inter-Process Communications Example

Using the pseudo TTY facility

This application starts up a process (vmstat), and whenever that process prints out text, that text is displayed on a text field.

The TTY class is a way for viola to run another process, read from its stdout, and also send to its stdin.

\name {demoTTY} \class {txtDisp} \script { switch (arg[0]) { case "newData": insert(arg[1], '\n'); return; break; } usual(); } \width {600} \height {200} \ \class {TTY} \name {demoTTY_tty} \path {/usr/ucb/vmstat} \args {3} \script { /* executes "/usr/ucb/vmstat 3" and pipes its output to * the "demoTTY" text field object for displaying. */ switch (arg[0]) { case "input": /* We're hereby notified that some data is available * for us to read. So, get the input, and relay it to * a text field for rendering */ send("demoTTY", "newData", input()); return; break; case "init": usual(); /* Set the input delimeter string to say that we expect * to be notified of incoming data only if that text * stream matches a newline ("\n"). */ set("inDelimStr1", "\n"); startClient(); /* start the external process */ return; break; } usual(); } \

Using the pseudo TTY facility

This is a little application that connects to a socket on a machine that is possibliy remote. The app reads data from the socket and renders the data in a scrolling graph.

Thusly, one could extend a static HTML document to contain, say, a continuously updating ticker-tape type applet.

The example shown here is extrememly simple, but one can see how this mechanism opens up a great number of applications which need to continuously read and present information from a data stream. Such applications might be a CPU activity monitor, stock market monitor, internet relay chat type communication application, etc.

Here's the listing of the monitor app.

\name {monitor2} \children {monitor2.graph monitor2.txt monitor2.socket} \class {vpane} \script { switch (arg[0]) { case "graph": return send(nthChild(0), arg[0], arg[1]); break; case "txt": return send(nthChild(1), arg[0], arg[1]); break; } usual(); } \width {200} \height {200} \ \name {monitor2.graph} \parent {monitor2} \class {field} \script { switch (arg[0]) { case "graph": y = arg[1] / 100.0 * hh; drawLine(1, y, 1, hh); copyArea(0, 0, ww, hh, 1, 0); return; break; case "config": usual(); clearWindow(); ww = get("width"); hh = get("height"); return; break; } usual(); } \ \name {monitor2.txt} \parent {monitor2} \children {monitor2.txt.tf monitor2.txt.sb} \class {hpane} \script { switch (arg[0]) { case "txt": send(nthChild(0), arg[0], arg[1]); return; break; } usual(); } \ \name {monitor2.txt.tf} \parent {monitor2.txt} \class {txtEdit} \shownDepend {monitor2.txt.sb} \script { switch (arg[0]) { case "txt": if (configed) { insert(arg[1]); insert('\n'); } return; break; case "config": configed = 1;/* a kludge, to be fixed */ break; } usual(); } \ \name {monitor2.txt.sb} \parent {monitor2.txt} \class {slider} \shownNotify {monitor2.txt.tf} \maxWidth {20} \ \name {monitor2.socket} \parent {monitor2} \class {socket} \host {some.where.com} \port {7777} \script { /* sym share price * * NOTE: when initiated, this object wil try to connect to * socket 7777 on the 'host' machine (fictitious machine in * this listing!... replace "some.where.com" with the name * of the machine which you'll run the ticker program liste * below. */ switch (arg[0]) { case "input": data = input(); send(parent(), "graph", int(nthWord(data, 3))); send(parent(), "txt", data); return; break; case "init": usual(); set("outDelimStr", "\r\n"); set("inDelimStr1", '\n'); startClient(); return; break; } usual(); } \maxHeight {1} \

It gets its data from a socket, so let's just make a simple daemon that does nothing but spew out random data.

#include <stdio.h> #include <math.h> int abs(x) { if (x < 0) return -x; return x; } void main() { srandom(time(0)); for (;;) { fprintf(stdout, "%c%c%c %d %d\n", (int)'a' + (abs(random()) % 26), (int)'a' + (abs(random()) % 26), (int)'a' + (abs(random()) % 26), 100 * (abs(random()) % 100), abs(random()) % 100); fflush(stdout); sleep(1); } }

Compile it and install this program via inetd. To do so, copy ticker to /etc/ticker. Insert this line into /etc/services:

ticker 7777/tcp And this line into /etc/inetd.conf: ticker stream tcp nowait nobody /usr/etc/ticker ticker

Restart inetd.... [XXX should replace this root-requiring method with a simpler example... using perl script or something easier to setup.]


8.5 A Clock

\name {clock} \class {field} \script { switch (arg[0]) { case "tick": /* Get the time info */ date = date(); second = int(nthWord(date, 6)); minute = int(nthWord(date, 5)); hour = int(nthWord(date, 4)); if (hour >= 12) hour--; /* Calculate the angle of the hands in degrees */ secondD = (second / 60.0 * 360.0) - 90.0; minuteD = (minute / 60.0 * 360.0) - 90.0; hourD = (hour / 12.0 * 360.0) - 90.0 + (minute / 60.0 * 30.0); /* Calculate the end points of the hands */ secondX = secondR * cos(secondD) + centerX; secondY = secondR * sin(secondD) + centerY; minuteX = minuteR * cos(minuteD) + centerX; minuteY = minuteR * sin(minuteD) + centerY; hourX = hourR * cos(hourD) + centerX; hourY = hourR * sin(hourD) + centerY; /* Redraw the clock only if the minute has changed. * The second hand is drawn by using inverted pixels, * so that it's easy to "undraw" the second hand. */ if (lminuteX != minuteX) { clearWindow(); send("clock", "render"); /* brutally redraw */ drawLine(centerX, centerY, minuteX, minuteY); drawLine(centerX, centerY, hourX, hourY); invertLine(centerX, centerY, lsecondX, lsecondY); } invertLine(centerX, centerY, lsecondX, lsecondY); invertLine(centerX, centerY, secondX, secondY); lsecondX = secondX; lsecondY = secondY; lminuteX = minuteX; lminuteY = minuteY; lhourX = hourX; lhourY = hourY; /* Let's do this again soon */ after(1000, "clock", "tick"); return; break; case "render": /* Draw the hour marks */ for (i = 1; i <= 12; i++) { x = letterR * cos((i / 12.0 * 360) - 90) + centerX - 5; y = letterR * sin((i / 12.0 * 360) - 90) + centerY - 5; drawText(x, y, 0/*fixed font*/, i); } return; break; case "expose": clearWindow(); lminuteX = 0; /* forces redrawing */ lhourX = 0; /* forces redrawing */ break; case "config": usual(); send(self(), "resize", arg[3], arg[4]); return; break; case "resize": if (arg[1] < arg[2]) radius = arg[1] / 2.0; else radius = arg[2] / 2.0; centerX = arg[1] / 2.0; centerY = arg[2] / 2.0; secondR = radius * 0.95; minuteR = radius * 0.9; hourR = radius * 0.6; letterR = (radius - 9) * 0.94; /* Initial call back, to start the endless loop */ after(2000, "clock", "tick"); lminuteX = 0; break; } usual(); } \width {150} \height {150} \
GOTO Preface, Previous Chapter (7), Next Chapter (9)