TweetFollow Us on Twitter

Keyboard Wars
Volume Number:4
Issue Number:1
Column Tag:Pascal Procedures

Keyboard Wars!

By Steven Sheets, Contributing Editor, Hoffman Estates, IL

ADB, Keyboards & TypingBattle

One of the most obvious change to the Mac SE and the Mac // has been the redesign of the keyboard and mouse interface. There are no longer 2 separate interfaces for the mouse and the keyboard, each of which could handle only 1 device. Instead the mouse and keyboards are connected through the new Apple Desktop Bus (or ADB). All of Apple’s new computers (Mac SE, Mac //, Apple //gs) use ADB. This interface allows multiple ADB devices to be connected to the computer at the same time. Thus a Mac SE could have 2 keyboards, a mouse and a third-party ADB device (perhaps a graphics tablet) all connected to the computer at the same time. While most programs will recognize a key being pressed no matter what keyboard (an A is always an A), a program can be written that recognizes the difference between keyboards and acts accordingly. This article will expand on various ideas involving the ADB, the new keyboards, and the sample program. The sample program that is provided is TypingBattle, a multiple keyboard typing contest.

Apple Desktop Bus

The Apple Desktop Bus is a low-speed input-only interface for the new computers from Apple. The Mac SE and the //gs have a single ADB port while the Mac // has two ports. Each ADB device (except for the Mouse which must be the last item on a ADB line) is a pass through device, similar to the SCSI interface. The first ADB keyboard is plugged into the computer’s ADB port, the next ADB keyboard is plugged into the previous keyboard (or a second computer port if there is one). Logically up to 16 ADB devices can be connected to a single Computer. In practice, however, the signal is not powerful enough to go through that many devices. Depending on what style of keyboard is being used, currently 3-5 keyboards and a mouse is the maximum configuration.

ADB devices communicate with the computer using the ADB Manager and special ADB commands. Normally ADB communication is the domain of the ADB device drivers. The Macintosh Start Manager finds all the ADB devices connected to the computer and places that info into an ADB device table. It then initializes any ADB device drivers in the System file. These drivers (stored as ‘ADBS’ resources) will then handle any ADB communication.

The Mac // and Mac SE System Files contain two standard ADB device drivers, the mouse driver and the universal keyboard driver. The mouse driver handles all mouse movements. It also handles all clicking of the mouse button and issues standard MouseDown and MouseUp events to the Event manager. While two mice can be connected to the Mac //, the mouse driver was not designed to be able to differentiate between mice or pass this information over to the application. No information is passed in the MouseUp or MouseDown event record to explain which Mouse was clicked. Still, for a bit of amusement, connect to two mice to the Mac // and give them to two different people. The cursor will move with each move of either mouse, causing much frustration.

Fig. 1 Two keyboards battle for typing supremacy

The ADB universal keyboard driver handles any keys being pressed or released on an ADB keyboard. It generates a KeyDown or a KeyUp event for the Event Manager. However unlike the mouse driver, the keyboard driver passes along information explaining, not only what key was pressed, but on which keyboard. Prior to the ADB, the Message field of a KeyDown/KeyUp Event record contained the ASCII Character in the first byte and the Virtual Key Code in the second byte. With ADB, the Message field’s first and second bytes are the same, while the third bye contains the ADB address of the keyboard that was used. This information is the basis of the sample program, TypingBattle. Two global byte variables are now associated with the ADB Manager; KbdLast, the ADB address of the last keyboard used and KbdType, the Keyboard type of the last keyboard used.

While the sample program only uses the Event record for information about the multiple keyboards, the ADB manager can be accessed directly. The CountADB function returns the total number of ADB devices connected to the computer. GetIndADB (based on the device count from 1 to CountADB number) and GetADBInfo (based on the ADB address of a device) return information about the Device Type, ADB address, service routine address and data area address. Other ADB Manager calls (ADBReInit, ADBOp and SetADBInfo) should not be used directly by an application. Any non-keyboard or non-mouse type of ADB interface would use these commands in it’s own ADB device driver.

The usage of the ADB has yet to be fully recognized. For a hardware developer, designing an ADB device has several advantages. For example, the majority of the current graphics tablets available for the Mac connect to the serial ports, requiring special software drivers (which do not always work with all software packages) and the loss of that serial port for other uses. An ADB graphic tablet that emulates an ADB mouse would work without any special software drivers or loss of port. For the same reasons, an ADB Piano Keyboard would easily interface into all existing Music Packages.

Keyboards

There are currently 3 ADB keyboards that can be used on the Mac SE & Mac //. The standard ADB Keyboard is essentially a duplicate of the older non-ADB MacPlus keyboard. It has a standard keyboard layout with cursor keys and a Numeric Keypad. Programs that were written to use the MacPlus cursor keys will function identically on the standard ADB Keyboard.

Most people do not realize the second ADB keyboard will work on the Mac SE & Mac //. The Apple //gs uses the ADB interface. It uses the same Mouse as the Mac SE & Mac //. However the //gs has it’s own ADB keyboard. This keyboard is similar in layout to the standard ADB keyboard, except slightly smaller. Remember that any ADB computer can work with any ADB device. While only a large lab would have both the Macintosh and //gs, it is sometime convenient to know that in a rush, either computer can use the other keyboard.

