TweetFollow Us on Twitter

Typecasting
Volume Number:2
Issue Number:8
Column Tag:Intermediate Mac'ing

Typecasting Rascal to Pascal

By David E. Smith, Editor & Publisher

A Complete (nearly) TML Shell

When I first saw this Keyboard Sleuth program, I thought, "How neat! But who has Rascal?", so after looking over the program, I figured I would just translate it into TML Pascal. Right! Three days and 60 hours later, I finally duplicated the program function in Pascal. In so doing, I gained an appreciation for how much work the development system shell in Rascal does for you, and how much programming effort must be spent typecasting variables in Pascal. Admittedly, I am an assembly language programmer, so when you tell me I need four bytes for a pointer, I don't care in the least what object the pointer points to. Not so in Pascal. Every time you turn around, you have to figure out some "magic" variable type to fool Pascal into letting you violate it's strongly typed variable rules. This is particularly a pain in the neck when dealing with the message field of the event record, since it's meaning changes (and hence it's type!) with each different event, even though it's length of course is always four bytes, or LONGINT. If I had understood typecasting better, the conversion would have been much easier.Whoever thought strongly typed variables would make a programmer's life easier never had to program for a living. Macintosh programs would decrease in size and complexity dramatically if mixing of variable types was both allowed and automatic by the compiler. This is one area where a good Basic compiler could really make toolbox programming simple and quick. (Perhaps that is the appeal of a langauge like Rascal in the first place!) Here are some typecasting rules that should make this easier for you than it was for me.

Typecasting on the Mac

Types in the Macintosh:

SignedByte: any byte in memory (-128..127)

Byte: unsigned byte (0..255)

Ptr: a pointer or address (^SignedByte)

Handle: address of a Ptr (^Ptr)

The Signed Byte is the fundamental type. Any size of bytes of memory can be created as a pascal data structure using the following:

Packed Array[1..size] of SignedByte

I'm still not sure what a string is, but the Macintosh string type is Str255, which is defined as STRING[255], which I finally found out on page I-91 of Inside Macintosh, means a four byte pointer to what I assume is a packed array of char that can be up to 255 characters in length. A byte length count is added at the beginning for a total size of 256 bytes. This was my assumption in the program, and it seems to work. So everytime you see Str255 in a program, it's not really the string, but a four byte ptr to the length byte of the string.

To get around the type casting problem for a pre-defined Pascal type, you do something like this:

Var
 key: Byte
 code: Char
Begin
 key:=Byte(code);  {types must be same length}

This forces code, which is a Char type into Key, which is a Byte type, by "reminding" Pascal with the Byte designator, that key is of type Byte. You'd think the compiler could figure that out for himself!

For non-predefinded types, it gets more complicated. Basically you have to create something in an allowed type and then force it into your type. The example below shows how to peek at a byte location in memory. We create a Handle (which always points to a signed byte), then typecast it into the Handle we really want (magicHandle), then use another Pascal mystery, the case boolean of, to equate the four byte LONGINT type to four individual bytes of Byte type so we can read a single byte. This is the most magic of all. It seems as if it violates the fundamental law of the universe, by making TRUE and FALSE both valid at the same time! I guess only a computer could accomplish that.

Peeking into Memory

Type
 magicHandle = ^magicptr;
 magicptr = ^magic;
 magic =packed record
 case boolean of
 true: (L:LONGINT);
 false: (byte3,byte2,byte1,byte0:Byte)
 end;
Var
 tempHandle:Handle;               {to signed byte}
 magicman:magicHandle;   {this seems magic to me}
 addr:  integer;
 mysize:integer;
Begin
 addr:=$021E;  {some place in memory to peek}
 mysize:=SIZEOF(magicman);
 tempHandle:=NEWHANDLE(mysize);
 magicman:=magicHandle(tempHandle);  {typecasting!}
 magicman^:=pointer(addr);     {more typecasting!}
 KbdType:=magicman^^.byte3;   {Finally!}
 disposHandle(tempHandle);
End;

Inside Macintosh says strong typing ability lets Pascal programmers write programs without really considering the size of variables (page I-86), which would be an advantage if we were writing Pascal programs. But on the Mac, you really write toolbox programs, which are assembly programs, which requires you to know the length of each type, which requires a lot of typecasting (I guess). If anyone else has an explanation of why we should ever have to typecast same length variables, I'd like to hear it.

