Logo Search packages:      
Sourcecode: qtads version File versions

osqt.cc

/* Copyright (C) 2003 Nikos Chantziaras.
 *
 * This file is part of the QTads program.  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, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

/* Qt-specific Tads OS functions (used by both Tads 2 and Tads 3).
 *
 * This file is used by both QTads as well as HTML QTads.  Functions
 * that have to be excluded in the HTML-version are inside an #ifndef
 * block:
 *
 *     #ifndef HTMLQT
 *     // Function not needed by HTML QTads.
 *     #endif
 *
 * This file should only contain Tads OS specific functions.  That
 * doesn't mean that you can't use C++ code inside the functions; you
 * can use any C++ feature you want, as long as the function headers
 * are compatible with the prototypes in "osifc.h".  The only exception
 * are static functions (they aren't exported).
 *
 * Again: No functions are allowed in here that aren't listed in
 * "osifc.h"!
 */

/* A note about Multimedia Tads Character Entities
 * ===============================================
 *
 * QTads is able to display Multimedia Tads Character Entities (like
 * "”" or "&emdash;") even in Tads 2 games.  These characters are
 * nothing more than Unicode values.  But Tads 2 does not support
 * Unicode (Tads 3 does).  To work around this, QTads uses UTF-8
 * encoded Unicode for output.  The problem with UTF-8 is that it only
 * contains 7-bit ASCII as a subset, so 8-bit character sets like
 * Latin1 don't work.  Because of this, QTads uses a modification of
 * UTF-8 that is able to include a full 8-bit character set as a subset.
 * A char* string with this encoding looks like this:
 *
 *   If a byte in this string is 0, then this is where the string ends
 *   (as usual; in this aspect, it's like regular UTF-8).
 *
 *   If a byte in this string is greater than (but *not* equal to) 1,
 *   then it's a normal single byte (8-bit) character in whatever
 *   character set the game is currently using.
 *
 *   But if a byte in this string has the value 1, then this means that
 *   a UTF-8 character is following.  Visually, the format of this
 *   sequence is (each [] is a byte):
 *
 *     [1] [byte 1] [byte 2 (if any)] [byte 3 (if any)]
 *
 * In the source code, we refer to this special encoding as "QTF".  The
 * function qtf2QString() (defined below) can be used to convert these
 * specially encoded strings to regular Unicode QStrings.
 *
 * Note that this method has a potential drawback; if a game tries to
 * display a character with value 1, this character will be
 * misinterpreted as the beginning of the above special sequence.
 * However, this is not likely to happen (I would say that it's even
 * impossible), since of all the ASCII control-characters, only the
 * character values 10 and 13 have a special meaning in Tads; there's
 * absolutely no reason why a game would want to display the character
 * value 1.  If it does, it's a bug in the game, since no Tads
 * interpreter in this world knows how to display this character.
 *
 * Note that QTF is not used in Tads 3, since Tads 3 already supports
 * Unicode and there's no need for this kind of hack.
 */

#include "config.h"

#include <qapplication.h>
#include <qstring.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qcursor.h>
#include <qdatetime.h>
#include <qtextcodec.h>

#include <vector>
#include <cstring>
#include <cstdio>
#include <cctype>

#include "os.h"

#ifndef HTMLQT
#include "qtadsio.h"
#include "install_dirs.h"
#endif


/* Holds track of the output-mode we are in.  This is an internal
 * variable and not part of the Tads OS interface.
 */
static int status_mode = 0;


/* Converts a QTF encoded string to a QString.
 */
static inline
QString
qtf2QString( const char* s )
{
      QString res;
      const size_t len = std::strlen(s);
      for (size_t i = 0; i < len; ++i) {
            if (s[i] == 1) {
                  // A multibyte UTF-8 character follows.
                  // Determine its length and copy it to the
                  // result.
                  char tmp[4];
                  unsigned int c = (unsigned int)(unsigned char)s[i+1];

                  // The result of the switch is the length in
                  // bytes.  The code is ugly, but very fast.
                  // The bits of the first byte of a UTF-8
                  // character look like this:
                  //   111xxxxx: Character is 3 bytes long.
                  //   110xxxxx: Character is 2 bytes long.
                  //   0xxxxxxx: Character is 1 byte long.
                  switch (1 + ((((c & 0x80) >> 7) | ((c & 0x80) >> 6))
                             & (1 + ((c & 0x20) >> 5))))
                  {
                    case 1:
                        tmp[0] = s[++i];
                        tmp[1] = '\0';
                        break;
                    case 2:
                        tmp[0] = s[++i];
                        tmp[1] = s[++i];
                        tmp[2] = '\0';
                        break;
                    case 3:
                        tmp[0] = s[++i];
                        tmp[1] = s[++i];
                        tmp[2] = s[++i];
                        tmp[3] = '\0';
                        break;
                  }
                  res += QString::fromUtf8(tmp);
            } else {
                  // It's a normal 8 bit character.
                  res += s[i];
            }

            Q_ASSERT(i <= len);
      }
      return res;
}


