-
Notifications
You must be signed in to change notification settings - Fork 17
/
gi_debug.h
196 lines (151 loc) · 8.04 KB
/
gi_debug.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#ifndef _GI_DEBUG_H
#define _GI_DEBUG_H
/* gi_debug.h: Debug feature layer for Glk API.
gi_debug version 0.9.5.
Designed by Andrew Plotkin <[email protected]>
http://eblong.com/zarf/glk/
This file is copyright 2014-7 by Andrew Plotkin. It is
distributed under the MIT license; see the "LICENSE" file.
------------------------------------------------
The debug module allows a Glk library to send out-of-band debug
commands to the game program it's linked to. The program returns
debug output to the library, which can then display it.
(Note: 98% of the time, the "game program" is an IF interpreter
such as Glulxe. In such cases, debug commands are handled by the
interpreter; they do *not* get passed through to the interpreted
game file. Debug commands may do things like pause, inspect, or
change the state of the interpreted game.)
As with all UI decision, the interface of the debug feature is
left up to the Glk library. Abstractly, we imagine a "debug
console" window with its own input line and scrolling text output.
(If at all possible, avoid trying to parse debug commands out of
regular game input! The CheapGlk library does this, but that's
because it's cheap. It's much better to provide a separate window
outside the regular game UI.)
* Configuration
The debug feature is cooperative: both the library and the game
must support it for debug commands to work. This requires a dance
of #ifdefs to make sure that everything builds in all
configurations.
(This is why the gi_debug.c and .h files are so tiny! All they do
is glue together Glk library and game (interpreter) code.)
Library side: If the library supports debugging, the
GIDEBUG_LIBRARY_SUPPORT #define in this header (gi_debug.h) will
be present (not commented out). By doing this, the library
declares that it offers the functions gidebug_output() and
gidebug_pause().
Older Glk libraries do not include this header at all. Therefore,
a game (interpreter) should have its own configuration option.
For example, in Glulxe, you define VM_DEBUGGER when compiling with
a library that has this header and GIDEBUG_LIBRARY_SUPPORT defined.
When building with an older library (or a library which comments
out the GIDEBUG_LIBRARY_SUPPORT line), you don't define VM_DEBUGGER,
and then the interpreter does not attempt to call debug APIs.
Game (interpreter) side: If the interpreter supports debug commands,
it should call gidebug_debugging_available() in its startup code.
(See unixstrt.c in the Glulxe source.) If it does not do this, the
library knows that debug commands cannot be handled; it should
disable or hide the "debug console" UI.
* Game responsibilities
When the game calls gidebug_debugging_available(), it passes two
callbacks: one to handle debug commands, and one to be notified
at various points in the game's life-cycle. (See below.)
The command callback should execute the command. The syntax of
debug commands is entirely up to the game. Any results should be
reported via gidebug_output(), which will display them in the
debug console.
The cycle callback is optional. The game might use it to compute
command timing and report it via gidebug_output().
The game may call gidebug_output() at any time; it doesn't have to
be the result of a command. For example, a game crash message could
be reported this way. However, remember that not all Glk libraries
support the debug console; even if it exists, the player might not
be watching it. Assume that game authors know about the debug system,
but players in general do not.
The game may call gidebug_pause() to stop execution for debugging.
(Glulxe does this on any crash, or if the game hits a @debugtrap
opcode.) This function accepts and executes debugging commands
until the user signals that it's time to continue execution.
* Library responsibilities
The library must implement gidebug_output(), to send a line of
text to the debug console, and gidebug_pause(), to stop and handle
debug commands, as described above.
When the user enters a command in the debug console, the library
should pass it (as a string) to gidebug_perform_command(). It
will be relayed to the game's command callback.
The library should call gidebug_announce_cycle() at various points
in the game's life-cycle. The argument will be relayed to the
game's cycle callback.
The library should call and pass...
- gidebug_cycle_Start: just before glk_main() begins
- gidebug_cycle_End: when glk_exit() is called or glk_main() returns
- gidebug_cycle_InputWait: when glk_select() begins
- gidebug_cycle_InputAccept: when glk_select() returns
- gidebug_cycle_DebugPause: when gidebug_pause() begins
- gidebug_cycle_DebugUnpause: when gidebug_pause() ends
*/
/* Uncomment if the library supports a UI for debug commands.
Comment it out if the library doesn't. */
#define GIDEBUG_LIBRARY_SUPPORT (1)
typedef enum gidebug_cycle_enum {
gidebug_cycle_Start = 1,
gidebug_cycle_End = 2,
gidebug_cycle_InputWait = 3,
gidebug_cycle_InputAccept = 4,
gidebug_cycle_DebugPause = 5,
gidebug_cycle_DebugUnpause = 6,
} gidebug_cycle;
typedef int (*gidebug_cmd_handler)(char *text);
typedef void (*gidebug_cycle_handler)(int cycle);
/* The gidebug-layer functions are always available (assuming this header
exists!) The game should have a compile-time option (e.g. VM_DEBUGGER)
so as not to rely on this header. */
/* The game calls this if it offers debug commands. (The library may
or may not make use of them.)
The cmdhandler argument must be a function that accepts a debug
command (a UTF-8 string) and executes it, displaying output via
gidebug_output(). The function should return nonzero for a "continue"
command (only relevant inside gidebug_pause()).
The cyclehandler argument should be a function to be notified
when the game starts, stops, and blocks for input. (This is optional;
pass NULL if not needed.)
*/
extern void gidebug_debugging_available(gidebug_cmd_handler cmdhandler, gidebug_cycle_handler cyclehandler);
/* The library calls this to check whether the game accepts debug commands.
(Returns nonzero if the game has called gidebug_debugging_available().
If this returns zero, the library should disable or hide the debug
console.)
*/
extern int gidebug_debugging_is_available(void);
/* The library calls this when the user enters a command in the debug
console. The command will be passed along to the game's cmdhandler,
if one was supplied. This will return nonzero for a "continue"
command (only relevant inside gidebug_pause()).
This may only be called when the game is waiting for input! This
means one of two circumstances: while inside glk_select(), or
while inside gidebug_pause(). If you call it at any other time,
you've made some kind of horrible threading mistake.
*/
extern int gidebug_perform_command(char *cmd);
/* The library calls this at various points in the game's life-cycle.
The argument will be passed along to the game's cyclehandler,
if one was supplied.
*/
extern void gidebug_announce_cycle(gidebug_cycle cycle);
#if GIDEBUG_LIBRARY_SUPPORT
/* These functions must be implemented in the library. (If the library
has declared debug support.) */
/* Send a line of text to the debug console. The text will be a single line
(no newlines), in UTF-8.
*/
extern void gidebug_output(char *text);
/* Block and wait for debug commands. The library should accept debug
commands and pass them to gidebug_perform_command(), repeatedly,
until that function returns nonzero. It may also stop of its own
accord (say, when an "unpause" menu item is triggered).
This should call gidebug_announce_cycle(gidebug_cycle_DebugPause)
upon entry, and the same with gidebug_cycle_DebugUnpause upon exit.
*/
extern void gidebug_pause(void);
#endif /* GIDEBUG_LIBRARY_SUPPORT */
#endif /* _GI_DEBUG_H */