Inform6 command echo & invisible commands

Ah. Yeah, it’s illegal to print any text when there’s a line input request pending.

You’ll either need to do something else, or cancel the line input request – although the trick with that is that unless you then re-invoke the parser to make another request afterwards, you’ll end up getting the VM stuck doing nothing in particular.

You can probably just re-invoke the parser directly, but another option would be to borrow some tricks from my I7 extension that lets you suspend an input request while you print some stuff and then resume it again afterwards.

I will look into both. Thank you!

I’m curious how @zarf’s code circumvented this. I see the cancel_line_event call in his I6 code but if the external event gets rejected before ever getting to I6…

I’m not sure what you’re asking. When the event arrives, I call cancel_line_event and then set up the input buffer for a normal turn. When the next turn arrives, the game requests line input in the usual way.

I’m asking because my current extevent call ends up generating this error:

Quixe run: glk_put_jstring: window has pending line request

Which seems to suggest I can’t call it until there is no pending line requests but your code doesn’t cancel the line request until the I6 code is called which doesn’t happen for me because the error above stops the process.

So, somehow, you got your extevent call to successfully filter down to your I6 code which I’m not able to accomplish.

If I don’t make my extevent call but type a command instead, my HandleGlkEvent routine is called and I can read the info but, as far as I understand it, that’s after the line_input has happened and the typed command is echoed to the screen.

Okay. Hm. I’m not entirely sure what’s going on.

The glk_put_jstring error occurs when something is printed. Do you still have that print line in your HandleGlkEvent() routine? If so, remove it.

Are you calling an I7 rulebook before the glk_cancel_line_event() call? Sometimes I7 tries to print a newline during rulebook processing, even if the rulebook has nothing to do with printing. It’s possible to suppress this if that’s the situation.

That’s a useful tidbit! Let me run with that and I’ll post again. Thank you!

Yes, by “do something else” I meant “do anything except print”. If you’re just wanting to update some internal world state by reading the JS data, and not actually interrupt the player input in progress, then you don’t need to do anything special – just avoid calling anything that can possibly print, and then continue waiting for the player’s input to do whatever on the next turn or later.

If the point is instead to interrupt the line input in progress and do some other action (that does involve a printed response), then you’ll have to cancel the input and do something more complicated, as I mentioned above.

Okay, so that solved the problem. No print calls in HandleGlkEvent.

Now I’ve run into a different issue. Right now I have a verb that triggers javascript code which, in turn, triggers an external event.

HOWEVER, the external event isn’t sent because gli_selectref is null when the execution reaches line 358 (or thereabouts) in GlkApi’s handle_external_input().

Is there something I can do to make sure that select_ref won’t be null when I send my external event?

select_ref is null if the game isn’t awaiting input. That is, the game is still processing the previous input (your verb); it hasn’t come around to accepting the next event yet.

It normally doesn’t make sense for game code to trigger an input event. When a verb is executing, it can run whatever game code it wants. Input events come from the player or from the outside world.

I get that you’re doing something unusual, but I don’t have a clear idea what.

One cheesy possibility is for the javascript code to execute a tiny delay before sending the extevent. That is, call window.setTimeout() with an argument of 1 millisecond. By the time the event comes in, the previous input will have finished processing.

window.setTimeout(function() { GlkOte.extevent(args); }, 1);

And that’s probably how it’s going to happen most of the time but right now while I’m testing (and possibly in the actual use) I need to be able to trigger it from Inform6.

Let me try the delay and see if it resolves the issue.

This was a complex question and there were a number of sub-questions that have their own solutions. Unfortunately, I don’t seem to be able to select more than one, so I’m posting this as a placeholder.

@zarf and @mirality solved everything, read the whole thread to see how :smiley:

Hey @zarf, what’s VM_PrintToBuffer()? My Inform install won’t compile it. Is it an I7 thing? If so, what’s the I6 equivalent?

Yes, it’s in the libraries that get built with an I7 game. It’s just a wrapper that calls PrintAnyToArray(). Roughly like

buf-->0 = PrintAnyToArray(buf+WORDSIZE, bufsize, func, arg);

EDIT: forgot the bufsize argument!

It may be helpful to install I7, compile a one-line game, and look at the I6 code (test.inform/Build/auto.inf) that gets generated. Then you can see what’s going on in there.

1 Like

I followed your suggestion @zarf , elucidating, but it still leaves me with a question about your code. Based on the release notes, when you’re using HandleGlkEvent:

If this was line input (context==0), you must call the Glk cancel_line_event function. 
(You can pass an array argument to see what the player had typed so far.) Then, fill in the 
length of the input to be returned in abortres-->0. If this is nonzero, 
write the input characters
sequentially into the array starting at abortres-->WORDSIZE, 
up to (but not including) abortres-->(WORDSIZE+len). 
Do not exceed 256 characters.
Then return 2; KeyboardPrimitive() will end and return the line. 

I see your call to glk_cancel_line_event() and you returning 2 but I don’t see where you prime the abortres values (unless you mean it to be 0) and I don’t see how the value passed into VM_PrintToBuffer ever gets to the VM as neither the Glulx or ZMachine versions of VM_PrintToBuffer do anything apart from opening an output stream to the buffer and printing to it.

How does the content of your buffer from your VM_PrintToBuffer() call get passed on to the VM?

Well, it is in the VM already. All I6 code runs in the VM by definition.

I think you’re asking how the contents get passed to the parser. The answer is that my HandleGlkEvent routine writes to the I6 buffer array. This has an unfortunately generic name, but it’s the key array for line input. It’s where the player’s input would have wound up if the line input had been completed rather than cancelled. This array is defined in parserm.h.

When HandleGlkEvent returns 2, it indicates that the parser should continue on its normal path, parsing and carrying out the command in the buffer array. In my example code, that’s -VISIT-NUM 12345 (where the numeric argument came in from the extevent). The game then has a verb set up to handle this as a regular player command.

(Indeed, the player could type -VISIT-NUM 12345 and get exactly the same result. I was careful to put in checks to make sure this wouldn’t break the game.)

I think you can ignore the comments about “abortres”. I don’t remember what was in that release note. The third argument to HandleGlkEvent() will be buffer, so maybe I should have referred to it as args rather than buffer in my sample code, but there’s no practical difference.

OHHHHHHH. That makes sense of what I was seeing. That is a rather unfortunate name.

What happens if I name a routine variable “buffer”, will it be permitted? Will it interfere with the parserm.h one?

Local variables can shadow global variables (and arrays, etc). That’s not a problem.

Okay, so this works. I cancel the line event, print out the results of my external event, all good. But then the ‘>’ prompt doesn’t come back and I can no longer type input.

I even tried called glk_request_line_event() to no avail.

I suspect I’ve missed/misunderstood something obvious you’ve both told me in this thread but I don’t know what it is.

Figured it out. My “return 2;” was caught inside a switch block but the compiler didn’t give an error. I’ve reported the bug at https://github.com/DavidKinder/Inform6/issues/45.

However, for my purposes, I required a verb to be in the input buffer after my external event completed. I didn’t want it to do anything so I created this:

[ NullInputSub; ];

Verb meta 'nullinput'
    *       -> NullInput
    * topic -> NullInput;

and added this to my HandleGlkEvent before I returned 2
inputBuffer-->0 = PrintAnyToArray(inputBuffer + WORDSIZE, 256, "nullinput");

All working! Thank you both!