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.
354 lines
13 KiB
Python
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.")
|