The final problem is one of incrementing or decrementing an address in memory so you can point somewhere else. This gave me fits until I learned how you do it. First you convert the pointer back into a number (it wasn't already?). Then you do your number thing on it (add, subtract, etc.) Then you convert it back into a pointer. A whole set of functions let you do all this mashing of types. Here they are from page I-80 of Inside Macintosh:

Var
 anInteger: INTEGER;
 ALongInt:  LONGINT;
 aPointer:  Ptr;
Begin
anInteger: =   ORD(aLongInt); {two low bytes}
anInteger:= ORD(aPointer);{two low bytes}
aLongInt:=ORD(anInteger); {packed in high order}
aLongInt:=ORD4(anInteger);{packed in low order}
aLongInt:=ORD(aPointer);  {make ptr a number}
aPointer:=POINTER(anInteger); {make into an address}
aPointer:=POINTER(aLongInt);{make into an address}
end;

The POINTER function converts a number into an address. So to increment an address, you:

NewAddress:=POINTER(ORD(@Str)+1);

This converts the pointer to the string to a LONGINT number, allowing you to increment it by one, and converts it back to a pointer. Now you've learned what took me 60 hours of Mac study to figure out. Namely how to point to the string data past the length byte. If it's old hat to you, then you're a Mac Hacker of long standing. If it's new, then welcome to the Macintosh world.

How Do You Print a String?

The main thing that Keyboard Sleuth does is poke around in the Mac and try and figure out what kind of keyboard you have. Then it prints all the information to the screen and a log file and sits in a loop waiting for you to press a key. When you do, it zaps out a message of what key number you pressed and what it's ascii value is and displays the character itself, if it's not a control character. So the main idea is to display back a lot of information to the user. How do you do that on the Mac? If you look at all the Mac programming books, and I've been through them all, generally they all assume its the user that wants to display stuff on the screen, not the program. In Rascal, it's easy as the following example shows from our program.

Rascal PutString Procedure

Procedure PutString(str: PtrB); 
    BEGIN
        writestring(str);       (* to the screen *)
        IF logfile THEN
            fPutS(logfile, str);   (* to the file *)
    END;

Making the Simple into the Complex

What they don't tell you is a lot of housekeeping is done on the Mac for you. For example, if you print a bunch of stuff on the screen, what happens when you get to the bottom of the screen? Oh, you mean you want scrolling? Well, that's a horse of a different color. Or suppose someone opens the calculator DA, and your list is obliterated. Oh, you mean you want updating? That's TextEdit. Or suppose you want to save the data you displayed for the user. Oh, you want files? Well better go see standard file dialog about that! Or suppose you want to back up so the user can see what you printed out for him. Oh, well, you better get some controls then and handle a scroller event. And on and on it goes until your simple Print "hello world" becomes a full blown mac application. Such is what I got caught up into when I started converting this program to Pascal. I quit when I got to scrolling and saved controls for another day!

Pascal Version of PutString

Take a look at the procedure PutString in our Pascal program listing and you will see all of these issues we've discussed. We insert the text, pointing to the string data, not the length, into our text edit record. This basically gets the data on the screen. Then we check to see if we have reached the end of the screen, by looking at the line counts times the character height from the text edit record. In this way, it still works if someone changes fonts later. If we need scrolling, we scroll up, again using the character height as a scroll value. The TEInsert writes our text to the screen, and generates a new update event if the insertion changes anything downstream, which is doesn't in the case of printing lists.

KeyTrans Assembly Routine

The final problem had to do with the keytrans routine. This routine pokes around for the correct ROM routine to translate a key code into ascii. The clever thing is that this routine changes depending on what your country is, so by installing custom translation routines, you make custom Macs. By using this Mac routine to convert to ascii for us, we can see what character is returned and guess at the keyboard type. To do this we have to call a machine language routine. So keytrans is an assembly glue routine that allows us to call a Mac routine and return the results back to Pascal. This led to more investigations on how to call assembly from Pascal and how to call Pascal from assembly. The key to it all is the A6 frame pointer. How it works is shown in the figure below.

File Stuff Not Polished

Be aware that the file routine in OpenLog is very primitive and doesn't do anything with the error checking. I had the program bomb on me once or twice for some reason after the standard file dialog, so you should put some effort into beefing up the error checking coming back from the various disk I/O routines. This is a great little Mac demo program that is both useful and makes a good shell program showing off file I/O, text edit and scrolling. I learned a lot from it. Thanks Joel!

program KeyboardSleuth;

{  Keyboard Sleuth: analyze key mappings
   Stand-alone version written in Rascal
   By Joel West, August 1986, for MacTutor
 
 ** Converted to TML Pascal by David E. Smith **
 
   Tries to figure out what keyboard is installed
   Uses several approaches:
      -Dump and analyze keyboard #
 -Check keypad for Mac 512 vs. Mac Plus
 -Look at INTL resources to find for country code
 -Check for mapping of space key (US vs. Foreign)
 Then allows user to type keys and shows their keycodes 
 and ASCII values. Dumps this to screen and to a logfile }

{ Include files and constants }

{$I MemTypes.ipas  }
{$I QuickDraw.ipas }
{$I OSIntf.ipas    }
{$I ToolIntf.ipas  }
{$I PackIntf.ipas  }
{$I HFS.ipas       }

{ ---------------- GLOBAL CONSTANTS ------------ } 
CONST
    Key1Trans = $29E;          { Low Memory Globals }
    Key2Trans = $2A2;
    EOL = 13;                  { end of line file delimiter (RETURN) 
}
 {menu res id's }
 AppleMenu = 256;
 FileMenu  = 257;
 EditMenu  = 258;
 
{ ---------------- ASCII values ------------ }
    Space = $20;                {   }

{ Key #10, where US,UK "/" is (key # differs in US) }

    Slash = $2F;                { /    UK      }
    Minus = $2D;               { -    German, Spanish, Swedish }
    Equals = $3D;             { =    French  }
    Ograve = $98;             { ò    Italian }
    Eaigu = $8E;                { é    French Canadian }
    
{ Key # 36, where UK "`" (accent grave) is
   Used only to distinguish Spanish from German and Swedish } 
   
    Degree = $A1;            { °    Spanish/Latin American }
    Hash = $8A;                { #    German   }
    Apos = $27;                 { '    Swedish  }
 
{ ---------------- Keycap Numbers ------------ }

    USspKey = 49;             { space bar in US }
    UKspKey = 52;             { space bar in UK, Euro-Classics}
    UKslKey = 10;               { / key in UK }
    UKgrKey = 36;               { ` (dead) key in UK }

{ ---------------- GLOBAL VARIABLES ------------ } 
VAR
{my stuff}
 mywindow:  WindowPtr;  { our window pointer }
finished: Boolean; {program terminator}
ClockCursor:CursHandle; {handle to waiting watch }
{STDFile stuff}
 logfile: INTEGER; { file status }          
 logname: STR255;{ file name }
volNumber:  INTEGER;    { vRefNum }
fileNumber: INTEGER; { file number }
{Screen stuff}
DragArea:   Rect;           
GrowArea:   Rect;           
Screen:     Rect;         {holds the screen dimensions }
{TextEdit stuff}
DestRect: Rect;  
ViewRect: Rect;
theText: TEHandle;
scrollflg:Boolean;

{ ---------------- BEGIN CODE ------------ } 

Function KeyTrans(keyno,modifies: Integer) : Integer;    EXTERNAL; 
{$U keytrans}
{ Translate key number and modifiers to 
   their corresponding ASCII value  }

Function CR:str255;
 begin
 CR:= chr(EOL)
 end;
 
PROCEDURE Openlog;
{ open keyboard logfile to save all messages for later review }
label 1;
Var   
 where:     Point;
 Prompt:    STR255;
 origName:  STR255;
 reply:     SFReply; { standard file reply record }
 Info:  FInfo;   { Finder file info reply record }
 vol:   INTEGER;   { vRefNum }
 fileno:INTEGER;   { file number }
 resultCode:OSErr; 
Begin
   where.v := 50;
   where.h := 50;
   Prompt := 'Save your log file as:';
   origName := 'KeyBoard Log';
   DILoad;{in case disks are switched}
   SFPutFile(Where, Prompt, origName, Nil, reply);
   logname := reply.fName;
   vol := reply.vRefNum;
   IF reply.good = FALSE THEN 
   logfile := 0  {bad file} 
 ELSE 
   logfile:= 1;  {good file}
 IF logfile = 0 THEN goto 1;
 
 resultCode:=GetFInfo (logname, vol, Info);
 case resultCode of
 
 NoErr:  { file exists..delete it }
 Begin
 if Info.fdType <>'TEXT' then 
 begin
 logfile:=0;
 goto 1;
 end;
 resultCode:=RstFlock(logname,vol);
 if resultCode <> NoErr then 
 begin
 logfile:=0;
 goto 1;
 end;
 resultCode:=FSDelete(logname,vol);
 if resultCode <> NoErr then
 begin
 logfile:=0;
 goto 1;
 end;
 resultCode:= Create (logname, vol, 'MACA', 'TEXT');
 if resultCode <> NoErr then
 begin
 logfile:=0;
 goto 1;
 end;   
 end; 
 FNFErr:{ file not found so create one }
 begin
 resultCode:= Create (logname, vol, 'MACA', 'TEXT');
 if resultCode <> NoErr then
 begin
 logfile:=0;
 goto 1;
 end;
 end;   
 OTHERWISE logfile:=0;
 End; { case }
 if logfile = 0 then goto 1;
 
resultCode:= FSOpen (logname, vol,fileno);  { open log file }
 if resultCode <> NoErr then
 begin
 logfile:=0;
 goto 1;
 end;
 resultCode:= SetFPos (fileno, FSFromStart, 0);
 if resultCode <> NoErr then
 begin
 logfile:=0;
 goto 1
 end;
 volNumber:=vol;
 fileNumber:=fileno; 
1:
 if logfile = 1 then 
 SetWTitle(mywindow, logname)
 else 
 SetWTitle(mywindow, 'No Log File!');
End;

Procedure PutString(str: Str255);
{ Write a string to the log file and to the screen }
Var
 resultCode:OSErr;
 strlen:LONGINT;
 scrollup:integer;
 curlines:integer;
 linepos: integer;
 newpos:integer;
 endpos:integer;
BEGIN   
 strlen:=length(str);
 TEInsert(POINTER(ORD(@str)+1),strlen,theText);
 TEIdle(theText);
 HLock(handle(theText));
 IF (not scrollflg) then
 begin  
 scrollup:=theText^^.lineHeight;
 curlines:=theText^^.nLines;
 linepos:=curlines*scrollup;
 endpos:=theText^^.ViewRect.bottom;
 if (linepos>=endpos) then 
 scrollflg:=true;
 end;
 if scrollflg then TEScroll(0,-theText^^.lineHeight,theText)   HUnlock(handle(theText));
 IF logfile = 1 THEN
 Begin
 resultCode:= FSWrite (fileNumber, strlen, POINTER(ORD(@str)+1));
        if resultCode <> NoErr then logfile:=0;
 End;
END;

Function IntToString(num: Integer):str255;
{integer to string}
VAR
 s: Str255;
 longnum: LongInt;
BEGIN
 longnum:=num;
 NumToString(longnum, s);
 IntToString:=s;
END;

Function KbdType: Integer;
{ Get low memory value at $21E, a byte, the keyboard no. }
Type
 magicHandle=^magicptr;
 magicptr = ^magic;
 magic = packed record
 case boolean of
    true:  (l: longint);
    false: (byte3,byte2,byte1,byte0: Byte)
 end;
Var
 tempHandle: Handle;    {handle to signed byte}
 magicman:magicHandle;  {handle to magic}
 addr:  INTEGER;
 mysize:INTEGER;
 
BEGIN   
   addr:= $021E;
 mysize:=SIZEOF(magicman);
 tempHandle:=NewHandle(mysize);
 magicman:=magicHandle(tempHandle);
 magicman^:=pointer(addr);
 KbdType:=magicman^^.byte3;
 disposHandle(tempHandle);
END;

Procedure ShowIntlNation;
{ Show }
VAR
 country: integer;
 ih: intl0Hndl;
 s:str255;
 known: Boolean; 
BEGIN
    ih := intl0Hndl(IUGetIntl(0)); { get INTL 0 resource }
    country := (ih^^.intl0Vers) div 16;     { country is upper byte }
 s:='This Mac is configured for ';
 known:=true;  {be optomistic}
 
{ There are symbolic constants for these (verUS, verFrance, etc.), but 
unless even if you have the latest update to your development system, 
you probably won't have all 26.  I've hard-coded them for clarity.   
}
   
       CASE country OF
           0:   s:=concat(s,'the US or Canada'); 
           1:   s:=concat(s,'France'); 
           2:   s:=concat(s,'U.K. or Ireland'); 
           3:   s:=concat(s,'Deutschland');       { Germany }
           4:   s:=concat(s,'Italia'); 
           5:   s:=concat(s,'Nederland');          { Netherlands }
           6:   s:=concat(s,'Belgique ou Luxembourg'); 
           7:   s:=concat(s,'Sverige');            { Sweden }
           8:   s:=concat(s,'Españá');             { Spain }
           9:   s:=concat(s,'Danmark'); 
          10:   s:=concat(s,'Portugal'); 
          11:   s:=concat(s,'Quebec');             { French Canada }
          12:   s:=concat(s,'Norge');              { Norway }
          13:   s:=concat(s,'Yisra’el'); 
          14:   s:=concat(s,'Nippon');             { Japan }
          15:   s:=concat(s,'Australia or New Zealand'); 
          16:   s:=concat(s,'Arabiyah'); 
          17:   s:=concat(s,'Suomi');              { Finland }
          18:   s:=concat(s,'Suisse');             { French Swiss }
          19:   s:=concat(s,'Schweiz');            { German Swiss }
          20:   s:=concat(s,'Ellas');              { Greece }
          21:   s:=concat(s,'Island');             { Iceland }
          22:   s:=concat(s,'Malta'); 
          23:   s:=concat(s,'Kypros');             { Cyprus } 
          24:   s:=concat(s,'Türkiye'); 
          25:   s:=concat(s,'Jugoslavija');    
 OTHERWISE
 Begin
 known:=false;
s:=concat(s,'an unknown country, #',IntToString(country),'. ');
 End;
    END;  {case} 
 if known then s:=concat(s,'. ');
 s:=concat(s,CR,CR);
      PutString(s);
END;

Procedure ShowModel;
{ Guess which type of Macintosh keyboard }
Var
 s,ss:str255;
 Kbd:INTEGER;
BEGIN
{ Use derived keyboard numbers }
 Kbd:=KbdType;
 ss:=IntToString(Kbd);
   s:=concat('The keyboard type is ',ss);
 CASE Kbd OF
 11: s:=concat(s,', which is a Mac Plus keyboard.');
 3:s:=concat(s,', which is the Classic Mac
  keyboard.');
 OTHERWISEs:=concat(s,', which is unknown.');
 END;  {case}
 s:=concat(s,CR);
 PutString(s);
END;

Procedure GuessKeyNation;
{ Guess which country keyboard mappings are set for  }
Var
 s: str255;
BEGIN 
{ Try mapping of certain keys to figure US vs. non-US board }
IF (KeyTrans(USspKey,0) = Space) THEN
 begin
 s:='This is US, Canadian or down under.';
 end  {IF..THEN}
ELSE
BEGIN
 IF (KeyTrans(UKspKey,0) = Space) THEN
      BEGIN
 { Use UK "/" key to guess at nationality }
      CASE KeyTrans(UKslKey,0) OF
      Slash:              { /    UK      } 
        s:=concat(s,'I am British or Dutch.');
      Ograve:             { ò    Italian }
        s:=concat(s,'Sono Italiano.');
      Equals:             { =    French  }
        s:=concat(s,'Je suis français, suisse ou belge.');
      Eaigu:              { é    French Canadian }
        s:=concat(s,'Je suis canadien.');
      Minus:              { -    German, Spanish, Swedish  }
 
 { Use UK accent grave (dead `) to tell 
 German, Spanish, and Swedish }
 
        CASE KeyTrans(UKgrKey,0) OF
        Hash:       { #    German   }
          s:=concat(s,'Ich bin ein Deutscher.');
        Degree:     { ç    Spanish  }
          s:=concat(s,'Habla Español.');
        Apos:       { '    Swedish   }
          s:=concat(s,'This is Swedish.');
        OTHERWISE   { I have no country! }
          s:=concat(s,'¡No tengo un país!');
        END; {case UKgrKey}
   OTHERWISE
 begin
        s:=concat(s,'I am a Mac without a country!');
 end; {otherwise}
      END;  {CASE} 
   END  {IF...THEN}
 ELSE
 begin
   s:=concat(s,'Neither US nor European, what is it?');
 end; {else}
END;  {IF..THEN..ELSE}
s:=concat(s,CR,CR,'Type keys, or click mouse to quit.',CR);
PutString(s);
END;  {proc}

Procedure DoMyStuff;
Var
 s: str255; 
BEGIN 
 OpenLog;              { log file }
 ShowIntlNation;       { Find country code }
 ShowModel;            { Examine keyboard type }
 GuessKeyNation;       { Look at key mappings } 
 showWindow(mywindow);  
END; 

{ Following  is standard Mac Shell stolen from TML Examples}

PROCEDURE DoMenu(select:longint);
Var
 Menu_No:    integer;   
   Item_No:    integer;   
   NameHolder: Str255;    {DA or Font name holder }
   DNA:        integer;   {OpenDA result
Begin
   If select <> 0 then 
   begin
     Menu_No := HiWord(select);   {get the Hi word of...}
     Item_no := LoWord(select);   {get the Lo word of...} 
     Case Menu_No of  
      AppleMenu: 
 Begin
GetItem(GetMHandle(AppleMenu), Item_No, NameHolder);
       DNA := OpenDeskAcc(NameHolder);
             End; {applemenu}
 FileMenu: Finished:=true;          {quit}
 EditMenu: 
 Begin
 If Not SystemEdit(Item_no - 1) then
   Case Item_No of
 1: begin end;           {undo}
   { 2:             line divider}
 3: TECut(theText);      {cut}
 4: TECopy(theText );    {copy}
 5: TEPaste(theText );   {paste}
 6: TEDelete(theText );  {clear}
        End; {case}
 End;  {editmenu}
 end; {case menu_no} 
      HiliteMenu(0);     {unhilite after processing menu}
   end; {If select <> 0}   
End; {of DoMenu procedure}

PROCEDURE doMouseDowns(Event:EventRecord);
Var   
 Location :integer;
      WindowPointedTo:WindowPtr;
      MouseLoc   :Point;
      WindoLoc   :integer;
Begin
   MouseLoc := Event.Where;
   WindoLoc := FindWindow(MouseLoc, WindowPointedTo);
   Case WindoLoc of 
      inMenuBar:   DoMenu(MenuSelect(MouseLoc)); 
      inSysWindow: SystemClick(Event,WindowPointedTo); 
      inContent:
   if WindowPointedto <> FrontWindow then 
   SelectWindow(WindowPointedTo);
 inGrow:Begin End; {no grow}      inDrag:          DragWindow(WindowPointedTo,MouseLoc,DragArea); 
 inGoAway:
   Begin
   If TrackGoAway(WindowPointedTo,MouseLoc) then 
 Begin  
 DisposeWindow(WindowPointedTo);
 finished:=true;
 End;
 End; {inGoAway}
End{ of case};
End;

PROCEDURE doKeyDowns(Event:EventRecord);
Type
 magicHandle=^magicptr;
 magicptr = ^magic;
 magic = packed record
 case boolean of
    true:  (l: longint);
    false: (byte3,byte2,byte1:Byte;chr0: Char)
 end;
Var 
 CharCode:  char;
 keycode: Byte;
 mods:  INTEGER;
 s:str255;
 keyc:  INTEGER;
 asc:   INTEGER; 
 tempHandle: Handle;    {handle to signed byte}
 magicman:magicHandle;   {handle to magic}
 mysize:INTEGER; 
Begin   
 mysize :=SIZEOF(magicman);
 tempHandle :=NewHandle(mysize);
 magicman :=magicHandle(tempHandle);
 magicman^^.l  :=Event.message;
 CharCode :=magicman^^.chr0;
 keycode:=magicman^^.byte1;
 keyc   := keycode;
 mods   := Event.modifiers; 
 s:=concat('Key #',IntToString(keyc));

 IF BitAnd(mods,optionKey) = optionKey THEN
 s:=concat(s,' with Option'); 
 IF BitAnd(mods,shiftKey) = shiftKey THEN
 s:=concat(s,', shifted');
 IF BitAnd(mods,alphaLock) = alphaLock THEN
 s:=concat(s,', Caps Locked');
 asc := KeyTrans(keyc,mods);     { try translate to ASCII }
 { Don't want to print control characters }
 IF asc >= 32 THEN
 BEGIN                
 s:=concat(s,' is ',chr(asc),' (ascii ',IntToString(asc),').');
 END;
 s:=concat(s,CR);
 PutString(s)             
END;
               
PROCEDURE doActivates(Event: EventRecord);
Var   TargetWindow:WindowPtr;
Begin
   TargetWindow := pointer(ord4(Event.message));
   If Odd(Event.modifiers) then
   Begin{activate}
   SetPort(TargetWindow);
   End
   else {deactivate} 
    Begin End;
End;

PROCEDURE doUpdates(Event:EventRecord);
Var   
 UpDateWindow,TempPort: WindowPtr;
Begin
   UpDateWindow := pointer(ord4(Event.message)); 
   if UpDateWindow = mywindow then
   Begin
   GetPort(TempPort);      {Save the current port}
   SetPort(mywindow);      {set the port to one in Evt.msg}
   BeginUpDate(mywindow);
   EraseRect(mywindow^.visRgn^^.rgnBBox);
 TEUpdate(mywindow^.visRgn^^.rgnBBox,theText);
   EndUpDate(mywindow);
   SetPort(TempPort);       {restore to the previous port}
 End;
End;

PROCEDURE EndProgram;     
Var
 resultcode:OSErr; 
Begin
    IF logfile =1 THEN
 begin
 resultCode:= FSClose(fileNumber);
 end;
 ExitToShell;
End;

PROCEDURE MainEventLoop;
Var   Event:EventRecord;
        DoIt: Boolean;    
Begin
 InitCursor;
   Repeat                
        SystemTask;      {support DAs}  
        DoIt := GetNextEvent(EveryEvent,Event);
        If DoIt{is true} then {we'll DoIt}
 Case Event.what of
mouseDown  : doMouseDowns(Event);  {1}
mouseUp : begin end; {2}
KeyDown     : doKeyDowns  (Event); {3}
keyUp   : begin end; {4}
autoKey : begin end; {5}
updateEvt   : doUpdates   (Event); {6}
diskEvt : begin end; {7}
activateEvt : doActivates (Event); {8}
 {abort evt now reserved for future} {9}
networkEvt: begin end;    {A}
driverEvt : begin end;    {B}
app1Evt : begin end; {C}
app2Evt : begin end; {D}
app3Evt : begin end; {E}
app4Evt : begin end; {F}
End;{of Case}
 Until Finished; {end program}
   EndProgram; {call our finish up stuff}
End;

PROCEDURE InitThings;
Begin
   InitGraf(@thePort);                
   ClockCursor := GetCursor(watchCursor);
   HLock(Handle(ClockCursor));
   SetCursor(ClockCursor^^);   
   InitFonts;                     
   InitWindows;                   
   InitMenus;                     
   TEInit;                        
   InitDialogs(Nil);              
   FlushEvents(everyEvent,0);
   scrollflg:=false; {too early to scroll!}
   finished:=false;{clear program terminator}   
End;

PROCEDURE SetupLimits;
Begin
Screen := ScreenBits.Bounds;   {screen 512 by 342 pixels}
SetRect(DragArea,Screen.left+4,Screen.top+24,
 Screen.right-4,Screen.bottom-4);
SetRect(GrowArea,Screen.left,Screen.top+24,
 Screen.right,Screen.bottom);   
End;

Procedure SetupWindows;
Const
 sbarwidth=16; {width of scroll bars}
Var
 myrect:      Rect;
 windtype: integer;
 Visible:  boolean;
 GoAway:   boolean;
 RefVal:   LongInt;
Begin
   SetRect(myrect,10,40,500,330); { window size -global cord}
   windtype := 4;                 {set window type - nogrowdocproc   
}
   Visible := false;              {set the window to invisible }
   GoAway := true;             {give the window a GoAway box }
   mywindow:= NewWindow(Nil,    { allocate space in Heap}
   myrect,         
   'Keyboard Sleuth',
   Visible,        
   windtype,       
   POINTER(-1),      {front}
   GoAway,         { goaway region in title area }
   RefVal);        { 32-bit value used by App}
   SetPort(mywindow);
 TextFont(Geneva);

{ Set Up Text Edit Record for this Window }

with myWindow^.portRect do
SetRect(ViewRect,4,4,right-(sbarwidth-1),
 bottom-(sbarwidth-1));
DestRect:=ViewRect;
theText:= TENew(DestRect,ViewRect);

   { NOTE: NewWindow initiated an ActivatEvt and an }
   {      UpDateEvt event. Being queued up by event manager.}
End;  
 
PROCEDURE SetupMenus;
Var myMenu:MenuHandle;
  NameHolder:STR255;             
Begin
   myMenu := GetMenu(AppleMenu);{from resource file}
   AddResMenu(myMenu,'DRVR');   {adds DAs}
   InsertMenu(myMenu,0);        
   myMenu := GetMenu(FileMenu); {Quiting...}
   InsertMenu(myMenu,0); 
   myMenu := GetMenu(EditMenu); {DA support...}
   InsertMenu(myMenu,0); 
   DrawMenuBar;           {show the menu bar}
End;

{ ---------------- MAIN PROGRAM ------------ }

BEGIN
   InitThings;
   SetupLimits;
   SetupWindows;  {do first so its low in heap}
   SetupMenus;   
   DoMyStuff;
   MainEventLoop;
END.


; EXAMPLE ASSEMBLY SUBROUTINE
; key test thing
; VERSION 11 July 1986
; (C) Copyright 1986 MacTutor 
INCLUDE MACTRAPS.D  
XDEF  keyTrans           ; required for linker     
; =========== system globals =============
Key1Trans equ $29E;    { Low Memory Globals }
Key2Trans equ $2A2;
; =========== key translation routine ======
KeyTrans: 
; key code (2 bytes) and modifiers (2 bytes) passed 
; ascii char code returned (2 bytes)
link  a6, #-4
movem.l A0-A1/D0-D2, -(SP)
; get key code from stack into D2
; get modifiers from stack into D1
move.w  8(A6), D1;second parameter (modifiers)
move.w  10(A6), D2 ;first parameter (key code)
move.w  #9, D0 ;shift count for flags
lsrD0,D1;move bits to lower byte
andi  #7, D1;mask 3 bits to get modify in D1
cmpi  #64, D2  ;keycode <64 then key1
BGEkey2 ;=>64 then key2

key1:
clr.l D0
LEAshowit, A0  ;get return address
move.l  A0, -(SP);return address to stack
move.l  #key1trans, A0  ;global for ptr to key1trans 
move.l  (A0), A0 ;get address of key1trans
jmp(A0) ;call subroutine

key2:
subi  #64, D2    ;adjust key code
clr.l D0
LEAshowit, A0  ;get return address
move.l  A0, -(SP);return address to stack
move.l  #key2trans, A0   ;global for ptr to key2trans 
move.l  (A0), A0 ;get address of key1trans
jmp(A0) ;call subroutine

showit:
; return function result from D0
move.w  D0, 12(A6) ;pass back function result
movem.l (SP)+, A0-A1/D0-D2
unlk  a6
move.l  (SP)+, A0;get return address
addq.l  #4, SP   ;remove passed parameters
JMP (A0)
; ------------   END OF PROGRAM ----------------
Link file
!PAS$Xfer

/Globals -4

Sleuth

PAS$Library
OSTraps
ToolTraps
PackTraps
keytrans

/TYPE 'APPL' 'DAV1'
/BUNDLE
/RESOURCES
Sleuth/Rsrc

$ 


*  Sleuth.R 
* 

Sleuth/Rsrc.Rel

Type DAV1 = STR 
  ,0
D. Smith & J. West 

Type FREF
  ,128
  APPL 0

Type BNDL
  ,128
  DAV1 0
  ICN#
  0  128
  FREF
  0  128

Type MENU
* the desk acc menu
 ,256
\14

* the file menu
 ,257
File
  Quit /Q

* the edit menu
 ,258
Edit
  Undo /Z
  (-
  Cut /X
  Copy /C
  Paste /V
  Clear

Type ICN# = GNRL
  ,128 (32)
.H
001F C000
0060 2000
0080 1000
011F 8800
0120 4400
0240 2200
0240 1200
0280 1200
0380 1200
0000 1200
0000 2200
0000 2400
0000 4400
0000 8800
0001 1000
0002 2000
0004 4000
0004 8000
0004 8000
0004 8000
0004 8000
0004 8000
3FFF FFFC
76DB 6DBA
B6DB 6DB9
8000 0001
BB6D B6DF
BB6D B6DF
8000 0001
B6FF FEED
76FF FEEE
3FFF FFFC
*
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Tunnelblick 3.7.3 - GUI for OpenVPN.
Tunnelblick is a free, open source graphic user interface for OpenVPN on OS X. It provides easy control of OpenVPN client and/or server connections. It comes as a ready-to-use application with all... Read more
Opera 48.0.2685.50 - High-performance We...
Opera is a fast and secure browser trusted by millions of users. With the intuitive interface, Speed Dial and visual bookmarks for organizing favorite sites, news feature with fresh, relevant content... Read more
Tor Browser Bundle 7.0.7 - Anonymize Web...
The Tor Browser Bundle is an easy-to-use portable package of Tor, Vidalia, Torbutton, and a Firefox fork preconfigured to work together out of the box. It contains a modified copy of Firefox that... Read more
FotoMagico 5.5 - Powerful slideshow crea...
FotoMagico lets you create professional slideshows from your photos and music with just a few, simple mouse clicks. It sports a very clean and intuitive yet powerful user interface. High image... Read more
Adobe Audition CC 2018 11.0.0 - Professi...
Audition CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Audition customer). Adobe Audition CC 2018 empowers you to create and... Read more
Alfred 3.5.1 - Quick launcher for apps a...
Alfred is an award-winning productivity application for OS X. Alfred saves you time when you search for files online or on your Mac. Be more productive with hotkeys, keywords, and file actions at... Read more
AirRadar 4.0 - $9.95
With AirRadar, scanning for wireless networks is now easier and more personalized! It allows you to scan for open networks and tag them as favourites or filter them out. View detailed network... Read more
DEVONthink Pro 2.9.16 - Knowledge base,...
Save 10% with our exclusive coupon code: MACUPDATE10 DEVONthink Pro is your essential assistant for today's world, where almost everything is digital. From shopping receipts to important research... Read more
ForkLift 3.0.8 Beta - 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
Data Rescue 5.0.1 - Powerful hard drive...
Data Rescue’s new and improved features let you scan, search, and recover your files faster than ever before. We have modernized the file-preview capabilities, added new files types to the recovery... Read more

Game of Thrones: Conquest guide - how to...
Fans of base building games might be excited to know that yet another entry in the genre has materialized - Game of Thrones: Conquest. Yes, you can now join the many kingdoms of the famed book series, or create your own, as you try to conquer... | Read more »
Halcyon 6: Starbase Commander (Games)
Halcyon 6: Starbase Commander 1.4.2.0 Device: iOS Universal Category: Games Price: $6.99, Version: 1.4.2.0 (iTunes) Description: An epic space strategy RPG with base building, deep tactical combat, crew management, alien diplomacy,... | Read more »
Legacy of Discord celebrates its 1 year...
It’s been a thrilling first year for fans of Legacy of Discord, the stunning PvP dungeon-crawling ARPG from YOOZOO Games, and now it’s time to celebrate the game’s first anniversary. The developers are amping up the festivities with some exciting... | Read more »
3 reasons to play Thunder Armada - the n...
The bygone days of the Battleship board game might have past, but naval combat simulators still find an audience on mobile. Thunder Armada is Chinese developer Chyogames latest entry into the genre, drawing inspiration from the explosive exchanges... | Read more »
Experience a full 3D fantasy MMORPG, as...
Those hoping to sink their teeth into a meaty hack and slash RPG that encourages you to fight with others might want to check out EZFun’s new Eternity Guardians. Available to download for iOS and Android, Eternity Guardians is an MMORPG that lets... | Read more »
Warhammer Quest 2 (Games)
Warhammer Quest 2 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: Dungeon adventures in the Warhammer World are back! | Read more »
4 of the best Halloween updates for mobi...
Halloween is certainly one of our favorite times for mobile game updates. Many popular titles celebrate this spooky season with fun festivities that can stretch from one week to even the whole month. As we draw closer and closer to Halloween, we'... | Read more »
Fire Rides guide - how to swing to succe...
It's another day, which means another Voodoo game has come to glue our hands to our mobile phones. Yes, it's been an especially prolific month for this particular mobile publisher, but we're certainly not complaining. Fire Rides is yet another... | Read more »
Time Recoil (Games)
Time Recoil 1.0.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.1 (iTunes) Description: Time Recoil is a top-down shooter where you kill to slow time, dominate slow motion gunfights, and trigger devastating special... | Read more »
Campfire Cooking (Games)
Campfire Cooking 1.0 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0 (iTunes) Description: | Read more »

Price Scanner via MacPrices.net

Apple restocks full line of refurbished 13″ M...
Apple has restocked a full line of Apple Certified Refurbished 2017 13″ MacBook Pros for $200-$300 off MSRP. A standard Apple one-year warranty is included with each MacBook, and shipping is free.... Read more
13″ 3.1GHz/256GB MacBook Pro on sale for $167...
Amazon has the 2017 13″ 3.1GHz/256GB Space Gray MacBook Pro on sale today for $121 off MSRP including free shipping: – 13″ 3.1GHz/256GB Space Gray MacBook Pro (MPXV2LL/A): $1678 $121 off MSRP Keep an... Read more
13″ MacBook Pros on sale for up to $120 off M...
B&H Photo has 2017 13″ MacBook Pros in stock today and on sale for up to $120 off MSRP, each including free shipping plus NY & NJ sales tax only: – 13-inch 2.3GHz/128GB Space Gray MacBook... Read more
15″ MacBook Pros on sale for up to $200 off M...
B&H Photo has 15″ MacBook Pros on sale for up to $200 off MSRP. Shipping is free, and B&H charges sales tax in NY & NJ only: – 15″ 2.8GHz MacBook Pro Space Gray (MPTR2LL/A): $2249, $150... Read more
Roundup of Apple Certified Refurbished iMacs,...
Apple has a full line of Certified Refurbished 2017 21″ and 27″ iMacs available starting at $1019 and ranging up to $350 off original MSRP. Apple’s one-year warranty is standard, and shipping is free... Read more
Sale! 27″ 3.8GHz 5K iMac for $2098, save $201...
Amazon has the 27″ 3.8GHz 5K iMac (MNED2LL/A) on sale today for $2098 including free shipping. Their price is $201 off MSRP, and it’s the lowest price available for this model (Apple’s $1949... Read more
Sale! 10″ Apple WiFi iPad Pros for up to $100...
B&H Photo has 10.5″ WiFi iPad Pros in stock today and on sale for $50-$100 off MSRP. Each iPad includes free shipping, and B&H charges sales tax in NY & NJ only: – 10.5″ 64GB iPad Pro: $... Read more
Apple iMacs on sale for up to $130 off MSRP w...
B&H Photo has 21-inch and 27-inch iMacs in stock and on sale for up to $130 off MSRP including free shipping. B&H charges sales tax in NY & NJ only: – 27″ 3.8GHz iMac (MNED2LL/A): $2179 $... Read more
2017 3.5GHz 6-Core Mac Pro on sale for $2799,...
B&H Photo has the 2017 3.5GHz 6-Core Mac Pro (MD878LL/A) on sale today for $2799 including free shipping plus NY & NJ sales tax only . Their price is $200 off MSRP. Read more
12″ 1.2GHz Space Gray MacBook on sale for $11...
Amazon has the 2017 12″ 1.2GHz Space Gray Retina MacBook on sale for $100 off MSRP. Shipping is free: 12″ 1.2GHz Space Gray MacBook: $1199.99 $100 off MSRP Read more

Jobs Board

*Apple* Retail - Multiple Positions - Farmin...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Frameworks Engineer, *Apple* Watch - Apple...
Job Summary Join the team that is shaping the future of software development for Apple Watch! As a software engineer on the Apple Watch Frameworks team you will Read more
*Apple* News Product Marketing Mgr., Publish...
Job Summary The Apple News Product Marketing Manager will work closely with a cross-functional group to assist in defining and marketing new features and services. Read more
Fraud Analyst, *Apple* Advertising Platform...
Job Summary Apple Ad Platforms has an opportunity to redefine advertising on mobile devices. Apple reaches hundreds of millions of iPhone, iPod touch, and iPad Read more
*Apple* Information Security - Security Data...
Job Summary This role is responsible for helping to strengthen Apple 's information security posture through the identification and curation of security event data. Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.