Chapter 8
Application Examples
- Geometry management example
- Text field list
- RGB color setter
- Inter-process communications example
- Clock
This chapter contains small examples showing varying aspects and
usages of the viola language and toolkit.
8.1 Geometry Management Examples
- Geometry management class (vpane)
- Text field (txtDisp)
- Text button (txtButton)
See chapter 2.
8.2 A Text Field List
- Geometry management
- Text field
- Slider
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
- Radio button (radio)
- Text field (txtDisp)
- System method.
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
#include
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)