NewtonKeyboardEnabler/Newton Keyboard Enabler/Main.cp

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);
}