/** * glk_exit: * * If you want to shut down your program in the middle of your `glk_main()` * function, you can call glk_exit(). * * This function does not return. * * If you print some text to a window and then shut down your program, you can * assume that the player will be able to read it. Most likely the Glk library * will give a “`Hit any key to exit`” prompt. * (There are other possiblities, however. * A terminal-window version of Glk might simply exit and leave the last screen * state visible in the terminal window.) * * <note><para> * You should only shut down your program with glk_exit() or by returning from * your <function>glk_main()</function> function. If you call the ANSI * <function>exit()</function> function, bad things may happen. Some versions of * the Glk library may be designed for multiple sessions, for example, and you * would be cutting off all the sessions instead of just yours. You would * probably also prevent final text from being visible to the player. * </para></note> * * > # Chimara # * > If there are any windows open at the time glk_exit() is called, then * > Chimara will leave them open. * > This way, the final text remains visible. * > Note that bad things most definitely <emphasis>will</emphasis> happen if * > you use the ANSI `exit()`. */ void glk_exit(void) { ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key); shutdown_glk_pre(); /* Find the biggest text buffer window */ winid_t win, largewin = NULL; glui32 largearea = 0; for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) { if(win->type == wintype_TextBuffer) { glui32 w, h; if(!largewin) { largewin = win; glk_window_get_size(largewin, &w, &h); largearea = w * h; } else { glk_window_get_size(win, &w, &h); if(w * h > largearea) { largewin = win; largearea = w * h; } } } } if(largewin) { glk_set_window(largewin); glk_set_style(style_Alert); glk_put_string("\n"); glk_put_string(glk_data->final_message); glk_put_string("\n"); flush_window_buffer(largewin); } /* Wait for a keypress if any text grid or buffer windows are open */ gboolean should_wait = FALSE; g_mutex_lock(&glk_data->shutdown_lock); for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) { if(win->type == wintype_TextGrid || win->type == wintype_TextBuffer) { g_signal_handler_unblock(win->widget, win->shutdown_keypress_handler); should_wait = TRUE; } } if (should_wait) g_cond_wait(&glk_data->shutdown_key_pressed, &glk_data->shutdown_lock); g_mutex_unlock(&glk_data->shutdown_lock); shutdown_glk_post(); gdk_threads_add_idle((GSourceFunc)emit_stopped_signal, glk_data->self); g_thread_exit(NULL); }
/* Internal function: shut down all requests and anything not necessary while showing the last displayed configuration of windows. */ void shutdown_glk_pre(void) { ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key); /* Stop any timers */ glk_request_timer_events(0); /* Cancel any pending input requests and flush all window buffers */ winid_t win; for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) { switch(win->input_request_type) { case INPUT_REQUEST_CHARACTER: case INPUT_REQUEST_CHARACTER_UNICODE: glk_cancel_char_event(win); break; case INPUT_REQUEST_LINE: case INPUT_REQUEST_LINE_UNICODE: glk_cancel_line_event(win, NULL); break; case INPUT_REQUEST_NONE: default: ; /* TODO: Handle mouse and hyperlink requests */ } flush_window_buffer(win); } /* Close any open resource files */ if(glk_data->resource_map != NULL) { giblorb_destroy_map(glk_data->resource_map); glk_data->resource_map = NULL; glk_stream_close(glk_data->resource_file, NULL); } /* Empty the input queues */ while(g_async_queue_try_pop(glk_data->char_input_queue)) ; while(g_async_queue_try_pop(glk_data->line_input_queue)) ; /* Wait for any pending window rearrange */ g_mutex_lock(&glk_data->arrange_lock); if(glk_data->needs_rearrange) g_cond_wait(&glk_data->rearranged, &glk_data->arrange_lock); g_mutex_unlock(&glk_data->arrange_lock); }
/* Internal function: write a Latin-1 buffer with length to a stream. */ static void write_buffer_to_stream(strid_t str, gchar *buf, glui32 len) { switch(str->type) { case STREAM_TYPE_WINDOW: /* Each window type has a different way of printing to it */ switch(str->window->type) { /* Printing to these windows' streams does nothing */ case wintype_Blank: case wintype_Pair: case wintype_Graphics: str->write_count += len; break; /* Text grid/buffer windows */ case wintype_TextGrid: { gchar *utf8 = convert_latin1_to_utf8(buf, len); if(utf8 != NULL) { /* Deal with newlines */ int i; gchar *line = utf8; for(i=0; i<len; i++) { if(utf8[i] == '\n') { utf8[i] = '\0'; write_utf8_to_window_buffer(str->window, line); flush_window_buffer(str->window); /* Move cursor position forward to the next line */ gdk_threads_enter(); GtkTextIter cursor_pos; GtkTextView *textview = GTK_TEXT_VIEW(str->window->widget); GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview); GtkTextMark *cursor_mark = gtk_text_buffer_get_mark(buffer, "cursor_position"); gtk_text_buffer_get_iter_at_mark( buffer, &cursor_pos, cursor_mark); gtk_text_view_forward_display_line(textview, &cursor_pos); gtk_text_view_backward_display_line_start(textview, &cursor_pos); gtk_text_buffer_move_mark(buffer, cursor_mark, &cursor_pos); gdk_threads_leave(); line = utf8 + (i < len-1 ? (i+1):(len-1)); } } /* No more newlines left. */ write_utf8_to_window_buffer(str->window, line); g_free(utf8); } str->write_count += len; } break; case wintype_TextBuffer: { gchar *utf8 = convert_latin1_to_utf8(buf, len); if(utf8 != NULL) { write_utf8_to_window_buffer(str->window, utf8); g_free(utf8); } } str->write_count += len; break; default: ILLEGAL_PARAM("Unknown window type: %u", str->window->type); } /* Now write the same buffer to the window's echo stream */ if(str->window->echo_stream != NULL) write_buffer_to_stream(str->window->echo_stream, buf, len); break; case STREAM_TYPE_MEMORY: if(str->unicode && str->ubuffer) { int foo = 0; while(str->mark < str->buflen && foo < len) str->ubuffer[str->mark++] = (unsigned char)buf[foo++]; } if(!str->unicode && str->buffer) { int copycount = MIN(len, str->buflen - str->mark); memmove(str->buffer + str->mark, buf, copycount); str->mark += copycount; } /* Move the EOF marker if we wrote past it */ if(str->mark > str->endmark) str->endmark = str->mark; str->write_count += len; break; case STREAM_TYPE_FILE: if(str->binary) { if(str->unicode) { gchar *writebuffer = convert_latin1_to_ucs4be_string(buf, len); ensure_file_operation(str, filemode_Write); fwrite(writebuffer, sizeof(gchar), len * 4, str->file_pointer); g_free(writebuffer); } else /* Regular file */ { ensure_file_operation(str, filemode_Write); fwrite(buf, sizeof(gchar), len, str->file_pointer); } } else /* Text mode is the same for Unicode and regular files */ { gchar *utf8 = convert_latin1_to_utf8(buf, len); if(utf8 != NULL) { ensure_file_operation(str, filemode_Write); g_fprintf(str->file_pointer, "%s", utf8); g_free(utf8); } } str->write_count += len; break; case STREAM_TYPE_RESOURCE: ILLEGAL(_("Writing to a resource stream is illegal.")); break; default: ILLEGAL_PARAM("Unknown stream type: %u", str->type); } }