/* --------------------------------------------------------------------
 * Basic file I/O interface.
 *
 * There's no need to implement this in a Qt-specific way, since we use
 * only portable functions from the standard C-library.  Qt is only
 * used when there's no native portable way to handle something.
 *
 * Note that the code doesn't care if the system distinguishes between
 * text and binary files, since (as far as I know) the standard
 * functions always do the right thing; a "b" in the mode string is
 * ignored on systems that treat text and binary files the same (like
 * most/all Unix-systems).
 */

/* Open text file for reading and writing.
 */
osfildef*
osfoprwt( const char* fname, os_filetype_t )
{
      Q_ASSERT(fname != 0);

      osfildef* fp = std::fopen(fname, "a+");
      // If the operation was successful, position the seek pointer
      // to the beginning of the file.
      if (fp != 0) {
            std::rewind(fp);
      }
      return fp;
}


/* Open binary file for reading/writing.
 */
osfildef*
osfoprwb( const char* fname, os_filetype_t )
{
      Q_ASSERT(fname != 0);

      osfildef* fp = std::fopen(fname, "a+b");
      if (fp != 0) {
            std::rewind(fp);
      }
      return fp;
}


/* Access a file - determine if the file exists.
 */
int
osfacc( const char* fname )
{
      Q_ASSERT(fname != 0);

      return QFileInfo(fname).exists() ? 0 : 1;
}


/* Write a null-terminated string to a text file.
 */
void
os_fprintz( osfildef* fp, const char* str )
{
      Q_ASSERT(fp != 0);
      Q_ASSERT(str != 0);

      std::fprintf(fp, "%s", str);
}


// --------------------------------------------------------------------

/* Convert string to all-lowercase.
 */
char*
os_strlwr( char* s )
{
      Q_ASSERT(s != 0);
      Q_ASSERT(std::strlen(s) >= std::strlen(QString::fromUtf8(s).lower().utf8()));

      std::strcpy(s, QString::fromUtf8(s).lower().utf8());
      return s;
}


/* --------------------------------------------------------------------
 * Special file and directory locations.
 */

/* Seek to the resource file embedded in the current executable file.
 *
 * We don't support this, and *probably* never will.  Would be a nice
 * feature to have though.
 */
osfildef*
os_exeseek( const char*, const char* )
{
      return static_cast<osfildef*>(0);
}


/* --------------------------------------------------------------------
 * External, dynamically-loaded code.
 *
 * This Tads-feature is deprecated.  I don't see why it should be
 * supported.  Did any game actually use it?
 */

/* Load an external function from a file given the name of the file.
 */
int
(*os_exfil(const char*))(void*)
{
      qWarning("Dynamically-loaded code is not supported in QTads");
      return static_cast<int (*)(void*)>(0);
}


/* Load an external function from an open file.
 */
int
(*os_exfld(osfildef*, unsigned))(void*)
{
      qWarning("Dynamically-loaded code is not supported in QTads");
      return static_cast<int (*)(void*)>(0);
}


/* Call an external function.
 */
int
os_excall(int (*)(void*), void*)
{
      qWarning("Dynamically-loaded code is not supported in QTads");
      return 0;
}


/* --------------------------------------------------------------------
 */

/* Look for a file in the standard locations: current directory,
 * program directory, TADS path.
 *
 * TODO: For now, we only look in the current directory.
 */
