Files
scummvm-cursorfix/engines/glk/tads/tads2/os.h
2026-02-02 04:50:13 +01:00

405 lines
18 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 of the License, 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. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* Portable interfaces to OS-specific functions
*
* This file defines interfaces to certain functions that must be called
* from portable code, but which must have system-specific implementations.
*/
#ifndef GLK_TADS_TADS2_OS
#define GLK_TADS_TADS2_OS
#include "common/system.h"
#include "glk/tads/os_frob_tads.h"
#include "glk/tads/os_glk.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/appctx.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* ------------------------------------------------------------------------ */
/*
* A note on character sets:
*
* Except where noted, all character strings passed to and from the
* osxxx functions defined herein use the local operating system
* representation. On a Windows machine localized to Eastern Europe,
* for example, the character strings passed to and from the osxxx
* functions would use single-byte characters in the Windows code page
* 1250 representation.
*
* Callers that use multiple character sets must implement mappings to
* and from the local character set when calling the osxxx functions.
* The osxxx implementations are thus free to ignore any issues related
* to character set conversion or mapping.
*
* The osxxx implementations are specifically not permitted to use
* double-byte Unicode as the native character set, nor any other
* character set where a null byte could appear as part of a non-null
* character. In particular, callers may assume that null-terminated
* strings passed to and from the osxxx functions contain no embedded
* null bytes. Multi-byte character sets (i.e., character sets with
* mixed single-byte and double-byte characters) may be used as long as
* a null byte is never part of any multi-byte character, since this
* would guarantee that a null byte could always be taken as a null
* character without knowledge of the encoding or context.
*/
/* ------------------------------------------------------------------------ */
/*
* "Far" Pointers. Most platforms can ignore this. For platforms with
* mixed-mode addressing models, where pointers of different sizes can
* be used within a single program and hence some pointers require
* qualification to indicate that they use a non-default addressing
* model, the keyword OSFAR should be defined to the appropriate
* compiler-specific extension keyword.
*
* If you don't know what I'm talking about here, you should just ignore
* it, because your platform probably doesn't have anything this
* sinister. As of this writing, this applies only to MS-DOS, and then
* only to 16-bit implementations that must interact with other 16-bit
* programs via dynamic linking or other mechanisms.
*/
/* ------------------------------------------------------------------------ */
/*
* ANSI C99 exact-size integer types.
*
* C99 defines a set of integer types with exact bit sizes, named intXX_t
* for a signed integer with XX bits, and uintXX_t for unsigned. XX can be
* 8, 16, 32, or 64. TADS uses the 16- and 32-bit sizes, so each platform
* is responsible for defining the following types:
*
*. int16_t - a signed integer type storing EXACTLY 16 bits
*. uint16_t - an unsigned integer type storing EXACTLY 16 bits
*. int32_t - a signed integer type storing EXACTLY 32 bits
*. uint32_t - an unsigned integer type storing EXACTLY 32 bits
*
* Many modern compilers provide definitions for these types via the
* standard header stdint.h. Where stdint.h is provided, the platform code
* can merely #include <stdint.h>.
*
* For compilers where stdint.h isn't available, you must provide suitable
* typedefs. Note that the types must be defined with the exact bit sizes
* specified; it's not sufficient to use a bigger type, because we depend
* in some cases on overflow and sign extension behavior at the specific
* bit size.
*/
/* ------------------------------------------------------------------------ */
/*
* Thread-local storage (TLS).
*
* When TADS is compiled with threading support, it requires some variables
* to be "thread-local". This means that the variables have global scope
* (so they're not stored in "auto" variables on the stack), but each
* thread has a private copy of each such variable.
*
* Nearly all systems that support threads also support thread-local
* storage. Like threading support itself, though, TLS support is at
* present implemented only in non-portable OS APIs rather than standard C
* language features. TLS is a requirement if TADS is compiled with
* threading, but it's not needed for non-threaded builds. TADS only
* requires threading at present (version 3.1) for its network features;
* since these features are optional, systems that don't have threading and
* TLS support will simply need to disable the network features, which will
* allow all of the threading and TLS definitions in osifc to be omitted.
*
* There appear to be two common styles of TLS programming models. The
* first provides non-standard compiler syntax for declarative creation of
* thread-local variables. The Microsoft (on Windows) and Gnu compilers
* (on Linux and Unix) do this: they provide custom storage class modifiers
* for declaring thread locals (__declspec(thread) for MSVC, __thread for
* gcc). Compilers that support declarative thread locals handle the
* implementation details through code generation, so the program merely
* needs to add the special TLS storage class qualifier to an otherwise
* ordinary global variable declaration, and then can access the thread
* local as though it were an ordinary global.
*
* The second programming model is via explicit OS API calls to create,
* initialize, and access thread locals. pthreads provides such an API, as
* does Win32. In fact, when you use the declarative syntax with MSVC or
* gcc, the compiler generates the appropriate API calls, but the details
* are transparent to the program; in contrast, when using pthreads
* directly, the program must actively call the relevant APIs.
*
* It's probably the case that every system that has compiler-level support
* for declarative thread local creation also has procedural APIs, so the
* simplest way to abstract the platform differences would be to do
* everything in terms of APIs. However, it seems likely that compilers
* with declarative syntax might be able to generate more efficient code,
* since optimizers always benefit from declarative information. So we'd
* like to use declarative syntax whenever it's available, but fall back on
* explicit API calls when it's not. So our programming model is a union
* of the two styles:
*
* 1. For each thread local, declare the thread local:
*. OS_DECL_TLS(char *, my_local);
*
* 2. At main program startup (for the main thread only), initialize each
* thread local:
*. os_tls_create(my_local);
*
* 3. Never get or set the value of a thread local directly; instead, use
* the get/set functions:
*. char *x = os_tls_get(char *, my_local);
*. os_tls_set(my_local, "hello");
*
* One key feature of this implementation is that each thread local is
* stored as a (void *) value. We do it this way to allow a simple direct
* mapping to the pthreads APIs, since that's going to be the most common
* non-declarative implementation. This means that a thread local variable
* can contain any pointer type, but *only* a pointer type. The standard
* pattern for dealing with anything more ocmplex is the same as in
* pthreads: gather up the data into a structure, malloc() an instance of
* that structure at entry to each thread (including the main thread), and
* os_tls_set() the variable to contain a pointer to that structure. From
* then on, use os_tls_set(my_struct *, my_local)->member to access the
* member variables in the structure. And finally, each thread must delete
* the structure at thread exit.
*/
/*
*
* Declare a thread local.
*
* - For compilers that support declarative TLS variables, the local OS
* headers should use the compiler support by #defining OS_DECL_TLS to the
* appropriate local declarative keyword.
*
* - For systems without declarative TLS support but with TLS APIs, the
* global declared by this macro actually stores the slot ID (what pthreads
* calls the "key") for the variable. This macro should therefore expand
* to a declaration of the appropriate API type for a slot ID; for example,
* on pthreads, #define OS_DECL_TLS(t, v) pthread_key_t v.
*
* - For builds with no thread support, simply #define this to declare the
* variable as an ordinary global: #define OS_DECL_TLS(t, v) t v.
*/
/* #define OS_DECL_TLS(typ, varname) __thread typ varname */
/*
* For API-based systems without declarative support in the compiler, the
* main program startup code must explicitly create a slot for each thread-
* local variable by calling os_tls_create(). The API returns a slot ID,
* which is shared among threads and therefore can be stored in an ordinary
* global variable. OS_DECL_TLS will have declared the global variable
* name in this case as an ordinary global of the slot ID type. The
* os_tls_create() macro should therefore expand to a call to the slot
* creation API, storing the new slot ID in the global.
*
* Correspondingly, before the main thread exits, it should delete each
* slot it created, b calling os_tls_delete().
*
* For declarative systems, there's no action required here, so these
* macros can be defined to empty.
*/
/* #define os_tls_create(varname) pthread_key_create(&varname, NULL) */
/* #define os_tls_delete(varname) pthread_key_delete(varname) */
/*
* On API-based systems, each access to get or set the thread local
* requires an API call, using the slot ID stored in the actual global to
* get the per-thread instance of the variable's storage.
*. #define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
*. #define os_tls_set(varname, val) pthread_setspecific(varname, val)
*
* On declarative systems, the global variable itself is the thread local,
* so get/set can be implemented as direct access to the variable.
*. #define os_tls_get(typ, varname) varname
*. #define os_tls_set(varname, val) varname = (val)
*/
/*
* Common TLS definitions - declarative thread locals
*
* For systems with declarative TLS support in the compiler, the OS header
* can #define OS_DECLARATIVE_TLS to pick up suitable definitions for the
* os_tls_xxx() macros. The OS header must separately define OS_DECL_TLS
* as appropriate for the local system.
*/
#ifdef OS_DECLARATIVE_TLS
#define os_tls_create(varname)
#define os_tls_delete(varname)
#define os_tls_get(typ, varname) varname
#define os_tls_set(varname, val) varname = (val)
#endif
/*
* Common TLS definitions - pthreads
*
* For pthreads systems without declarative TLS support in the compiler,
* the OS header can simply #define OS_PTHREAD_TLS to pick up the standard
* definitions below.
*/
#ifdef OS_PTHREAD_TLS
#include <pthread.h>
#define OS_DECL_TLS(typ, varname) pthread_key_t varname
#define os_tls_create(varname) pthread_key_create(&varname, NULL)
#define os_tls_delete(varname) pthread_key_delete(varname)
#define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
#define os_tls_set(varname, val) pthread_setspecific(varname, val)
#endif
/* ------------------------------------------------------------------------ */
/*
* OS main entrypoint
*/
int os0main(int oargc, char **oargv,
int (*mainfn)(int, char **, char *),
const char *before, const char *config);
/*
* new-style OS main entrypoint - takes an application container context
*/
int os0main2(int oargc, char **oargv,
int (*mainfn)(int, char **, appctxdef *, char *),
const char *before, const char *config,
appctxdef *appctx);
/* open the error message file for reading */
osfildef *oserrop(const char *arg0);
/* ------------------------------------------------------------------------ */
/*
* Special "switch" statement optimization flags. These definitions are
* OPTIONAL - if a platform doesn't provide these definitions, suitable
* code that's fully portable will be used.
*
* On some compilers, the performance of a "switch" statement can be
* improved by fully populating the switch with all possible "case"
* values. When the compiler can safely assume that every possible "case"
* value is specifically called out in the switch, the compiler can
* generate somewhat faster code by omitting any range check for the
* controlling expression of the switch: a range check is unnecessary
* because the compiler knows that the value can never be outside the
* "case" table.
*
* This type of optimization doesn't apply to all compilers. When a given
* platform's compiler can be coerced to produce faster "switch"
* statements, though, there might be some benefit in defining these
* symbols according to local platform rules.
*
* First, #define OS_FILL_OUT_CASE_TABLES if you want this type of switch
* optimization at all. This symbol is merely a flag, so it doesn't need
* a value - #defining it is enough to activate the special code. If you
* don't define this symbol, then the code that cares about this will
* simply generate ordinary switches, leaving holes in the case table and
* using "default:" to cover them. If the platform's osxxx.h header does
* #define OS_FILL_OUT_CASE_TABLES, then the portable code will know to
* fill out case tables with all possible alternatives where it's possible
* and potentially beneficial to do so.
*
* Second, if you #define OS_FILL_OUT_CASE_TABLES, you MUST ALSO #define
* OS_IMPOSSIBLE_DEFAULT_CASE. The value for this symbol must be some
* code to insert into a "switch" statement at the point where a
* "default:" case would normally go. On some compilers, special
* extensions allow the program to explicitly indicate within a switch
* that all possible cases are covered, so that the compiler doesn't have
* to be relied upon to infer this for itself (which would be impossible
* for it to do in many cases anyway). You can use an empty definition
* for this symbol if your compiler doesn't have any special construct for
* indicating a fully-populated case table.
*
* Note that *most* switch statements in portable code won't care one way
* or the other about these definitions. There's a time/space trade-off
* in fully populating a switch's case table, so only the most
* time-critical code will bother trying.
*/
/* ------------------------------------------------------------------------ */
/*
* TADS 2 swapping configuration. Define OS_DEFAULT_SWAP_ENABLED to 0
* if you want swapping turned off, 1 if you want it turned on. If we
* haven't defined a default swapping mode yet, turn swapping on by
* default.
*/
#ifndef OS_DEFAULT_SWAP_ENABLED
# define OS_DEFAULT_SWAP_ENABLED 1
#endif
/*
* If the system "long description" (for the banner) isn't defined, make
* it the same as the platform ID string.
*/
#ifndef OS_SYSTEM_LDESC
# define OS_SYSTEM_LDESC OS_SYSTEM_NAME
#endif
/*
* TADS 2 Usage Lines
*
* If the "usage" lines (i.e., the descriptive lines of text describing
* how to run the various programs) haven't been otherwise defined,
* define defaults for them here. Some platforms use names other than
* tc, tr, and tdb for the tools (for example, on Unix they're usually
* tadsc, tadsr, and tadsdb), so the usage lines should be adjusted
* accordingly; simply define them earlier than this point (in this file
* or in one of the files included by this file, such as osunixt.h) for
* non-default text.
*/
#ifndef OS_TC_USAGE
# define OS_TC_USAGE "usage: tc [options] file"
#endif
#ifndef OS_TR_USAGE
# define OS_TR_USAGE "usage: tr [options] file"
#endif
#ifndef OS_TDB_USAGE
# define OS_TDB_USAGE "usage: tdb [options] file"
#endif
/*
* Ports can define a special TDB startup message, which is displayed
* after the copyright/version banner. If it's not defined at this
* point, define it to an empty string.
*/
#ifndef OS_TDB_STARTUP_MSG
# define OS_TDB_STARTUP_MSG ""
#endif
/*
* If a system patch sub-level isn't defined, define it here as zero.
* The patch sub-level is used on some systems where a number of ports
* are derived from a base port (for example, a large number of ports
* are based on the generic Unix port). For platforms like the Mac,
* where the porting work applies only to that one platform, this
* sub-level isn't meaningful.
*/
#ifndef OS_SYSTEM_PATCHSUBLVL
# define OS_SYSTEM_PATCHSUBLVL "0"
#endif
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif