1 line
10 KiB
C++
1 line
10 KiB
C++
/***
|
|
* 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 <LowMem.h>
|
|
// 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<sizeof(gKeyMap); i++)
|
|
gKeyMap[i] = 0;
|
|
|
|
::ReleaseResource(thePref);
|
|
}
|
|
|
|
/***
|
|
* Tastencode senden
|
|
***/
|
|
static void PostKeyMessage(
|
|
UInt8 inKey, UInt8 inKeyCode)
|
|
{
|
|
// keine Taste => 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<SInt16*>
|
|
(&theKMAP[0x84]);
|
|
|
|
// ab hier geht es mit den Tabellen los
|
|
UInt8 *theKMapP =
|
|
reinterpret_cast<UInt8*>
|
|
(&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<UInt16*>
|
|
(LMGetKeyMapPtr()));
|
|
|
|
// ROL.W #1,<ea>
|
|
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 <Serial.h>
|
|
|
|
// 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<<gestaltAppleEventsPresent)) {
|
|
|
|
if(::AEInstallEventHandler(
|
|
kCoreEventClass,
|
|
kAEOpenApplication,
|
|
NewAEEventHandlerProc(DoAENoErr),
|
|
0L, 0))
|
|
return;
|
|
|
|
if(::AEInstallEventHandler(
|
|
kCoreEventClass,
|
|
kAEOpenDocuments,
|
|
NewAEEventHandlerProc(DoAENoErr),
|
|
0L, 0))
|
|
return;
|
|
|
|
if(::AEInstallEventHandler(
|
|
kCoreEventClass,
|
|
kAEPrintDocuments,
|
|
NewAEEventHandlerProc(DoAENoErr),
|
|
0L, 0))
|
|
return;
|
|
|
|
if(::AEInstallEventHandler(
|
|
kCoreEventClass,
|
|
kAEQuitApplication,
|
|
NewAEEventHandlerProc(DoAEQuitAppl),
|
|
0L, 0))
|
|
return;
|
|
}
|
|
}
|
|
|
|
// globale Keyboard-Variablen initialisieren
|
|
InitKeyboard();
|
|
|
|
// ".AIn" und ".AOut" šffnen
|
|
OSErr theErr;
|
|
Str255 theStr;
|
|
::GetIndString(theStr, 128, 2);
|
|
theErr = ::OpenDriver(theStr, &gSDOut);
|
|
if(theErr) ::ExitToShell();
|
|
::GetIndString(theStr, 128, 1);
|
|
theErr = ::OpenDriver(theStr, &gSDIn);
|
|
if(theErr) goto raus;
|
|
|
|
// 9600 8N1
|
|
theErr = ::SerReset(gSDOut,
|
|
baud9600+data8+stop10+noParity);
|
|
if(theErr) goto raus;
|
|
|
|
// Handshaking ausschalten
|
|
SerShk theSHandShk;
|
|
theSHandShk.fXOn = 0;
|
|
theSHandShk.fCTS = 0;
|
|
theSHandShk.errs = 0;
|
|
theSHandShk.evts = 0;
|
|
theSHandShk.fInX = 0;
|
|
theSHandShk.fDTR = 0;
|
|
theErr = ::Control(gSDOut, 14, &theSHandShk);
|
|
if(theErr) goto raus;
|
|
|
|
long theTicks;
|
|
// 1/2 Sekunde auf das Keyboard warten
|
|
::Delay(30, &theTicks);
|
|
|
|
// Anzahl der Byte an der Schnittstelle ermitteln
|
|
SInt32 theCount;
|
|
::SerGetBuf(gSDIn, &theCount);
|
|
|
|
// und alle lesen
|
|
Str255 theBuf;
|
|
::FSRead(gSDIn, &theCount, &theBuf);
|
|
|
|
// Daten von der Tastatur zum Rechner, wenn die
|
|
// Schnittstelle angeschaltet wird (9600 8N1):
|
|
// <0x16><0x10> 0x02,
|
|
// 'd_id', 0x0CL, // Device-ID?
|
|
// 'kybd','appl', 0x01L, // Keyboard-Typ
|
|
// 'nofm', 0L, 0x1003dde7L // ???
|
|
if(reinterpret_cast<long*>(&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<sizeof(gKeyMap); i++)
|
|
gKeyMap[i] = 0;
|
|
::BlockMoveData(gKeyMap, LMGetKeyMapPtr(),
|
|
sizeof(KeyMap));
|
|
|
|
raus:
|
|
if(gSDOut) ::KillIO(gSDOut);
|
|
if(gSDIn) ::CloseDriver(gSDIn);
|
|
if(gSDOut) ::CloseDriver(gSDOut);
|
|
}
|