int
os_locate( const char* fname, int /*flen*/, const char* /*arg0*/, char* buf, size_t bufsiz )
{
      Q_ASSERT(fname != 0);
      Q_ASSERT(buf != 0);

      if (QFile::exists(fname)) {
            Q_ASSERT(bufsiz > std::strlen(QFileInfo(fname).absFilePath().ascii()));

            std::strncpy(buf, QFileInfo(fname).absFilePath().ascii(), bufsiz);
            buf[bufsiz - 1] = '\0';
            return 1;
      }
      return 0;
}


/* --------------------------------------------------------------------
 */

/* Create and open a temporary file.
 */
osfildef*
os_create_tempfile( const char* fname, char* buf )
{
      if (fname != 0 and fname[0] != '\0') {
            // A filename has been specified; use it.
            return std::fopen(fname, "w+b");
      }

      Q_ASSERT(buf != 0);

      // No filename needed; create a nameless temp-file.
      buf[0] = '\0';
      return std::tmpfile();
}


/* Delete a temporary file created with os_create_tempfile().
 */
int
osfdel_temp( const char* fname )
{
      Q_ASSERT(fname != 0);

      if (fname[0] == '\0' or QFile::remove(fname)) {
            return 0;
      }
      // QFile::remove() failed.
      return 1;
}


/* --------------------------------------------------------------------
 * Filename manipulation routines.
 */

/* Apply a default extension to a filename, if it doesn't already have
 * one.
 */
void
os_defext( char* fn, const char* ext )
{
      Q_ASSERT(fn != 0);
      Q_ASSERT(ext != 0);

      if (QFileInfo(fn).extension().isEmpty()) {
            os_addext(fn, ext);
      }
}


/* Unconditionally add an extention to a filename.
 *
 * TODO: Find out if there are systems that don't use the dot as the
 * extension separator.  (Only systems Qt supports of course.)
 */
void
os_addext( char* fn, const char* ext )
{
      Q_ASSERT(fn != 0);
      Q_ASSERT(ext != 0);

      std::strcat(fn, ".");
      std::strcat(fn, ext);
}


/* Remove the extension from a filename.
 */
void
os_remext( char* fn )
{
      Q_ASSERT(fn != 0);

      QFileInfo file(fn);

      if (file.extension().isEmpty()) {
            return;
      }

      QString res(fn);

      // Remove the extension *plus* the extension separator.  The
      // code assumes that the separator is always one character
      // long.
      res.truncate(res.length() - file.extension(false).length() - 1);
      std::strcpy(fn, res.ascii());
}


/* Get a pointer to the root name portion of a filename.
 *
 * Note that Qt's native path separator character is '/'.  It doesn't
 * matter on what OS we're running.
 */
char*
os_get_root_name( char* buf )
{
      Q_ASSERT(buf != 0);

      char* p = buf;

      for (p += std::strlen(buf) - 1; p > buf and *p != '/'; --p)
            ;
      if (p != buf) {
            ++p;
      }
      return p;
}


/* Build a full path name, given a path and a filename.
 */
void
os_build_full_path( char* fullpathbuf, size_t fullpathbuflen, const char* path,
                const char* filename )
{
      Q_ASSERT(fullpathbuf != 0);
      Q_ASSERT(path != 0);
      Q_ASSERT(filename != 0);

      std::strncpy(fullpathbuf, QFileInfo(QDir(path), filename).filePath().ascii(),
                 fullpathbuflen);
      fullpathbuf[fullpathbuflen - 1] = '\0';
}


// --------------------------------------------------------------------

/* Get a suitable seed for a random number generator.
 *
 * We don't just use the system-clock, but build a number as random as
 * the mechanisms of the standard C-library allow.
 */
void
os_rand( long* val )
{
      Q_ASSERT(val != 0);

      static bool initialized = false;
      std::time_t t = std::time(0);

      if (not initialized) {
            if (t == static_cast<std::time_t>(-1)) {
                  std::srand(std::rand());
            } else {
                  std::srand(static_cast<unsigned int>(t));
            }
            initialized = true;
      }

      // Generate a random number by using high-order bits, because
      // on some systems the low-order bits aren't very random.
      // Note that this is Voodoo Programming for me; I simply used
      // the `rand' manpage as a guide.
      *val = 1 + static_cast<long>(static_cast<long double>(65535)
                              * std::rand() / (RAND_MAX + 1.0));
}


/* --------------------------------------------------------------------
 * Display routines.
 */

/* Print a null-terminated string on the console.
 *
 * TODO: Recognition of "\r" characters in strings needs testing.
 */
