/*** * Newton Keyboard Enabler * * Erlaubt die Nutzung eines seriellen Newton- * Keyboards an einem Mac. Das Keyboard verhŠlt * sich in fast allen FŠllen genau wie ein * originales ADB-Keyboard (Ausnahme: MacsBug * kann es nicht nutzen) * * Entwickelt mit dem CodeWarrior 9 von * Metrowerks. * * (c)1996 MAXON Computer, Markus Fritze ***/ // c_moeller@macopen.com // true, wenn das Programm beendet werden soll Boolean gDoQuitFlag; /*** * unsere AppleEvent-Routinen * (schlie§lich sind wir ein ordentliches * MacOS Programm) ***/ static pascal OSErr DoAENoErr( const AppleEvent*, AppleEvent*, long) { return noErr; // AppleEvent ist ok } static pascal OSErr DoAEQuitAppl( const AppleEvent*, AppleEvent*, long) { gDoQuitFlag = true; // Programm beenden return noErr; } // einen (hoffentlich) undefinierten Code // benutzen wir als ID-Code fŸr die Tastatur #define NEWTON_KEYBOARD_CODE 117L // Zugriffsfunktionen Šhnlich // fŸr den Tastaturtreiber static inline SInt16 LMGetKeyLast() { return *(SInt16*)0x0184; }; static inline void LMSetKeyLast(SInt16 value) { *(SInt16*)0x0184 = value; }; static inline SInt16 LMGetHiKeyLast() { return *(SInt16*)0x0216; }; static inline void LMSetHiKeyLast(SInt16 value) { *(SInt16*)0x0216 = value; }; static inline SInt32 LMGetKeyTime() { return *(SInt32*)0x0186; }; static inline void LMSetKeyTime(SInt32 value) { *(SInt32*)0x0186 = value; }; static inline SInt32 LMGetKeyRepTime() { return *(SInt32*)0x018A; }; static inline void LMSetKeyRepTime(SInt32 value) { *(SInt32*)0x018A = value; }; // ohne "inline", wegen eines 68k Compilerbugs // beim CodeWarrior 9 static /*inline*/ KeyMap *LMGetKeyMapPtr() { return (KeyMap*)0x0174; }; // Unsere globalen Variablen fŸr die Tastatur Handle gKMAP; Handle gKCHR; UInt8 gKeyMap[16]; /*** * Keyboard-Variablen initialisieren ***/ static void InitKeyboard() { Handle thePref = ::Get1Resource('PREF', 128); // eigener Typ: Newton Keyboard gKMAP = ::Get1Resource('KMAP', **thePref); if(!gKMAP) ::ExitToShell(); ::HLockHi(gKMAP); // ein deutsches Keyboard: gKCHR = ::GetResource('KCHR', ((short*)*thePref)[1]); if(!gKCHR) // ein US-Keyboard: gKCHR = ::GetResource('KCHR', 0); if(!gKCHR) ::ExitToShell(); ::HLockHi(gKCHR); // eigene Keymap lšschen for(int i=0; i raus if(inKey == 0x00L) return; // Message zusammensetzen UInt32 theMessage = inKey | UInt16(inKeyCode << 8) | (NEWTON_KEYBOARD_CODE << 16); // Taste gedrŸckt if(!(inKeyCode & 0x80)) { SInt32 theTicks = LMGetTicks(); LMSetKeyTime(theTicks); LMSetKeyRepTime(theTicks); LMSetKeyLast(theMessage); LMSetHiKeyLast(NEWTON_KEYBOARD_CODE); ::PostEvent(keyDown, theMessage); // Taste losgelassen } else { // Key-Up-Flag lšschen theMessage &= 0xFFFF7FFF; ::PostEvent(keyUp, theMessage); } } /*** * Tastendruck (bzw. das Loslassen) dem MacOS * melden ***/ static void EnterKeycode(UInt8 inCode) { // aktuelle Taste im System lšschen LMSetKeyLast(0); LMSetHiKeyLast(0); // true, wenn Taste losgelassen wurde Boolean theDownFlag = (inCode & 0x80) == 0x80; // MacOS-Keycode erzeugen UInt8 theKeyCode; Ptr theKMAP = *gKMAP; theKeyCode = theKMAP[(inCode & 0x7F) + 4]; // Sondercode erkannt? if(theKeyCode & 0x80) { // erstmal das Kennungs-Bit lšschen theKeyCode &= 0x7F; // Anzahl der SondereintrŠge SInt16 theCount = *reinterpret_cast (&theKMAP[0x84]); // ab hier geht es mit den Tabellen los UInt8 *theKMapP = reinterpret_cast (&theKMAP[0x86]); while(theCount-- > 0) { // Code gefunden? if(*theKMapP++ != theKeyCode) { // zum nŠchsten Eintrag theKMapP += theKMapP[1] + 2; continue; } if((*theKMapP & 0x0F) == 0x00) return; break; } } // Capslock Abfrage if(theKeyCode == 0x39) { if(theDownFlag) { // Taste gedrŸckt? // Caps bereits gesetzt? if(gKeyMap[theKeyCode >> 3] & (1 << (theKeyCode & 7))) { // dann lšsen! theDownFlag = false; } } else { // Taste losgelassen? // (das interessiert uns nie!) return; } } // in die KeyMap eintragen (vorerst nur in // die eigene) if(theDownFlag) { gKeyMap[theKeyCode >> 3] |= 1 << (theKeyCode & 7); } else { gKeyMap[theKeyCode >> 3] &= ~(1 << (theKeyCode & 7)); // Flag fŸr "losgelassen" theKeyCode |= 0x80; } // Tastencodes in globalen Variablen merken LMSetKbdLast(theKeyCode); LMSetKbdType(NEWTON_KEYBOARD_CODE); // globale KeyMap updaten ::BlockMoveData(gKeyMap, LMGetKeyMapPtr(), sizeof(KeyMap)); // aktuelle Modifiers fŸr KeyTranslate lesen UInt16 theModifiers = *(3 + reinterpret_cast (LMGetKeyMapPtr())); // ROL.W #1, theModifiers = (theModifiers >> 15) | (theModifiers << 1); // ASCII-Codes (denkbar: zwei pro // Tastendruck!) errechnen static UInt32 state = 0; UInt32 lStructure = ::KeyTranslate(*gKCHR, theKeyCode | (theModifiers << 8), &state); // ggf. zwei Tasten posten PostKeyMessage(lStructure >> 16, theKeyCode); PostKeyMessage(lStructure, theKeyCode); } /*** * diese asynchrone Routine pollt das Keyboard * an der Seriellen ***/ #include // UPP fŸr die Callback-Routine IOCompletionUPP gIOUPP; // Refnums fŸr Serial ein/aus SInt16 gSDIn, gSDOut; // das empfangene Zeichen UInt8 gInChar; // der Parameterblock (asynchron!) ParamBlockRec gParamBlk; /*** * das nŠchste Byte von der * Tastatur asynchron lesen ***/ static void GetNextByte() { if(gDoQuitFlag) return; // Callback setzen gParamBlk.ioParam.ioCompletion = gIOUPP; // Port lesen gParamBlk.ioParam.ioRefNum = gSDIn; // Buffer auf unser Byte gParamBlk.ioParam.ioBuffer = (Ptr)&gInChar; // ein Byte lesen gParamBlk.ioParam.ioReqCount = 1L; // ab der aktuellen Position gParamBlk.ioParam.ioPosMode = fsAtMark; // kein Offset... gParamBlk.ioParam.ioPosOffset = 0L; // Anforderung absetzen PBReadAsync(&gParamBlk); } /*** * Diese Routine wird angesprungen, * wenn ein Byte eingetroffen ist. ***/ static void MyCompletion( ParmBlkPtr ioParam : __A0) { #pragma unused(ioParam) // Byte verarbeiten EnterKeycode(gInChar); // nŠchstes Byte anfordern GetNextByte(); } /*** * main() ***/ void main() { // 16k anstatt 2k an Stack! ::SetApplLimit((Ptr)((UInt32) ::GetApplLimit() - 0x4000)); // Crasht vor MacOS 7.5.4, falls eine zweite // FBA ebenfalls MaxApplZone() aufruft: // ::MaxApplZone(); // weitere Init-Calls sind bei FBAs nicht // erlaubt ::InitGraf(&qd.thePort); // AppleEvents installieren (wenn vorhanden) long response; if(!::Gestalt(gestaltAppleEventsAttr, &response)) { if(response & (1L<<0x10> 0x02, // 'd_id', 0x0CL, // Device-ID? // 'kybd','appl', 0x01L, // Keyboard-Typ // 'nofm', 0L, 0x1003dde7L // ??? if(reinterpret_cast(&theBuf)[3] != 'ybda') goto raus; gIOUPP = NewIOCompletionProc(MyCompletion); GetNextByte(); // erstes Byte erwarten gDoQuitFlag = false; while(!gDoQuitFlag) { EventRecord theEvent; // nur einmal pro Sekunde erwarten wir einen // Null-Event! ::WaitNextEvent( everyEvent, &theEvent, 60, 0L); if(theEvent.what == kHighLevelEvent) ::AEProcessAppleEvent(&theEvent); #if DEBUG // zum Debuggen: '^' + Control + Option // beendet das Programm! KeyMap theMap; ::GetKeys(theMap); if((theMap[0] & 0x40000) && ((theMap[1] & 0xC) == 0xC)) { break; } #endif } // auf ein letztes Byte warten! SysBeep(10); SysBeep(10); SysBeep(10); // auf Abschlu§ des aktuellen Polls warten while(gParamBlk.ioParam.ioResult > 0) {} // Tastaturstatus zurŸcksetzen LMSetKeyLast(0); LMSetHiKeyLast(0); for(int i=0; i