The third ADB keyboard is the Extended ADB Keyboard (sometimes still referred to by it’s development code name “Saratoga”). It has all the features of the standard ADB Keyboard plus additional keys. These additional keys include 15 Function keys, a Control Key, and 6 Special purpose keys.

The 15 Function key, called F1 through F15, are across the top of the keyboard. The first four function keys, F1, F2, F3 and F4, are designed to be assigned the functions Undo, Cut, Copy and Paste. These functions are printed next to each key on the keyboard. The Virtual Key Codes (ie. the third Byte of the Longint passed in the Event field of the event record for keyboard events) of these keys are $7A, $78, $63 and $76. New programs should recognize these Virtual Key Codes, and implement the functions accordingly. According to Apple’s Guidelines, the F5 thought F15 keys are intended to be defined by the user, not by the Application. Utilities like QuickKeys by CE Software or Apple’s promised MacroMaker allow the user to redefine any key to a specific action. Still, if the developer wishes to do so, a program could use these keys for it’s own purpose.

The Control key was added for compatibility with applications that communicate with another operating systems (ie. terminal packages). Pressing the Control key sets bit 12 of the modifier field of the event record for keyboard events. Since the Macintosh OS does not use the concept of Control characters, except for a communication application, this key should not be used by Macintosh applications.

The other 6 Special Keys are intended to give the user greater control of his application. Each application must handle each special key in it’s own way. The following are the Keys, their Virtual Key Codes and general guidelines for usage.

The Home key ($73) moves the user to the home position in the application. In a word processor, this key may move the scroll bar to the top of the document. In a database, this may move the display to the first field or the first record. The End key ($77) moves the user to the end position in the application. In a word processor, this key may move the scroll bar to the bottom of the document. The Page Up key ($74) and Page Down Key ($79) moves the user to the next page (up or down) in the application. In a word processor, these keys would be equivalent to clicking in the page up (or down) region of the vertical scroll bar. In a database, these keys may move the user to the previous or next record. According to Apple Guidelines, the Home, End, Page Up and Page Down keys should not effect the actual position of the current insertion point.

The Help key ($72) allows the user to request help from the program. A good usage of this key would be invoke the About menu (or Help menu, if one exists). The Forward Delete, or Fwd Del, key ($75) performs similar to the Backspace key. In a word processor, when the Backspace key is pressed, a character before the current insertion point is deleted. Using the Forward Delete key in the same situation, a character after the current insertion point should be deleted. Use this key carefully. It is a bad idea to allow the user to delete something he can not see (for example, in a database, delete the next unseen record).

TypingBattle

TypingBattle is a sample program designed to show how to handle more than one keyboard. It also implements the special keys of the Apple Extended Keyboard. The game is a simple typing speed contest. First the program prompts each user to type his name from his keyboard. This tells the application the number of players, the name of each player and the ADB address of each player’s keyboard. To start a round, each player must press the return key (so that no one is surprised). Roughly three seconds later, a random typing Sample is shown. The first player to correctly type it (including correct punctuation and case), and then press return, wins the round. The player can backspace over any mistakes, up until the time he presses return. Then, his line is judged for correctness. The round continues until a winner finishes or everyone has entered a mistake. The users can use the mouse to select a new round or start a new configuration. The About Menu or the Help Key will display the About Box.

Last Comments

• The program uses MultiLine, a simple Edit unit. Text Edit was not designed to have multiple edit fields open at the same time.

• Modifying the Resource source file or ResEdit can be used to add additional typing Samples.

• Whenever connecting new ADB devices, always power down the computer. A simple reset is not enough for the system to recognizes when new ADB device have been connected or detached.

• In the food for thought area, the sample program show how to use multiple keyboards with a single screen. What about multiple keyboards and multiple screens? The ADB working together with the Mac // multiple Video cards would give a true multiuser interface to the Mac. For fun, a MazeWar’s type of game would be simple to do. On the serious side, a database package that handled multiple screens/keyboards would have it’s advantages. The hardware would be much cheaper than setting up multiple Mac’s over Appletalk, while the performance might even increase (no time spent communicating between computers). Any takers?

{1}
{TypingBattle by Steve Sheets 11/15/87 }
{Simple Demonstration of Multible Keyboards. }

PROGRAM TypingBattle;
 USES
 MultiLine; {Unit to handle simple editing}
 CONST
 MaxAllow = 5;   {Max Number of Players}
 AboutID = 600;  {Various Resource IDs}
 TextID = 600;
 LineID = 601;
 AppleMenuID = 1;
 FileMenuID = 2;
 EditMenuID = 3;
 WindowH = 480;  {Placement Constants}
 VEdge = 40;
 CenterTop = 40;
 Edge = 10;
 Hi = 20;
 PlayOff = 50;
 TTop = -15;
 RTop = 5;
 WTop = 35;
 TimeCount = 180;{Timer to start round}