#ifndef HTMLQT
void
os_printz( const char* str )
{
      Q_ASSERT(str != 0);

      if (str[0] == '\0' or ::status_mode > 1) {
            return;
      }

      const QString tmp(QTadsIO::t3Mode() ? QString::fromUtf8(str) : ::qtf2QString(str));

      if (::status_mode == 0) {
            QTadsIO::print(tmp);
      } else {
            QString res;
            bool ret = false;
            uint i = 0;

            if (tmp[0] == '\n') {
                  while (i < tmp.length() and tmp[i] == '\n') {
                        ++i;
                  }
            }

            while (i < tmp.length() and ::status_mode == 1) {
                  if (tmp[i] == '\r') {
                        res = "";
                        ret = true;
                  } else if (tmp[i] == '\n') {
                        ::status_mode = 2;
                  } else {
                        res += tmp[i];
                  }
                  ++i;
            }

            if (not res.isEmpty() or ret) {
                  QTadsIO::statusPrint(res);
            }
      }
}
#endif


/* Set the status line mode.
 */
#ifndef HTMLQT
void
os_status( int stat )
{
      Q_ASSERT(stat >= 0 and stat <= 2);
      ::status_mode = stat;
}
#endif


/* Get the status line mode.
 */
#ifndef HTMLQT
int
os_get_status()
{
      return ::status_mode;
}
#endif


/* Display the given score and turn counts on the status line.
 */
#ifndef HTMLQT
void
os_score( int score, int turncount )
{
      QString res(QString::number(score));
      res += "/";
      res += QString::number(turncount);
      QTadsIO::scorePrint(res);
}
#endif


/* Display a string in the score area in the status line.
 */
#ifndef HTMLQT
void
os_strsc( const char* p )
{
      Q_ASSERT(p != 0);

      QTadsIO::scorePrint(QTadsIO::t3Mode() ? QString::fromUtf8(p) : ::qtf2QString(p));
}
#endif


/* Clear the main-window.
 */
#ifndef HTMLQT
void
oscls( void )
{
      QTadsIO::clear();
}
#endif


/* Flush any buffered display output.
 */
#ifndef HTMLQT
void
os_flush( void )
{
      QTadsIO::flush();
}
#endif


// --------------------------------------------------------------------

/* Set text attributes.
 */
#ifndef HTMLQT
void
os_set_text_attr( int attr )
{
      QTadsIO::highlight(attr & OS_ATTR_BOLD);
      QTadsIO::italics(attr & OS_ATTR_ITALIC);
}
#endif


/* --------------------------------------------------------------------
 */

/* Use plain ascii mode for the display.
 *
 * We don't provide this.  It's possible to emulate it, but there's no
 * point in doing so.
 */
#ifndef HTMLQT
void
os_plain( void )
{
      qWarning("Plain ASCII mode is not supported in QTads.");
}
#endif


/* Set the game title.
 */
#ifndef HTMLQT
void
os_set_title( const char* title )
{
      Q_ASSERT(title != 0);

      QTadsIO::title(QTadsIO::t3Mode() ? QString::fromUtf8(title) : ::qtf2QString(title));
}
#endif


/* Show the system-specific MORE prompt, and wait for the user to
 * respond.
 */
#ifndef HTMLQT
void
os_more_prompt()
{
      QTadsIO::morePrompt();
}
#endif


/* Set non-stop mode.  This tells the OS layer that it should disable
 * any MORE prompting it would normally do.
 */
#ifndef HTMLQT
void os_nonstop_mode(int flag)
{
      QTadsIO::nonstopMode(flag);
}
#endif


/* Set busy cursor.
 *
 * The implementation provided here assumes that this function is never
 * called twice in succession with the same argument.
 *
 * TODO: It sometimes doesn't reset the cursor back to normal, so I
 * disabled it completely.
 */
void
os_csr_busy( int /*flag*/ )
{
      /*
      if (flag) {
            QApplication::setOverrideCursor(Qt::WaitCursor);
      } else {
            QApplication::restoreOverrideCursor();
      }
      */
}


/* --------------------------------------------------------------------
 * User Input Routines.
 */

/* Ask the user for a filename, using a system-dependent dialog or
 * other mechanism.
 */
