aboutsummaryrefslogtreecommitdiff
path: root/SrcUnix/Platform_Unix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SrcUnix/Platform_Unix.cpp')
-rw-r--r--SrcUnix/Platform_Unix.cpp850
1 files changed, 850 insertions, 0 deletions
diff --git a/SrcUnix/Platform_Unix.cpp b/SrcUnix/Platform_Unix.cpp
new file mode 100644
index 0000000..9f9a918
--- /dev/null
+++ b/SrcUnix/Platform_Unix.cpp
@@ -0,0 +1,850 @@
+/* -*- 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 <errno.h> // EPERM, ENOENT, etc.
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/stat.h> // mkdir
+#include <time.h>
+#include <ctype.h>
+
+#include "omnithread.h" // omni_mutex
+#include <FL/x.H> // XKeyboardControl
+#include <FL/Fl.H> // 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 ("<missing string ") + buffer + ">";
+}
+
+
+// ---------------------------------------------------------------------------
+// ¥ 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
+}