File: ET.doc

package info (click to toggle)
et 80b2-8
  • links: PTS
  • area: main
  • in suites: potato, slink
  • size: 1,068 kB
  • ctags: 1,074
  • sloc: ansic: 10,313; tcl: 2,633; makefile: 179; sh: 19
file content (1947 lines) | stat: -rw-r--r-- 71,588 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
<title>An Introduction To Embedded Tk</title>
<h1>Embedded Tk</h1>

<p>
If you've ever tried to build a large-scale, compute-intensive, or
commercial application using Tcl/Tk, you probably had a difficult
time of it.
A pure Tcl/Tk script is terrific for writing small programs
or for prototyping, but it is often 
inadequate for really big problems.
This is due to several factors:

<ul>
<li>  Execution speed is usually too slow for serious computation.
<li>  Complex data structures are difficult to implement in Tcl.
<li>  The lack of structure and typing in the Tcl language complicates the
      development of large codes.
<li>  Tcl/Tk source is easily read by the end user,
      making it hard to protect proprietary algorithms.
<li>  A script will run only on machines where the correct version
      of Tcl/Tk has been installed.  This makes scripts more difficult
      to distribute.
</ul>

The usual way to avoid these troubles is to code
in C or C++ rather than Tcl/Tk.
C is fast and well-structured.
Compiled C code is difficult for users to read.
And statically-linked C programs will run
on any binary-compatible computer, independent of other software.
</p>

<p>
But programming a graphical user interface in pure C is time-consuming and
error-prone.
The job can be made somewhat easier by
using Tcl/Tk's C interface, and having your C program call
the Tcl/Tk library routines directly.
Many people have done this, some successfully.
The task is still tough, though, because 
unlike its scripting language, Tcl/Tk's C interface is not easy to use.
Properly initializing the Tcl/Tk interpreter takes skill and
finesse, and calling the interpreter from C is a dull chore.
</p>

<p>
And so the problem remains:  Do you go for the speed and structure
of C or the power and simplicity of Tcl/Tk?
</p>

<p>
The Embedded Tk system (hereafter <quote>ET</quote>) was
created to resolve this conundrum.
ET is a simple preprocessor and small interface library that make it
easy to mix Tcl/Tk and C in the same program.
With ET, you can put a few commands of 
Tcl/Tk code in the middle of a C routine.
ET also makes it very easy to write C functions that work as
new Tcl/Tk commands -- effectively allowing you to put pieces of
C code in the middle of your Tcl script.
These features gives you
the speed and structure of C with the power and simplicity of Tcl/Tk.
As an added benefit,
an application written using ET will compile into a stand-alone
executable that
will run on any binary-compatible computer, even
if the other computer doesn't have Tcl/Tk installed.

<h1 tag=helloworld>An Example: ``Hello, World!''</h1>

<p>
The ET system is designed to be easy to use.
To see this, let's look at the classic <quote>Hello, World!</quote>
program, coded using ET.

<pre>
    void main(int argc, char **argv){
      Et_Init(&amp;argc,argv);
      ET( button .b -text {Hello, World!} -command exit; pack .b );
      Et_MainLoop();
    }
</pre>

If you compile and link these 5 lines, you'll get
a stand-alone executable that pops up a 
<quote>Hello, World!</quote>
button, and goes away when the button is clicked.
</p>