int
os_askfile( const char* prompt, char* fname_buf, int fname_buf_len, int prompt_type,
          os_filetype_t file_type )
{
      Q_ASSERT(prompt_type == OS_AFP_SAVE or prompt_type == OS_AFP_OPEN);
      Q_ASSERT(prompt != 0);
      Q_ASSERT(fname_buf != 0);

      QString res;
      QString filter;
      QString ext;

      switch (file_type) {
        case OSFTGAME:
            filter = QObject::tr("TADS 2 Games (*.gam; *.Gam; *.GAM)");
            break;
        case OSFTSAVE:
            filter = QObject::tr("TADS 2 Saved Games (*.sav; *.Sav; *.SAV)");
            break;
        case OSFTLOG:
            filter = QObject::tr("Game Transcripts (*.txt; *.Txt; *.TXT)");
            break;
        case OSFTT3IMG:
#ifndef HTMLQT
            Q_ASSERT(QTadsIO::t3Mode());
#endif
            filter = QObject::tr("TADS 3 Games (*.t3; *.T3)");
            break;
        case OSFTT3SAV:
#ifndef HTMLQT
            Q_ASSERT(QTadsIO::t3Mode());
#endif
            filter = QObject::tr("TADS 3 Saved Games (*.t3v; *.T3v; *.T3V)");
            ext = "t3v";
            break;
      }

      // Always provide an "All Files" filter.
      if (not filter.isEmpty()) {
            filter += QObject::tr(";;All Files (*)");
      }

      if (prompt_type == OS_AFP_OPEN) {
#ifndef HTMLQT
            // TODO: HTML QTads implementation.
            res = QTadsIO::openFile(QString::null, filter,
                              QTadsIO::t3Mode()
                              ? QString::fromUtf8(prompt)
                              : ::qtf2QString(prompt));
#endif
      } else {
#ifndef HTMLQT
            res = QTadsIO::saveFile(QString::null, filter,
                              QTadsIO::t3Mode()
                              ? QString::fromUtf8(prompt)
                              : ::qtf2QString(prompt));
#endif
      }

      if (res.isEmpty()) {
            // User cancelled.
            return OS_AFE_CANCEL;
      }

      Q_ASSERT(fname_buf_len > static_cast<int>(std::strlen(res.ascii())));

      std::strncpy(fname_buf, res.ascii(), fname_buf_len);
      fname_buf[fname_buf_len - 1] = '\0';
      if (not ext.isEmpty()) {
            // Since `ext' is non-empty, an extension should be
            // appended (if none exists).
            os_defext(fname_buf, ext.ascii());
            fname_buf[fname_buf_len - 1] = '\0';
      }
      return OS_AFE_SUCCESS;
}


/* Read a string of input.
 */
#ifndef HTMLQT
unsigned char*
os_gets( unsigned char* buf, size_t bufl )
{
      Q_ASSERT(buf != 0);

      // TODO: Find a way to remove the reinterpret_cast.
      char* tmp = reinterpret_cast<char*>(buf);
      const QString& res = QTadsIO::getInput();
      if (not QTadsIO::gameRunning) {
            return 0;
      }
      std::strncpy(tmp, QTadsIO::t3Mode() ? res.utf8().operator const char*() : res.ascii(),
                 bufl);
      buf[bufl - 1] = '\0';
      return buf;
}
#endif


/* Read a character from the keyboard and return the low-level,
 * untranslated key code whenever possible.
 */
