How C programs print text out, really.
void CInputOutputModel::initialise(code_generator *gtr) { } void CInputOutputModel::initialise_data(code_generation *gen) { } void CInputOutputModel::begin(code_generation *gen) { } void CInputOutputModel::end(code_generation *gen) { }
§2. By input/output, we mean printing text, receiving textual commands, or reading or writing files. Inter can do this in one of two ways: either
- (a) With one of the following primitives, or
- (b) With an assembly-language opcode, and in particular @glk.
int CInputOutputModel::invoke_primitive(code_generation *gen, inter_ti bip, inter_tree_node *P) { text_stream *OUT = CodeGen::current(gen); switch (bip) { case SPACES_BIP: WRITE("for (int j = "); VNODE_1C; WRITE("; j > 0; j--) i7_print_char(proc, 32);"); break; case FONT_BIP: WRITE("i7_styling(proc, 1, "); VNODE_1C; WRITE(")"); break; case STYLE_BIP: WRITE("i7_styling(proc, 2, "); VNODE_1C; WRITE(")"); break; case PRINT_BIP: WRITE("i7_print_C_string(proc, "); CodeGen::lt_mode(gen, PRINTING_LTM); VNODE_1C; CodeGen::lt_mode(gen, REGULAR_LTM); WRITE(")"); break; case PRINTCHAR_BIP: WRITE("i7_print_char(proc, "); VNODE_1C; WRITE(")"); break; case PRINTNL_BIP: WRITE("i7_print_char(proc, '\\n')"); break; case PRINTOBJ_BIP: WRITE("i7_print_object(proc, "); VNODE_1C; WRITE(")"); break; case PRINTNUMBER_BIP: WRITE("i7_print_decimal(proc, "); VNODE_1C; WRITE(")"); break; case PRINTSTRING_BIP: WRITE("i7_print_C_string(proc, i7_text_to_C_string("); VNODE_1C; WRITE("))"); break; case PRINTDWORD_BIP: WRITE("i7_print_dword(proc, "); VNODE_1C; WRITE(")"); break; case BOX_BIP: WRITE("i7_print_box(proc, "); CodeGen::lt_mode(gen, BOX_LTM); VNODE_1C; CodeGen::lt_mode(gen, REGULAR_LTM); WRITE(")"); break; case ENABLEPRINTING_BIP: WRITE("{ i7word_t window_id = 0;\n"); WRITE("i7_opcode_setiosys(proc, 2, 0); Set to use Glk\n"); WRITE("i7_push(proc, 201); = GG_MAINWIN_ROCK;\n"); WRITE("i7_push(proc, 3); = wintype_TextBuffer;\n"); WRITE("i7_push(proc, 0);\n"); WRITE("i7_push(proc, 0);\n"); WRITE("i7_push(proc, 0);\n"); WRITE("i7_opcode_glk(proc, 35, 5, &window_id); glk_window_open, pushing a window ID\n"); WRITE("i7_push(proc, window_id);\n"); WRITE("i7_opcode_glk(proc, 47, 1, NULL); glk_set_window to that window ID\n"); WRITE("}\n"); break; default: return NOT_APPLICABLE; } return FALSE; }
§3. See C Literals for the implementation of i7_print_dword: it funnels through to i7_print_char, and so do all of these:
void i7_print_C_string(i7process_t *proc, char *c_string); void i7_print_decimal(i7process_t *proc, i7word_t x); void i7_print_object(i7process_t *proc, i7word_t x); void i7_print_box(i7process_t *proc, i7word_t x);
- This is part of the extract file inform7_clib.h.
void i7_print_C_string(i7process_t *proc, char *c_string) { if (c_string) for (int i=0; c_string[i]; i++) i7_print_char(proc, (i7word_t) c_string[i]); } void i7_print_decimal(i7process_t *proc, i7word_t x) { char room[32]; sprintf(room, "%d", (int) x); i7_print_C_string(proc, room); } void i7_print_object(i7process_t *proc, i7word_t x) { i7_print_decimal(proc, x); } void i7_print_box(i7process_t *proc, i7word_t x) { printf("Unimplemented: i7_print_box.\n"); i7_fatal_exit(proc); }
- This is part of the extract file inform7_clib.c.
§4. Which in turn uses the @glk opcode:
void i7_print_char(i7process_t *proc, i7word_t x);
- This is part of the extract file inform7_clib.h.
void i7_print_char(i7process_t *proc, i7word_t x) { if (x == 13) x = 10; i7_push(proc, x); i7word_t current = 0; i7_opcode_glk(proc, i7_glk_stream_get_current, 0, ¤t); i7_push(proc, current); i7_opcode_glk(proc, i7_glk_put_char_stream, 2, NULL); }
- This is part of the extract file inform7_clib.c.
§5. At this point, then, all of our I/O needs will be handled if we can just define two functions: i7_styling, for setting the font style, and i7_opcode_glk. So we're nearly done, right? Right?
But in fact we route both of these functions through hooks which the user can provide, so that the user can change the entire I/O model (if she is willing to code up an alternative):
void i7_styling(i7process_t *proc, i7word_t which, i7word_t what); void i7_default_stylist(i7process_t *proc, i7word_t which, i7word_t what); void i7_opcode_glk(i7process_t *proc, i7word_t glk_api_selector, i7word_t varargc, i7word_t *z); void i7_default_glk(i7process_t *proc, i7word_t glk_api_selector, i7word_t varargc, i7word_t *z);
- This is part of the extract file inform7_clib.h.
void i7_styling(i7process_t *proc, i7word_t which, i7word_t what) { (proc->stylist)(proc, which, what); } void i7_opcode_glk(i7process_t *proc, i7word_t glk_api_selector, i7word_t varargc, i7word_t *z) { (proc->glk_implementation)(proc, glk_api_selector, varargc, z); }
- This is part of the extract file inform7_clib.c.
§6. What makes this more burdensome is that @glk is not so much a single opcode as an entire instruction set: it is an compendium of over 120 disparate operations. Indeed, the glk_api_selector argument to i7_opcode_glk chooses which one is being used. For convenience, we define a set of names for them all — which does not imply any commitment to implement them all.
#define i7_glk_exit 0x0001 #define i7_glk_set_interrupt_handler 0x0002 #define i7_glk_tick 0x0003 #define i7_glk_gestalt 0x0004 #define i7_glk_gestalt_ext 0x0005 #define i7_glk_window_iterate 0x0020 #define i7_glk_window_get_rock 0x0021 #define i7_glk_window_get_root 0x0022 #define i7_glk_window_open 0x0023 #define i7_glk_window_close 0x0024 #define i7_glk_window_get_size 0x0025 #define i7_glk_window_set_arrangement 0x0026 #define i7_glk_window_get_arrangement 0x0027 #define i7_glk_window_get_type 0x0028 #define i7_glk_window_get_parent 0x0029 #define i7_glk_window_clear 0x002A #define i7_glk_window_move_cursor 0x002B #define i7_glk_window_get_stream 0x002C #define i7_glk_window_set_echo_stream 0x002D #define i7_glk_window_get_echo_stream 0x002E #define i7_glk_set_window 0x002F #define i7_glk_window_get_sibling 0x0030 #define i7_glk_stream_iterate 0x0040 #define i7_glk_stream_get_rock 0x0041 #define i7_glk_stream_open_file 0x0042 #define i7_glk_stream_open_memory 0x0043 #define i7_glk_stream_close 0x0044 #define i7_glk_stream_set_position 0x0045 #define i7_glk_stream_get_position 0x0046 #define i7_glk_stream_set_current 0x0047 #define i7_glk_stream_get_current 0x0048 #define i7_glk_stream_open_resource 0x0049 #define i7_glk_fileref_create_temp 0x0060 #define i7_glk_fileref_create_by_name 0x0061 #define i7_glk_fileref_create_by_prompt 0x0062 #define i7_glk_fileref_destroy 0x0063 #define i7_glk_fileref_iterate 0x0064 #define i7_glk_fileref_get_rock 0x0065 #define i7_glk_fileref_delete_file 0x0066 #define i7_glk_fileref_does_file_exist 0x0067 #define i7_glk_fileref_create_from_fileref 0x0068 #define i7_glk_put_char 0x0080 #define i7_glk_put_char_stream 0x0081 #define i7_glk_put_string 0x0082 #define i7_glk_put_string_stream 0x0083 #define i7_glk_put_buffer 0x0084 #define i7_glk_put_buffer_stream 0x0085 #define i7_glk_set_style 0x0086 #define i7_glk_set_style_stream 0x0087 #define i7_glk_get_char_stream 0x0090 #define i7_glk_get_line_stream 0x0091 #define i7_glk_get_buffer_stream 0x0092 #define i7_glk_char_to_lower 0x00A0 #define i7_glk_char_to_upper 0x00A1 #define i7_glk_stylehint_set 0x00B0 #define i7_glk_stylehint_clear 0x00B1 #define i7_glk_style_distinguish 0x00B2 #define i7_glk_style_measure 0x00B3 #define i7_glk_select 0x00C0 #define i7_glk_select_poll 0x00C1 #define i7_glk_request_line_event 0x00D0 #define i7_glk_cancel_line_event 0x00D1 #define i7_glk_request_char_event 0x00D2 #define i7_glk_cancel_char_event 0x00D3 #define i7_glk_request_mouse_event 0x00D4 #define i7_glk_cancel_mouse_event 0x00D5 #define i7_glk_request_timer_events 0x00D6 #define i7_glk_image_get_info 0x00E0 #define i7_glk_image_draw 0x00E1 #define i7_glk_image_draw_scaled 0x00E2 #define i7_glk_window_flow_break 0x00E8 #define i7_glk_window_erase_rect 0x00E9 #define i7_glk_window_fill_rect 0x00EA #define i7_glk_window_set_background_color 0x00EB #define i7_glk_schannel_iterate 0x00F0 #define i7_glk_schannel_get_rock 0x00F1 #define i7_glk_schannel_create 0x00F2 #define i7_glk_schannel_destroy 0x00F3 #define i7_glk_schannel_create_ext 0x00F4 #define i7_glk_schannel_play_multi 0x00F7 #define i7_glk_schannel_play 0x00F8 #define i7_glk_schannel_play_ext 0x00F9 #define i7_glk_schannel_stop 0x00FA #define i7_glk_schannel_set_volume 0x00FB #define i7_glk_sound_load_hint 0x00FC #define i7_glk_schannel_set_volume_ext 0x00FD #define i7_glk_schannel_pause 0x00FE #define i7_glk_schannel_unpause 0x00FF #define i7_glk_set_hyperlink 0x0100 #define i7_glk_set_hyperlink_stream 0x0101 #define i7_glk_request_hyperlink_event 0x0102 #define i7_glk_cancel_hyperlink_event 0x0103 #define i7_glk_buffer_to_lower_case_uni 0x0120 #define i7_glk_buffer_to_upper_case_uni 0x0121 #define i7_glk_buffer_to_title_case_uni 0x0122 #define i7_glk_buffer_canon_decompose_uni 0x0123 #define i7_glk_buffer_canon_normalize_uni 0x0124 #define i7_glk_put_char_uni 0x0128 #define i7_glk_put_string_uni 0x0129 #define i7_glk_put_buffer_uni 0x012A #define i7_glk_put_char_stream_uni 0x012B #define i7_glk_put_string_stream_uni 0x012C #define i7_glk_put_buffer_stream_uni 0x012D #define i7_glk_get_char_stream_uni 0x0130 #define i7_glk_get_buffer_stream_uni 0x0131 #define i7_glk_get_line_stream_uni 0x0132 #define i7_glk_stream_open_file_uni 0x0138 #define i7_glk_stream_open_memory_uni 0x0139 #define i7_glk_stream_open_resource_uni 0x013A #define i7_glk_request_char_event_uni 0x0140 #define i7_glk_request_line_event_uni 0x0141 #define i7_glk_set_echo_line_event 0x0150 #define i7_glk_set_terminators_line_event 0x0151 #define i7_glk_current_time 0x0160 #define i7_glk_current_simple_time 0x0161 #define i7_glk_time_to_date_utc 0x0168 #define i7_glk_time_to_date_local 0x0169 #define i7_glk_simple_time_to_date_utc 0x016A #define i7_glk_simple_time_to_date_local 0x016B #define i7_glk_date_to_time_utc 0x016C #define i7_glk_date_to_time_local 0x016D #define i7_glk_date_to_simple_time_utc 0x016E #define i7_glk_date_to_simple_time_local 0x016F
- This is part of the extract file inform7_clib.h.
A few other constants will also be useful. These are the window IDs for the three Glk windows used by the standard Inform 7 kits: I7_BODY_TEXT_ID is where text is regularly printed; I7_STATUS_TEXT_ID is for the "status line" at the top of a traditional interactive fiction display, but can simply be ignored for non-IF purposes; and I7_BOX_TEXT_ID is where box quotations would be displayed over the top of text, though C projects probably should not use this, and the default Glk implementation here ignores it.
#define I7_BODY_TEXT_ID 201 #define I7_STATUS_TEXT_ID 202 #define I7_BOX_TEXT_ID 203
- This is part of the extract file inform7_clib.h.
These are needed for different forms of file I/O:
#define i7_fileusage_Data 0x00 #define i7_fileusage_SavedGame 0x01 #define i7_fileusage_Transcript 0x02 #define i7_fileusage_InputRecord 0x03 #define i7_fileusage_TypeMask 0x0f #define i7_fileusage_TextMode 0x100 #define i7_fileusage_BinaryMode 0x000 #define i7_filemode_Write 0x01 #define i7_filemode_Read 0x02 #define i7_filemode_ReadWrite 0x03 #define i7_filemode_WriteAppend 0x05
- This is part of the extract file inform7_clib.h.
And these are modes for seeking a position in a file:
#define i7_seekmode_Start (0) #define i7_seekmode_Current (1) #define i7_seekmode_End (2)
- This is part of the extract file inform7_clib.h.
And these are "event types":
#define i7_evtype_None 0 #define i7_evtype_Timer 1 #define i7_evtype_CharInput 2 #define i7_evtype_LineInput 3 #define i7_evtype_MouseInput 4 #define i7_evtype_Arrange 5 #define i7_evtype_Redraw 6 #define i7_evtype_SoundNotify 7 #define i7_evtype_Hyperlink 8 #define i7_evtype_VolumeNotify 9
- This is part of the extract file inform7_clib.h.
Finally, these are the gestalt values: that is, the selection of bells and whistles which a Glk implementation can offer —
#define i7_gestalt_Version 0 #define i7_gestalt_CharInput 1 #define i7_gestalt_LineInput 2 #define i7_gestalt_CharOutput 3 #define i7_gestalt_CharOutput_ApproxPrint 1 #define i7_gestalt_CharOutput_CannotPrint 0 #define i7_gestalt_CharOutput_ExactPrint 2 #define i7_gestalt_MouseInput 4 #define i7_gestalt_Timer 5 #define i7_gestalt_Graphics 6 #define i7_gestalt_DrawImage 7 #define i7_gestalt_Sound 8 #define i7_gestalt_SoundVolume 9 #define i7_gestalt_SoundNotify 10 #define i7_gestalt_Hyperlinks 11 #define i7_gestalt_HyperlinkInput 12 #define i7_gestalt_SoundMusic 13 #define i7_gestalt_GraphicsTransparency 14 #define i7_gestalt_Unicode 15 #define i7_gestalt_UnicodeNorm 16 #define i7_gestalt_LineInputEcho 17 #define i7_gestalt_LineTerminators 18 #define i7_gestalt_LineTerminatorKey 19 #define i7_gestalt_DateTime 20 #define i7_gestalt_Sound2 21 #define i7_gestalt_ResourceStream 22 #define i7_gestalt_GraphicsCharInput 23
- This is part of the extract file inform7_clib.h.