diff options
Diffstat (limited to 'SrcShared/Patches/EmPatchMgr.cpp')
-rw-r--r-- | SrcShared/Patches/EmPatchMgr.cpp | 1203 |
1 files changed, 1203 insertions, 0 deletions
diff --git a/SrcShared/Patches/EmPatchMgr.cpp b/SrcShared/Patches/EmPatchMgr.cpp new file mode 100644 index 0000000..4a8f844 --- /dev/null +++ b/SrcShared/Patches/EmPatchMgr.cpp @@ -0,0 +1,1203 @@ +/* -*- mode: C++; tab-width: 4 -*- */ +/* ===================================================================== *\ + Copyright (c) 1998-2001 Palm, Inc. or its subsidiaries. + Copyright (c) 2001 PocketPyro, Inc. + All rights reserved. + + This file is part of the Palm OS Emulator. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +\* ===================================================================== */ + +#include "EmCommon.h" +#include "EmPatchMgr.h" + +#include "EmPatchModule.h" +#include "EmPatchModuleMap.h" +#include "EmPatchState.h" +#include "EmPatchLoader.h" + +#include "CGremlinsStubs.h" // StubAppEnqueueKey +#include "DebugMgr.h" // Debug::ConnectedToTCPDebugger +#include "EmEventPlayback.h" // EmEventPlayback::ReplayingEvents +#include "EmHAL.h" // EmHAL::GetLineDriverState +#include "EmLowMem.h" // EmLowMem::GetEvtMgrIdle, EmLowMem::TrapExists, EmLowMem_SetGlobal, EmLowMem_GetGlobal +#include "EmPalmFunction.h" // IsSystemTrap +#include "EmRPC.h" // RPC::SignalWaiters +#include "EmSession.h" // GetDevice +#include "Hordes.h" // Hordes::IsOn, Hordes::PostFakeEvent, Hordes::CanSwitchToApp +#include "Logging.h" // LogEvtAddEventToQueue, etc. +#include "MetaMemory.h" // MetaMemory mark functions +#include "PreferenceMgr.h" // Preference (kPrefKeyUserName) +#include "Profiling.h" // StDisableAllProfiling +#include "ROMStubs.h" // FtrSet, FtrUnregister, EvtWakeup, ... +#include "SessionFile.h" // SessionFile +#include "UAE.h" // gRegs, m68k_dreg, etc. + + +#pragma mark - + +// =========================================================================== +// ¥ EmPatchMgr +// =========================================================================== + +// ====================================================================== +// Global Interfaces +// ====================================================================== + + +//Interface to THE collection of all patch modules: + +extern IEmPatchModuleMap* gPatchMapIP; +extern IEmPatchLoader* gTheLoaderIP; + + +// ====================================================================== +// Private globals and constants +// ====================================================================== + +//Table of currently Patched shared libraries +// +static PatchedLibIndex gPatchedLibs; + +//Table of currently installed tail patches +// +static TailPatchIndex gInstalledTailpatches; + + +// Magic number used to identify Htal patch +// See comments in HtalLibSendReply. +// +const UInt16 kMagicRefNum = 0x666; + + + +// ====================================================================== +// Private functions +// ====================================================================== + +void PrvAutoload (void); +void PrvSetCurrentDate (void); + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::Initialize + * + * DESCRIPTION: Standard initialization function. Responsible for + * initializing this sub-system when a new session is + * created. Will be followed by at least one call to + * Reset or Load. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::Initialize (void) +{ + EmAssert (gSession); + + gTheLoaderIP->InitializePL (); + gTheLoaderIP->LoadAllModules (); + + gSession->AddInstructionBreakHandlers ( + InstallInstructionBreaks, + RemoveInstructionBreaks, + HandleInstructionBreak); + + if (gPatchMapIP != NULL) + { + gPatchMapIP->ClearAll (); + gPatchMapIP->LoadAll (); + } + + EmPatchState::Initialize (); +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::Reset + * + * DESCRIPTION: Standard reset function. Sets the sub-system to a + * default state. This occurs not only on a Reset (as + * from the menu item), but also when the sub-system + * is first initialized (Reset is called after Initialize) + * as well as when the system is re-loaded from an + * insufficient session file. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::Reset (void) +{ + gInstalledTailpatches.clear (); + + // Clear the installed lib patches (for "loaded" libraries) + // + gPatchedLibs.clear (); + + EmPatchState::Reset (); +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::Save + * + * DESCRIPTION: Standard save function. Saves any sub-system state to + * the given session file. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::Save (SessionFile& f) +{ + const long kCurrentVersion = 5; + + Chunk chunk; + EmStreamChunk s (chunk); + + s << kCurrentVersion; + + EmPatchState::Save (s, kCurrentVersion, EmPatchState::PSPersistStep1); + +// s << gSysPatchModule; +// s << gNetLibPatchModule; +// s << gPatchedLibs; + + s << (long) gInstalledTailpatches.size (); + + TailPatchIndex::iterator iter2; + for (iter2 = gInstalledTailpatches.begin (); iter2 != gInstalledTailpatches.end (); ++iter2) + { + s << iter2->fContext.fDestPC1; // !!! Need to support fDestPC2, too. But since only fNextPC seems to be used, it doesn't really matter. + s << iter2->fContext.fExtra; + s << iter2->fContext.fNextPC; + s << iter2->fContext.fPC; + s << iter2->fContext.fTrapIndex; + s << iter2->fContext.fTrapWord; + s << iter2->fCount; +// s << iter2->fTailpatch; // Patched up in ::Load + } + + EmPatchState::Save (s, kCurrentVersion, EmPatchState::PSPersistStep2); + + f.WritePatchInfo (chunk); +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::Load + * + * DESCRIPTION: Standard load function. Loads any sub-system state + * from the given session file. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::Load (SessionFile& f) +{ + Chunk chunk; + if (f.ReadPatchInfo (chunk)) + { + long version; + EmStreamChunk s (chunk); + + s >> version; + + if (version >= 1) + { + EmPatchState::Load (s, version, EmPatchState::PSPersistStep1); + + gPatchedLibs.clear (); + gInstalledTailpatches.clear (); + + + long numTailpatches; + s >> numTailpatches; + + int ii; + for (ii = 0; ii < numTailpatches; ++ii) + { + TailpatchType patch; + + s >> patch.fContext.fDestPC1; // !!! Need to support fDestPC2, too. But since only fNextPC seems to be used, it doesn't really matter. + patch.fContext.fDestPC2 = patch.fContext.fDestPC1; + s >> patch.fContext.fExtra; + s >> patch.fContext.fNextPC; + s >> patch.fContext.fPC; + s >> patch.fContext.fTrapIndex; + s >> patch.fContext.fTrapWord; + s >> patch.fCount; + + // Patch up the tailpatch proc. + + HeadpatchProc dummy; + GetPatches (patch.fContext, dummy, patch.fTailpatch); + + gInstalledTailpatches.push_back (patch); + } + } + + EmPatchState::Load (s, version, EmPatchState::PSPersistStep2); + } + else + { + f.SetCanReload (false); + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::Dispose + * + * DESCRIPTION: Standard dispose function. Completely release any + * resources acquired or allocated in Initialize and/or + * Load. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::Dispose (void) +{ + gInstalledTailpatches.clear (); + gPatchedLibs.clear (); + + EmPatchState::Dispose (); + + if (gPatchMapIP != NULL) + { + gPatchMapIP->ClearAll (); + } + + if (gTheLoaderIP) + { + gTheLoaderIP->ClearPL (); + } +} + + +Err EmPatchMgr::GetGlobalMemBanks (void** membanksPP) +{ + if (membanksPP != NULL) + *membanksPP = gEmMemBanks; + + return 0; +} + +Err EmPatchMgr::GetGlobalRegs (void** regsPP) +{ + if (regsPP != NULL) + *regsPP = &gRegs; + + return 0; +} + + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::PostLoad + * + * DESCRIPTION: Do some stuff that is normally taken care of during the + * process of resetting the device (autoloading + * applications, setting the device date, installing the + * HotSync user-name, and setting the 'gdbS' feature). + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::PostLoad (void) +{ + if (EmPatchState::UIInitialized ()) + { + // If we're listening on a socket, install the 'gdbS' feature. The + // existance of this feature causes programs written with the prc tools + // to enter the debugger when they're launched. + + if (Debug::ConnectedToTCPDebugger ()) + { + FtrSet ('gdbS', 0, 0x12BEEF34); + } + else + { + FtrUnregister ('gdbS', 0); + } + + // Reconfirm the strict intl checks setting, whether on or off. + + Preference<Bool> intlPref (kPrefKeyReportStrictIntlChecks); + + if (EmPatchMgr::IntlMgrAvailable ()) + { + ::IntlSetStrictChecks (*intlPref); + } + + // Reconfirm the overlay checks setting, whether on or off. + + Preference<Bool> overlayPref (kPrefKeyReportOverlayErrors); + (void) ::FtrSet (omFtrCreator, omFtrShowErrorsFlag, *overlayPref); + + // Install the HotSync user-name. + + // Actually, let's not do that. From Scott Maxwell: + // + // Would it be possible to save the HotSync user name with each session? This + // would be very convenient for working on multiple projects because each + // session could have a different user name. + // + // To which I said: + // I think that what you're seeing is Poser (re-)establishing the user preference + // from the Properties/Preferences dialog box after the session is reloaded. I + // could see this way of working as being valuable, too, so I'm not sure which way + // to go: keep things the way they are or change them. + // + // To which he said: + // + // How about having the preferences dialog grab the name from the Palm RAM? + // That way you could easily maintain it per session. + // + // Sounds good to me... + +// Preference<string> userNamePref (kPrefKeyUserName); +// ::SetHotSyncUserName (userNamePref->c_str ()); + + CEnableFullAccess munge; + + if (EmLowMem::TrapExists (sysTrapDlkGetSyncInfo)) + { + char userName[dlkUserNameBufSize]; + Err err = ::DlkGetSyncInfo (NULL, NULL, NULL, userName, NULL, NULL); + if (!err) + { + Preference<string> userNamePref (kPrefKeyUserName); + userNamePref = string (userName); + } + } + + // Auto-load any files in the Autoload[Foo] directories. + + ::PrvAutoload (); + + // Install the current date. + + ::PrvSetCurrentDate (); + + // Wake up any current application so that they can respond + // to events we pump in at EvtGetEvent time. + + ::EvtWakeup (); + } + + // Re-open any needed transports. This could probably be done + // at the time the session file is loaded, but we put it here + // with the rest of the (deferred) post-load activities for + // consistancy. + + for (EmUARTDeviceType ii = kUARTBegin; ii < kUARTEnd; ++ii) + { + if (EmHAL::GetLineDriverState (ii)) + { + EmTransport* transport = gEmuPrefs->GetTransportForDevice (ii); + + if (transport) + { + transport->Open (); + } + } + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::GetLibPatchTable + * + * DESCRIPTION: . + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +IEmPatchModule* EmPatchMgr::GetLibPatchTable (uint16 refNum) +{ + if (refNum >= gPatchedLibs.size ()) + { + gPatchedLibs.resize (refNum + 1); + } + + InstalledLibPatchEntry &libPtchEntry = gPatchedLibs[refNum]; + + if (libPtchEntry.IsDirty () == true) + { + string libName = ::GetLibraryName (refNum); + + IEmPatchModule *patchModuleIP = NULL; + + if (gPatchMapIP != NULL) + { + gPatchMapIP->GetModuleByName (libName, patchModuleIP); + } + + + libPtchEntry.SetPatchTableP (patchModuleIP); + libPtchEntry.SetDirty (false); + } + + return libPtchEntry.GetPatchTableP (); +} + + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::HandleSystemCall + * + * DESCRIPTION: If this is a trap we could possibly have head- or + * tail-patched, handle those cases. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +CallROMType EmPatchMgr::HandleSystemCall (const SystemCallContext& context) +{ + EmAssert (gSession); + if (gSession->GetNeedPostLoad ()) + { + gSession->SetNeedPostLoad (false); + EmPatchMgr::PostLoad (); + } + + HeadpatchProc hp; + TailpatchProc tp; + EmPatchMgr::GetPatches (context, hp, tp); + + CallROMType handled = EmPatchMgr::HandlePatches (context, hp, tp); + + return handled; +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::GetPatches + * + * DESCRIPTION: + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::GetPatches ( const SystemCallContext& context, + HeadpatchProc& hp, + TailpatchProc& tp) +{ + IEmPatchModule* patchModuleIP = NULL; + + // If this is in the system function range, check our table of + // system function patches. + + if (::IsSystemTrap (context.fTrapWord)) + { + static IEmPatchModule *sysPatchModuleIP = NULL; + + if (sysPatchModuleIP == NULL && gPatchMapIP != NULL) + { + gPatchMapIP->GetModuleByName (string ("~system"), sysPatchModuleIP); + } + + patchModuleIP = sysPatchModuleIP; + } + + else if (context.fExtra == kMagicRefNum) // See comments in HtalLibSendReply. + { + static IEmPatchModule *htalPatchModuleIP = NULL; + + if (htalPatchModuleIP == NULL && gPatchMapIP != NULL) + { + gPatchMapIP->GetModuleByName (string ("~Htal"), htalPatchModuleIP); + } + + patchModuleIP = htalPatchModuleIP; + } + + // Otherwise, see if this is a call to a patched library + else + { + patchModuleIP = GetLibPatchTable (context.fExtra); + } + + // Now that we've got the right patch table for this module, see if + // that patch table has head- or tailpatches for this function. + + if (patchModuleIP != NULL) + { + patchModuleIP->GetHeadpatch (context.fTrapIndex, hp); + patchModuleIP->GetTailpatch (context.fTrapIndex, tp); + } + else + { + hp = NULL; + tp = NULL; + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::HandlePatches + * + * DESCRIPTION: + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +CallROMType EmPatchMgr::HandlePatches (const SystemCallContext& context, + HeadpatchProc hp, + TailpatchProc tp) +{ + CallROMType handled = kExecuteROM; + + // First, see if we have a SysHeadpatch for this function. If so, call + // it. If it returns true, then that means that the head patch + // completely handled the function. + + // !!! May have to mess with PC here in case patches do something + // to enter the debugger. + + if (hp) + { + handled = CallHeadpatch (hp); + } + + // Next, see if there's a SysTailpatch function for this trap. If + // so, install the TRAP that will cause us to regain control + // after the trap function has executed. + + if (tp) + { + if (handled == kExecuteROM) + { + SetupForTailpatch (tp, context); + } + else + { + CallTailpatch (tp); + } + } + + return handled; +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::HandleInstructionBreak + * + * DESCRIPTION: Handle a tail patch, if any is registered for this + * memory location. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::HandleInstructionBreak (void) +{ + // Get the address of the tailpatch to call. May return NULL if + // there is no tailpatch for this memory location. + + TailpatchProc tp = RecoverFromTailpatch (gCPU->GetPC ()); + + // Call the tailpatch handler for the trap that just returned. + + CallTailpatch (tp); +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::InstallInstructionBreaks + * + * DESCRIPTION: Set the MetaMemory bit that tells the CPU loop to stop + * when we get to the desired locations. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::InstallInstructionBreaks (void) +{ + TailPatchIndex::iterator iter = gInstalledTailpatches.begin (); + + while (iter != gInstalledTailpatches.end ()) + { + MetaMemory::MarkInstructionBreak (iter->fContext.fNextPC); + ++iter; + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::RemoveInstructionBreaks + * + * DESCRIPTION: Clear the MetaMemory bit that tells the CPU loop to stop + * when we get to the desired locations. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::RemoveInstructionBreaks (void) +{ + TailPatchIndex::iterator iter = gInstalledTailpatches.begin (); + + while (iter != gInstalledTailpatches.end ()) + { + MetaMemory::UnmarkInstructionBreak (iter->fContext.fNextPC); + ++iter; + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::SetupForTailpatch + * + * DESCRIPTION: Set up the pending TRAP $F call to be tailpatched. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::SetupForTailpatch (TailpatchProc tp, const SystemCallContext& context) +{ + // See if this function is already tailpatched. If so, merely increment + // the use-count field. + + TailPatchIndex::iterator iter = gInstalledTailpatches.begin (); + + while (iter != gInstalledTailpatches.end ()) + { + if (iter->fContext.fNextPC == context.fNextPC) + { + ++(iter->fCount); + return; + } + + ++iter; + } + + // This function is not already tailpatched, so add a new entry + // for the the PC/opcode we want to save. + + EmAssert (gSession); + gSession->RemoveInstructionBreaks (); + + TailpatchType newTailpatch; + + newTailpatch.fContext = context; + newTailpatch.fCount = 1; + newTailpatch.fTailpatch = tp; + + gInstalledTailpatches.push_back (newTailpatch); + + gSession->InstallInstructionBreaks (); +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::RecoverFromTailpatch + * + * DESCRIPTION: . + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +TailpatchProc EmPatchMgr::RecoverFromTailpatch (emuptr startPC) +{ + // Get the current PC so that we can find the record for this tailpatch. + + emuptr patchPC = startPC; + + // Find the PC. + + TailPatchIndex::iterator iter = gInstalledTailpatches.begin (); + + while (iter != gInstalledTailpatches.end ()) + { + if (iter->fContext.fNextPC == patchPC) + { + TailpatchProc result = iter->fTailpatch; + + // Decrement the use-count. If it reaches zero, remove the + // patch from our list. + + if (--(iter->fCount) == 0) + { + EmAssert (gSession); + gSession->RemoveInstructionBreaks (); + + gInstalledTailpatches.erase (iter); + + gSession->InstallInstructionBreaks (); + } + + return result; + } + + ++iter; + } + + return NULL; +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::CallHeadpatch + * + * DESCRIPTION: If the given system function is head patched, then call + * the headpatch. Return "handled" (which means whether + * or not to call the ROM function after this one). + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +CallROMType EmPatchMgr::CallHeadpatch (HeadpatchProc hp, bool /* noProfiling */) +{ + CallROMType handled = kExecuteROM; + + if (hp) + { + // If (noProfiling == true) then stop all profiling activities. + // Stop cycle counting and stop the recording of function entries + // and exits. We want our trap patches to be as transparent as possible. + + StDisableAllProfiling stopper (/*noProfiling*/); + + handled = hp (); + } + + return handled; +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::CallTailpatch + * + * DESCRIPTION: If the given function is tail patched, then call the + * tailpatch. + * + * PARAMETERS: none + * + * RETURNED: nothing + * + ***********************************************************************/ + +void EmPatchMgr::CallTailpatch (TailpatchProc tp, bool /* noProfiling */) +{ + if (tp) + { + // Stop all profiling activities. Stop cycle counting and stop the + // recording of function entries and exits. We want our trap patches + // to be as transparent as possible. + + StDisableAllProfiling stopper (/*noProfiling*/); + + tp (); + } +} + + + + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::PuppetString + * + * DESCRIPTION: Puppet stringing function for inserting events into + * the system. We want to insert events when: + * + * - Gremlins is running + * - The user types characters + * - The user clicks with the mouse + * - We need to trigger a switch another application + * + * This function is called from headpatches to + * SysEvGroupWait and SysSemaphoreWait. When the Palm OS + * needs an event, it calls EvtGetEvent. EvtGetEvent + * looks in all the usual places for events to return. If + * it doesn't find any, it puts the system to sleep by + * calling SysEvGroupWait (or SysSemaphoreWait on 1.0 + * systems). SysEvGroupWait will wake up and return when + * an event is posted via something like EvtEnqueuePenPoint, + * EvtEnqueueKey, or KeyHandleInterrupt. + * + * To puppet-string Palm OS, we therefore patch those + * functions and post events, preventing them from actually + * going to sleep. + * + * PARAMETERS: callROM - return here whether or not the original ROM + * function still needs to be called. Normally, the + * answer is "yes". + * + * clearTimeout - set to true if the "timeout" parameter + * of the function we've patched needs to be prevented + * from being "infinite". + * + * RETURNED: nothing + * + ***********************************************************************/ + +static void PrvForceNilEvent (void) +{ + // No event was posted. What we'd like right now is to force + // EvtGetEvent to return a nil event. We can do that by returning + // a non-zero result code from SysEvGroupWait. EvtGetEvent doesn't + // look too closely at the result, but let's try to be as close to + // reality as possible. SysEvGroupWait currently returns "4" + // (CJ_WRTMOUT) to indicate a timeout condition. It should + // probably get turned into sysErrTimeout somewhere along the way, + // but that translation doesn't seem to occur. + + m68k_dreg (gRegs, 0) = 4; +} + +void EmPatchMgr::PuppetString (CallROMType& callROM, Bool& clearTimeout) +{ + callROM = kExecuteROM; + clearTimeout = false; + + // Set the return value (Err) to zero in case we return + // "true" (saying that we handled the trap). + + m68k_dreg (gRegs, 0) = 0; + + // If the low-memory global "idle" is true, then we're being + // called from EvtGetEvent or EvtGetPen, in which case we + // need to check if we need to post some events. + + if (EmLowMem::GetEvtMgrIdle ()) + { + // If there's an RPC request waiting for a nilEvent, + // let it know that it happened. + + if (EmPatchState::GetLastEvtTrap () == sysTrapEvtGetEvent) + { + RPC::SignalWaiters (hostSignalIdle); + } + + // If we're in the middle of calling a Palm OS function ourself, + // and we are somehow at the point where the system is about to + // doze, then just return now. Don't let it doze! Interrupts are + // off, and HwrDoze will never return! + + if (gSession->IsNested ()) + { + ::PrvForceNilEvent (); + callROM = kSkipROM; + return; + } + + EmAssert (gSession); + + // Check if Minimization is going on. + + if (EmEventPlayback::ReplayingEvents ()) + { + if (EmPatchState::GetLastEvtTrap () == sysTrapEvtGetEvent) + { + if (!EmEventPlayback::ReplayGetEvent ()) + { + ::PrvForceNilEvent (); + callROM = kSkipROM; + return; + } + } + else if (EmPatchState::GetLastEvtTrap () == sysTrapEvtGetPen) + { + EmEventPlayback::ReplayGetPen (); + } + + // Never let the timeout be infinite. If the above event-posting + // attempts failed (which could happen, for instance, if we attempted + // to post a pen event with the same coordinates as the previous + // pen event), we'd end up waiting forever. + + clearTimeout = true; + } + + // Check if Hords is going on. + + else if (Hordes::IsOn ()) + { + if (EmPatchState::GetLastEvtTrap () == sysTrapEvtGetEvent) + { + if (!Hordes::PostFakeEvent ()) + { + if (LogEnqueuedEvents ()) + { + LogAppendMsg ("Hordes::PostFakeEvent did not post an event."); + } + + ::PrvForceNilEvent (); + callROM = kSkipROM; + return; + } + } + else if (EmPatchState::GetLastEvtTrap () == sysTrapEvtGetPen) + { + Hordes::PostFakePenEvent (); + } + else + { + if (LogEnqueuedEvents ()) + { + LogAppendMsg ("Last event was 0x%04X, so not posting event.", EmPatchState::GetLastEvtTrap ()); + } + } + + // Never let the timeout be infinite. If the above event-posting + // attempts failed (which could happen, for instance, if we attempted + // to post a pen event with the same coordinates as the previous + // pen event), we'd end up waiting forever. + + clearTimeout = true; + } + + // Gremlins aren't on; let's see if the user has typed some + // keys that we need to pass on to the Palm device. + + else if (gSession->HasKeyEvent ()) + { + EmKeyEvent event = gSession->GetKeyEvent (); + + UInt16 modifiers = 0; + + if (event.fShiftDown) + modifiers |= shiftKeyMask; + + if (event.fCapsLockDown) + modifiers |= capsLockMask; + + if (event.fNumLockDown) + modifiers |= numLockMask; + + // We don't really want to set this one. commandKeyMask + // means something special in Palm OS +// if (event.fCommandDown) +// modifiers |= commandKeyMask; + + if (event.fOptionDown) + modifiers |= optionKeyMask; + + if (event.fControlDown) + modifiers |= controlKeyMask; + + if (event.fAltDown) + modifiers |= 0; // no bit defined for this + + if (event.fWindowsDown) + modifiers |= 0; // no bit defined for this + + ::StubAppEnqueueKey (event.fKey, 0, modifiers); + } + + // No key events, let's see if there are pen events. + + else if (gSession->HasPenEvent ()) + { + EmPoint pen (-1, -1); + EmPenEvent event = gSession->GetPenEvent (); + if (event.fPenIsDown) + { + pen = event.fPenPoint; + } + + PointType palmPen = pen; + StubAppEnqueuePt (&palmPen); + } + + // E. None of the above. Let's see if there's an app + // we're itching to switch to. + + else if (EmPatchState::GetNextAppDbID () != 0) + { + /*Err err =*/ SwitchToApp (EmPatchState::GetNextAppCardNo (), EmPatchState::GetNextAppDbID ()); + + EmPatchState::SetNextAppCardNo (0); + EmPatchState::SetNextAppDbID (0); + + clearTimeout = true; + } + } + else + { + if (Hordes::IsOn () && LogEnqueuedEvents ()) + { + LogAppendMsg ("Event Manager not idle, so not posting an event."); + } + } +} + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::IntlMgrAvailable + * + * DESCRIPTION: Indicates whether the international manager is available. + * + * PARAMETERS: + * + * RETURNED: True if it is (OS > 4.0), false otherwise. + * + ***********************************************************************/ + +Bool EmPatchMgr::IntlMgrAvailable (void) +{ + UInt32 romVersionData; + FtrGet (sysFileCSystem, sysFtrNumROMVersion, &romVersionData); + UInt32 romVersionMajor = sysGetROMVerMajor (romVersionData); + + return (romVersionMajor >= 4); +} + + + +/*********************************************************************** + * + * FUNCTION: EmPatchMgr::SwitchToApp + * + * DESCRIPTION: Switches to the given application or launchable document. + * + * PARAMETERS: cardNo - the card number of the app to switch to. + * + * dbID - the database id of the app to switch to. + * + * RETURNED: Err number of any errors that occur + * + ***********************************************************************/ + +Err EmPatchMgr::SwitchToApp (UInt16 cardNo, LocalID dbID) +{ + UInt16 dbAttrs; + UInt32 type, creator; + + Err err = ::DmDatabaseInfo ( + cardNo, + dbID, + NULL, /*name*/ + &dbAttrs, + NULL, /*version*/ + NULL, /*create date*/ + NULL, /*modDate*/ + NULL, /*backup date*/ + NULL, /*modNum*/ + NULL, /*appInfoID*/ + NULL, /*sortInfoID*/ + &type, + &creator); + + if (err) + return err; + + //--------------------------------------------------------------------- + // If this is an executable, call SysUIAppSwitch + //--------------------------------------------------------------------- + if (::IsExecutable (type, creator, dbAttrs)) + { + err = ::SysUIAppSwitch (cardNo, dbID, + sysAppLaunchCmdNormalLaunch, NULL); + + if (err) + return err; + } + + //--------------------------------------------------------------------- + // else, this must be a launchable data database. Find it's owner app + // and launch it with a pointer to the data database name. + //--------------------------------------------------------------------- + else + { + DmSearchStateType searchState; + UInt16 appCardNo; + LocalID appDbID; + + err = ::DmGetNextDatabaseByTypeCreator (true, &searchState, + sysFileTApplication, creator, + true, &appCardNo, &appDbID); + if (err) + return err; + + // Create the param block + + emuptr cmdPBP = (emuptr) ::MemPtrNew (sizeof (SysAppLaunchCmdOpenDBType)); + if (cmdPBP == EmMemNULL) + return memErrNotEnoughSpace; + + // Fill it in + + ::MemPtrSetOwner ((MemPtr) cmdPBP, 0); + EmMemPut16 (cmdPBP + offsetof (SysAppLaunchCmdOpenDBType, cardNo), cardNo); + EmMemPut32 (cmdPBP + offsetof (SysAppLaunchCmdOpenDBType, dbID), dbID); + + // Switch now + + err = ::SysUIAppSwitch (appCardNo, appDbID, sysAppLaunchCmdOpenDB, (MemPtr) cmdPBP); + if (err) + return err; + } + + return errNone; +} |