/* -*- mode: C++; tab-width: 4 -*- */ /* ===================================================================== *\ Copyright (c) 1998-2001 Palm, Inc. or its subsidiaries. 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 "Platform.h" #include "ErrorHandling.h" // Errors::ThrowIfNULL #include "Miscellaneous.h" // StMemory //#include "PreferenceMgr.h" #include "ResStrings.h" #include "SessionFile.h" #include "Strings.r.h" // kStr_ ... #include // EPERM, ENOENT, etc. #include #include #include // mkdir #include #include #include "omnithread.h" // omni_mutex #include // XKeyboardControl #include // Fl::args // =========================================================================== // ¥ Globals // =========================================================================== ByteList gClipboardDataPalm; ByteList gClipboardDataHost; omni_mutex gClipboardMutex; omni_condition gClipboardCondition (&gClipboardMutex); Bool gClipboardHaveOutgoingData; Bool gClipboardNeedIncomingData; Bool gClipboardPendingIncomingData; Bool gClipboardHaveIncomingData; long long PrvGetMicroseconds (void) { struct timeval tv; gettimeofday (&tv, NULL); long long usecs = ((long long) tv.tv_sec) * 1000000ULL + tv.tv_usec; return usecs; } // =========================================================================== // ¥ Platform // =========================================================================== #ifndef __QNXNTO__ // Compare lexigraphically two strings int _stricmp( const char *s1, const char *s2 ) { return strcasecmp( s1, s2 ); } int _strnicmp( const char *s1, const char *s2, int n ) { return strncasecmp( s1, s2, n ); } char* _strdup( const char *s ) { return strdup( s ); } #endif // --------------------------------------------------------------------------- // ¥ Platform::Initialize // --------------------------------------------------------------------------- // Initializes platform-dependent stuff. void Platform::Initialize( void ) { } // --------------------------------------------------------------------------- // ¥ Platform::Reset // --------------------------------------------------------------------------- void Platform::Reset( void ) { } // --------------------------------------------------------------------------- // ¥ Platform::Save // --------------------------------------------------------------------------- void Platform::Save(SessionFile&) { } // --------------------------------------------------------------------------- // ¥ Platform::Load // --------------------------------------------------------------------------- void Platform::Load(SessionFile&) { } // --------------------------------------------------------------------------- // ¥ Platform::Dispose // --------------------------------------------------------------------------- void Platform::Dispose( void ) { } // --------------------------------------------------------------------------- // ¥ Platform::GetString // --------------------------------------------------------------------------- string Platform::GetString( StrCode id ) { const char* str = _ResGetString (id); if (str) return string (str); char buffer[20]; sprintf (buffer, "%ld", (long) id); return string (""; } // --------------------------------------------------------------------------- // ¥ Platform::GetIDForError // --------------------------------------------------------------------------- int Platform::GetIDForError( ErrCode error ) { switch (error) { // From /usr/include/asm/errno.h case EPERM: break; // 1 /* Operation not permitted */ case ENOENT: return kStr_FileNotFound; // 2 /* No such file or directory */ case ESRCH: break; // 3 /* No such process */ case EINTR: break; // 4 /* Interrupted system call */ case EIO: return kStr_IOError; // 5 /* I/O error */ case ENXIO: break; // 6 /* No such device or address */ case E2BIG: break; // 7 /* Arg list too long */ case ENOEXEC: break; // 8 /* Exec format error */ case EBADF: break; // 9 /* Bad file number */ case ECHILD: break; // 10 /* No child processes */ case EAGAIN: break; // 11 /* Try again */ case ENOMEM: return kStr_MemFull; // 12 /* Out of memory */ case EACCES: return kStr_SerialPortBusy; // 13 /* Permission denied */ case EFAULT: break; // 14 /* Bad address */ case ENOTBLK: break; // 15 /* Block device required */ case EBUSY: return kStr_FileBusy; // 16 /* Device or resource busy */ case EEXIST: return kStr_DuplicateFileName; // 17 /* File exists */ case EXDEV: break; // 18 /* Cross-device link */ case ENODEV: return kStr_DiskMissing; // 19 /* No such device */ case ENOTDIR: break; // 20 /* Not a directory */ case EISDIR: break; // 21 /* Is a directory */ case EINVAL: break; // 22 /* Invalid argument */ case ENFILE: break; // 23 /* File table overflow */ case EMFILE: return kStr_TooManyFilesOpen; // 24 /* Too many open files */ case ENOTTY: break; // 25 /* Not a typewriter */ case ETXTBSY: return kStr_FileBusy; // 26 /* Text file busy */ case EFBIG: break; // 27 /* File too large */ case ENOSPC: return kStr_DiskFull; // 28 /* No space left on device */ case ESPIPE: break; // 29 /* Illegal seek */ case EROFS: return kStr_DiskWriteProtected; // 30 /* Read-only file system */ case EMLINK: break; // 31 /* Too many links */ case EPIPE: break; // 32 /* Broken pipe */ case EDOM: break; // 33 /* Math argument out of domain of func */ case ERANGE: break; // 34 /* Math result not representable */ case EDEADLK: break; // 35 /* Resource deadlock would occur */ case ENAMETOOLONG: return kStr_BadFileName; // 36 /* File name too long */ #if 0 // Comment out this whole block. We don't map them to any specific // error messages, and by commenting them out, we protect ourselves // against any Unixen that don't define them. case ENOLCK: break; // 37 /* No record locks available */ case ENOSYS: break; // 38 /* Function not implemented */ case ENOTEMPTY: break; // 39 /* Directory not empty */ case ELOOP: break; // 40 /* Too many symbolic links encountered */ // case EWOULDBLOCK: break; // EAGAIN /* Operation would block */ case ENOMSG: break; // 42 /* No message of desired type */ case EIDRM: break; // 43 /* Identifier removed */ case ECHRNG: break; // 44 /* Channel number out of range */ case EL2NSYNC: break; // 45 /* Level 2 not synchronized */ case EL3HLT: break; // 46 /* Level 3 halted */ case EL3RST: break; // 47 /* Level 3 reset */ case ELNRNG: break; // 48 /* Link number out of range */ case EUNATCH: break; // 49 /* Protocol driver not attached */ case ENOCSI: break; // 50 /* No CSI structure available */ case EL2HLT: break; // 51 /* Level 2 halted */ case EBADE: break; // 52 /* Invalid exchange */ case EBADR: break; // 53 /* Invalid request descriptor */ case EXFULL: break; // 54 /* Exchange full */ case ENOANO: break; // 55 /* No anode */ case EBADRQC: break; // 56 /* Invalid request code */ case EBADSLT: break; // 57 /* Invalid slot */ // case EDEADLOCK: break; // EDEADLK case EBFONT: break; // 59 /* Bad font file format */ case ENOSTR: break; // 60 /* Device not a stream */ case ENODATA: break; // 61 /* No data available */ case ETIME: break; // 62 /* Timer expired */ case ENOSR: break; // 63 /* Out of streams resources */ case ENONET: break; // 64 /* Machine is not on the network */ case ENOPKG: break; // 65 /* Package not installed */ case EREMOTE: break; // 66 /* Object is remote */ case ENOLINK: break; // 67 /* Link has been severed */ case EADV: break; // 68 /* Advertise error */ case ESRMNT: break; // 69 /* Srmount error */ case ECOMM: break; // 70 /* Communication error on send */ case EPROTO: break; // 71 /* Protocol error */ case EMULTIHOP: break; // 72 /* Multihop attempted */ case EDOTDOT: break; // 73 /* RFS specific error */ case EBADMSG: break; // 74 /* Not a data message */ case EOVERFLOW: break; // 75 /* Value too large for defined data type */ case ENOTUNIQ: break; // 76 /* Name not unique on network */ case EBADFD: break; // 77 /* File descriptor in bad state */ case EREMCHG: break; // 78 /* Remote address changed */ case ELIBACC: break; // 79 /* Can not access a needed shared library */ case ELIBBAD: break; // 80 /* Accessing a corrupted shared library */ case ELIBSCN: break; // 81 /* .lib section in a.out corrupted */ case ELIBMAX: break; // 82 /* Attempting to link in too many shared libraries */ case ELIBEXEC: break; // 83 /* Cannot exec a shared library directly */ case EILSEQ: break; // 84 /* Illegal byte sequence */ case ERESTART: break; // 85 /* Interrupted system call should be restarted */ case ESTRPIPE: break; // 86 /* Streams pipe error */ case EUSERS: break; // 87 /* Too many users */ case ENOTSOCK: break; // 88 /* Socket operation on non-socket */ case EDESTADDRREQ: break; // 89 /* Destination address required */ case EMSGSIZE: break; // 90 /* Message too long */ case EPROTOTYPE: break; // 91 /* Protocol wrong type for socket */ case ENOPROTOOPT: break; // 92 /* Protocol not available */ case EPROTONOSUPPORT: break; // 93 /* Protocol not supported */ case ESOCKTNOSUPPORT: break; // 94 /* Socket type not supported */ case EOPNOTSUPP: break; // 95 /* Operation not supported on transport endpoint */ case EPFNOSUPPORT: break; // 96 /* Protocol family not supported */ case EAFNOSUPPORT: break; // 97 /* Address family not supported by protocol */ case EADDRINUSE: break; // 98 /* Address already in use */ case EADDRNOTAVAIL: break; // 99 /* Cannot assign requested address */ case ENETDOWN: break; // 100 /* Network is down */ case ENETUNREACH: break; // 101 /* Network is unreachable */ case ENETRESET: break; // 102 /* Network dropped connection because of reset */ case ECONNABORTED: break; // 103 /* Software caused connection abort */ case ECONNRESET: break; // 104 /* Connection reset by peer */ case ENOBUFS: break; // 105 /* No buffer space available */ case EISCONN: break; // 106 /* Transport endpoint is already connected */ case ENOTCONN: break; // 107 /* Transport endpoint is not connected */ case ESHUTDOWN: break; // 108 /* Cannot send after transport endpoint shutdown */ case ETOOMANYREFS: break; // 109 /* Too many references: cannot splice */ case ETIMEDOUT: break; // 110 /* Connection timed out */ case ECONNREFUSED: break; // 111 /* Connection refused */ case EHOSTDOWN: break; // 112 /* Host is down */ case EHOSTUNREACH: break; // 113 /* No route to host */ case EALREADY: break; // 114 /* Operation already in progress */ case EINPROGRESS: break; // 115 /* Operation now in progress */ case ESTALE: break; // 116 /* Stale NFS file handle */ case EUCLEAN: break; // 117 /* Structure needs cleaning */ case ENOTNAM: break; // 118 /* Not a XENIX named type file */ case ENAVAIL: break; // 119 /* No XENIX semaphores available */ case EISNAM: break; // 120 /* Is a named type file */ case EREMOTEIO: break; // 121 /* Remote I/O error */ case EDQUOT: break; // 122 /* Quota exceeded */ case ENOMEDIUM: break; // 123 /* No medium found */ case EMEDIUMTYPE: break; // 124 /* Wrong medium type */ #endif } return 0; } // --------------------------------------------------------------------------- // ¥ Platform::GetIDForRecovery // --------------------------------------------------------------------------- int Platform::GetIDForRecovery( ErrCode error ) { return 0; } // --------------------------------------------------------------------------- // ¥ Platform::GetShortVersionString // --------------------------------------------------------------------------- // Returns a short version string. The format of the string is: // // #.# (.#) ([dab]#) // // # = one or more numeric digits // . = literal '.' // Patterns in parentheses are optional // Patterns in brackets mean "one of these characters" // Spaces are shown above for clarity; they do not appear in the string // // Valid strings would be: 2.1d7, 2.1.1b14, 2.99, 2.1.1 string Platform::GetShortVersionString( void ) { return string("3.5"); } /*********************************************************************** * * FUNCTION: Platform::CopyToClipboard * * DESCRIPTION: * * PARAMETERS: none * * RETURNED: Nothing * ***********************************************************************/ void Platform::CopyToClipboard (const ByteList& palmChars, const ByteList& hostChars) { ByteList palmChars2 (palmChars); ByteList hostChars2 (hostChars); // See if any mapping needs to be done. if (hostChars2.size () > 0 && palmChars2.size () == 0) { Platform::RemapHostToPalmChars (hostChars2, palmChars2); } else if (palmChars2.size () > 0 && hostChars2.size () == 0) { Platform::RemapPalmToHostChars (palmChars2, hostChars2); } omni_mutex_lock lock (gClipboardMutex); gClipboardDataPalm = palmChars2; gClipboardDataHost = hostChars2; gClipboardHaveOutgoingData = true; } /*********************************************************************** * * FUNCTION: Platform::CopyFromClipboard * * DESCRIPTION: * * PARAMETERS: none * * RETURNED: Nothing * ***********************************************************************/ void Platform::CopyFromClipboard (ByteList& palmChars, ByteList& hostChars) { omni_mutex_lock lock (gClipboardMutex); gClipboardNeedIncomingData = true; gClipboardHaveIncomingData = false; while (!gClipboardHaveIncomingData) gClipboardCondition.wait (); // Copy the data to our outgoing lists. palmChars = gClipboardDataPalm; hostChars = gClipboardDataHost; // See if any mapping needs to be done. if (hostChars.size () > 0 && palmChars.size () == 0) { Platform::RemapHostToPalmChars (hostChars, palmChars); } else if (palmChars.size () > 0 && hostChars.size () == 0) { Platform::RemapPalmToHostChars (palmChars, hostChars); } } /*********************************************************************** * * FUNCTION: Platform::RemapHostToPalmChars * * DESCRIPTION: * * PARAMETERS: none * * RETURNED: Nothing * ***********************************************************************/ void Platform::RemapHostToPalmChars (const ByteList& hostChars, ByteList& palmChars) { // Converting line endings is all we do for now. ByteList::const_iterator iter = hostChars.begin (); while (iter != hostChars.end ()) { uint8 ch = *iter++; if (ch == 0x0A) { palmChars.push_back (chrLineFeed); } else { palmChars.push_back (ch); } } } /*********************************************************************** * * FUNCTION: Platform::RemapHostToPalmChars * * DESCRIPTION: * * PARAMETERS: none * * RETURNED: Nothing * ***********************************************************************/ void Platform::RemapPalmToHostChars (const ByteList& palmChars, ByteList& hostChars) { // Converting line endings is all we do for now. ByteList::const_iterator iter = palmChars.begin (); while (iter != palmChars.end ()) { uint8 ch = *iter++; if (ch == chrLineFeed) { hostChars.push_back (0x0A); } else { hostChars.push_back (ch); } } } /*********************************************************************** * * FUNCTION: Platform::PinToScreen * * DESCRIPTION: * * PARAMETERS: None * * RETURNED: True if the rectangle was changed. * ***********************************************************************/ Bool Platform::PinToScreen (EmRect& r) { // !!! TBD return false; } /*********************************************************************** * * FUNCTION: ToHostEOL * * DESCRIPTION: Converts a string of characters into another string * where the EOL sequence is consistant for files on the * current platform. * * PARAMETERS: dest - receives the result. The buffer is sized by * this function, so the caller doesn't have to * allocate any space itself. * * destLen - receives the length of the resulting string. * * src - pointer to input characters. * * srcLen - number of characters pointed to by src. * * RETURNED: Nothing. * ***********************************************************************/ void Platform::ToHostEOL ( StMemory& dest, long& destLen, const char* src, long srcLen) { char* d = (char*) Platform::AllocateMemory (srcLen); char* p = d; Bool previousWas0x0D = false; for (long ii = 0; ii < srcLen; ++ii) { char ch = src[ii]; // Convert 0x0D to 0x0A. if (ch == 0x0D) { *p++ = 0x0A; } // If we're looking at a 0x0A that's part of // a 0x0D/0x0A, just swallow it. else if (ch == 0x0A && previousWas0x0D) { // Nothing } // Copy all other characters straight through. else { *p++ = ch; } previousWas0x0D = ch == 0x0D; } destLen = p - d; d = (char*) Platform::ReallocMemory (d, destLen); dest.Adopt (d); } // ----------------------------------------------------------------------------- // find the ROM file path embedded in the saved ram image Bool Platform::ReadROMFileReference (ChunkFile& docFile, EmFileRef& f) { // First look for a ROM file path. string path; if (docFile.ReadString (SessionFile::kROMUnixPathTag, path)) { f = EmFileRef (path); return true; } // If a path can't be found, look for a simple ROM name. string name; if (docFile.ReadString (SessionFile::kROMNameTag, name)) { // !!! TBD } return false; } void Platform::WriteROMFileReference (ChunkFile& docFile, const EmFileRef& f) { docFile.WriteString (SessionFile::kROMUnixPathTag, f.GetFullPath ()); } // --------------------------------------------------------------------------- // ¥ Platform::Delay // --------------------------------------------------------------------------- // Delay for a time period appropriate for a sleeping 68328. void Platform::Delay (void) { // Delay 10 msecs. Delaying by this amount pauses us 1/100 of a second, // which is the rate at which the device's tickcount counter increments. // // Wait on an event instead of just calling Sleep(10) so that another // thread can wake us up before our time. omni_thread::sleep( 0, 10000 ); // 10k nanoseconds = 1/100 sec } // --------------------------------------------------------------------------- // ¥ Platform::CycleSlowly // --------------------------------------------------------------------------- void Platform::CycleSlowly (void) { // Nothing to do in Unix. } // --------------------------------------------------------------------------- // ¥ Platform::RealAllocateMemory // --------------------------------------------------------------------------- void* Platform::RealAllocateMemory (size_t size, Bool clear, const char*, int) { void* result; if (clear) result = calloc (size, 1); else result = malloc (size); Errors::ThrowIfNULL (result); return result; } // --------------------------------------------------------------------------- // ¥ Platform::RealReallocMemory // --------------------------------------------------------------------------- void* Platform::RealReallocMemory (void* p, size_t size, const char*, int) { void* result = realloc (p, size); Errors::ThrowIfNULL (result); return result; } // --------------------------------------------------------------------------- // ¥ Platform::RealDisposeMemory // --------------------------------------------------------------------------- void Platform::RealDisposeMemory (void* p) { if (p) { free (p); } } /*********************************************************************** * * FUNCTION: Platform::ForceStartupScreen * * DESCRIPTION: See if the user has requested that the Startup dialog * be presented instead of attempting to use the latest * session file or creation settings. * * The current signal is to toggle the CAPSLOCK key. * * PARAMETERS: none * * RETURNED: True if the user has signalled that the dialog should * be presented. * ***********************************************************************/ Bool Platform::ForceStartupScreen (void) { return false; } // --------------------------------------------------------------------------- // ¥ Platform::StopOnResetKeyDown // --------------------------------------------------------------------------- Bool Platform::StopOnResetKeyDown( void ) { // Comment this out for now. It seems that Windows doesn't always return // the expected result. That is, this function often returns TRUE even // though the Control is not down. // // Since this functionality is really only required by Palm OS engineers, // and since they're working on Macs, we don't really need this feature // in the Windows version now anyway. // return ::GetAsyncKeyState (VK_CONTROL) != 0; return false; } // --------------------------------------------------------------------------- // ¥ Platform::CollectOptions // --------------------------------------------------------------------------- int Platform::CollectOptions (int argc, char** argv, int& errorArg, int (*cb)(int, char**, int&)) { if (!Fl::args (argc, argv, errorArg, cb) || errorArg < argc - 1) return false; return true; } // --------------------------------------------------------------------------- // ¥ Platform::PrintHelp // --------------------------------------------------------------------------- void Platform::PrintHelp (void) { printf ("%s\n", Fl::help); } // --------------------------------------------------------------------------- // ¥ Platform::GetMilliseconds // --------------------------------------------------------------------------- uint32 Platform::GetMilliseconds( void ) { long long usecs = ::PrvGetMicroseconds (); uint32 millis = (uint32) (usecs / 1000); return millis; } // --------------------------------------------------------------------------- // ¥ Platform::CreateDebuggerSocket // --------------------------------------------------------------------------- CSocket* Platform::CreateDebuggerSocket (void) { return NULL; } // --------------------------------------------------------------------------- // ¥ Platform::ExitDebugger // --------------------------------------------------------------------------- // Perform platform-specific operations when debug mode is exited. void Platform::ExitDebugger( void ) { } // --------------------------------------------------------------------------- // ¥ Platform::ViewDrawLine // --------------------------------------------------------------------------- void Platform::ViewDrawLine( int xStart, int yStart, int xEnd, int yEnd ) { } // --------------------------------------------------------------------------- // ¥ Platform::ViewDrawPixel // --------------------------------------------------------------------------- void Platform::ViewDrawPixel( int xPos, int yPos ) { } static void PrvQueueNote (int frequency, int duration, int amplitude) { // Use XBell under X to play a simple tone. For more advanced // sound functionality, direct synth manipulation (under Linux), // or wave playback (probably via Esound), would be needed. // !TODO: figure out how to get XGetKeyboardControl working, so // that the actual keyboard state can be restored, instead of // just "the default". if (duration > 0 && amplitude > 0) { XKeyboardControl new_state; /* fl_display global contains the XDisplay of the last "current" fltk widget, under X/Windows */ new_state.bell_percent = amplitude * 100 / 64; new_state.bell_pitch = frequency; new_state.bell_duration = duration; XChangeKeyboardControl (fl_display, KBBellPercent | KBBellPitch | KBBellDuration, &new_state); XBell (fl_display, 100); // Give beep command XFlush (fl_display); // Flush beep command to the server omni_thread::sleep (0, duration * 1000000); // wait for asynch beep /* Put bell state back to "default" values */ new_state.bell_percent = -1; new_state.bell_pitch = -1; new_state.bell_duration = -1; XChangeKeyboardControl (fl_display, KBBellPercent | KBBellPitch | KBBellDuration, &new_state); } } CallROMType Platform::SndDoCmd (SndCommandType& cmd) { switch (cmd.cmd) { case sndCmdFreqDurationAmp: PrvQueueNote (cmd.param1, cmd.param2, cmd.param3); break; case sndCmdNoteOn: return kExecuteROM; case sndCmdFrqOn: PrvQueueNote (cmd.param1, cmd.param2, cmd.param3); break; case sndCmdQuiet: return kExecuteROM; } return kSkipROM; } void Platform::StopSound (void) { } void Platform::Beep (void) { XBell (fl_display, 100); // Give beep command. Make it loud XFlush (fl_display); // Flush beep command to the server }