daemon@houligan.UUCP (04/06/86)
To establish my credentials, I have written a calendar application to work under both the User Agent and Bourne shell. I wrote it mainly because at the time (6/85) there was no such application and also to learn how to use the UNIX PC specific libraries. It took three months and it wasn't a dull process. I submitted it to AT&T's Vendor Involvement Program for evaluation, and it has been accepted for listing in their catalog. First, the theory. Basically, there are two levels at which you can access the window manager. The lowest level is described in window(7). At this level, windows are treated as file descriptors and manipulated with ioctl's. Built on top of this is the highest level, described in tam(3). This essentially provides you with more convenient although less flexible access to the window manager. You can, and in many cases will, have to mix the two. Neither is device-independent by any means. Built on top of tam, several utility functions are provided, the most important being menu(), form(), message(), track(), wind(), and exhelp(). Although they could be improved and expanded, these are very useful and save a lot of development time, so I highly recommend them. In summary, the basic strategy is to use tam for bread and butter window manipulation and I/O, while employing the higher level utilities where you need them, and delving into ioctl's only when absolutely necessary. Now, the practice. By way of background, I found early on that I cannot stand the User Agent, so I quickly deleted 'exec /usr/bin/ua' from my .profile. This was the beginning of a long and continuing effort to find ways to eliminate the need for the User Impediment and also my problems with the tam library. I guess I'm just too set in my ways to appreciate the User Agent. The first problem is that the tam functions for creating a new window would shrink my login Bourne shell window and put a border around it, permanently until logout. I think the tam functions to create a new window expect an open window to be supplied via 'open("/dev/window",...' and attached to the standard file descriptors before they are called the first time inside a process. This is what the User Agent will do by default before running an application (see ua(4)). So the solution was to always do 'open("/dev/window",...' before calling these functions and also tell the User Agent through the Suffixes file to not open a window before running the application. The next problem appeared when attempting to follow the mouse to move a highlighted area in my main window. I first tried to use track(). It didn't work. Period. So I chalked this up to new software, and attempted to use wreadmouse() in tam. Now, in all honesty, this function and its ioctl counterpart are really not designed for you to track the mouse by constantly sampling its position. They want you to restrict mouse reports by specifying when you are interested in its position relative to a rectangle or changes in the buttons. However, you can, in theory, sample the position by specifying the rectangle as the interior of the window. So, I set up a tight loop waiting on a mouse report with the current position, and based on this update the window with the new highlighted area. As soon as the mouse touched the inside of the window, a system trap occurred, which dumps half a screen full of information to the screen, most of which is decipherable only by the machine's designer, except for a plain English message "Unexpected DMA Page Fault" and a prompt to reset the machine, which I did. Unfortunately, when it got to the point of booting from the hard disk, it wouldn't. It displayed the half tone pattern on the screen, displayed the working icon, the head started seeking, then silence. After passing all diagnostics, and rebuilding the disk, I tried it again. Same behavior. This is pure conjecture, but what must have happened is that the loader area on the hard disk was blown away by page swapping activity gone awry. Needless to say, this cooled my enthusiasm for tracking the mouse, and I have never attempted to track the mouse in a tight loop with screen updating again. Fortunately, menu() allows you to supply an already open window for the menu through the M_USEWIN flag. As my main window could be structured as a menu of text items, albeit a dynamic one, this was an acceptable solution for this particular application. As a side note, if you use the ioctl for tracking the mouse, you will have to put the tty driver into cbreak and no echo mode, which tam does automatically inside winit(). If you don't, you won't receive a mouse report until you press return and it will be echo'd on the display. Now, some miscellaneous notes and bugs. If you want to use message() to display a status line icon, don't use ST_CAL, this prints nothing and reports no error. This applies to 3.0, and is apparently because ST_CAL is reserved for use by the Personal Calendar application. If you use exhelp() directly or indirectly through message(), it will for some reason not wait on its child, so that the next time you do a wait() on one of your child processes, it will fall through immediately because of the dead, unwaited for help child. You will have to use the return value of wait() to check if the correct process terminated. This applies to 2.0 and possibly 3.0. Apart from this, exhelp() works perfectly as described, although debugging syntax errors in the help file is a pain, and under 2.0, if help was accessed through message(), the text of the message would be displayed below the help window and could not be erased except through a clear screen. If you want a child process to run after the parent dies and you have a window attached to the standard file descriptors of the parent, the window will remain on the screen after the parent dies unless you close the standard file descriptors in the child. Under 2.0 at least, if you wound up logged onto to anything other than '/dev/w1', no status icons would be displayed until you did log onto the '/dev/w1'. I don't remember how I got into that state on a single user system. Both menu() and form() work perfectly under 2.0, but under 3.0 the user cannot drag the highlighted bar with the mouse. You can see this in the Administration menus and forms. As I said before, track() did not work under 2.0, it may under 3.0 or possibly I was just missing something. If you want to run vi as a child process in a window you created in the parent, you will have to modify the value of the lines and columns capabilities in the TERMCAP variable in the environment to reflect the interior dimensions of the window. If you're only running under the User Agent, this is unnecessary. You will have to place 'eval `tset -s -Q`' in /etc/profile to set the TERMCAP variable after TERM is set. The process of installation and preparing an installation disk(s) works exactly as documented in the UNIX PC Interface Specification and ua(4). I found no bugs, problems, or inconsistencies with the procedures applicable to my application. There are probably several other bugs that I found but don't remember and more that I never encountered. All in all, it is a fairly good system of libraries that work for the most part or have work arounds. The utility functions do reduce the work involved significantly. However, if you want to write an application that adheres to the Interface Specification, it is a worthwhile investment of time to write your own utilities for menus, forms, and error reporting that implement sections of the specification regarding item selection, keyboard conventions, and error messages. Another timesaver that I created was a translator from small specifications to the declarations and initialization required for menu() and form(), which can get very large and are in a form which makes it difficult to visualize their appearance on the screen. -- Jeff Lohman ...!{brl-bmd,pur-ee,sun}!gould!jlohman Gould, Computer Systems Division 6901 W. Sunrise Blvd. Ft. Lauderdale, FL 33310-4499 (305) 587-2900 x5237