#ifndef HTMLQT
int
os_getc_raw( void )
{
      // If this is false, it means that we have been called and
      // returned 0, so this time we should return the extended
      // key-code.
      static bool done = true;
      // QKeyEvent lacks a default ctor, so we provide some random
      // values.
      static QKeyEvent e(QEvent::KeyPress, 0, 0, 0);

      if (done) {
            e = QTadsIO::getRawChar();
            // Store an upper-case version in case it's an
            // ALT+[KEY] combination.
            int asc = std::toupper(e.ascii());
            if (e.ascii() == 0 or e.key() == Qt::Key_Tab or not QTadsIO::gameRunning
                or ((e.state() & Qt::AltButton) and (asc >= 'A' and asc <= 'Z')))
            {
                  // Extended character, Alt+Character or the
                  // game should quit.  Prepare to return the
                  // actual code on our next call.  (Although a
                  // Tab is not an extended character, Tads
                  // requires that it is handled as one.)
                  done = false;
                  return 0;
            } else {
                  // It's not an extended character.  Just return
                  // its ASCII code.
                  return e.ascii();
            }
      }

      // In case `done' was false.
      done = true;

      if (not QTadsIO::gameRunning) {
            // If the game should quit, return the EOF flag.
            return CMD_EOF;
      }

      if (e.state() & Qt::AltButton) {
            // It's an ALT+[KEY] combination.
            // Return CMD_ALT + [Key value].  'A' has the value 0.
            int asc = std::toupper(e.ascii());
            if (asc >= 'A' and asc <= 'Z') {
                  return CMD_ALT + asc - 'A';
            }
      }

      switch (e.key()) {
        case Qt::Key_Up:
            return CMD_UP;
        case Qt::Key_Down:
            return CMD_DOWN;
        case Qt::Key_Left:
            return CMD_LEFT;
        case Qt::Key_Right:
            return CMD_RIGHT;
        case Qt::Key_End:
            return CMD_END;
        case Qt::Key_Home:
            return (e.state() & Qt::ControlButton) ? CMD_HOME : CMD_CHOME;
        case Qt::Key_Delete:
            return CMD_DEL;
        case Qt::Key_PageUp:
            return CMD_PGUP;
        case Qt::Key_PageDown:
            return CMD_PGDN;
        case Qt::Key_F1:
            return CMD_F1;
        case Qt::Key_F2:
            return (e.state() & Qt::ShiftButton) ? CMD_SF2 : CMD_F2;
        case Qt::Key_F3:
            return CMD_F3;
        case Qt::Key_F4:
            return CMD_F4;
        case Qt::Key_F5:
            return CMD_F5;
        case Qt::Key_F6:
            return CMD_F6;
        case Qt::Key_F7:
            return CMD_F7;
        case Qt::Key_F8:
            return CMD_F8;
        case Qt::Key_F9:
            return CMD_F9;
        case Qt::Key_F10:
            return CMD_F10;
        case Qt::Key_Tab:
            return CMD_TAB;
      }

      // The execution thread should never reach this point.
      Q_ASSERT(true == false);
      return 0;
}
#endif


/* Wait for a character to become available from the keyboard.
 */
#ifndef HTMLQT
void
os_waitc( void )
{
      QTadsIO::waitChar();
}
#endif


/* Get an input event.
 *
 * TODO: Implement timeout.
 */
#ifndef HTMLQT
int
os_get_event( unsigned long /*timeout*/, int use_timeout, os_event_info_t* info )
{
      if (use_timeout) {
            // We don't support timed events (yet).
            return OS_EVT_NOTIMEOUT;
      }

      // Wait for an input from the keyboard.
      int res = os_getc_raw();

      if (not QTadsIO::gameRunning) {
            // For some reason, the game stopped executing.  Return
            // an "End Of File" event.
            return OS_EVT_EOF;
      }

      if (res == 0) {
            // os_getc_raw() returned 0, which means that it was an
            // extended key-code.
            info->key[0] = 0;
            info->key[1] = os_getc_raw();
      } else {
            // It was a normal key-code.
            info->key[0] = res;
            info->key[1] = 0;
      }
      // Notify the caller that the event was a key-press (the only
      // type of event we support anyway).
      return OS_EVT_KEY;
}
#endif


// --------------------------------------------------------------------

/* Ask for input through a dialog.
 *
 * TODO: Implement default-button.
 */
