So I’m trying to add a typical help-menu function to Swedish Inform. Navigation is the standard key-press deal – arrow keys and return to move, X to quit. That means I need keyboard capture.
Problem is, while Swedish Inform will compile to both z-machine and Glulx, getting keyboard input from (what I presume to be) the main window is much harder in Glulx than it is on the z-Machine.
It’s confusing. You can compile a game under Glulx and it will run. You can use graphics, as per the examples they give. But when I try the snippets I find to make a “press this key to exit loop” style function, the cursor keeps blinking after the prompting text appears, and the routine itself just freezes then and there. I get the feeling I’m missing something fundamental, probably having to do with streams.
So which streams are open by default, when a Glulx program is running? What am I missing?
This is what I’ve got, taken from the specs.
!% +language_name=Swedish
Include "SweAlpha";
Constant Story "";
Include "Parser";
Include "VerbLib";
Include "SweRout";
Include "SwedishG";
Include "Infglk";
Object Rum1 "Rummet"
with description "",
has light;
#Ifdef TARGET_GLULX;
[ read_char
ch done;
#Ifnot;
[ read_char
ch;
#Endif;
! read_char is included as it is the best way to force the buffer to be
! emptied
#ifdef TARGET_ZCODE;
@read 1 dummy;
#ifnot; ! TARGET_GLULX;
done = false;
glk($00D2, gg_mainwin); ! request_char_event
while (~~done) {
glk($00C0, gg_event); ! select
switch (gg_event-->0) {
5: ! evtype_Arrange
DrawStatusLine();
2: ! evtype_CharInput
if (gg_event-->1 == gg_mainwin)
done = true;
}
}
#endif; ! TARGET_
return ch;
];
[Initialise;
location=Rum1;
"^^^Text.^^";
];
[ HelpSub key;
print "See below.";
do {
key = read_char();
} until ((key == 'Q') ||
(key == 'X') || (key == 'q') ||
(key == 'x'));
];
Verb 'help'
* -> Help;
You might be better off making use of the Standard Library routine KeyCharPrimitive(), as this appears to handle the Glulx details automatically. I did a quick test with Inform 6.31 and StdLib 6/11, and it seemed to work.
[ HelpSub key;
print "See below.";
do {
key = KeyCharPrimitive();
} until ((key == 'Q') ||
(key == 'X') || (key == 'q') ||
(key == 'x'));
];
I’m not sure whether that routine is in the Swedish library, but if not, seeing what it does may help:
[ KeyCharPrimitive win nostat done res ix jx ch;
jx = ch; ! squash compiler warnings
if (win == 0) win = gg_mainwin;
if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
! get_line_stream
done = glk_get_line_stream(gg_commandstr, gg_arguments, 31);
if (done == 0) {
glk_stream_close(gg_commandstr, 0);
gg_commandstr = 0;
gg_command_reading = false;
! fall through to normal user input.
}
else {
! Trim the trailing newline
if (gg_arguments->(done-1) == 10) done = done-1;
res = gg_arguments->0;
if (res == '\') {
res = 0;
for (ix=1 : ix<done : ix++) {
ch = gg_arguments->ix;
if (ch >= '0' && ch <= '9') {
@shiftl res 4 res;
res = res + (ch-'0');
}
else if (ch >= 'a' && ch <= 'f') {
@shiftl res 4 res;
res = res + (ch+10-'a');
}
else if (ch >= 'A' && ch <= 'F') {
@shiftl res 4 res;
res = res + (ch+10-'A');
}
}
}
jump KCPContinue;
}
}
done = false;
glk_request_char_event(win);
while (~~done) {
glk_select(gg_event);
switch (gg_event-->0) {
5: ! evtype_Arrange
if (nostat) {
glk_cancel_char_event(win);
res = $80000000;
done = true;
break;
}
DrawStatusLine();
2: ! evtype_CharInput
if (gg_event-->1 == win) {
res = gg_event-->2;
done = true;
}
}
ix = HandleGlkEvent(gg_event, 1, gg_arguments);
if (ix == 0) ix = LibraryExtensions.RunWhile(ext_handleglkevent, 0, gg_event, 1, gg_arguments);
if (ix == 2) {
res = gg_arguments-->0;
done = true;
}
else if (ix == -1) {
done = false;
}
}
if (gg_commandstr ~= 0 && gg_command_reading == false) {
if (res < 32 || res >= 256 || (res == '\' or ' ')) {
glk_put_char_stream(gg_commandstr, '\');
done = 0;
jx = res;
for (ix=0 : ix<8 : ix++) {
@ushiftr jx 28 ch;
@shiftl jx 4 jx;
ch = ch & $0F;
if (ch ~= 0 || ix == 7) done = 1;
if (done) {
if (ch >= 0 && ch <= 9) ch = ch + '0';
else ch = (ch - 10) + 'A';
glk_put_char_stream(gg_commandstr, ch);
}
}
}
else {
glk_put_char_stream(gg_commandstr, res);
}
glk_put_char_stream(gg_commandstr, 10);
}
.KCPContinue;
return res;
];