{Variables: Menus, Done Flag, Number Players, Main Window,
Status of game, Number of Samples, Number Players done, lots
strings, arrays holding Players Scores, Who’s Done, Names & Bus IDs and 
finally Edit fields holding Message, Names & Text.}

 VAR
 AppleMenu, FileMenu, EditMenu : MenuHandle;
 Done : boolean;
 theNum : integer;
 MyWindow : WindowPtr;
 Status, NumSamples, NumDone : integer;
 WelStr, ScoreStr, PreStr, WinStr, PressStr : str255;
 NoOneStr, EnterStr, SepStr, theTitle : str255;
 Score : ARRAY[1..MaxAllow] OF integer;
 isDone : ARRAY[1..MaxAllow] OF boolean;
 theName : ARRAY[1..MaxAllow] OF str255;
 theBus : ARRAY[1..MaxAllow] OF integer;
 MessRec : MLRec;
 NameRec, TextRec : ARRAY[1..MaxAllow] OF MLRec;

{Returns Number Of Samples (ie. Number of strings in the STR# resource.}
 FUNCTION GetNumSamples : integer;
 TYPE
 WP = ^integer;
 WH = ^WP;
 VAR
 W : WH;
 BEGIN
 W := POINTER(GetResource(‘STR#’, TextID));
 GetNumSamples := W^^;
 ReleaseResource(POINTER(W));
 END;

{Given M (Longint Message field of Keydown event), return B (Bus ID), 
V (Virtual code) & C (Key pressed).}

 PROCEDURE CalcKey (M : longint;
 VAR B, V : integer;
 VAR C : char);
 BEGIN
 C := Chr(M MOD 256);
 B := (M DIV 65536) MOD 256;
 V := (M DIV 256) MOD 256;
 END;

{Do About Box.}
 PROCEDURE DoAbout;
 VAR
 n : integer;
 BEGIN
 n := Alert(AboutID, NIL);
 END;

{Initialize variables}
 PROCEDURE DoSetup;
 BEGIN
 GetIndString(WelStr, LineID, 1);
 GetIndString(ScoreStr, LineID, 2);
 GetIndString(PreStr, LineID, 3);
 GetIndString(WinStr, LineID, 4);
 GetIndString(PressStr, LineID, 5);
 GetIndString(NoOneStr, LineID, 6);
 GetIndString(theTitle, LineID, 7);
 GetIndString(EnterStr, LineID, 8);
 GetIndString(SepStr, LineID, 9);
 NumSamples := GetNumSamples;

 AppleMenu := GetMenu(AppleMenuID);
 AddResMenu(AppleMenu, ‘DRVR’);
 InsertMenu(AppleMenu, 0);
 FileMenu := GetMenu(FileMenuID);
 InsertMenu(FileMenu, 0);
 EditMenu := GetMenu(EditMenuID);
 InsertMenu(EditMenu, 0);
 DrawMenuBar;
 InitCursor;
 MyWindow := NIL;
 Done := false;
 END;

{Given an integer H & V, make a centered rectange R.}
 PROCEDURE MakeRect (VAR R : rect;
 h, v : integer);
 VAR
 N : integer;
 BEGIN
 N := (screenbits.bounds.right - screenbits.bounds.left - H) DIV 2;
 SetRect(R, N, VEdge, N + H, VEdge + V);
 END;

{Handle Special Keys, return true if none was pressed.  In this case, 
only handle Help by calling About Box.  Note the Hex Codes.}

 FUNCTION NotSpecKeys (Virtual : integer) : boolean;
 BEGIN
 IF Virtual = $72 THEN
 BEGIN
 DoAbout;
 NotSpecKeys := false;
 END
 ELSE
 NotSpecKeys := true;
 END;

{Finds out who is playing (ie. set Names & Bus IDs) or quit game (ie. 
Done true).}

 PROCEDURE GetPlayers;
 CONST
 ConV = 85;
 ConH = 360;
 DLOff = 15;
 kOff = 25;
 kTop = 15;
 kHi = 20;
 LineLeft = 20;
 butBot = -15;
 OKLeft = 90;
 QUITLeft = 210;
 Voff = 15;
 VAR
 tempPort : Grafptr;
 myW : WindowPtr;
 cont, F2 : boolean;
 tempEvent : EventRecord;
 tempWindow : windowptr;
 tempCode, tempVirtual : integer;
 tempChar : char;
 OKcon, QUITcon, tempCon : ControlHandle;
 Lines : ARRAY[1..MaxAllow] OF MLRec;

 PROCEDURE DoBox (V : integer);
 VAR
 R2 : Rect;
 BEGIN
 WITH Lines[V] DO
 BEGIN
 R2.left := Fr.left - 1;
 R2.right := Fr.right + 1;
 R2.top := Fr.top - 1;
 R2.bottom := Fr.bottom + 1;
 IF V > theNum THEN
 PenPat(Gray);
 FrameRect(R2);
 PenPat(Black);
 END;
 END;

 PROCEDURE CheckOK;
 BEGIN
 IF (theNum > 0) AND (theNum <= MaxAllow) THEN
 HiliteControl(OKcon, 0)
 ELSE
 HiliteControl(OKcon, 255);
 END;

 PROCEDURE RegKey (V : integer;
 C : Char);
 VAR
 count, L : integer;
 BEGIN
 count := 0;
 FOR L := 1 TO theNum DO
 IF theBus[L] = v THEN
 count := L;
 IF count = 0 THEN
 BEGIN
 IF (theNum < MaxAllow) AND (Ord(C) >= 32) THEN
 BEGIN
 theNum := theNum + 1;
 theBus[theNum] := V;
 Lines[theNum].St := ‘ ‘;
 Lines[theNum].St[1] := C;
 Lines[theNum].Cr := ‘_’;
 MLreset(Lines[theNum]);
 DoBox(theNum);
 CheckOk;
 END;
 END
 ELSE
 BEGIN
 MLchar(Lines[count], C);
 IF Lines[count].St = ‘’ THEN
 BEGIN
 IF count <> theNum THEN
 BEGIN
 FOR L := count TO theNum - 1 DO
 BEGIN
 MLtext(Lines[L], Lines[L + 1].St);
 theBus[L] := theBus[L + 1];
 END;
 Lines[theNum].St := ‘’;
 END;
 Lines[theNum].Cr := ‘ ‘;
 MLreset(Lines[theNum]);
 theNum := theNum - 1;
 DoBox(theNum + 1);
 CheckOk;
 END
 END;
 END;

 PROCEDURE DoGPSetup;
 VAR
 nn, count : integer;
 S : str255;
 Bx : rect;
 BEGIN
 nn := ConV + (MaxAllow * kOff);
 MakeRect(Bx, ConH, nn);
 myW := NewWindow(NIL, Bx, ‘’, true, 1, POINTER(-1), false, 0);
 SetPort(myW);
 SetRect(Bx, OKLeft, nn - 20 + butBot, OKLeft + 60, nn + butBot);
 GetIndString(S, LineID, 10);
 OKcon := NewControl(myW, Bx, S, true, 0, 0, 0, 0, 0);
 SetRect(Bx, QUITLeft, nn - 20 + butBot, QUITLeft + 60, nn + butBot);
 GetIndString(S, LineID, 11);
 QUITcon := NewControl(myW, Bx, S, true, 0, 0, 0, 0, 0);
 cont := false;
 theNum := 0;

 FOR count := 1 TO MaxAllow DO
 BEGIN
 nn := (count * kOff) + DLOff;
 SetRect(Bx, LineLeft, nn, ConH - LineLeft, nn + kHi);
 MLinit(Lines[count], ‘’, ‘’, ‘ ‘, Bx, false);
 END;
 END;

BEGIN
DoGPSetup;
CheckOk;
REPEAT
SystemTask;
IF GetNextEvent(everyEvent, tempEvent) THEN
BEGIN
 IF tempEvent.what = mouseDown THEN
 BEGIN
 F2 := true;
 IF FindWindow(tempEvent.where, tempWindow) = inContent THEN
 BEGIN
 GlobalToLocal(tempEvent.where);
 IF FindControl(tempEvent.where, myW, tempCon) <> 0 THEN
 IF TrackCOntrol(tempCon, tempEvent.where, NIL) <> 0 THEN
 BEGIN
 IF tempCon = QUITcon THEN
 BEGIN
 Done := true;
 F2 := false;
 END
 ELSE IF tempCon = OKcon THEN
 BEGIN
 Cont := true;
 FOR tempCode := 1 TO theNum DO
 theName[tempCode] := Lines[tempCode].St;
 F2 := false;
 END;
 END
 ELSE
 F2 := false;
 END;
 IF F2 THEN
 sysbeep(1);
 END;
 IF tempEvent.what = keydown THEN
 BEGIN
 CalcKey(tempEvent.message, tempCode, tempVirtual, tempChar);
 IF NotSpecKeys(tempVirtual) THEN
 RegKey(tempCode, tempChar);
 END;
 IF tempEvent.what = updateEvt THEN
 IF myW = WindowPtr(tempEvent.message) THEN
 BEGIN
 GetPort(tempPort);
 SetPort(myW);
 BeginUpdate(myW);
 MoveTo((ConH - StringWidth(EnterStr)) DIV 2, kTop + Voff);
 DrawString(EnterStr);
 FOR tempCode := 1 TO MaxAllow DO
 BEGIN
 MLupdate(Lines[tempCode]);
 DoBox(tempCode);
 END;
 DrawControls(myW);
 EndUpdate(myW);
 SetPort(tempPort);
 END;
END;
UNTIL cont OR done;
KillControls(myW);
DisposeWindow(myW);
END;

{Dispose Window (if any), get Players (if any), if so create the Window 
and}
{Edit fields for the next game.}
 PROCEDURE DoConfigure;
 VAR
 tempRect : Rect;
 flag : boolean;
 count : integer;
 Bx : rect;
 BEGIN
 IF MyWindow <> NIL THEN
 BEGIN
 DisposeWindow(MyWindow);
 MyWindow := NIL;
 END;

 GetPlayers;

 IF NOT done THEN
 BEGIN
 MakeRect(tempRect, WindowH, WTop + (theNum * PlayOff));
 MyWindow := NewWindow(NIL, tempRect, theTitle, false, 1, POINTER(-1), 
false, 0);
 SetPort(MyWindow);
 Bx.left := Edge;
 Bx.right := WindowH - Edge;
 Bx.top := Edge;
 Bx.bottom := Bx.top + Hi;
 MLinit(MessRec, ‘’, ‘’, ‘ ‘, Bx, false);
 FOR count := 1 TO theNum DO
 BEGIN
 Bx.top := (count * PlayOff) + TTop;
 Bx.bottom := Bx.top + Hi;
 Score[count] := 0;
 MLinit(NameRec[count], CONCAT(theName[count], ScoreStr), ‘’, ‘ ‘, Bx, 
false);
 Bx.top := (count * PlayOff) + RTop;
 Bx.bottom := Bx.top + Hi;
 MLinit(TextRec[count], ‘’, ‘’, ‘ ‘, Bx, true);
 END;
 ShowWindow(MyWindow);
 END;
 END;

{Start a game by setting status to 1, setting correct message, clearing 
score,}
{text and done flags.}
 PROCEDURE DoStart;
 VAR
 count : integer;
 tempPort : GrafPtr;
 BEGIN
 GetPort(tempPort);
 SetPort(MyWindow);
 Status := 1;
 MLtext(MessRec, WelStr);
 FOR count := 1 TO theNum DO
 BEGIN
 Score[count] := 0;
 MLtext(NameRec[count], ‘0’);
 MLtext(TextRec[count], ‘’);
 IsDone[count] := false;
 END;
 SetPort(tempPort);
 END;

{Handle updating the window by calling Edit fields update.}
 PROCEDURE DoUp;
 VAR
 count : integer;
 tempR : rect;
 BEGIN
 SetRect(tempR, 0, 0, 1000, 1000);
 EraseRect(tempR);
 MLupdate(MessRec);
 FOR count := 1 TO theNum DO
 BEGIN
 MLupdate(NameRec[count]);
 MLupdate(TextRec[count]);
 END;
 END;

{Flush the Event buffer of Keydowns.}
 PROCEDURE FlushKeys;
 BEGIN
 FlushEvents(keyDownMask, 0);
 END;

{Depending on Game Status, handle the Key down.  Who is Players number 
(not bus ID).  Status 1 is everyone waiting for all Players to press 
return for the next round.Status 2 has everyone playing.}

PROCEDURE DoKey (Who : integer;
 Key : char);
VAR
 ll : longint;
 Sp : str255;
 count : integer;
 dummy : boolean;
 tempPort : GrafPtr;
BEGIN
 GetPort(tempPort);
 SetPort(MyWindow);
 SetPort(MyWindow);
 IF (Status = 1) AND (Key = Chr(13)) THEN
 BEGIN  {Player Pressed return.}
 IsDone[who] := true;
 dummy := true;
 FOR count := 1 TO theNum Do
 dummy := dummy AND IsDone[count];

{If everyone pressed return, wait awhile, then display the Sample, flush 
any old key events & go to Status 2.}

 IF dummy THEN
 BEGIN
 MLtext(MessRec, PreStr);
 ll := Tickcount;
 NumDone := 0;
 FOR count := 1 TO theNum DO
 BEGIN
 IsDone[count] := false;
 MLtext(TextRec[count], ‘’);
 END;
 count := (Random MOD NumSamples) + 1;
 GetIndString(Sp, TextID, count);
 WHILE tickcount < ll + TimeCount DO
 ;
 MLtext(MessRec, Sp);
 Status := 2;
 FlushKeys;
 END;
 END
 ELSE IF Status = 2 THEN
 BEGIN
 {If player has not pressed return,}
 IF NOT IsDone[who] THEN
 BEGIN
 MLchar(TextRec[who], Key);
 {Handle the Key.}
 IF Key = Chr(13) THEN
 BEGIN
 {If return, he is done.}
 NumDone := NumDone + 1;
 IsDone[who] := true;
 IF EqualString(TextRec[who].St, MessRec.St, true, false) THEN
 BEGIN
 {Handle him winning.}
 Status := 1;
 Score[who] := Score[who] + LENGTH(MessRec.St);
 MLtext(MessRec, CONCAT(WinStr, theName[who], PressStr));
 NumToString(Score[who], Sp);
 MLtext(NameRec[who], Sp);
 FOR count := 1 TO theNum DO
 IsDone[count] := false;
 END
 ELSE IF NumDone = theNum THEN
 BEGIN {Handle no one winning.}
 Status := 1;
 MLtext(MessRec, NoOneStr);
 FOR count := 1 TO theNum DO
 IsDone[count] := false;
 END;
 END;
 END;
 END;
SetPort(tempPort);
END;

{Handle Menu.}
PROCEDURE MainMenu (tempResult : LONGINT);
 VAR
 tempInteger : integer;
 tempStr : str255;
BEGIN
 tempInteger := LoWord(tempResult);
 CASE HiWord(tempResult) OF
 AppleMenuID : 
 IF tempInteger = 1 THEN
 DoAbout
 ELSE
 BEGIN
 GetItem(appleMenu, tempInteger, tempStr);
 tempInteger := OpenDeskAcc(tempStr);
 END;
 FileMenuID : 
 IF tempInteger IN [1, 2] THEN
 BEGIN
 IF tempInteger = 2 THEN
 DoConfigure;
 IF NOT done THEN
 DoStart;
 END
 ELSE IF tempInteger = 4 THEN
 Done := true;
 EditMenuID : 
 IF NOT SystemEdit(tempInteger - 1) THEN
 sysbeep(1);
 OTHERWISE
 END;
 HiliteMenu(0);
END;

{Main Event Loop.}
PROCEDURE DoMainLoop;
 VAR
 tempEvent : EventRecord;
 tempWindow : windowptr;
 tempCode, tempBus, tempVirtual : integer;
 tempPort : Grafptr;
 tempChar : Char;
 tempRect : rect;
BEGIN
REPEAT
SystemTask;
IF GetNextEvent(everyEvent, tempEvent) THEN
BEGIN
CASE tempEvent.what OF
mouseDown : 
 BEGIN
 tempCode := FindWindow(tempEvent.where, tempWindow);
 CASE tempCode OF
 inMenuBar : 
 MainMenu(MenuSelect(tempEvent.where));


 inSysWindow : 
 SystemClick(tempEvent, tempWindow);
 inContent : 
 IF tempWindow <> FrontWindow THEN
 SelectWindow(tempWindow);
 inDrag : 
 IF (MyWindow = tempWindow) AND (tempWindow <> NIL) THEN
 BEGIN
 SetRect(tempRect, -32000, -32000, 32000, 32000);
 DragWindow(tempWindow, tempEvent.where, tempRect);
 END;
 OTHERWISE
 END; { of tempCode case }
 END; { of mouseDown }
keydown : 
 BEGIN
 CalcKey(tempEvent.message, tempBus, tempVirtual, tempChar);
 IF BitAnd(tempEvent.modifiers, cmdKey) <> 0 THEN
 MainMenu(MenuKey(tempChar))
 ELSE IF NotSpecKeys(tempVirtual) THEN
 FOR tempCode := 1 TO theNum DO
 IF (theBus[tempCode] = tempBus) AND (theNum > (tempCode - 1)) THEN
 DoKey(tempCode, tempChar)
 END;
updateEvt : 
 BEGIN
 tempWindow := WindowPtr(tempEvent.message);
 GetPort(tempPort);
 SetPort(tempWindow);
 BeginUpdate(tempWindow);
 IF (tempWindow = MyWindow) AND (tempWindow <> NIL) THEN
 DoUp;
 EndUpdate(tempWindow);
 SetPort(tempPort);
 END;
OTHERWISE
END;
END;
UNTIL Done;
END;

{Game over.}
 PROCEDURE DoQuit;
 VAR
 n : integer;
 BEGIN
 DeleteMenu(AppleMenuID);
 DeleteMenu(FileMenuID);
 DeleteMenu(EditMenuID);
 IF MyWindow <> NIL THEN
 DisposeWindow(MyWindow);
 END;

{Main Body}
BEGIN
 DoSetup;
 DoAbout;
 DoConfigure;
 IF NOT done THEN
 BEGIN
 DoStart;
 DoMainLoop;
 END;
 DoQuit;
END.
{MultiLine Unit by Steve Sheets 11/15/87     }
{Simple Edit Fields for Multible Keyboards.  }

UNIT MultiLine;

INTERFACE

{Data Type containing Text Str, Prompt Str, Cursor Char, Cursor Width, 
Frame Rect, Frame Flag, Horizontal Start Position, Horizontal Current 
Postion, Horizontal Prompt Position, Vertical Start Postion.}
 TYPE
 MLRec = RECORD
 St, Pr : Str255;
 Cr : char;
 CW : integer;
 Fr : rect;
 FF : boolean;
 StartH, CurH, PrH, StartV : integer;
 END;

{Draws the Rec Edit Field}
 PROCEDURE MLupdate (Rec : MLRec);

{Resets the Rec Edit Field, after someone has changed a setting.}
 PROCEDURE MLreset (VAR Rec : MLRec);

{Initalize the Rec Edit Field}
 PROCEDURE MLinit (VAR Rec : MLRec;
 Prompt : str255;
 Tx : str255;
 Curs : char;
 Box : rect;
 FrFlag : boolean);

{Handle a Key (Printable Keys & Backspace)}
 PROCEDURE MLchar (VAR Rec : MLRec;
 C : char);

{Reset the Text Str to S (calls MLReset).}
 PROCEDURE MLtext (VAR Rec : MLRec;
 S : str255);

IMPLEMENTATION

 PROCEDURE MLupdate;
 VAR
 R2 : rect;
 BEGIN
 WITH Rec DO
 BEGIN
 IF FF THEN
 BEGIN
 FrameRect(Fr);
 R2.left := Fr.left + 1;
 R2.right := Fr.right - 1;
 R2.top := Fr.top + 1;
 R2.bottom := Fr.bottom - 1;
 EraseRect(R2);
 END
 ELSE
 EraseRect(Fr);
 MoveTo(StartH, StartV);
 IF Pr <> ‘’ THEN
 DrawString(Pr);
 DrawString(St);
 IF CW > 0 THEN
 DrawChar(Cr);
 END;
 END;

 PROCEDURE MLreset;
 BEGIN
 WITH Rec DO
 BEGIN
 CurH := StartH;
 IF Pr <> ‘’ THEN
 CurH := CurH + StringWidth(Pr);
 PrH := CurH;
 IF St <> ‘’ THEN
 CurH := CurH + StringWidth(St);
 IF Cr <> ‘ ‘ THEN
 CW := CharWidth(Cr)
 ELSE
 CW := 0;
 END;
 MLupdate(Rec);
 END;

 PROCEDURE MLinit;
 BEGIN
 WITH Rec DO
 BEGIN
 Fr := Box;
 St := Tx;
 Pr := Prompt;
 StartH := Fr.left + 5;
 StartV := Fr.top + 16;
 FF := FrFlag;
 Cr := Curs;
 END;
 MLreset(Rec);
 END;

 PROCEDURE MLchar;
 VAR
 Small : str255;
 L : integer;
 BEGIN
 WITH Rec DO
 IF Ord(C) = 8 THEN
 BEGIN
 L := Length(St);
 IF L > 0 THEN
 BEGIN
 CurH := CurH - CharWidth(St[L]);

 TextMode(srcBic);
 MoveTo(CurH, StartV);
 DrawChar(St[L]);
 IF CW > 0 THEN
 DrawChar(Cr);

 TextMode(srcOr);
 MoveTo(CurH, StartV);
 IF CW > 0 THEN
 DrawChar(Cr);
 Delete(St, L, 1);
 END;
 END
 ELSE IF (Ord(C) >= 32) THEN
 BEGIN
 IF CharWidth(C) + CW + CurH < Fr.right THEN
 BEGIN
 MoveTo(CurH, StartV);
 IF CW > 0 THEN
 BEGIN
 TextMode(srcBic);
 DrawChar(Cr);
 TextMode(srcOr);
 MoveTo(CurH, StartV);
 END;
 DrawChar(C);
 CurH := CurH + CharWidth(C);
 IF CW > 0 THEN
 DrawChar(Cr);

 Small := ‘@’;
 Small[1] := C;
 St := CONCAT(St, Small);
 END;
 END;
 END;

 PROCEDURE MLtext;
 BEGIN
 Rec.St := S;
 MLReset(Rec);
 END;
END.

TypingBattle.Rsrc


{2}
Type MENU
     ,1
\14
     About TypingBattle...
     (-

     ,2
File
     New Game
     New Configuration
     (-
     Quit

     ,3
Edit
     Undo/Z
     (-
     Cut/X
     Copy/C
     Paste/V
     Clear

Type DITL
     ,600
3

button
130 170 150 230
OK

staticText
20 18 40 382
Welcome to TypingBattle by Steve Sheets for MacTutor

staticText
60 18 110 382
TypingBattle is a Multiplaying Typing Contest designed to be used ++
on a Mac SE or Mac // with 1 or more ADB Keyboards.

Type ALRT
     ,600
40 56 210 456
600
4444

Type STR#
     ,601
11
Everyone press return to start a new sequence.
 Score: 
Prepare to type:
The winner is 
.  Everyone press return to start a new sequence.
No one has finished.  Everyone press return to start a new sequence.
TypingBattle
Enter your name from the correct keyboard:
: 
OK
Quit

Type STR#
     ,600
5
Now is the time for all good men
ABCDEFGHIJKLMNOPQRSTUVWXYZ
The quick red fox jumped over the lazy brown cow.
I have not yet begun to fight.
An Apple a day keeps the doctor away.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Cloud 4.1.1 - File sharing from your men...
Cloud is simple file sharing for the Mac. Drag a file from your Mac to the CloudApp icon in the menubar and we take care of the rest. A link to the file will automatically be copied to your clipboard... Read more
OmniFocus 2.7.1 - GTD task manager with...
OmniFocus helps you manage your tasks the way that you want, freeing you to focus your attention on the things that matter to you most. Capturing tasks and ideas is always a keyboard shortcut away in... Read more
CleanApp 5.1.1 - Application deinstaller...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more
ForkLift 3.0 Beta 2 - Powerful file mana...
ForkLift is a powerful file manager and ferociously fast FTP client clothed in a clean and versatile UI that offers the combination of absolute simplicity and raw power expected from a well-executed... Read more
Sublime Text 3126 - Sophisticated text e...
Sublime Text is a sophisticated text editor for code, markup, and prose. You'll love the slick user interface, extraordinary features, and amazing performance. Features Goto Anything. Use Goto... Read more
1Password 6.3.3 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
WhatsApp 0.2.1880 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
NeoFinder 6.9.3 - Catalog your external...
NeoFinder (formerly CDFinder) rapidly organizes your data, either on external or internal disks, or any other volumes. It catalogs all your data, so you stay in control of your data archive or disk... Read more
Amadeus Pro 2.3.1 - Multitrack sound rec...
Amadeus Pro lets you use your Mac computer for any audio-related task, such as live audio recording, digitizing tapes and records, converting between a variety of sound formats, etc. Thanks to its... Read more
Yasu 4.0.0 β - System maintenance app; p...
Yasu was created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks quickly within a... Read more

Our 5 Favorite iMessage Sticker Packs
At long last, iMessage joins the ranks of messaging apps the likes of LINE and Whatsapp, adding an impressive collection of stickers. They’re a great way to add a little something extra to your daily conversations. [Read more] | Read more »
How to get past Vulture Island's tr...
Vulture Island is a colorful and quirky mish-mash of platforming and puzzles. It’s creative and fresh, but sometimes the game can throw a curveball at you, leaving you stuck as to how you should progress. These tips will help you explore smoothly... | Read more »
The new Clash of Kings is just for Weste...
If you’ve played the original Clash of Kings, you’ll probably recognise the city building, alliance forging and strategic battles in Clash of Kings: The West. What sets this version apart is that it’s tailor made for a Western audience and the... | Read more »
Frost - Survival card game (Games)
Frost - Survival card game 1.12.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.12.1 (iTunes) Description: *Warning: the game will work on iPhone 5C and above and iPad Pro / 4. Other devices are not supported* | Read more »
How to build and care for your team in D...
Before you hit the trail and become a dog sledding legend, there’s actually a fair bit of prep work to be done. In Dog Sled Saga, you’re not only racing, you’re also building and caring for a team of furry friends. There’s a lot to consider—... | Read more »
How to win every race in Dog Sled Saga
If I had to guess, I’d say Dog Sled Saga is the most adorable racing game on the App Store right now. It’s a dog sled racing sim full of adorable, loyal puppies. Just look at those fluffy little tails wagging. Behind that cute, pixelated facade is... | Read more »
Let the war games commence in Gunship Ba...
Buzz Lightyear famously said, “This isn’t flying, this is falling – with style!” In the case of Gunship Battle: Second War, though, this really is flying - with style! The flight simulator app from Joycity puts you in control of 20 faithfully... | Read more »
How to get a high score in Fired Up
Fired Up is Noodlecake Games’ high score chasing, firefighting adventure. You take control of a wayward firefighter who propels himself up the side of a highrise with blasts of water. Sound silly? It is. It’s also pretty difficult. You can’t... | Read more »
NBA 2K17 (Games)
NBA 2K17 1.0 Device: iOS iPhone Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: Following the record-breaking launch of NBA 2K16, the NBA 2K franchise continues to stake its claim as the most authentic sports video... | Read more »
Dog Sled Saga (Games)
Dog Sled Saga 1.0.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.1 (iTunes) Description: A game by Dan + Lisa As a rookie musher, foster a dogsledding team whose skills will grow if they're treated right. Week by... | Read more »

Price Scanner via MacPrices.net

13-inch 2.5GHz MacBook Pro (Apple refurbished...
Apple has Certified Refurbished 13″ 2.5GHz MacBook Pros available for $829, or $270 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.5GHz MacBook Pros... Read more
Save 30% on Camera Guard’s Secure Protection...
To celebrate the release of macOS Sierra, Miami-based security solutions company, ProtectStar has announced a special 30% discount on Camera Guard Professional for Mac 2016. This innovative security... Read more
DVDFab Special Deal – Get a 1-Year Free Licen...
Beijing, China based specialist in the field of DVD, Blu-ray and video backup solutions, Fengtao Software has launched its Autumn Special Deals 2016, giving a 1-year free license of a randomly picked... Read more
21-inch iMacs on sale for up to $120 off MSRP
B&H Photo has 21″ iMacs on sale for up to $120 off MSRP including free shipping plus NY sales tax only: - 21″ 3.1GHz iMac 4K: $1379 $120 off MSRP - 21″ 2.8GHz iMac: $1199.99 $100 off MSRP - 21″ 1... Read more
13-inch 2.7GHz/256GB Retina MacBook Pro on sa...
Amazon.com has the 13″ 2.7GHz/256GB Retina Apple MacBook Pro on sale for $151 off MSRP including free shipping: - 13″ 2.7GHz/256GB Retina MacBook Pro (sku MF840LL/A): $1348 $151 off MSRP Read more
Apple TVs on sale for up to $50 off MSRP
Best Buy has 32GB and 64GB Apple TVs on sale for $40-$50 off MSRP on their online store. Choose free shipping or free local store pickup (if available). Sale prices for online orders only, in-store... Read more
Apple refurbished 13-inch Retina MacBook Pros...
Apple has Certified Refurbished 13″ Retina MacBook Pros available for up to $270 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 13″ 2.7GHz... Read more
Duplicate Sweeper Free On Mac App Store For O...
To celebrate the launch of Apple’s latest macOS Sierra, Stafford, United Kingdom based Wide Angle Software has announced that its duplicate file finder software, Duplicate Sweeper, is now available... Read more
13-inch Retina MacBook Pros on sale for up to...
B&H Photo has 13″ Retina Apple MacBook Pros on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY tax only: - 13″ 2.7GHz/128GB Retina MacBook Pro: $1174.99 $125 off MSRP - 13... Read more
Evidence Surfaces Pointing To New A10X Chip F...
Citing a job description for a Project Lead position at Apple’s Austin, Texas engineering labs, Motley Fool’s Ashraf Eassa deduces that development is progressing well on Apple’s next-generation in-... Read more

Jobs Board

Sr. *Apple* Mac Engineer - Net2Source Inc....
…staffing, training and technology. We have following position open with our client. Sr. Apple Mac Engineer6+ Months CTH Start date : 19th Sept Travelling Job If Read more
*Apple* Retail - Multiple Positions-Norfolk,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
Lead *Apple* Solutions Consultant - Apple (...
# Lead Apple Solutions Consultant Job Number: 51829230 Detroit, Michigan, United States Posted: Sep. 19, 2016 Weekly Hours: 40.00 **Job Summary** The Lead ASC is an Read more
US- *Apple* Store Leader Program - Apple (Un...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.