How to replace chunks of code using inform 6 in Inform 10?

I have an extension with the following code (originally by Petter Sjölund).

It works in Inform 7 but not Inform 10:

Summary
Include (-

[ VM_KeyChar 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) {
		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 == 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); ! newline
	}
  .KCPContinue;
	return res;
];

[ VM_KeyDelay tenths  key done ix;
	glk_request_char_event(gg_mainwin);
	glk_request_timer_events(tenths*100);
	while (~~done) {
		glk_select(gg_event);
		ix = HandleGlkEvent(gg_event, 1, gg_arguments);
		if (ix == 2) {
			key = gg_arguments-->0;
			done = true;
		}
		else if (ix >= 0 && gg_event-->0 == 1 or 2) {
			key = gg_event-->2;
			done = true;
		}
	}
	glk_cancel_char_event(gg_mainwin);
	glk_request_timer_events(0);
	return key;
];

[ VM_ReadKeyboard  a_buffer a_table done ix;
	if (gg_commandstr ~= 0 && gg_command_reading ~= false) {
		done = glk_get_line_stream(gg_commandstr, a_buffer+WORDSIZE,
			(INPUT_BUFFER_LEN-WORDSIZE)-1);
		if (done == 0) {
			glk_stream_close(gg_commandstr, 0);
			gg_commandstr = 0;
			gg_command_reading = false;
		}
		else {
			! Trim the trailing newline
			if ((a_buffer+WORDSIZE)->(done-1) == 10) done = done-1;
			a_buffer-->0 = done;
			VM_Style(INPUT_VMSTY);
			glk_put_buffer(a_buffer+WORDSIZE, done);
			VM_Style(NORMAL_VMSTY);
			print "^";
			jump KPContinue;
		}
	}
	done = false;
	glk_request_line_event(gg_mainwin, a_buffer+WORDSIZE, INPUT_BUFFER_LEN-WORDSIZE, 0);
	while (~~done) {
		glk_select(gg_event);
		switch (gg_event-->0) {
		  5: ! evtype_Arrange
			DrawStatusLine();
		  3: ! evtype_LineInput
			if (gg_event-->1 == gg_mainwin) {
				a_buffer-->0 = gg_event-->2;
				done = true;
			}
		}
		ix = HandleGlkEvent(gg_event, 0, a_buffer);
		if (ix == 2) done = true;
		else if (ix == -1) done = false;
	}
	if (gg_commandstr ~= 0 && gg_command_reading == false) {
		glk_put_buffer_stream(gg_commandstr, a_buffer+WORDSIZE, a_buffer-->0);
		glk_put_char_stream(gg_commandstr, 10); ! newline
	}
  .KPContinue;
	VM_Tokenise(a_buffer,a_table);
	! It's time to close any quote window we've got going.
	if (gg_quotewin) {
		glk_window_close(gg_quotewin, 0);
		gg_quotewin = 0;
	}

	! === NEW ===

	if ((glk_gestalt(gestalt_LineInputEcho, 0)) && echoed_already == 0) {
		glk_set_style(style_Input);
		for (ix=WORDSIZE: ix<(a_buffer-->0)+WORDSIZE: ix++) print (char) a_buffer->ix;
		style roman;
		print "^";
	}
	echoed_already = 0;

	! === END ===

	#ifdef ECHO_COMMANDS;
	print "** ";
	for (ix=WORDSIZE: ix<(a_buffer-->0)+WORDSIZE: ix++) print (char) a_buffer->ix;
	print "^";
	#endif; ! ECHO_COMMANDS
];

-) replacing "Keyboard Input".

When I run it, I get the following error:
found a second definition of the name 'VM_KeyChar' when loading '/main/BasicInformKit'

This happens for the other two functions as well.

But…isn’t that the point? Aren’t we supposed to be doing two definitions?

I thought maybe Inform 10 doesn’t have that section anymore, but I found this code here:

Code
@h Keyboard Input.
The VM must provide three routines for keyboard input:

(a) |VM_KeyChar()| waits for a key to be pressed and then returns the
character chosen as a ZSCII character.
(b) |VM_KeyDelay(N)| waits up to $N/10$ seconds for a key to be pressed,
returning the ZSCII character if so, or 0 if not.
(c) |VM_ReadKeyboard(b, t)| reads a whole newline-terminated command
into the buffer |b|, then parses it into a word stream in the table |t|.

There are elaborations to due with mouse clicks, but this isn't the place
to document all of that.

=
[ VM_KeyChar win  key;
    if (win) @set_window win;
    @read_char 1 -> key;
    return key;
];

[ VM_KeyDelay tenths  key;
    @read_char 1 tenths VM_KeyDelay_Interrupt -> key;
    return key;
];
[ VM_KeyDelay_Interrupt; rtrue; ];

[ VM_ReadKeyboard a_buffer a_table;
	read a_buffer a_table;
];

Strictly speaking I don’t need to port this to Inform 10, but a lot of people are on that version. How can I replace this code correctly?

(note: the only goal of this code is, when ‘echoing’ is off, to capture the player’s command to correctly echo back typed in commands).

The syntax for Inform 10 is:

Include (-

[ FuncName ;
	! replacement code here
]; 

-) replacing "FuncName".

Unlike in 6M62, each routine requires its own replacement; it can’t be done by section.

Note that VM_* routines compile differently for Z-Machine and Glulx, so you’ll want #ifdef blocks if you’re planning to handle both.

2 Likes

Thank you, this works perfectly!

And I only intend to target glulx. I appreciate your time!

I apologize to @mathbrush if ii am hijacking this post a little bit.

Re: the VM_* Routines. Is there an API defined somewhere that lists these out and explains them?

1 Like

Not exactly. At least, not one of which I’m aware. (@Zed – do you know of anything like that?)

Some documentation can be found in the template files Glulx.i6t and ZMachine.i6t.

So far as I know, the documentation in those files is all the documentation that exists.

1 Like