mud/src/mudlib/zmachine/glk.py
Jared Miller 5ea030a0ac
Re-copy fixed repos/zvm source into src/mudlib/zmachine
Copies the cleaned-up zvm source (ruff-compliant, ty-clean) back into
the zmachine module. Adds __init__.py with proper exports and updates
.gitignore for debug.log/disasm.log.
2026-02-09 20:44:21 -05:00

354 lines
13 KiB
Python

#
# This module defines a ctypes foreign function interface to a Glk
# library that has been built as a shared library. For more information
# on the Glk API, see http://www.eblong.com/zarf/glk/.
#
# Note that the way this module interfaces with a Glk library is
# slightly different from the standard; the standard interface
# actually assumes that a Glk library is in fact not a library, but a
# "front-end" program that is statically linked to a Glk back-end,
# also known as a Glk "program", by calling the back-end's glk_main()
# function.
#
# Instead of this, we assume that the Glk library is actually a shared
# library that is initialized by some external source--be it a Python
# script or a compiled program--and used by this module. Note that
# this is actually the way some Glk libraries, such as WindowsGlk, are
# made to function.
#
# For the license of this file, please consult the LICENSE file in the
# root directory of this distribution.
#
import ctypes
# These are ctypes-style declarations that reflect the Glk.h file,
# which defines the Glk API, version 0.7.0. The most recent version
# of Glk.h can be found here:
#
# http://www.eblong.com/zarf/glk/glk.h
#
# Note that there are ctypes extension libraries that can do this kind
# of thing for us (that is, take a .h file and automatically generate
# a ctypes wrapper from it); however, the only one that exists at the
# time of this writing is ctypeslib, which has dependencies that would
# make our build process quite complex. Given the relatively small
# size of the Glk API and the freedom we get from hand-coding the
# interface ourselves, we're not using ctypeslib.
glsi32 = ctypes.c_int32
glui32 = ctypes.c_uint32
winid_t = ctypes.c_void_p
strid_t = ctypes.c_void_p
frefid_t = ctypes.c_void_p
schanid_t = ctypes.c_void_p
# TRUE, FALSE, and NULL aren't defined in glk.h, but are mentioned in
# Section 1.9 of the Glk spec 0.7.0.
TRUE = 1
FALSE = 0
NULL = ctypes.pointer(glui32(0))
gestalt_Version = 0
gestalt_CharInput = 1
gestalt_LineInput = 2
gestalt_CharOutput = 3
gestalt_CharOutput_CannotPrint = 0
gestalt_CharOutput_ApproxPrint = 1
gestalt_CharOutput_ExactPrint = 2
gestalt_MouseInput = 4
gestalt_Timer = 5
gestalt_Graphics = 6
gestalt_DrawImage = 7
gestalt_Sound = 8
gestalt_SoundVolume = 9
gestalt_SoundNotify = 10
gestalt_Hyperlinks = 11
gestalt_HyperlinkInput = 12
gestalt_SoundMusic = 13
gestalt_GraphicsTransparency = 14
gestalt_Unicode = 15
evtype_None = 0
evtype_Timer = 1
evtype_CharInput = 2
evtype_LineInput = 3
evtype_MouseInput = 4
evtype_Arrange = 5
evtype_Redraw = 6
evtype_SoundNotify = 7
evtype_Hyperlink = 8
class event_t(ctypes.Structure):
_fields_ = [("type", glui32), ("win", winid_t), ("val1", glui32), ("val2", glui32)]
keycode_Unknown = 0xFFFFFFFF
keycode_Left = 0xFFFFFFFE
keycode_Right = 0xFFFFFFFD
keycode_Up = 0xFFFFFFFC
keycode_Down = 0xFFFFFFFB
keycode_Return = 0xFFFFFFFA
keycode_Delete = 0xFFFFFFF9
keycode_Escape = 0xFFFFFFF8
keycode_Tab = 0xFFFFFFF7
keycode_PageUp = 0xFFFFFFF6
keycode_PageDown = 0xFFFFFFF5
keycode_Home = 0xFFFFFFF4
keycode_End = 0xFFFFFFF3
keycode_Func1 = 0xFFFFFFEF
keycode_Func2 = 0xFFFFFFEE
keycode_Func3 = 0xFFFFFFED
keycode_Func4 = 0xFFFFFFEC
keycode_Func5 = 0xFFFFFFEB
keycode_Func6 = 0xFFFFFFEA
keycode_Func7 = 0xFFFFFFE9
keycode_Func8 = 0xFFFFFFE8
keycode_Func9 = 0xFFFFFFE7
keycode_Func10 = 0xFFFFFFE6
keycode_Func11 = 0xFFFFFFE5
keycode_Func12 = 0xFFFFFFE4
keycode_MAXVAL = 28
style_Normal = 0
style_Emphasized = 1
style_Preformatted = 2
style_Header = 3
style_Subheader = 4
style_Alert = 5
style_Note = 6
style_BlockQuote = 7
style_Input = 8
style_User1 = 9
style_User2 = 10
style_NUMSTYLES = 11
class stream_result_t(ctypes.Structure):
_fields_ = [("readcount", glui32), ("writecount", glui32)]
wintype_AllTypes = 0
wintype_Pair = 1
wintype_Blank = 2
wintype_TextBuffer = 3
wintype_TextGrid = 4
wintype_Graphics = 5
winmethod_Left = 0x00
winmethod_Right = 0x01
winmethod_Above = 0x02
winmethod_Below = 0x03
winmethod_DirMask = 0x0F
winmethod_Fixed = 0x10
winmethod_Proportional = 0x20
winmethod_DivisionMask = 0xF0
fileusage_Data = 0x00
fileusage_SavedGame = 0x01
fileusage_Transcript = 0x02
fileusage_InputRecord = 0x03
fileusage_TypeMask = 0x0F
fileusage_TextMode = 0x100
fileusage_BinaryMode = 0x000
filemode_Write = 0x01
filemode_Read = 0x02
filemode_ReadWrite = 0x03
filemode_WriteAppend = 0x05
seekmode_Start = 0
seekmode_Current = 1
seekmode_End = 2
stylehint_Indentation = 0
stylehint_ParaIndentation = 1
stylehint_Justification = 2
stylehint_Size = 3
stylehint_Weight = 4
stylehint_Oblique = 5
stylehint_Proportional = 6
stylehint_TextColor = 7
stylehint_BackColor = 8
stylehint_ReverseColor = 9
stylehint_NUMHINTS = 10
stylehint_just_LeftFlush = 0
stylehint_just_LeftRight = 1
stylehint_just_Centered = 2
stylehint_just_RightFlush = 3
# Function prototypes for the core Glk API. It is a list of 3-tuples; each
# item in the list represents a function prototype, and each 3-tuple
# is in the form (result_type, function_name, arg_types).
CORE_GLK_LIB_API = [
(None, "glk_exit", ()),
(None, "glk_tick", ()),
(glui32, "glk_gestalt", (glui32, glui32)),
(glui32, "glk_gestalt_ext", (glui32, glui32, ctypes.POINTER(glui32), glui32)),
(winid_t, "glk_window_get_root", ()),
(winid_t, "glk_window_open", (winid_t, glui32, glui32, glui32, glui32)),
(None, "glk_window_close", (winid_t, ctypes.POINTER(stream_result_t))),
(
None,
"glk_window_get_size",
(winid_t, ctypes.POINTER(glui32), ctypes.POINTER(glui32)),
),
(None, "glk_window_set_arrangement", (winid_t, glui32, glui32, winid_t)),
(
None,
"glk_window_get_arrangement",
(
winid_t,
ctypes.POINTER(glui32),
ctypes.POINTER(glui32),
ctypes.POINTER(winid_t),
),
),
(winid_t, "glk_window_iterate", (winid_t, ctypes.POINTER(glui32))),
(glui32, "glk_window_get_rock", (winid_t,)),
(glui32, "glk_window_get_type", (winid_t,)),
(winid_t, "glk_window_get_parent", (winid_t,)),
(winid_t, "glk_window_get_sibling", (winid_t,)),
(None, "glk_window_clear", (winid_t,)),
(None, "glk_window_move_cursor", (winid_t, glui32, glui32)),
(strid_t, "glk_window_get_stream", (winid_t,)),
(None, "glk_window_set_echo_stream", (winid_t, strid_t)),
(strid_t, "glk_window_get_echo_stream", (winid_t,)),
(None, "glk_set_window", (winid_t,)),
(strid_t, "glk_stream_open_file", (frefid_t, glui32, glui32)),
(strid_t, "glk_stream_open_memory", (ctypes.c_char_p, glui32, glui32, glui32)),
(None, "glk_stream_close", (strid_t, ctypes.POINTER(stream_result_t))),
(strid_t, "glk_stream_iterate", (strid_t, ctypes.POINTER(glui32))),
(glui32, "glk_stream_get_rock", (strid_t,)),
(None, "glk_stream_set_position", (strid_t, glsi32, glui32)),
(glui32, "glk_stream_get_position", (strid_t,)),
(None, "glk_stream_set_current", (strid_t,)),
(strid_t, "glk_stream_get_current", ()),
(None, "glk_put_char", (ctypes.c_ubyte,)),
(None, "glk_put_char_stream", (strid_t, ctypes.c_ubyte)),
(None, "glk_put_string", (ctypes.c_char_p,)),
(None, "glk_put_string_stream", (strid_t, ctypes.c_char_p)),
(None, "glk_put_buffer", (ctypes.c_char_p, glui32)),
(None, "glk_put_buffer_stream", (strid_t, ctypes.c_char_p, glui32)),
(None, "glk_set_style", (glui32,)),
(None, "glk_set_style_stream", (strid_t, glui32)),
(glsi32, "glk_get_char_stream", (strid_t,)),
(glui32, "glk_get_line_stream", (strid_t, ctypes.c_char_p, glui32)),
(glui32, "glk_get_buffer_stream", (strid_t, ctypes.c_char_p, glui32)),
(None, "glk_stylehint_set", (glui32, glui32, glui32, glsi32)),
(None, "glk_stylehint_clear", (glui32, glui32, glui32)),
(glui32, "glk_style_distinguish", (winid_t, glui32, glui32)),
(glui32, "glk_style_measure", (winid_t, glui32, glui32, ctypes.POINTER(glui32))),
(frefid_t, "glk_fileref_create_temp", (glui32, glui32)),
(frefid_t, "glk_fileref_create_by_name", (glui32, ctypes.c_char_p, glui32)),
(frefid_t, "glk_fileref_create_by_prompt", (glui32, glui32, glui32)),
(frefid_t, "glk_fileref_create_from_fileref", (glui32, frefid_t, glui32)),
(None, "glk_fileref_destroy", (frefid_t,)),
(frefid_t, "glk_fileref_iterate", (frefid_t, ctypes.POINTER(glui32))),
(glui32, "glk_fileref_get_rock", (frefid_t,)),
(None, "glk_fileref_delete_file", (frefid_t,)),
(glui32, "glk_fileref_does_file_exist", (frefid_t,)),
(None, "glk_select", (ctypes.POINTER(event_t),)),
(None, "glk_select_poll", (ctypes.POINTER(event_t),)),
(None, "glk_request_timer_events", (glui32,)),
(None, "glk_request_line_event", (winid_t, ctypes.c_char_p, glui32, glui32)),
(None, "glk_request_char_event", (winid_t,)),
(None, "glk_request_mouse_event", (winid_t,)),
(None, "glk_cancel_line_event", (winid_t, ctypes.POINTER(event_t))),
(None, "glk_cancel_char_event", (winid_t,)),
(None, "glk_cancel_mouse_event", (winid_t,)),
]
# Function prototypes for the optional Unicode extension of the Glk
# API.
UNICODE_GLK_LIB_API = [
(None, "glk_put_char_uni", (glui32,)),
(None, "glk_put_string_uni", (ctypes.POINTER(glui32),)),
(None, "glk_put_buffer_uni", (ctypes.POINTER(glui32), glui32)),
(None, "glk_put_char_stream_uni", (strid_t, glui32)),
(None, "glk_put_string_stream_uni", (strid_t, ctypes.POINTER(glui32))),
(None, "glk_put_buffer_stream_uni", (strid_t, ctypes.POINTER(glui32), glui32)),
(glsi32, "glk_get_char_stream_uni", (strid_t,)),
(glui32, "glk_get_buffer_stream_uni", (strid_t, ctypes.POINTER(glui32), glui32)),
(glui32, "glk_get_line_stream_uni", (strid_t, ctypes.POINTER(glui32), glui32)),
(strid_t, "glk_stream_open_file_uni", (frefid_t, glui32, glui32)),
(
strid_t,
"glk_stream_open_memory_uni",
(ctypes.POINTER(glui32), glui32, glui32, glui32),
),
(None, "glk_request_char_event_uni", (winid_t,)),
(
None,
"glk_request_line_event_uni",
(winid_t, ctypes.POINTER(glui32), glui32, glui32),
),
]
class GlkLib:
"""Encapsulates the ctypes interface to a Glk shared library. When
instantiated, it wraps the shared library with the appropriate
function prototypes from the Glk API to reduce the chance of
mis-calls that may result in segfaults (this effectively simulates
the strong type-checking a C compiler would perform)."""
def __init__(self, lib_name):
"""Instantiates the instance, binding it to the given shared
library (which is referenced by name)."""
self._dll = ctypes.CDLL(lib_name)
self.__bind_prototypes(CORE_GLK_LIB_API)
if self.glk_gestalt(gestalt_Unicode, 0) == 1: # type: ignore[unresolved-attribute]
self.__bind_prototypes(UNICODE_GLK_LIB_API)
else:
self.__bind_not_implemented_prototypes(UNICODE_GLK_LIB_API)
def __bind_prototypes(self, function_prototypes):
"""Create function prototypes from the given list of 3-tuples
of the form (result_type, function_name, arg_types), bind them
to functions in our shared library, and then bind the function
instances as methods to this object."""
for function_prototype in function_prototypes:
result_type, function_name, arg_types = function_prototype
prototype = ctypes.CFUNCTYPE(result_type, *arg_types)
function = prototype((function_name, self._dll))
setattr(self, function_name, function)
def __bind_not_implemented_prototypes(self, function_prototypes):
"""Create functions with the names from the given list of
3-tuples of the form (result_type, function_name, arg_types)
that simply raise NotImplementedError, and bind them to this
object. This should be used when a Glk library doesn't
support some optional extension of the Glk API."""
def notImplementedFunction(*args, **kwargs):
raise NotImplementedError("Function not implemented by this Glk library.")
for function_prototype in function_prototypes:
_, function_name, _ = function_prototype
setattr(self, function_name, notImplementedFunction)
def glk_char_to_lower(self, ch):
raise NotImplementedError("Use unicode.lower() instead.")
def glk_char_to_upper(self, ch):
raise NotImplementedError("Use unicode.upper() instead.")
def glk_buffer_to_lower_case_uni(self, buf, len, numchars):
raise NotImplementedError("Use unicode.lower() instead.")
def glk_buffer_to_upper_case_uni(self, buf, len, numchars):
raise NotImplementedError("Use unicode.upper() instead.")
def glk_buffer_to_title_case_uni(self, buf, len, numchars, lowerrest):
raise NotImplementedError("Use unicode.title() instead.")