int
os_input_dialog( int icon_id, const char* prompt, int standard_button_set,
             const char** buttons, int button_count, int default_index, int cancel_index )
{
      Q_ASSERT(prompt != 0);
      Q_ASSERT(icon_id == OS_INDLG_ICON_NONE or icon_id == OS_INDLG_ICON_WARNING
             or icon_id == OS_INDLG_ICON_INFO or icon_id == OS_INDLG_ICON_QUESTION
             or icon_id == OS_INDLG_ICON_ERROR);
      Q_ASSERT(standard_button_set == 0 or standard_button_set == OS_INDLG_OK
             or standard_button_set == OS_INDLG_OKCANCEL
             or standard_button_set == OS_INDLG_YESNO
             or standard_button_set == OS_INDLG_YESNOCANCEL);

      //QMessageBox::Icon = icon;

      /*
      switch (icon_id) {
        case OS_INDLG_ICON_NONE:
            icon = QMessageBox::NoIcon;
            break;
        case OS_INDLG_ICON_WARNING:
            icon = QMessageBox::Warning;
            break;
        case OS_INDLG_ICON_INFO:
            icon = QMessageBox::Information;
            break;
        case OS_INDLG_ICON_QUESTION:
            icon = QMessageBox::Information;
            break;
        case OS_INDLG_ICON_ERROR:
            icon = QMessageBox::Critical;
            break;
      }
      */

      std::vector<QString> buttonVec;

      if (standard_button_set != 0) {
            switch (standard_button_set) {
              case OS_INDLG_OK:
                  buttonVec.push_back(QObject::tr("O&K"));
                  break;
              case OS_INDLG_OKCANCEL:
                  buttonVec.push_back(QObject::tr("O&K"));
                  buttonVec.push_back(QObject::tr("&Cancel"));
                  break;
              case OS_INDLG_YESNO:
                  buttonVec.push_back(QObject::tr("&Yes"));
                  buttonVec.push_back(QObject::tr("&No"));
                  break;
              case OS_INDLG_YESNOCANCEL:
                  buttonVec.push_back(QObject::tr("&Yes"));
                  buttonVec.push_back(QObject::tr("&No"));
                  buttonVec.push_back(QObject::tr("&Cancel"));
                  break;
              default:
                  qWarning("os_input_dialog: unrecognized button set");
            }
      } else for (int i = 0; i < button_count; ++i) {
            Q_ASSERT(buttons[i] != 0);
            buttonVec.push_back(buttons[i]);
      }

#ifndef HTMLQT
      // TODO: HTML QTads implementation.
      int res = QTadsIO::inputDialog(QTadsIO::t3Mode()
                               ? QString::fromUtf8(prompt)
                               : ::qtf2QString(prompt),
                               buttonVec, default_index);
      return (res == 0) ? cancel_index : res;
#endif
}


/* --------------------------------------------------------------------
 * Time-functions.
 */

/* Get the current system high-precision timer.
 */
long
os_get_sys_clock_ms( void )
{
      static QTime zeroPoint(QTime::currentTime());
      static int lastRet = -1;
      static int wraps = 0;

      int ret = zeroPoint.elapsed();

      if (ret < lastRet) {
            // Timer has wrapped to zero.  This only happens when
            // 24 hours have passed since this function has been
            // called for the first time.  It's unlikely that
            // someone will run QTads for 24 hours, but better to
            // be sure.
            ++wraps;
            zeroPoint.start();
      }

      lastRet = ret;
      return ret + (wraps * 86400000);
}


/* Sleep for a while.
 *
 * TODO: Implement it.
 */
void
os_sleep_ms( long /*delay_in_milliseconds*/ )
{
}


/* Set a file's type information.
 *
 * TODO: Find out if this can be empty on all systems Qt supports.
 */
void
os_settype( const char*, os_filetype_t )
{
}


/* --------------------------------------------------------------------
 */

/* Get filename from startup parameter, if possible.
 *
 * TODO: Find out what this is supposed to do.
 */
int
os_paramfile( char* /*buf*/ )
{
      return FALSE;
}


/* Initialize the OS layer and check OS-specific command-line
 * arguments.
 *
 * Not really needed by the Tads 2 portable layer, but we call it from
 * other Qt-code.
 */
int
os_init( int* /*argc*/, char* /*argv*/[], const char* /*prompt*/, char* /*buf*/,
       int /*bufsiz*/ )
{
      ::status_mode = 0;
      return 0;
}


/* Reverse the effect of any changes made in os_init().
 *
 * Not really needed by the Tads 2 portable layer, but we call it from
 * other Qt-code.
 */
void
os_uninit( void )
{
}


/* Pause prior to exit, if desired.
 *
 * TODO: Implement it.
 */
#ifndef HTMLQT
void
os_expause( void )
{
}
#endif


/* Terminate the program and exit with the given exit status.
 */
void
os_term( int /*status*/ )
{
#ifndef HTMLQT
      QTadsIO::quitApp = true;
#endif
}


/* Check for user break.
 *
 * TODO: Find out if there is something that looks like a "user break"
 * in Qt.
 */
#ifndef HTMLQT
int
os_break( void )
{
      return false;
}
#endif