<p>
Let's take this program apart to see how it works.
The first thing it does is call the <func>Et_Init</func> procedure.
This procedure performs the tedious and confusing work needed
to start up the Tcl/Tk interpreter, initialize widget bindings,
create the main window <quote>.</quote>, and so forth.
The last line is a call to another procedure <func>Et_MainLoop</func>
that implements the event loop.
(If you don't know what an event loop is, don't worry.
We'll have more to say about event loops in section <ref tag=eventloop>.)
The most interesting part of the example is the middle line, the
one that looks like a call to a function named <func>ET</func>.
The <func>ET</func> function is special.
It looks and is used like a regular C function, but takes a Tcl/Tk
script as its argument instead of a C expression.
Its function is to execute the enclosed Tcl/Tk.
In this particular example, the <func>ET</func> function
creates the <quote>Hello, World!</quote> button.
</p>

<p>
Because of the <func>ET</func> function, we can't give the
<quote>Hello, World!</quote> source code directly to a C compiler
and expect it to work.
We have to run it through a preprocessor first.  Like this:
<pre>
    et2c hello.c &gt; hello_.c
</pre>
The <cmd>et2c</cmd> preprocessor converts the <func>ET</func>
function into real, compilable C code.
The preprocessor also takes care of some
other housekeeping details, like adding prototypes to the top of the
file so that we don't have to bother with 
a <code>#include</code>.
After it has been preprocessed, the source code can be compiled like
any other C program.
<pre>
    cc -O -o hello hello_.c et.o -ltk -ltcl -lXll -lm
</pre>
Notice that you must link the program with 
ET's <filename>et.o</filename>
library file, and with libraries for Tcl/Tk and X11.
(See section <ref tag=macandwin> for instructions on building
applications for Microsoft-Windows or Macintosh.)
</p>

<p>
And that's all there is too it!
</p>

<h1>How To Avoid Reading The Rest Of This Article</h1>

<p>
If you're restless to start programming and are the type of
person who prefers to learn at the keyboard rather than from a
book, this section is for you.
It contains a terse overview of the features of ET.
Peruse this section, glance quickly at the examples, and you'll
be ready to start coding.
You can use the rest of the article as a reference guide when you run
into trouble.
</p>

<p>
On the other hand, if you are new to graphical interface programming,
are a little unsteady with C, or just have a more deliberate and
cautious attitude toward life, then you may prefer to lightly skim
or even skip this section and focus instead on the
tutorial-like text that follows.
</p>

<p>
The ET system consists of two things: the <cmd>et2c</cmd>
preprocessor and the <filename>et.o</filename> library.
The preprocessor takes care of translating ET source code
(which looks a whole lot like C) into genuine C code that
your compiler will understand.
The <filename>et.o</filename> library contains a few support 
routines.
</p>

<p>
Among the support routines in <filename>et.o</filename> are
<func>Et_Init</func> and <func>Et_MainLoop</func>
for initializing the ET package and implement the event loop, respectively.
A third routine, <func>Et_ReadStdin</func>,
allows standard input
to be read and interpreted by the Tcl/Tk interpreter at run-time.
The <filename>et.o</filename> library
defines three global C variables as a convenience.
<code>Et_Interp</code> is a pointer to the Tcl/Tk interpreter used by ET.
<code>Et_MainWindow</code> is the main window.
<code>Et_Display</code> is the <code>Display</code> pointer required
as the first argument to many Xlib routines.
ET also provides two global Tcl variables, <code>cmd_name</code> and
<code>cmd_dir</code>.  These contain the name of the executable and the
directory where the executable is found.
</p>

<p>
The <cmd>et2c</cmd> preprocessor is used to convert an ET source
file into real C code.
It creates the illusion of giving the C language some new statements,
like <code>ET_INSTALL_COMMANDS</code> and <code>ET_PROC</code> and
some special new functions like <func>ET</func>.
</p>

<p>
The <func>ET</func> function is used as if it were
a regular C function, except that its argument is a
Tcl/Tk script.
The job of the <func>ET</func> is to execute the script.
<func>ET</func> returns either <code>ET_OK</code>
or <code>ET_ERROR</code>
depending upon whether the script suceeded or failed.
Similar routines <func>ET_STR</func>, <func>ET_INT</func>, and
<func>ET_DBL</func> also take a Tcl/Tk script
as their argument, but return the string, the integer,
or the double-precision floating point number
that was the result of the last Tcl/Tk command in the argument script.
</p>

<p>
Wherever the string <code>%d(x)</code> occurs inside an
<func>ET</func> function, the integer C expression <code>x</code>
is converted to ASCII and substituted in place of the
<code>%d(x)</code>.
Similarly, <code>%s(x)</code> can be used to substitute
a character string, and <code>%f(x)</code> substitutes
a floating point value.
The string <code>%q(x)</code> works like <code>%s(x)</code> except
that a backslash is inserted before each character
that has special meaning to Tcl/Tk.
</p>

<p>
The special construct
<code>ET_PROC( newcmd ){...}</code> defines a
C function that is invoked whenever the <code>newcmd</code>
Tcl/Tk command is executed.
Formal parameters to this function, <code>argc</code> and <code>argv</code>,
describe the arguments to the command.
The formal parameter <code>interp</code> is a pointer to the
Tcl/Tk interpreter.
If a file named <filename>aux.c</filename> contains one or more
<code>ET_PROC</code> macros, the commands associated with
those macros are registered with the Tcl/Tk interpreter by
invoking <code>ET_INSTALL_COMMANDS(aux.c)</code> after
the <func>Et_Init</func> in the
main procedure.
</p>

<p>
The statement <code>ET_INCLUDE( script.tcl )</code>
causes the Tcl/Tk
script in the file <code>script.tcl</code> to be made a part of the
C program and executed at the point where
the <func>ET_INCLUDE</func> macro is found.
The external Tcl/Tk script is normally read into the C program
at compile-time and thus becomes part of the executable.
However, if the <cmd>-dynamic</cmd> 
command-line option is given to the
<cmd>et2c</cmd> preprocessor, loading of the external 
Tcl/Tk script is deferred to run-time.
</p>

<p>
Finally, at the top of its output files,
the <cmd>et2c</cmd> preprocessor inserts
<code>#define</code>s that
make <code>ET_OK</code> and <code>ET_ERROR</code>
equivalent to
<code>TCL_OK</code> and <code>TCL_ERROR</code>.  This often 
eliminates the need
to put <quote><code>#include &lt;tcl.h&gt</code></quote>
at the beginning of files that use ET.
</p>

<p>
And that's everything in ET!  All the rest is just detail.
</p>

<h1 tag=eventloop>A Quick Review Of Event Driven Programs</h1>

<p>
Before we delve into the details of ET, it may
be helpful to review the concept of an <em>event loop</em> and an
<em>event-driven program</em>.
Many ET users have never before written an
event-driven graphical user interface (GUI)
and may be unfamiliar with how such programs operate.
If you are such a user, you may profit from this quick review.
But if you are already familiar with event-driven programs, feel free
skip ahead to section <ref tag=basicstruct>.
</p>

<p>
The only inputs to a GUI are <quote>events.</quote>
An event is a notification that something interesting has happened.
Events arrive whenever the mouse moves, or a mouse button is
pressed or released, or a key of the keyboard is pressed,
and so forth.
A event-driven GUI differs from more familiar command-line 
programs in that its inputs (e.g. events) do
not arrived in any predictable sequence.
Any kind of events can arrive at any time, and the GUI program
must be prepared to deal with them.
</p>

<p>
The code for an event-driven GUI can be divided into two parts: 
the <em>initialization</em> code and the <em>event loop</em>.
The initialization code runs first and does nothing more than
allocate and initialize the internal data structures of the application.
As soon as the initialization code completes, the application enters
the event loop.
Within the event loop, the program waits for the next event to
arrive, reads the event, and processes it appropriately.
The loop then repeats.
The event loop does not exit until the program terminates.
</p>

<p>
This is a schematic view of a typical GUI program:
<pre>
   main(){
      /* Initialization code */
      while( /* More work to do */ ){
        /* Wait for the next event to arrive */
        /* Read the next event */
        /* Take appropriate action for the event just read */
      }
   }
</pre>
Don't worry about the details here.
Most of the event loop processing is handled automatically by
Tcl/Tk and ET.
The important things to know are that the event loop exists, it
runs after the initialization code, 
and that it doesn't terminate until the program exits.
</p>

<p>
If you've never written an event-driven program before, and you are
like most people, then you will have a little trouble at first.
To help you get started, here are some important points to 
remember:

<ol>
<li><strong>The initialization code does not interact with the user.</strong>

<p>
The initialization code does only one thing -- initialize.
It creates the main windows of the application (but it doesn't
draw the windows -- that happens in the event loop!) and it
sets up internal data structures.  But the initialization code
should never wait for input or respond to an event.
Waiting and reading inputs and responding to events should happen
only in the event loop.
</p>

<li><strong>All user-initiated processing occurs in callbacks.</strong>

<p>
Everything that a GUI program does is in response to some
event.  Any C procedure or Tcl/Tk command that is called in
response to an event is referred to as a <em>callback</em>.
Because all inputs to a GUI program are in the form of events,
the only place for user-initiated processing to occur is within
the callback routines.
</p>

<li><strong>Don't let a callback compute for more than
            a fraction of a second.</strong>
<p>
A callback should do its job quickly and then return.
Otherwise, the event loop will not be able to respond to new
events as they arrive, 
and the program will appear to <quote>hang</quote>.
If you have a callback that needs to execute for more than a
few hundred milliseconds, you should either invoke the
<quote>update idletasks</quote> Tcl/Tk command periodically within
the callback, or you should break the callback's calculations
up into several separate routines that can be invoked by
separate events.
</p>

<li><strong>Don't leak memory.</strong>
<p>
Once started, GUI programs tend to run for a long time -- hours, days,
weeks or even months.  Hence, you should take special care to avoid
<em>memory leaks</em>.  A memory leak occurs when you allocate a
chunk of memory from the heap using <func>malloc</func> but don't
return that memory to the heap using <func>free</func> when you are
done with it.
Because the memory was not released by <func>free</func> it can never
be reused.
When this happens, the amount of memory required by your application
will constantly increase, until at some point it will consume all
memory available, and then die.
Memory leaks are probably the most common bug in GUI programs (which is
why I mention them.)
</p>
</ol>

<h1 tag=basicstruct>The Main Body Of An ET Application</h1>

<p>
The <func>main</func> routines for ET applications all look
pretty much alike.
Here's a template:

<pre>
  void main(int argc, char **argv){
    Et_Init(&amp;argc,argv);   /* Start the Tcl/Tk interpreter */
    /* Create new Tcl/Tk commands here */
    /* Initialize data structures here */
    /* Create windows for the application here */
    Et_MainLoop();         /* The event loop */
  }
</pre>

When you need to write an ET application, but you aren't
sure where to begin, this template is a good starting point.
Type in the above
template and make sure you can successfully compile and run it.
(The program that results from compiling the template creates a
blank window that doesn't respond to any mouse or keyboard
inputs.  Its the equivalent of <quote><cmd>wish /dev/null</cmd></quote>.)
After you get the template running,
slowly begin adding bits of code, recompiling and testing
as you go, until you have a complete application.
</p>

<p>
Let's take a closer look at each line of the
template, so that you can better understand what is going on.
</p>

<p>
The first line of <func>main</func>
is a call to the <func>Et_Init</func> procedure.
The <func>Et_Init</func> procedure initializes the ET
system and the Tcl/Tk interpreter.
It must be called before any other ET function or statement.
The parameters are the <code>argc</code>
and <code>argv</code> formal parameters of <func>main</func>.
<func>Et_Init</func> uses these parameters to look for command-line
options.
ET currently understands four:

<ul>
<li><cmd>-display</cmd> designates the X server to use.
    The value of this
    option will override your <cmd>DISPLAY</cmd> environment variable.
<li><cmd>-name</cmd> changes the application name for the program.
    By default, the application name is same as the the filename 
    of the executable itself.
    The application name is used to derive the Tcl/Tk
    interpreter name for use with the Tcl/Tk <code>send</code> command.
    The application name is also used for processing X11 resources, 
    and as the default text on the application's title bar.
<li><cmd>-geometry</cmd> changes the starting size and/or position
    of the program.
<li><cmd>-sync</cmd> turns on synchronous mode in the X server.
    This makes the program run a lot slower, but is sometimes useful
    when debugging.  It is very rarely used.
</ul>
</p>

<p>
Notice the <quote><code>&amp;</code></quote> before the <code>argc</code>
parameter to <func>Et_Init</func>.
The number of command line arguments is passed to <func>Et_Init</func>
by address, not by value.
This is so <func>Et_Init</func> can change the value of <code>argc</code>.
Whenever <func>Et_Init</func> sees one of the above command-line options,
it removes that option from the option list in <code>argc</code>
and <code>argv</code>.
Hence, after <func>Et_Init</func> returns, only application-specific
command line options remain.
</p>

<p>
For example, suppose you invoke an ET program like this:
<pre>
    myapp -quiet -display stego:0 file1.data
</pre>
The values of <code>argc</code> and <code>argv</code>
passed into the <func>Et_Init</func> function are:
<pre>
    argc = 5
    argv = { "myapp", "-quiet", "-display", "stego:0", "file1.data", 0 }
</pre>
The <func>Et_Init</func> function will see the <cmd>-display stego:0</cmd>
part and act upon it accordingly.  It will then remove those fields
from the argument list, so that after <func>Et_Init</func> returns,
the values are these:
<pre>
    argc = 3
    argv = { "myapp", "-quiet", "file1.data", 0 }
</pre>
In this way, the initialization code that follows <func>Et_Init</func>
never sees the ET-specific command line arguments.
</p>

<p>
After the <func>Et_Init</func> procedure comes the initialization
code.
Normally, you begin the initialization by creating and registering
all the new Tcl/Tk commands you will need.
The details are described in section <ref tag=etproc>.
Basically it involves replacing the comment in the template with
one or more <code>ET_INSTALL_COMMANDS</code> statements.
Once you've created the new Tcl/Tk commands, you may need to
construct internal C data structures, or create linkages between
C variables and Tcl variables using Tcl's <func>Tcl_LinkVar</func>
function.
Command-line options that haven't been removed by 
<func>Et_Init</func> are often processed here, as well.
Finally, you will probably want to create the initial windows for
the application.
The <func>ET</func> function (see section <ref tag=et>) 
and <func>ET_INCLUDE</func> procedure (see section <ref tag=etinclude>)
are both good for this.
</p>

<p>
Of course, this is only a suggested outline of how to initialize
your application.
You should feel free to do something different if your program
requires it.
The only ground rule is that the initialization code shouldn't
try to interact with the user.
Instead, use callback routines to respond to user inputs.
</p>

<p>
The last line of <func>main</func> is a call to the
<func>Et_MainLoop</func> procedure.
<func>Et_MainLoop</func> implements the event loop.
It will not return until the program is ready to exit.
</p>

<h1 tag=etproc>Writing Tcl/Tk Routines In C</h1>

<p>
One of the first things people tend to do with ET is
create new Tcl/Tk commands, written in C, that do computations that
are either too slow or impossible with a pure Tcl.
This is a two-step process.
First you have to write the C code using the <code>ET_PROC</code>
construct.
Then you have to register your new Tcl/Tk command with the Tcl/Tk
interpreter using the <code>ET_INSTALL_COMMANDS</code> statement.
We will consider each of these steps in turn.
</p>

<h2>The Decimal Clock Sample Program</h2>

<p>
To help illustrate the concepts, this section introduces a
new sample program: the <em>decimal clock</em>.
The decimal clock displays the current time of day as a decimal
number of hours. For instance,
8:30am displays as <quote><code>8.500</code></quote>.
11:15pm shows as <quote><code>23.250</code></quote>.
And so forth.
A screen shot of this program is shown
in figure <ref tag=decimalclock>.
</p>

<image gif=dclock.gif eps=dclock.ps tag=decimalclock
  caption="Typical appearance of the decimal clock">

<p>
We'll begin by looking at the main procedure for the decimal
clock program.
<pre>
  void main(int argc, char **argv){
    Et_Init(&argc, argv);
    ET_INSTALL_COMMANDS;
    ET(
      label .x -bd 2 -relief raised -width 7
      pack .x
      proc Update {} {
        .x config -text [DecimalTime]
        after 3600 Update
      }
      Update
    );
    Et_MainLoop();
  }
</pre>
As you can see, the main procedure is just a copy of the
program template from section <ref tag=basicstruct>, with some
of the comments replaced by actual initialization code.
The first initialization action is to invoke the special ET statement
<code>ET_INSTALL_COMMANDS</code>.  Don't worry about what this
does just yet -- we'll return to it a little later.
The second initialization action is a single
<func>ET</func> function containing 7 lines of Tcl/Tk.
This Tcl/Tk code does three things:

<ul>
<li> It creates a label widget in which to show the decimal time.
<li> It creates a new Tcl/Tk procedure named <code>Update</code> that
     updates the label widget to show the current time, and then arranges
     to call itself again after 0.001 hours (3.6 seconds).
<li> It invokes the <code>Update</code> procedure once in order to
     initialize the text of the label widget, and to start the
     periodic updates.
</ul>

Like all well-behaved ET programs, the main procedure for the
decimal clock concludes by entering the event loop.
</p>

<h2>The ET_PROC Statement</h2>

<p>
The core of the decimal clock program is a new Tcl/Tk command,
<code>DecimalTime</code>, that returns the current time of day
as a decimal number of hours.
This new command is written in C, using the special
<code>ET_PROC</code> construct of ET.  The code looks like this:

<pre>
  #include "tcl.h"
  #include &lt;time.h&gt;

  ET_PROC( DecimalTime ){
    struct tm *pTime;  /* The time of day decoded */
    time_t now;        /* Number of seconds since the epoch */

    now = time(0);
    pTime = localtime(&now);
    sprintf(interp->result,"%2d.%03d",pTime->tm_hour,
      (pTime->tm_sec + 60*pTime->tm_min)*10/36);
    return ET_OK;
  }
</pre>

The magic is in the <code>ET_PROC</code> keyword.
The <cmd>et2c</code> preprocessor recognizes this keyword and
converts the code that follows it into a compilable C function that
implements the Tcl/Tk command.
In general, you can create new Tcl/Tk commands using a
template like this:

<pre>
  ET_PROC( name-of-the-new-command ){
    /* C code to implement the command */
  }
</pre>

You could, of course, construct approprate C functions by hand, but 
that involves writing a bunch of messy details that detract
from the legibility of the code.
The <code>ET_PROC</code> mechanism is much easier to write and
understand, and much less subject to error.
</p>

<p>
Though they do not appear explicitly in the source code, every
function created using <code>ET_PROC</code> has four formal parameters.
</p>

<ul>
<li><strong>argc</strong>
<p>
This parameter is an integer that holds the number of arguments 
on the Tcl command that invokes the function.
Its role is exactly the same as the <code>argc</code> parameter to
the <func>main</func> function of a standard C program.
</p>
<li><strong>argv</strong>
<p>
Like <code>argc</code> before it, this parameter works just like the
<code>argv</code> parameter to <func>main</func>.
The variable <code>argv[0]</code> contains the name of the the
command itself (<quote><code>DecimalTime</code></quote> in this
example), <code>argv[1]</code> contains the name of the first
argument, <code>argv[2]</code> contains the name of the second
argument, and so forth up to <code>argv[argc]</code> which is a
null pointer.
</p>
<li><strong>interp</strong>
<p>
This parameter is a pointer to the Tcl/Tk interpreter.
It has type <quote><code>Tcl_Interp*</code></quote>.
The <code>interp</code> 
parameter has many uses, but is most often used to set the
return value of the Tcl/Tk function.
(Note that you have to <code>#include</code> either 
<code>&lt;tcl.h&gt</code> or
<code>&lt;tk.h&gt</code> somewhere in your
source file in order to use the <code>interp</code> parameter, since
one of these header files are needed to define the fields of the
<code>Tcl_Interp</code> structure.)
</p>
<li><strong>clientData</strong>
<p>
This is a pointer to the <code>Tk_Window</code> structure that defines
the main window (e.g. the <quote>.</quote> window) of the application.
It has a type of <quote><code>void*</code></quote> and will need to be
typecast before it is used.
On the other hand, it is seldom used, so this isn't normally a problem.
</p>
</ul>

<p>
The decimal clock example uses the <code>interp</code>
formal parameter on the sixth line of the <code>ET_PROC</code>
function.
In particular, the <code>DecimalTime</code> function writes its
result (e.g. the time as a decimal number) into the <code>result</code>
field of <code>interp</code>.
It's OK to write up to about 200 bytes of text into
the <code>result</code> field of the <code>interp</code> parameter,
and that text will become the return value of the Tcl/Tk
command.
If you need to return more than about 200 bytes of text, then
you should set the result using one of the routines from the
Tcl library designed for that purpose:
<func>Tcl_SetResult</func>,
<func>Tcl_AppendResult</func>, or 
<func>Tcl_AppendElement</func>.
(These routines are documented by Tcl's manual pages
under the name <quote>SetResult</quote>.)
If all this seems too complicated, then you can choose to
do nothing at all, in which case
the return value  defaults to an empty string.
</p>

<p>
Another important feature of every <code>ET_PROC</code> function is
its return value.
Every <code>ET_PROC</code> should return either <code>ET_OK</code>
or <code>ET_ERROR</code>, depending on whether or not the
function encountered any errors.
(<code>ET_OK</code> and <code>ET_ERROR</code> are <code>#define</code>
constants
inserted by <cmd>et2c</code> and have the save values as
<code>TCL_OK</code> and <code>TCL_ERROR</code>.)
It is impossible for the <code>DecimalClock</code> function to fail,
so it always returns <code>ET_OK</code>, but most <code>ET_PROC</code>
functions can return either result.
</p>

<p>
Part of Tcl's <em>result protocol</em> is that if a command
returns <code>ET_ERROR</code> it should put an error message in
the <code>interp-&gt;result</code> field.
If we had wanted to be pedantic, we could have put a test in the
<code>DecimalTime</code> function to make sure it is called with
no arguments.
Like this:
<pre>
  ET_PROC( DecimalTime ){
    struct tm *pTime;  /* The time of day decoded */
    time_t now;        /* Number of seconds since the epoch */

    if( argc!=1 ){
      Tcl_AppendResult(interp,"The ",argv[0],
        " command should have no argument!",0);
      return ET_ERROR;
    } 
    /* The rest of the code is omitted ... */
  }
</pre>
New Tcl/Tk commands that take a fixed format normally
need to have some checks like this, to make sure they aren't called
with too many or too few arguments.
</p>

<h2>The ET_INSTALL_COMMANDS statement</h2>

<p>
We've seen how
the <code>ET_PROC</code> constuct will <em>create</em> a new
Tcl/Tk command.
But that command must still be <em>registered</em>
with the Tcl interpreter before it can be used.
Fortunately, ET makes this very easy.
</p>

<p>
ET uses the <code>ET_INSTALL_COMMANDS</code> keyword to register
<code>ET_PROC</code> commands with the Tcl interpreter.
The <cmd>et2c</cmd> preprocessor converts the 
<code>ET_INSTALL_COMMANDS</code> keyword into a sequence of C
instructions that register every <code>ET_PROC</code>
in the current file.
In the <func>main</func> procedure of the
decimal clock example, the <code>ET_INSTALL_COMMANDS</code>
keyword that immediately follows the <func>Et_Init</func> function
is used to register the <code>DecimalTime</code> command.
As it turns out, <code>DecimalTime</code> is the only <code>ET_PROC</code>
function in the same source file, but even if there had be 100
others, they would have all been registered by that single
<code>ET_INSTALL_COMMANDS</code> statement.
</p>

<p>
The <code>ET_INSTALL_COMMANDS</code> keyword can also be used to
register <code>ET_PROC</code> functions in separate source files,
simply by putting the name of the source file in 
parentheses after the <code>ET_INSTALL_COMMANDS</code> keyword.
Like this:
<pre>
   ET_INSTALL_COMMANDS( otherfile.c );
</pre>
A larger program will typically have many <code>ET_INSTALL_COMMANDS</code>
statements immediately following the <func>Et_Init</func> function,
one statement for each file that contains <code>ET_PROC</code>
functions.
One recent commercial project used 33
<code>ET_INSTALL_COMMANDS</code> statements following the
<func>Et_Init</func> function!
</p>

<h2>Summary Of Writing Tcl/Tk Commands In C</h2>

<p>
Before leaving this section,
let's briefly summarize the steps needed to create new Tcl/Tk commands
in C using ET.
First you create one or more commands using the <code>ET_PROC</code> 
construct, as follows:
<pre>
  ET_PROC( name-of-the-new-command ){
    /* C code to implement the command */
    return ET_OK;  /* Don't forget the return value! */
  }
</pre>
Then, you register these commands with the Tcl interpreter using
an <code>ET_INSTALL_COMMANDS</code> statement after the
<func>Et_Init</func> function call within <func>main</func>.
Like this:
<pre>
  ET_INSTALL_COMMANDS( name-of-file-containing-ET_PROCs.c );
</pre>
And that's all you have to do!
</p>

<p>
The <code>ET_PROC</code> construct lets you
put a C routine in the middle of Tcl/Tk.
The next section will take a closer look at <func>ET</func> which
allows you to put Tcl/Tk in the middle of a C routine.
</p>

<h1 tag=et>The ET() Function And Its Siblings</h1>

<p>
If you've been keeping up with the examples, you've already seen
the <func>ET</func> function used twice to insert a few lines of
Tcl/Tk in the middle of a C procedure.
But the <func>ET</func> function can do a lot more, as this
section will show.
</p>

<h2>Moving Information From Tcl/Tk To C</h2>

<p>
The first thing to note about <func>ET</func> is that, just like
a real C function, it has a return value.
<func>ET</func> returns an integer status code which is either
<code>ET_OK</code> or <code>ET_ERROR</code> depending on whether the
enclosed Tcl/Tk was successful or failed.
(<func>ET</func> might also return 
<code>TCL_RETURN</code>, <code>TCL_BREAK</code>,
or <code>TCL_CONTINUE</code> under rare circumstances.)
</p>

<p>
The status return of <func>ET</func> is nice, but in practice it
turns out to be mostly useless.
What you really need is the string value returned by the enclosed Tcl/Tk
script.
That's the purpose of the <func>ET_STR</func> function.
</p>

<p>
The <func>ET_STR</func> function works a lot like <func>ET</func>.
You put in a Tcl/Tk script as the argument, and the script gets executed.
But instead of returning a status code, <func>ET_STR</func> returns a
pointer to a string that was the result of the last Tcl/Tk command
in its argument.
</p>

<p>
The <func>ET_STR</func> function turns out to be a very handy mechanism
for querying values from Tcl/Tk.
For instance, suppose your program has an entry widget named
<quote><code>.entry</code></quote> and some piece of C code needs
to know the current contents of the entry.
You can write this:
<pre>
  char *entryText = ET_STR(.entry get);
</pre>
Or imagine that you need to know the current size and position of
your main window.
You might use code like this:
<pre>
  int width, height, x, y;
  sscanf(ET_STR(wm geometry .),"%dx%d+%d+%d",&width,&height,&x,&y);
</pre>
Does your C routine need to know the value of a Tcl variable?
You could use the cumbersome <func>Tcl_GetVar</func> function,
but it's much easier to say:
<pre>
  char *zCustomerName = ET_STR(set CustomerName);
</pre>
Possible uses for <func>ET_STR</func> seem limitless.
</p>

<p>
But, there are two subtleties with <func>ET_STR</func> that programmers
should always keep in mind.
The first is that the Tcl/Tk script in the argument is executed at
Tcl's global variable context level.
This means that all of the Tcl/Tk variables <func>ET_STR</func>
creates, and the only Tcl/Tk variables it can access, are global
variables.
This limitation also applies to the regular <func>ET</func> function, and to
two other function we haven't talked about yet: 
<func>ET_INT</func> and <func>ET_DBL</func>.
ET provides no means for C code to access or modify local variables.
On the other hand, this has not proven to be a serious hardship in
practice.
</p>

<p>
The second subtlety with <func>ET_STR</func> is more dangerous, but
fortunately applies to <func>ET_STR</func> only.
Recall that <func>ET_STR</func> returns a <em>pointer</em> to a string,
not the string itself.
The string actually resides in memory that is held deep within the
bowels of Tcl/Tk.
The danger is that the next Tcl/Tk command may choose
to change, deallocate, or reuse this memory, corrupting the value
returned by <func>ET_STR</func>.
We say that the return value of <func>ET_STR</func> is
<quote>ephemeral.</quote>
</p>

<p>
One way to overcome the ephemerality of <func>ET_STR</func>
is by making a copy of the returned string.
The <func>strdup</func> function is good for this.
(Unfortunately, <func>strdup</func> is missing from a lot of C libraries.
You may have to write your own string duplicator.)
In place of the examples given above, you might write
<pre>
  char *entryText = strdup( ET_STR(.entry get) );
</pre>
or
<pre>
  char *zCustomerName = strdup( ET_STR(set CustomerName) );
</pre>
The <func>strdup</func> function uses <func>malloc</func> to get the
memory it needs, so if you use this approach, 
be sure to <func>free</func> the value when you
are done to avoid a memory leak!
</p>

<p>
The other way to overcome the ephemerality of <func>ET_STR</func>
is simply not to use the returned string for very long.
You should be safe in using the returned string as long as you don't
invoke any other Tcl/Tk commands, or return to the event loop.
Code like this
<pre>
  sscanf(ET_STR(wm geometry .),"%dx%d+%d+%d",&width,&height,&x,&y);
</pre>
is OK since we need the return value only for the duration of
the <func>sscanf</func> function and <func>sscanf</func> doesn't
use Tcl/Tk.
</p>

<p>
In addition to <func>ET</func> and <func>ET_STR</func>, the
ET system provides two other functions named <func>ET_INT</func>
and <func>ET_DBL</func>.
Both take a Tcl/Tk script for their argument, as you would expect.
But <func>ET_INT</func> returns an integer result and
<func>ET_DBL</func> returns a floating-point value (a 
<code>double</code>).
In a sense, these two functions are extensions of <func>ET_STR</func>.
In fact, <func>ET_INT</func> does essentially the same thing as
<pre>
   int v = strtol( ET_STR(...), 0, 0);
</pre>
and <func>ET_DBL</func> is equivalent to
<pre>
   double r = strtod( ET_STR(...), 0);
</pre>
Because <func>ET_INT</func> and <func>ET_DBL</func> return a value, not
a pointer, their results are not ephemeral nor subject to the
problems that can come up with <func>ET_STR</func>.
</p>

<h2>Moving Information From C To Tcl/Tk</h2>

<p>
We've seen how <func>ET_STR</func>, <func>ET_INT</func> and
<func>ET_DBL</func> can be used to pass values from Tcl/Tk back
to C.
But how do you go the other way and transfer C variable values
into Tcl/Tk?
ET has a mechanism to accomplish this too, of course.
</p>

<p>
Within the argument to any <func>ET</func> function (or
<func>ET_STR</func> or <func>ET_INT</func> or <func>ET_DBL</func>),
the string <quote><code>%d(x)</code></quote> is special.
When ET sees such a string, it evalutes the integer C expression
<quote><code>x</code></quote>, converts the resulting integer into
decimal, and substitutes the integer's decimal value for the original
string.
For example, suppose you want to initialize the Tcl/Tk variable
named <code>nPayment</code> to be twelve times the value of a C
variable called <code>nYear</code>.
You might write the following code:
<pre>
    ET( set nPayment %d(12*nYear) );
</pre>
As another example, suppose you want to draw a circle
on the canvas <code>.cnvs</code> centered at (x,y) with radius r.
You could say:
<pre>
    id = ET_INT( .cnvs create oval %d(x-r) %d(y-r) %d(x+r) %d(y+r) );
</pre>
Notice here how the <func>ET_INT</func> function was used to record
the integer object ID returned by the Tcl/Tk canvas <code>create</code>
command.
This allows us to later delete or modify the circle by
referring to its ID.
For example, to change the fill color of the circle, we could execute
the following:
<pre>
    ET( .cnvs itemconfig %d(id) -fill skyblue );
</pre>
</p>

<p>
If you want to substitute a string or floating-point value into
an <func>ET</func> argument, you can use <code>%s(x)</code>
and <code>%f(x)</code> in place of <code>%d(x)</code>.
The names of these substitutions phrases were inspired by the
equivalent substitution tokens in the standard C library
function <func>printf</func>.
Note, however, that you cannot specify a field-width, precision, or
option flag in <func>ET</func> like you can in <func>printf</func>.
In other words, you can use conversions like
<code>%-10.3f</code> in <func>prinf</func> but not in <func>ET</func>.
The <func>ET</func> function will accepts only
specification, such as <code>%f</code>.
</p>

<p>
But the <func>ET</func> function does support a conversion specifier
that standard <func>printf</func> does not:
the <code>%q(x)</code> substitution.
The <code>%q</code> works like <code>%s</code>
in that it expects its argument to be a null-terminated string,
but unlike <code>%s</code> the <code>%q</code> converter
inserts extra backslash characters into the string in order
to escape characters that have special meaning to Tcl/Tk.
Consider an example.
<pre>
  char *s = "The price is $1.45";
  ET( puts "%q(s)" );
</pre>
Because <code>%q(s)</code> was used instead of 
<code>%s(s)</code>, an extra backslash
is inserted immediately before the <quote><code>$</code></quote>.
The command string passed to the Tcl/Tk interpreter is therefore:
<pre>
  puts "The price is \$1.45"
</pre>
This gives the expected result.  Without the extra backslash, Tcl/Tk would
have tried to expand <quote><code>$1</code></quote> as a variable, resulting in an error message like this: 
<pre>
  can't read "1": no such variable
</pre>
In general, it is always a good idea to use 
<code>%q(...)</code> instead of <code>%s(...)</code>
around strings that originate from outside the program--you never know
when such strings may contain a character that needs to be escaped.
</p>

<h2>Summary Of The ET() Function</h2>

<p>
And that's everything there is to know about the
<func>ET</func> function and its siblings.
In case you missed something amid all the details, here's a
10-second review of the essential facts:

<ul>
<li>The <func>ET</func> executes Tcl/Tk code and returns a 
    success/failure code.
<li><func>ET_STR</func>, <func>ET_INT</func> and <func>ET_DBL</func>
    do the same, but return a string, and integer or a double which
    was the result of the last Tcl/Tk command executed.
<li>The return value from <func>ET_STR</func> is ephemeral.
<li>The strings <code>%s(...)</code>, <code>%d(...)</code> and
    <code>%f(...)</code> insert string, integer and double C expressions
    into the argument of <func>ET</func> and its siblings.
<li>The string <code>%q(...)</code> works like <code>%s(...)</code>
    but adds backslashes before characters that are special to Tcl/Tk.
</ul>

Now lets move on and talk about a similar construct,
<func>ET_INCLUDE</func>, that allows you incorporate whole
files full of Tcl/Tk into your application.
</p>

<h1 tag=etinclude>Including External Tcl/Tk Scripts In A C Program</h1>

<p>
In the sample programs seen so far in this article, 
Tcl/Tk code in an
<func>ET</func> function was used to construct the main window.
This works fine for the examples, since their windows are
uncomplicated and can be constructed with a few lines code.
But in a real application, or even a more complex example,
the amount of Tcl/Tk code needed to initialize the program's windows
can quickly grow to hundreds or thousands of lines.
It is impractical and irksome to put this much code into an
<func>ET</func> statement, so the ET system provides another
way to get the job done: the <func>ET_INCLUDE</func> statement.
</p>

<p>
The <func>ET_INCLUDE</func> statement is similar in concept to the
<code>#include</code> statement in the C preprocessor.
Both take a filename as their argument, and both read the named
file into the original source program.
The <func>ET_INCLUDE</func> statement expects its file to
be pure Tcl/Tk code, though.
Its job is to turn the Tcl/Tk source into a form that the
C compiler can understand, and to arrange for the Tcl/Tk to
be executed when control reaches the <func>ET_INCLUDE</func>
statement.
</p>

<p>
An example may help to clarify this idea.
In the decimal clock program (way back
at the beginning of section <ref tag=etproc>), 
there are 7 lines of Tcl/Tk in an <func>ET</func> function used
to create the application's main window.
Now suppose we move those 7 lines of Tcl/Tk into a separate
file named <filename>dclock.tcl</filename>.
Then we could replace the <func>ET</func> function with an
<func>ET_INCLUDE</func> statement that references the new file
like this:

<pre>
  void main(int argc, char **argv){
    Et_Init(&argc, argv);
    ET_INSTALL_COMMANDS;
    ET_INCLUDE( dclock.tcl );
    Et_MainLoop();
  }
</pre>

When the <cmd>et2c</cmd> preprocessor sees the <func>ET_INCLUDE</func>
statement, it locates the specified file, reads that file into the
C program, and makes arrangements for the text of the file to be
executed as if it had all appeared within an <func>ET</func> function.
</p>

<p>
Well, <em>almost</em> like an <func>ET</func> function.
There are a couple of minor differences.
The <func>ET_INCLUDE</func> does not understand
the various <code>%s(...)</code> substitutions as <func>ET</func> does.
Also, <func>ET_INCLUDE</func> is a true procedure, not a function.
It doesn't return a value like <func>ET</func> so
you can't use an <func>ET_INCLUDE</func> in an expression.
</p>

<p>
It is important to understand the difference between an
<func>ET_INCLUDE</func> statement like this
<pre>
    ET_INCLUDE( dclock.tcl );
</pre>
and the <code>source</code> command of Tcl/Tk, used as follows:
<pre>
    ET( source dclock.tcl );
</pre>
The <func>ET_INCLUDE</func> statement reads the Tcl/Tk into the program at
compile-time, effectively making the Tcl/Tk code part of the executable.
The Tcl <code>source</code> command, on the other hand, opens and
reads the file at run-time, as the application executes.  This
makes the executable a little smaller, but it also means that the
file containing the Tcl/Tk must be available to the executable
whenever it runs.
If you move just the executable, but not the Tcl/Tk file, to
another computer, or even another directory, then it will no
longer work because it won't be able to locate and read the Tcl/Tk file.
</p>

<p>
The ability to read an external Tcl/Tk script and make it part of the
executable program is an important feature of ET.
But while you are developing and testing a program, it is sometimes 
convenient to turn this feature off and to have the application read
its scripts at run-time instead of compile-time.
That way, you can make changes to the Tcl/Tk script and rerun your
program with the changes, but without having to recompile.
You can do this using the <cmd>-dynamic</cmd> option to the
<cmd>et2c</cmd> proprocessor.
Whenever you run <cmd>et2c</cmd> with the <cmd>-dynamic</cmd> command-line
option, it effective turns instances of the statement
<pre>
    ET_INCLUDE( filename.tcl );
</pre>
into the statement
<pre>
    ET( source filename.tcl );
</pre>
This feature has proven very helpful during development.
But be careful to turn it off before doing your final build, or else
you won't be able to move your executable to other machines!
</p>

<p>
There is just one other feature of the <func>ET_INCLUDE</func>
statement that we need to discuss before moving on, and that is
the algorithm it uses to locate the Tcl/Tk source code files.
Just like the C preprocessor's <code>#include</code> statement, the
<func>ET_INCLUDE</func> mechanism can include
files found in other directories.
</p>

<p>
The <cmd>et2c</cmd> preprocessor always looks first in the working
directory for files named by an <func>ET_INCLUDE</func> statement.
If the file is found there, no further search is made.
But if the file is not found, then <cmd>et2c</cmd> will also look
in all directories named in <cmd>-I</cmd> command line options.
For example, if you run <cmd>et2c</cmd> like this:
<pre>
    et2c -I../tcl -I/usr/local/lib/tcl app.c >app_.c
</pre>
and the <filename>app.c</filename> file contains a line of the form:
<pre>
    ET_INCLUDE( setup.tcl );
</pre>
then <cmd>et2c</cmd> will search for the <filename>setup.tcl</filename>
first in the <quote><filename>.</filename></quote> directory, then
in <filename>../tcl</filename> and in
<filename>/usr/local/lib/tcl</filename>.
It will use the first instance of <filename>setup.tcl</filename> that
it finds.
</p>

<h1 tag=globals>Global Variables In ET</h1>

<p>
The <filename>et.o</filename> library for ET defines three
global C variables that are sometimes of use to programmers.
In addition, <func>Et_Init</func> creates two new global
Tcl/Tk variables that many programs find useful.
This section will describe what all of these variables do, and
suggest ways that they can be used.
</p>

<h2>Global C Variables Created By ET</h2>

<p>
Perhaps the most useful of the global variables available in
ET is <code>Et_Interp</code>.
This variable is a pointer to the Tcl/Tk interpreter, the one
created by <func>Et_Init</func> and used to execute all Tcl/Tk
commands within the program.
The <code>Et_Interp</code> variable has the same value as the
<code>interp</code> formal parameter found in every <func>ET_PROC</func>
function.
</p>

<p>
The <code>Et_Interp</code> variable is useful because you may
often want to call C routines in the Tcl/Tk library, and most
of these routines require a pointer to the interpreter as their
first parameter.
For instance, suppose in the initialization code you want to create
a link between the global C variable <code>nClients</code> and a
Tcl/Tk variable by the same name.
Using the <code>Et_Interp</code> variable as the first parameter
to the Tcl function <func>Tcl_LinkVar</func>, you could write:
<pre>
   Tcl_LinkVar(Et_Interp,"nClients",(char*)&nClients,TCL_LINK_INT);
</pre>
Having done this, any changes to the C <code>nClients</code> variable
will be reflected in the Tcl/Tk variable, and vice versa.
</p>

<p>
Perhaps the second most useful global varible is <code>Et_Display</code>.
This variable contains the <code>Display</code> pointer required as
the first argument to most Xlib routines.
It is used by daring, down-to-the-bare-metal programmers who like to
call Xlib directly.
</p>

<p>
Here's an example.
Suppose you want to create a new Tcl/Tk command, 
<code>PitchedBell</code>, that makes
the X terminal emit a beep with a pitch specified by its sole argument.
Once such a command is implemented, then the following Tcl/Tk code
would emit a single tone at the pitch of concert A:
<pre>
   PitchedBell 440
</pre>
Here a short piece of Tcl/Tk code that plays the opening bar of
Beethoven's Fifth Symphony:
<pre>
   foreach pitch {784 784 784 659} {
      PitchedBell $pitch
      after 200
   }
</pre>
You probably get the idea.
Here's the code that implements the <code>PitchedBell</code> command:
<pre>
   #include &lt;tk.h>   /* Will also pickup &lt;Xlib.h> */

   ET_PROC( PitchedBell ){
      XKeyboardControl ctrl;   /* For changing the bell pitch */

      if( argc!=2 ){
        interp->result = 
          "Wrong # args.  Should be: ``PitchedBell PITCH''";
        return ET_ERROR;
      }
      ctrl.bell_pitch = atoi( argv[1] );
      XChangeKeyboardControl(Et_Display,KBBellPitch,&ctrl);
      XBell(Et_Display,0);
      XFlush(Et_Display);
      return ET_OK;
   }
</pre>
After checking to make sure it has exactly one argument, the
<code>PitchedBell</code> command uses the 
<func>XChangeKeyboardControl</func> 
function of Xlib to change the bell pitch.
It then rings the bell using the <func>XBell</func> Xlib function, and
finally flushes the Xlib message queue using <func>XFlush</func> to
force the bell to be rung immediately.
All three of these Xlib functions require a <code>Display</code>
pointer as their first argument, a role that is perfectly filled
by the <code>Et_Display</code> global variable.
</p>

<p>
The third and final global C variable in ET is <code>Et_MainWindow</code>.
This variable is a pointer to 
the Tcl/Tk structure that defines the application's
main window.
Back in the days of Tk3.6, several Tcl/Tk library functions
required this value as a parameter.
But the Tcl/Tk library interface changed in the move to Tk4.0, so that
the main window pointer is no longer required.
Hence, the <code>Et_MainWindow</code> variable isn't used much any more.
It has been kept around as an historical artifact.
</p>

<h2>Tcl/Tk Variables Created By ET</h2>

<p>
Besides the 3 global C variables, ET also provides two
Tcl/Tk variables that are of frequent use:
<code>cmd_name</code> and <code>cmd_dir</code>.
The <code>cmd_name</code> variable contains the name of the file
holding the executable for the application, and <code>cmd_dir</code>
is the name of the directory containing that file.
</p>

<p>
The <code>cmd_name</code> and <code>cmd_dir</code> variables are
useful to programs that need to read or write auxiliary data files.
In order to open an auxiliary file, the program needs to know the
files pathname, but it is not a good idea to hard-code a complete
pathname into the program.
Otherwise, the auxiliary file can't be moved without recompiling
the program.
By careful use of <code>cmd_name</code> and/or <code>cmd_dir</code>,
we can arrange to have auxiliary files located in a directory
relative to the executable, rather that at some fixed location.
That way, a system adminstrator is free to move the auxiliary file
to a different directory as long as the executable moves with it.
</p>

<p>
For example, suppose you are writing a program named
<filename>acctrec</filename> that needs to access a data file
named <filename>acctrec.db</filename>.
Furthermore, suppose the data file is located in a directory
<filename>../data</filename> relative to the executable.
Then to open the data file for reading, a program could
write:
<pre>
  char *fullName = ET_STR( return $cmd_dir/../data/$cmd_name.db );
  FILE *fp = fopen(fullName,"r");
</pre>
Using this scheme, both the executable and the datafile can be placed
anywhere in the filesystem, as long as they are in the same position
relative to one another.
They can also be renamed, so long as they retain the same base name.
This flexibility is a boon to system adminstraters, and also
make the program less sensitive to installation errors.
</p>

<h1 tag=readstdin>Reading From Standard Input</h1>

<p>
There's one last feature of ET that we haven't discussed:
the <func>Et_ReadStdin</func> procedure.
If this procedure is called (with no arguments) in between
the calls to <func>Et_Init</func> and <func>Et_MainLoop</func>,
ET will make arrangements to read all data that appears
on standard input and interpret that data as Tcl/Tk commands.
</p>

<p>
You can use the <func>Et_ReadStdin</func> to implement the
interactive <cmd>wish</cmd> interpreter for Tcl/Tk.
The code would look like this:
<pre>
  main(int argc, char **argv){
    Et_Init(&argc,argv);
    Et_ReadStdin();
    Et_MainLoop();
  }
</pre>
Let's call this program <cmd>etwish</cmd> in order to distinguish
it from the standard <cmd>wish</cmd> that comes with Tcl/tk.
The <cmd>etwish</cmd> program differs from <cmd>wish</cmd> in two ways.
First, <cmd>wish</cmd> reads a set of 15 or so Tcl/Tk scripts from
a well-known directory when it first starts up.
Thus, to install <cmd>wish</cmd>, you have to have both 
the <filename>wish</filename> executable 
and the 15 startup scripts.
But with <cmd>etwish</cmd>, the 15 startup scripts are compiled
into the executable (using <func>ET_INCLUDE</func> statements inside
the <func>Et_Init</func> function) so the external scripts are
no longer required.  This does make the <cmd>etwish</cmd> executable
slightly larger (by about 64K bytes), but it also makes the
program much easier to install and administer.
</p>

<p>
The second difference between <cmd>wish</cmd> and the <cmd>etwish</cmd>
program shown above is that <cmd>etwish</cmd> is always interactive.
It will not read a script from a file given as a command line argument
like standard <cmd>wish</cmd> will.
But we can remove that difference using a little more code.
<pre>
  main(int argc, char **argv){
    Et_Init(&argc,argv);
    if( argc>2 && (strcmp(argv[1],"-f")==0 || strcmp(argv[1],"-file")==0) ){
      ET( source "%q(argv[2])" );
    }else if( argc>1 ){
      ET( source "%q(argv[1])" );
    }else{
      Et_ReadStdin();
    }
    Et_MainLoop();
  }
</pre>
This revised program serves as a great template
for building customized editions of <cmd>wish</cmd> that have one
or more new Tcl/Tk commands written in C.
All you have to do is code the new commands using 
the <func>ET_PROC</func> mechanism and insert a single
<code>ET_INSTALL_COMMANDS</code> statement right after the
<func>Et_Init</func>.
</p>

<h1 tag=compiling>Compiling ET Applications</h1>

<p>
We've already discussed the basics of compiling ET applications
back in section <ref tag=helloworld> when we put together the
<quote>Hello, World!</quote> example.
Basically, all you do is preprocess your source files with <cmd>et2c</cmd>
and then run the results through the C compiler.
But that synopsis omits a lot of detail.
This section fills in the missing information.
</p>

<h2>Compiling ET Itself</h2>

<p>
But before we begin talking about how to compile ET applications,
we need to first mention how to compile ET itself --
the <cmd>et2c</cmd> preprocessor and the <filename>et.o</filename>
library.
(If you have one of the platforms supported by the
CD-ROM in the back of this book, then you already have precompiled versions
of <cmd>et2c</cmd> and <filename>et.o</filename> and
can skip this step.)
</p>

<p>
The source code to the <cmd>et2c</cmd> preprocessor is contained
in a single file named <cmd>et2c.c</cmd>.
The preprocessor is written in highly portable K&amp;R C and should
compile without change on just about any 32-bit architecture.
All you have to do is this:
<pre>
  cc -O -o et2c et2c.c
</pre>
</p>

<p>
Compiling the <filename>et.o</filename> library is a little more
problematic, but still not difficult.
There are three steps.
First you have to select an appropriate source code file.
There are different versions of the source code (sometimes radically
different) depending on which version of Tcl/Tk you are using.
For Tk version 3.6, choose <filename>et36.c</filename>.
For Tk version 4.0, choose <filename>et40.c</filename>.
For Tk version 4.1 on UNIX and X11, choose <filename>et41.c</filename>.
Your ET distribution may also have other options, such as versions for
MS-Windows or Macintosh, or versions with built-in support for
various Tcl extensions.
</p>

<p>
Let's suppose, for the sake of discussion, that you selected the
source file <filename>et41.c</filename>.
The next step is to preprocess this file using <cmd>et2c</cmd>.
This step is a little tricky because we have to use the 
<cmd>-I</cmd> option to <cmd>et2c</cmd> to tell
the preprocessor where to find the Tcl/Tk startup scripts.
</p>

<p>
Recall that the stardard Tcl/Tk interpreter program,
<cmd>wish</cmd>, reads and executes a series of Tcl/Tk scripts
when it first starts up.
These scripts set up default widget bindings, create procedures
for handling menus, and so forth.
The names of the directories from which these scripts are loaded are
hard-coded in the <cmd>wish</cmd> executable.
There are about 15 different startup scripts (the number
varies from one version of Tcl/Tk to the next) and <cmd>wish</cmd>
will not run without them.
</p>

<p>
But ET applications don't read the startup scripts at run-time.
Instead, a series of <func>ET_INCLUDE</func> statements inside
the <func>Et_Init</func> function bind the startup scripts into
an ET executable at compile-time.
This feature is what enables ET applications to run on machines that
do not have Tcl/Tk installed.
</p>

<p>
It is because of 15 or so startup scripts included by 
<func>ET_INCLUDE</func> statements in the
ET library that we have to preprocess the library source code
using <cmd>et2c</cmd>.
But we also have to tell <cmd>et2c</cmd> what directories to use
when searching for the startup scripts.
If Tcl/Tk has already been installed on your system, then you
can find out the names of the startup script directories by executing the
following <cmd>wish</cmd> script:
<pre>
  #! wish
  puts $tk_library
  puts $tcl_library
</pre>
Let's suppose that the startup scripts are located in the
directories <filename>/usr/local/lib/tcl</filename>
and <filename>/usr/local/lib/tk</filename>.
Then the command to preprocess the ET library source code
would be the following:
<pre>
  et2c -I/usr/local/lib/tcl -I/usr/local/lib/tk et41.c >et.c
</pre>
</p>

<p>
After preprocessing the library source code, all that remains
is to compile it.
The library references the <code>&lt;tk.h&gt</code> header file,
which in turn references <code>&lt;tcl.h&gt</code>, so you
may have to add some <cmd>-I</cmd> options to the compiler command
line to specify the directories where these header files are located.
The following is typical:
<pre>
  cc -c -o et.o -I/usr/include/tcl -I/usr/include/tk et.c
</pre>
</p>

<h2>Compiling The Application Code</h2>

<p>
Once you get <cmd>et2c</cmd> and <filename>et.o</filename> compiled,
the hard work is done.
To build your application, simply run each source file through
the <cmd>et2c</cmd> preprocessor before compiling it, and add
the <filename>et.o</filename> library with the final link.
For example, the steps to compile a program from two source files,
<filename>appmain.c</filename> and <filename>appaux.c</filename>,
are the something like following on most systems:
<pre>
   et2c appmain.c >temp.c
   cc -c temp.c -o appmain.o
   et2c appaux.c >temp.c
   cc -c temp.c -o appaux.o
   cc appmain.o appaux.o et.o -ltk -ltcl -lX11 -lm
</pre>
</p>

<p>
If you're using a Makefile, you might want to redefine the default
rule for converting C source code into object code to incorporate
the <cmd>et2c</cmd> preprocessor step.
Like this:
<pre>
    .c.o:
          et2c $< >temp.c
          cc -c -o $@ temp.c
</pre>
The <cmd>et2c</cmd> does not harm files that don't use ET constructs,
so this rule will work for every file in your project.
</p>

<h2>Turning Off Script Compression In The Preprocessor</h2>

<p>
The <cmd>et2c</cmd> preprocessor attempts to save memory and
improve performance of your application by removing comments and
unnecessary spaces from the Tcl/Tk code inside
<func>ET</func> functions and loaded by <func>ET_INCLUDE</func>
statements.
This mechanism works well most of the time, but it is not
foolproof.
It is theoretically possible for a valid Tcl/Tk script to be corrupted
by <cmd>et2c</cmd>'s compression attempts.
If you experience trouble, and suspect that
<cmd>et2c</cmd> is messing up your Tcl/Tk code, then you can
turn script compression off using the 
<cmd>-nocompress</cmd> command line option.
</p>

<h2>Compiling Using An Older K&amp;R Compiler</h2>

<p>
If it is your misfortune not to have an ANSI C compiler, you can
still use ET.
The source code to <code>et2c</code> is pure K&amp;R C and should work
fine under older compilers.
The source code to <filename>et.o</filename> is another matter.
To compile the library using an older compiler you need to
first give a <code>-K+R</code> option to <code>et2c</code> and then give
a <code>-DK_AND_R</code> option to the C compiler:
<pre>
  et2c -K+R -I/usr/local/lib/tcl -I/usr/local/lib/tk et40.c >et.c
  cc -DK_AND_R -I/usr/include/tcl -I/usr/include/tk -c et.c
</pre>
When compiling application code with an older compiler, just
give the <code>-K+R</code> option to <code>et2c</code>.
It is not necessary to give the <code>-DK_AND_R</code> option to the
C compiler when compiling objects other than <code>et.c</code>.
</p>

<h1 tag=examples>Other ET Sample Programs</h1>

<p>
Besides the very simple <quote>Hello, World!</quote> and
decimal clock programs presented above, ET is distributed
with a number of non-trivial sample programs.
This section will briefly overview what several of these
example programs do, and why ET was important to their implementation.
We won't try to explain the details of how the programs work, though.
You can figure that out for yourself by looking at the source code.
</p>

<h2>The Color Chooser</h2>

<p>
There is a color chooser tool for X11 called
<cmd>color</cmd>.
The sources to <cmd>color</cmd> are in the files
<filename>color.c</filename> and <filename>color.tcl</filename>.
A screen image of the program is shown in 
figure <ref tag=colorprog>.
</p>

<image gif=colorprog.gif eps=colorprog.eps width=4in tag=colorprog
  caption="Typical appearance of the color program">

<p>
The X11 Window System supports displays with over 280 quadrillion
distinct colors (48 bits per pixel).
But from this vast number, a few hundred colors are assigned
English names like <quote>blue</quote> or
<quote>turquoise</quote> or <quote>peachpuff</quote>.
All the rest are given arcane hexadecimal designations like
<quote>#b22cd8517f32</quote>.
It is best to use colors with English names
whenever possible.
</p>

<p>
The purpose of the <cmd>color</cmd> program it to help select
colors with English names.
At the top of the application is large swatch showing one of the
280 quadrillion X11 colors, together with either its English name
(if it has one) or its hexadecimal value.
Sliders on the lower left side of the window allow the user to vary
the color of the swatch by changing various color components.
On the lower right side of the window are six smaller swatches that
show colors with English names that are similar to the color in the
main swatch.
Moving any of the six color component sliders causes the colors in
all swatches, and the other sliders, to update in real time.
Clicking on any of the smaller swatches transfers its color to the
main swatch, updating all of the sliders and swatches appropriately.
</p>

<p>
In theory, there is nothing to prevent the <cmd>color</cmd> program
from being coded in pure Tcl/Tk, but in practice, such an
implementation would be much too slow.
For this reason, two key routines are coded in C.
The <code>ET_PROC</code> command <code>ChangeComponent</code> is
called whenever one of the color component sliders is moved.
This routine moves the other sliders, changes the color of the main
swatch, then computes close colors for the smaller swatches.
Another <code>ET_PROC</code> command named <code>ChangeColor</code>
is called whenever the user clicks on one of the smaller swatches.
This routine changes the color of the main swatch, then updates
the sliders and the smaller swatches accordingly.
</p>

<h2>The VT100 Terminal Emulator</h2>

<p>
The example named <cmd>tkterm</cmd> implements a VT100 terminal emulator.
The <cmd>tkterm</cmd> program can be used as a direct replacement
for the more familiar emulator programs <cmd>xterm</cmd> or <cmd>rxvt</cmd>.
</p>

<p>
The sources for <cmd>tkterm</cmd> are contained in three separate files.
The main procedure is in <filename>tkterm.c</filename>.
Tcl/Tk for constructing the main window for the application is in
<filename>tkterm.tcl</filename>.
Finally, the file <filename>getpty.c</filename> takes care of the
messy details of allocating a pseudo-TTY for the emulator and 
invoking a shell in the pseudo-TTY.
(Much of the code in <filename>getpty.c</filename> was copied from
<cmd>rxvt</cmd>.)
</p>

<p>
The <cmd>tkterm</cmd> program simulates the VT100 display using
an ordinary Tcl/Tk text widget.
C routines in <filename>tkterm.c</filename> interpret the characters
and escape sequences coming into the program and use <func>ET</func>
functions to insert characters into their proper
places within the text widget.
The <filename>tkterm.c</filename> file is almost 1000 lines long, and is
mostly devoted to interpreting the VT100 escape codes.
</p>

<p>
The <cmd>tkterm</cmd> program is an example of an application that could
not be coded in pure Tcl/Tk, since Tcl/Tk has no provisions for dealing
with pseudo-TTYs or TTYs in <quote>raw</quote> mode.
But even if it could, we would probably still want to use some C code,
since it seems unlikely that a Tcl/Tk script would be able to process
the VT100 escape sequences efficiently.
</p>

<h2>A Real-Time Performance Monitor For Linux</h2>

<p>
The <cmd>perfmon</cmd> program is a system performance monitor for
the Linux operating system.
It uses bar graphs to shows the amount of memory, swap space, CPU time
currently being used.
The display is updated 10 times per second.
There are two source code files for this application:
<filename>perfmon.c</filename> and <filename>perfmon.tcl</filename>.
</p>

<p>
The main display of the <cmd>perfmon</cmd> program is implemented using
a Tcl/Tk canvas widget.
But for efficiency's sake, the logic that computes the current memory,
swap space, and CPU usages is all coded in C.
The C code obtains the system performance data by reading the files
<filename>/proc/stat</filename> and <filename>/proc/meminfo</filename>.
It then processes this information into the desired preformance
measurements and makes appropriate changes to the Tcl/Tk bar graphs using
<func>ET</func> function calls.
</p>

<p>
On a 90MHz Pentium and with an update frequency of 10 times per
second, the <cmd>prefmon</cmd> program uses a negligible amount of
the CPU time.
So in addition to being a nifty desktop utility for a Linux workstation,
this example demonstrates that Tcl/Tk applications can be very efficient.
</p>

<h2>An ASCII Text Editor And A File Browser</h2>

<p>
The two programs <cmd>tkedit</cmd> and <cmd>browser</cmd> implement,
respectively, an ASCII text editor and a UNIX file browser utility.
Source code to these programs is in the files
<filename>tkedit.c</filename>, <filename>tkedit.tcl</filename>,
<filename>browser.c</filename> and <filename>browser.tcl</filename>.
</p>

<p>
Both of these programs could just as well have been implemented as
pure Tcl/Tk scripts, with no loss of features or performance.
(In fact, the browser can be used as pure script by invoking the
<filename>browser.tcl</filename> using <cmd>wish</cmd>.)
But, sometimes you want a program to be a real executable, not a
script.
For instance, you may want to be able to run the program on machines
that do not have Tcl/Tk installed.
Or, perhaps you want the programs to run on machines that have a
different, incompatible version of Tcl/Tk installed.
</p>

<p>
The <cmd>tkedit</cmd> and <cmd>browser</cmd> programs are examples of
how to convert a pure Tcl/Tk script into a stand-alone program using
ET.
The idea is very simple.
Your C code simply initializes ET, invokes your script using a
single <func>ET_INCLUDE</func> statement, and then enters the event
loop.
Like this:
<pre>
  void main(int argc, char **argv){
    Et_Init(&argc,argv);
    ET_INCLUDE( browser.tcl );
    Et_MainLoop();
  }
</pre>
Compiling this code results in a stand-alone application that can
be run on any binary-compatible machine.
</p>

<h1 tag=macandwin>Using ET To Build MS-Windows And Macintosh Applications</h1>

<p>
ET, like Tcl/Tk, was originally written to support the open X11 windowing
system only.
But nowadays, people often need to write applications for popular
proprietary windowing systems such as Windows95 or Macintosh.
Beginning with release 4.1, Tcl/Tk supports these proprietary products,
and so does ET.
(Actually, only Windows95 is supported as of this writing.
The author has no access to a Macintosh system on which to
develop and test a Macintosh port.)
</p>

<p>
On a Macintosh, ET applications that don't call Xlib directly should
compile with little or no change.
The Mac won't support the <func>Et_ReadStdin</func> routine, or the
<code>Et_Display</code> global variable, but then again, neither of
these make much sense on a Mac.
The application will compile in much the same way as it does for X11,
except that you should use the <filename>et41mac.c</filename> source
file to the <filename>et.o</filename> library.
</p>

<p>
More change is required to support Windows95, however.
The Windows version of ET doesn't contain either 
<func>Et_Init</func> or <func>Et_MainLoop</func>.
Instead these functions will be invoked automatically.
An ET program for Windows should contain a single <func>Et_Main</func>
procedure definition to do all its setup, and nothing more.
Hence, if your application used to look like this:

<pre>
  void main(int argc, char **argv){
    Et_Init(&argc,argv);
    /* Your setup code here */
    Et_MainLoop();
  }
</pre>

then under Windows, it will look like this instead:

<pre>
  void Et_Main(int argc, char **argv){
    /* Your setup code here */
  }
</pre>

Besides that, and the obvious fact that <code>Et_Display</code> is
not supported, a Windows ET application should work just like an
X11 ET application.
It is compiled in the same way, except that you should use the
<filename>et41win.c</filename> source file for the
<filename>et.o</filename> library.
</p>

<h1>Summary And Acknowledgements</h1>

<p>
Over the past two years, many people have used ET to build
programs from a mixture of Tcl/Tk and C.
Projects have ranged in size from student programming assignments up to
large-scale (100,000+ lines) commercial development efforts.
In all cases, ET has proven to be
an effective alternative to other GUI toolkits.
</p>

<p>
The original implementation of ET grew out of a programming contract
from Lucent Technologies (formerly AT&amp;T Bell Laboratories).
Lucent Technologies was in turn funded under a contract from the United
States Navy.
Many thanks go to Richard Blanchard at Lucent Technologies and
to Charlie Roop, Dave Toms and Clair Guthrie at PMO-428 for allowing ET to be
released to the public domain.
</p>

<p>
The author can be reached at:
</p>

<p>
D. Richard Hipp<br>
Hipp, Wyrick &amp; Company, Inc.<br>
6200 Maple Cove Lane<br>
Charlotte, NC 28269<br>
704-948-4565<br>
drh@vnet.net<br>
</p>