/* Initialize the time zone.
 *
 * TODO: Find out if this can be empty on all systems Qt supports.
 */
void
os_tzset( void )
{
}


/* Set the default saved-game extension.
 *
 * We don't need to implement this since this routine is intended to be
 * invoked only if the interpreter is running as a stand-alone game,
 * and this isn't possible in QTads.
 */
void
os_set_save_ext( const char* )
{
}


/* --------------------------------------------------------------------
 */

/* Translate a character from the HTML 4 Unicode character set to the
 * current character set used for display.
 *
 * We encode the character as QTF.
 */
#ifndef HTMLQT
void
os_xlat_html4( unsigned int html4_char, char* result, size_t result_buf_len )
{
      Q_ASSERT(result != 0);
      Q_ASSERT(result_buf_len > 4);
      Q_ASSERT(not QTadsIO::t3Mode());

      const QString& tmp = QString(QChar(html4_char));

      result[0] = 1;
      result[1] = '\0';

      //std::strncat(result, tmp.utf8(), result_buf_len - std::strlen(tmp.utf8()) - 1);
      std::strcat(result, tmp.utf8());
      result[result_buf_len - 1] = '\0';
}
#endif


/* Generate a filename for a character-set mapping file.
 */
void
os_gen_charmap_filename( char* filename, char* internal_id, char* /*argv0*/ )
{
#ifndef HTMLQT
      Q_ASSERT(filename != 0);

      std::strncpy(filename, QString(QString(::CHARMAP_INST_DIR) + "/"
                 + QString(internal_id).lower() + ".tcp").latin1(), OSFNMAX);
      filename[OSFNMAX - 1] = '\0';
#else
      filename[0] = '\0';
#endif
}


/* Receive notification that a character mapping file has been loaded.
 *
 * We simply switch the codec that QString uses to convert to and from
 * char* and QCString.
 */
void
os_advise_load_charmap( char* /*id*/, char* /*ldesc*/, char* sysinfo )
{
      QTextCodec::setCodecForCStrings(QTextCodec::codecForName(sysinfo));
}


/* --------------------------------------------------------------------
 */

/* Get system information.
 *
 * TODO:
 *   - Reimplement it for the HTML interpreter.
 */
int
os_get_sysinfo( int code, void* /*param*/, long* result )
{
      Q_ASSERT(result != 0);

      switch(code)
      {
        case SYSINFO_TEXT_COLORS:
            // No color support.
            *result = SYSINFO_TXC_NONE;
            break;

        case SYSINFO_TEXT_HILITE:
            // We do text highlighting.
            *result = 1;
            break;

        case SYSINFO_INTERP_CLASS:
            // We're a graphical text-only interpreter, which is a
            // funny thing to say.
            *result = SYSINFO_ICLASS_TEXTGUI;
            break;

        case SYSINFO_HTML:
        case SYSINFO_JPEG:
        case SYSINFO_PNG:
        case SYSINFO_WAV:
        case SYSINFO_MIDI:
        case SYSINFO_WAV_MIDI_OVL:
        case SYSINFO_WAV_OVL:
        case SYSINFO_MPEG:
        case SYSINFO_MPEG1:
        case SYSINFO_MPEG2:
        case SYSINFO_MPEG3:
        case SYSINFO_PREF_IMAGES:
        case SYSINFO_PREF_SOUNDS:
        case SYSINFO_PREF_MUSIC:
        case SYSINFO_PREF_LINKS:
        case SYSINFO_LINKS_HTTP:
        case SYSINFO_LINKS_FTP:
        case SYSINFO_LINKS_NEWS:
        case SYSINFO_LINKS_MAILTO:
        case SYSINFO_LINKS_TELNET:
        case SYSINFO_PNG_TRANS:
        case SYSINFO_PNG_ALPHA:
        case SYSINFO_OGG:
        case SYSINFO_MNG:
        case SYSINFO_MNG_TRANS:
        case SYSINFO_MNG_ALPHA:
        case SYSINFO_BANNERS:
            // We don't support any of these features.
            *result = 0;
            break;

        default:
            // We didn't recognize the code, which means that this
            // QTads version is too old.
            qWarning("Game specified an unknown os_get_sysinfo() code.");
            return false;
      }
      // We recognized the code.
      return true;
}

Generated by  Doxygen 1.6.0   Back to index