return ch;
}
-#ifdef GLK_MODULE_UNICODE
-
/**
* glk_buffer_to_lower_case_uni:
* @buf: A character array in UCS-4.
* @buf: A character array in UCS-4.
* @len: Available length of @buf.
* @numchars: Number of characters in @buf.
- * @lowerrest: #TRUE if the rest of @buf should be lowercased, #FALSE
+ * @lowerrest: %TRUE if the rest of @buf should be lowercased, %FALSE
* otherwise.
*
* Converts the first character of @buf to uppercase, if there is such a thing.
- * See glk_buffer_to_lower_case_uni(). If @lowerrest is #TRUE, then the
+ * See glk_buffer_to_lower_case_uni(). If @lowerrest is %TRUE, then the
* remainder of @buf is lowercased.
*
* Returns: The number of characters after conversion.
*/
glui32
-glk_buffer_to_title_case_uni(glui32 *buf, glui32 len, glui32 numchars,
- glui32 lowerrest)
+glk_buffer_to_title_case_uni(glui32 *buf, glui32 len, glui32 numchars, glui32 lowerrest)
{
g_return_val_if_fail(buf != NULL && (len > 0 || numchars > 0), 0);
g_return_val_if_fail(numchars <= len, 0);
/* GLib has a function that converts _one_ UCS-4 character to _one_
- uppercase UCS-4 character; so apparently we don't have to worry about the
+ titlecase UCS-4 character; so apparently we don't have to worry about the
string length changing... */
*buf = g_unichar_totitle(*buf);
/* Call lowercase on the rest of the string */
if(lowerrest)
- return glk_buffer_to_lower_case_uni(buf + 1, len - 1, numchars - 1) +1;
+ return glk_buffer_to_lower_case_uni(buf + 1, len - 1, numchars - 1) + 1;
return numchars;
}
-#endif /* GLK_MODULE_UNICODE */
#include "event.h"
+#include <string.h>
static GQueue *event_queue = NULL;
static GMutex *event_lock = NULL;
static GCond *event_queue_not_empty = NULL;
static GCond *event_queue_not_full = NULL;
+/* Internal function: initialize the event_queue, locking and signalling
+objects. */
void
events_init()
{
event_queue_not_full = g_cond_new();
}
-static void
-event_free(gpointer data, gpointer user_data)
-{
- g_free(data);
-}
-
+/* Internal function: free the event queue and all the objects allocated in
+events_init(). */
void
events_free()
{
- g_queue_foreach(event_queue, event_free, NULL);
+ g_queue_foreach(event_queue, (GFunc)g_free, NULL);
g_queue_free(event_queue);
g_mutex_free(event_lock);
g_cond_free(event_queue_not_empty);
g_cond_free(event_queue_not_full);
}
+/* Internal function: push an event onto the event queue. If the event queue is
+full, wait for max three seconds and then drop the event. */
void
event_throw(glui32 type, winid_t win, glui32 val1, glui32 val2)
{
g_get_current_time(&timeout);
g_time_val_add(&timeout, 3000000); /* 3 Seconds */
- /* Wait for room in the event queue */
g_mutex_lock(event_lock);
- if( g_queue_get_length(event_queue) >= EVENT_QUEUE_MAX_LENGTH ) {
- if( !g_cond_timed_wait(event_queue_not_full, event_lock, &timeout) ) {
- /* Drop the event */
+
+ /* Wait for room in the event queue */
+ if( g_queue_get_length(event_queue) >= EVENT_QUEUE_MAX_LENGTH )
+ if( !g_cond_timed_wait(event_queue_not_full, event_lock, &timeout) )
+ {
+ /* Drop the event after 3 seconds */
g_mutex_unlock(event_lock);
return;
}
- }
event_t *event = g_new0(event_t, 1);
event->type = type;
event->win = win;
event->val1 = val1;
event->val2 = val2;
-
g_queue_push_head(event_queue, event);
/* Signal that there is an event */
g_mutex_unlock(event_lock);
}
+/**
+ * glk_select:
+ * @event: Pointer to an #event_t.
+ *
+ * Causes the program to wait for an event, and then store it in the structure
+ * pointed to by @event. Unlike most Glk functions that take pointers, the
+ * argument of glk_select() may not be %NULL.
+ *
+ * Most of the time, you only get the events that you request. However, there
+ * are some events which can arrive at any time. This is why you must always
+ * call glk_select() in a loop, and continue the loop until you get the event
+ * you really want.
+ */
void
glk_select(event_t *event)
{
- event_t *retrieved_event;
-
g_return_if_fail(event != NULL);
g_mutex_lock(event_lock);
/* Wait for an event */
- if( g_queue_is_empty(event_queue) ) {
+ if( g_queue_is_empty(event_queue) )
g_cond_wait(event_queue_not_empty, event_lock);
- }
-
- retrieved_event = g_queue_pop_tail(event_queue);
- g_return_if_fail(retrieved_event != NULL);
-
- event->type = retrieved_event->type;
- event->win = retrieved_event->win;
- event->val1 = retrieved_event->val1;
- event->val2 = retrieved_event->val2;
+ event_t *retrieved_event = g_queue_pop_tail(event_queue);
+ if(retrieved_event == NULL)
+ {
+ g_mutex_unlock(event_lock);
+ g_warning("%s: Retrieved NULL event from non-empty event queue", __func__);
+ return;
+ }
+ memcpy(event, retrieved_event, sizeof(event_t));
g_free(retrieved_event);
/* Signal that the event queue is no longer full */
g_mutex_unlock(event_lock);
- /* Implementation defined events */
+ /* Implementation-defined events */
switch(event->type) {
case EVENT_TYPE_QUIT:
g_thread_exit(NULL);
#include <glib.h>
#include "glk.h"
-#define EVENT_QUEUE_MAX_LENGTH 100
+#define EVENT_QUEUE_MAX_LENGTH (100)
-#define EVENT_TYPE_QUIT -1
+/* Implementation-defined events */
+#define EVENT_TYPE_QUIT (-1)
void events_init();
void events_free();
* @sel: A selector, representing which capability to request information
* about.
* @val: Extra information, depending on the value of @sel.
- * @arr: Location of an array to store extra information in, or #NULL.
- * @arrlen: Length of @arr, or 0 if @arr is #NULL.
+ * @arr: Location of an array to store extra information in, or %NULL.
+ * @arrlen: Length of @arr, or 0 if @arr is %NULL.
*
- * Calls the gestalt system to request information about selector @sel,
- * possibly returning information in @arr.
+ * Calls the gestalt system to request information about the capabilities of the
+ * API. The selector @sel tells which capability you are requesting information
+ * about; the other three arguments are additional information, which may or may
+ * not be meaningful. The @arr and @arrlen arguments are always optional; you
+ * may always pass %NULL and 0, if you do not want whatever information they
+ * represent. glk_gestalt() is simply a shortcut for this; glk_gestalt(x, y) is
+ * exactly the same as glk_gestalt_ext(x, y, NULL, 0).
+ *
+ * The critical point is that if the Glk library has never heard of the selector
+ * sel, it will return 0. It is always safe to call glk_gestalt(x, y) (or
+ * glk_gestalt_ext(x, y, NULL, 0)). Even if you are using an old library, which
+ * was compiled before the given capability was imagined, you can test for the
+ * capability by calling glk_gestalt(); the library will correctly indicate that
+ * it does not support it, by returning 0.
+ *
+ * <note><para>
+ * It is also safe to call glk_gestalt_ext(x, y, z, zlen) for an unknown
+ * selector x, where z is not %NULL, as long as z points at an array of at
+ * least zlen elements. The selector will be careful not to write beyond that
+ * point in the array, if it writes to the array at all.
+ * </para></note>
+ *
+ * <note><para>
+ * If a selector does not use the second argument, you should always pass 0; do
+ * not assume that the second argument is simply ignored. This is because the
+ * selector may be extended in the future. You will continue to get the current
+ * behavior if you pass 0 as the second argument, but other values may produce
+ * other behavior.
+ * </para></note>
*
* Returns: an integer, depending on what selector was called.
*/
return gestalt_CharOutput_CannotPrint;
/* Can print all other Latin-1 characters */
return gestalt_CharOutput_ExactPrint;
+
+ /* Which characters can the player type in line input? */
+ case gestalt_LineInput:
+ /* Does not accept control chars */
+ if( val < 32 || (val >= 127 && val <= 159) )
+ return 0;
+ return 1;
+
+ /* Which characters can the player type in char input? */
+ case gestalt_CharInput:
+ /* Does not accept control chars or unknown */
+ if( val < 32 || (val >= 127 && val <= 159) || val == keycode_Unknown )
+ return 0;
+ return 1;
/* Selector not supported */
default:
/**
* glk_exit:
*
- * End the Glk program. As far as the client program is concerned, this
- * function does not return.
+ * Shuts down the Glk program. 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.
+ *
+ * <note><para>
+ * You should only shut down your program with glk_exit() or by returning from
+ * your glk_main() function. If you call the ANSI <function>exit()</function>
+ * function, bad things may happen. This Glk library is designed for multiple
+ * sessions, for example, and you would be cutting off all the sessions instead
+ * of just yours. You would also prevent final text from being visible to the
+ * player.
+ * </para></note>
*/
void
glk_exit(void)
g_thread_exit(NULL);
}
-/*
-void
-glk_select(event_t *event)
-{
- gtk_main_iteration();
-}
-*/
-
-
#include "input.h"
/** glk_request_char_event:
- * @win: A window to request char events from Request
+ * @win: A window to request char events from.
*
- * Request input of a Latin-1 character or special key. A window cannot have requests
- * for both character and line input at the same time. Nor can it have requests
- * for character input of both types (Latin-1 and Unicode). It is illegal to
- * call glk_request_char_event() if the window already has a pending request
- * for either character or line input.
+ * Request input of a Latin-1 character or special key. A window cannot have
+ * requests for both character and line input at the same time. Nor can it have
+ * requests for character input of both types (Latin-1 and Unicode). It is
+ * illegal to call glk_request_char_event() if the window already has a pending
+ * request for either character or line input.
*/
void
glk_request_char_event(winid_t win)
{
g_return_if_fail(win);
g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
- g_return_if_fail(win->window_type != wintype_TextBuffer || win->window_type != wintype_TextGrid);
+ g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
win->input_request_type = INPUT_REQUEST_CHARACTER;
g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
}
/** glk_request_char_event_uni:
- * @win: A window to request char events from Request
+ * @win: A window to request char events from.
*
- * Request input of a Unicode character or special key. A window cannot have requests
- * for both character and line input at the same time. Nor can it have requests
- * for character input of both types (Latin-1 and Unicode). It is illegal to
- * call glk_request_char_event_uni() if the window already has a pending request
- * for either character or line input.
+ * Request input of a Unicode character or special key. See
+ * glk_request_char_event().
*/
void
glk_request_char_event_uni(winid_t win)
{
g_return_if_fail(win);
g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
- g_return_if_fail(win->window_type != wintype_TextBuffer || win->window_type != wintype_TextGrid);
+ g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
win->input_request_type = INPUT_REQUEST_CHARACTER_UNICODE;
g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
}
+/* Internal function: Request either latin-1 or unicode line input. */
void
-glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
+request_line_event_common(winid_t win, gboolean insert, gchar *inserttext)
{
- GtkTextBuffer *window_buffer;
-
- g_return_if_fail(win);
- g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
- g_return_if_fail(win->window_type != wintype_TextBuffer || win->window_type != wintype_TextGrid);
-
- window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget));
-
- win->input_request_type = INPUT_REQUEST_LINE;
- win->line_input_buffer = buf;
- win->line_input_buffer_max_len = maxlen;
+ GtkTextBuffer *window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget));
/* Move the input_position mark to the end of the window_buffer */
GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
gtk_text_buffer_get_start_iter(window_buffer, &start_iter);
gtk_text_buffer_remove_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter);
gtk_text_buffer_apply_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter);
-
- if(initlen > 0) {
- gtk_text_buffer_insert(window_buffer, &end_iter, buf, initlen);
- }
-
+
+ /* Insert pre-entered text if needed */
+ if(insert)
+ gtk_text_buffer_insert(window_buffer, &end_iter, inserttext, -1);
+
+ /* Scroll to input point */
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
g_signal_handler_unblock( G_OBJECT(window_buffer), win->insert_text_handler );
gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
}
+/**
+ * glk_request_line_event:
+ * @win: A text buffer or text grid window to request line input on.
+ * @buf: A buffer of at least @maxlen bytes.
+ * @maxlen: Length of the buffer.
+ * @initlen: The number of characters in @buf to pre-enter.
+ *
+ * Requests input of a line of Latin-1 characters. A window cannot have requests
+ * for both character and line input at the same time. Nor can it have requests
+ * for line input of both types (Latin-1 and Unicode). It is illegal to call
+ * glk_request_line_event() if the window already has a pending request for
+ * either character or line input.
+ *
+ * The @buf argument is a pointer to space where the line input will be stored.
+ * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
+ * library will not accept more characters than this. If @initlen is nonzero,
+ * then the first @initlen bytes of @buf will be entered as pre-existing input
+ * -- just as if the player had typed them himself. (The player can continue
+ * composing after this pre-entered input, or delete it or edit as usual.)
+ *
+ * The contents of the buffer are undefined until the input is completed (either
+ * by a line input event, or glk_cancel_line_event(). The library may or may not
+ * fill in the buffer as the player composes, while the input is still pending;
+ * it is illegal to change the contents of the buffer yourself.
+ */
void
-glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
+glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
{
- GtkTextBuffer *window_buffer;
-
g_return_if_fail(win);
+ g_return_if_fail(buf);
g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
- g_return_if_fail(win->window_type != wintype_TextBuffer || win->window_type != wintype_TextGrid);
+ g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
+
+ win->input_request_type = INPUT_REQUEST_LINE;
+ win->line_input_buffer = buf;
+ win->line_input_buffer_max_len = maxlen;
+
+ gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
+ request_line_event_common(win, (initlen > 0), inserttext);
+ g_free(inserttext);
+}
- window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget));
+/**
+ * glk_request_line_event_uni:
+ * @win: A text buffer or text grid window to request line input on.
+ * @buf: A buffer of at least @maxlen characters.
+ * @maxlen: Length of the buffer.
+ * @initlen: The number of characters in @buf to pre-enter.
+ *
+ * Request input of a line of Unicode characters. This works the same as
+ * glk_request_line_event(), except the result is stored in an array of
+ * <type>glui32</type> values instead of an array of characters, and the values
+ * may be any valid Unicode code points.
+ *
+ * The result will be in Unicode Normalization Form C. This basically means that
+ * composite characters will be single characters where possible, instead of
+ * sequences of base and combining marks. See
+ * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex #15
+ * </ulink> for the details.
+ */
+void
+glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
+{
+ g_return_if_fail(win);
+ g_return_if_fail(buf);
+ g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
+ g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
win->line_input_buffer_unicode = buf;
win->line_input_buffer_max_len = maxlen;
- /* Move the input_position mark to the end of the window_buffer */
- GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
- GtkTextIter end_iter;
- gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
- gtk_text_buffer_move_mark(window_buffer, input_position, &end_iter);
-
- /* Set the entire contents of the window_buffer as uneditable
- * (so input can only be entered at the end) */
- GtkTextIter start_iter;
- gtk_text_buffer_get_start_iter(window_buffer, &start_iter);
- gtk_text_buffer_remove_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter);
- gtk_text_buffer_apply_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter);
-
+ gchar *utf8;
if(initlen > 0) {
GError *error = NULL;
- gchar *utf8;
utf8 = g_ucs4_to_utf8(buf, initlen, NULL, NULL, &error);
if(utf8 == NULL)
error_dialog(NULL, error, "Error during unicode->utf8 conversion: ");
return;
}
-
- gtk_text_buffer_insert(window_buffer, &end_iter, utf8, -1);
- g_free(utf8);
}
+ else
+ utf8 = g_strdup("");
- gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
- g_signal_handler_unblock( G_OBJECT(window_buffer), win->insert_text_handler );
- gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
+ request_line_event_common(win, (initlen > 0), utf8);
+ g_free(utf8);
}
-
-
+/* Internal function: Callback for signal key-press-event on a text buffer
+window. */
gboolean
-on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t window)
+on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
{
- if(window->input_request_type != INPUT_REQUEST_CHARACTER &&
- window->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE) {
+ if(win->input_request_type != INPUT_REQUEST_CHARACTER &&
+ win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
return FALSE;
- }
int keycode;
case GDK_KP_Left: keycode = keycode_Left; break;
case GDK_Right:
case GDK_KP_Right: keycode = keycode_Right; break;
+ case GDK_Linefeed:
case GDK_Return:
case GDK_KP_Enter: keycode = keycode_Return; break;
case GDK_Delete:
case GDK_BackSpace:
case GDK_KP_Delete: keycode = keycode_Delete; break;
case GDK_Escape: keycode = keycode_Escape; break;
- case GDK_Tab: keycode = keycode_Tab; break;
+ case GDK_Tab:
+ case GDK_KP_Tab: keycode = keycode_Tab; break;
case GDK_Page_Up:
case GDK_KP_Page_Up: keycode = keycode_PageUp; break;
case GDK_Page_Down:
case GDK_KP_Home: keycode = keycode_Home; break;
case GDK_End:
case GDK_KP_End: keycode = keycode_End; break;
- case GDK_F1: keycode = keycode_Func1; break;
- case GDK_F2: keycode = keycode_Func2; break;
- case GDK_F3: keycode = keycode_Func3; break;
- case GDK_F4: keycode = keycode_Func4; break;
+ case GDK_F1:
+ case GDK_KP_F1: keycode = keycode_Func1; break;
+ case GDK_F2:
+ case GDK_KP_F2: keycode = keycode_Func2; break;
+ case GDK_F3:
+ case GDK_KP_F3: keycode = keycode_Func3; break;
+ case GDK_F4:
+ case GDK_KP_F4: keycode = keycode_Func4; break;
case GDK_F5: keycode = keycode_Func5; break;
case GDK_F6: keycode = keycode_Func6; break;
case GDK_F7: keycode = keycode_Func7; break;
case GDK_F12: keycode = keycode_Func12; break;
default:
keycode = gdk_keyval_to_unicode(event->keyval);
+ /* If keycode is 0, then keyval was not recognized; also return
+ unknown if Latin-1 input was requested and the character is not in
+ Latin-1 */
+ if(keycode == 0 || (win->input_request_type == INPUT_REQUEST_CHARACTER && keycode > 255))
+ keycode = keycode_Unknown;
}
- if(window->input_request_type == INPUT_REQUEST_CHARACTER) {
- if(keycode >= 1 || keycode <= 255) {
- event_throw(evtype_CharInput, window, keycode, 0);
- } else {
- event_throw(evtype_CharInput, window, keycode_Unknown, 0);
- }
- } else {
- if(keycode == 0) {
- event_throw(evtype_CharInput, window, keycode_Unknown, 0);
- } else {
- event_throw(evtype_CharInput, window, keycode, 0);
- }
- }
-
+ event_throw(evtype_CharInput, win, keycode, 0);
+
/* Only one keypress will be handled */
- window->input_request_type = INPUT_REQUEST_NONE;
- g_signal_handler_block( G_OBJECT(window->widget), window->keypress_handler );
+ win->input_request_type = INPUT_REQUEST_NONE;
+ g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
return TRUE;
}
-void
+/* Internal function: Callback for signal insert-text on a text buffer window.
+Not used; cannot modify the text buffer from within the callback?? Segfault! */
+/*void
on_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, gpointer user_data)
{
gchar *newline_pos = strchr(text, '\n');
printf("position: %d\n", newline_pos-text);
*newline_pos = 'a';
}
-}
+}*/
+/* Internal function: Callback for signal insert-text on a text buffer window.
+Runs after the default handler has already inserted the text.*/
void
-after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t window)
+after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win)
{
- if( strchr(text, '\n') != NULL) {
+ if( strchr(text, '\n') != NULL )
+ {
/* Make the window uneditable again and remove signal handlers */
- GtkTextBuffer *window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->widget));
- gtk_text_view_set_editable(GTK_TEXT_VIEW(window->widget), FALSE);
- g_signal_handler_block(window_buffer, window->insert_text_handler);
+ GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
+ g_signal_handler_block(window_buffer, win->insert_text_handler);
/* Retrieve the text that was input */
GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
gchar *inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
-
/* Convert the string from UTF-8 to Latin-1 or Unicode */
- if(window->input_request_type == INPUT_REQUEST_LINE) {
+ if(win->input_request_type == INPUT_REQUEST_LINE)
+ {
GError *error = NULL;
gchar *latin1;
gsize bytes_written;
if(latin1 == NULL)
{
error_dialog(NULL, error, "Error during utf8->latin1 conversion: ");
- event_throw(evtype_LineInput, window, 0, 0);
+ event_throw(evtype_LineInput, win, 0, 0);
return;
}
/* Place input in the echo stream */
- if(window->echo_stream != NULL)
- glk_put_string_stream(window->echo_stream, latin1);
+ if(win->echo_stream != NULL)
+ glk_put_string_stream(win->echo_stream, latin1);
/* Copy the string (but not the NULL at the end) */
- memcpy(window->line_input_buffer, latin1, MIN(window->line_input_buffer_max_len, bytes_written-1));
+ int copycount = MIN(win->line_input_buffer_max_len, bytes_written - 1);
+ memcpy(win->line_input_buffer, latin1, copycount);
g_free(latin1);
- event_throw(evtype_LineInput, window, MIN(window->line_input_buffer_max_len, bytes_written-1), 0);
+ event_throw(evtype_LineInput, win, copycount, 0);
}
- else if(window->input_request_type == INPUT_REQUEST_LINE_UNICODE) {
+ else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
+ {
gunichar *unicode;
glong items_written;
unicode = g_utf8_to_ucs4_fast(inserted_text, -1, &items_written);
if(unicode == NULL)
{
error_dialog(NULL, NULL, "Error during utf8->unicode conversion");
- event_throw(evtype_LineInput, window, 0, 0);
+ event_throw(evtype_LineInput, win, 0, 0);
return;
}
/* Place input in the echo stream */
- // TODO: fixme
- // if(window->echo_stream != NULL)
- // glk_put_string_stream_uni(window->echo_stream, unicode);
+ /* TODO: fixme
+ if(win->echo_stream != NULL)
+ glk_put_string_stream_uni(window->echo_stream, unicode);*/
/* Copy the string (but not the NULL at the end) */
- memcpy(window->line_input_buffer_unicode, unicode, MIN(window->line_input_buffer_max_len, items_written)*sizeof(gunichar));
+ int copycount = MIN(win->line_input_buffer_max_len, items_written);
+ memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
g_free(unicode);
- event_throw(evtype_LineInput, window, MIN(window->line_input_buffer_max_len, items_written), 0);
+ event_throw(evtype_LineInput, win, copycount, 0);
}
- else {
+ else
g_warning("%s: Wrong input request type.", __func__);
- }
- window->input_request_type = INPUT_REQUEST_NONE;
+ win->input_request_type = INPUT_REQUEST_NONE;
}
}
#include "window.h"
#include "event.h"
-gboolean on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t window);
+gboolean on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win);
void on_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, gpointer user_data);
-void after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t window);
+void after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win);
#endif
window_stream_new(winid_t window)
{
/* Create stream and connect it to window */
- strid_t s = g_new0(struct glk_stream_struct, 1);
- s->file_mode = filemode_Write;
- s->stream_type = STREAM_TYPE_WINDOW;
- s->window = window;
+ strid_t str = g_new0(struct glk_stream_struct, 1);
+ str->file_mode = filemode_Write;
+ str->type = STREAM_TYPE_WINDOW;
+ str->window = window;
+
/* Add it to the global stream list */
- stream_list = g_list_prepend(stream_list, s);
- s->stream_list = stream_list;
+ stream_list = g_list_prepend(stream_list, str);
+ str->stream_list = stream_list;
- return s;
+ return str;
}
/**
* glk_stream_iterate:
- * @str: A stream, or #NULL.
- * @rockptr: Return location for the next window's rock, or #NULL.
+ * @str: A stream, or %NULL.
+ * @rockptr: Return location for the next window's rock, or %NULL.
*
- * Iterates over the list of streams; if @str is #NULL, it returns the first
+ * Iterates over the list of streams; if @str is %NULL, it returns the first
* stream, otherwise the next stream after @str. If there are no more, it
- * returns #NULL. The stream's rock is stored in @rockptr. If you don't want
- * the rocks to be returned, you may set @rockptr to #NULL.
+ * returns %NULL. The stream's rock is stored in @rockptr. If you don't want
+ * the rocks to be returned, you may set @rockptr to %NULL.
*
* The order in which streams are returned is arbitrary. The order may change
* every time you create or destroy a stream, invalidating the iteration.
*
- * Returns: the next stream, or #NULL if there are no more.
+ * Returns: the next stream, or %NULL if there are no more.
*/
strid_t
glk_stream_iterate(strid_t str, glui32 *rockptr)
/**
* glk_stream_set_current:
- * @str: An output stream, or NULL.
+ * @str: An output stream, or %NULL.
*
- * Sets the current stream to @str, or to nothing if @str is #NULL.
+ * Sets the current stream to @str, which must be an output stream. You may set
+ * the current stream to %NULL, which means the current stream is not set to
+ * anything.
*/
void
glk_stream_set_current(strid_t str)
{
if(str != NULL && str->file_mode == filemode_Read)
{
- g_warning("glk_stream_set_current: "
- "Cannot set current stream to non output stream");
+ g_warning("%s: Cannot set current stream to non output stream", __func__);
return;
}
/**
* glk_stream_get_current:
*
- * Returns the current stream, or #NULL if there is none.
+ * Returns the current stream, or %NULL if there is none.
*
* Returns: A stream.
*/
* glk_put_char:
* @ch: A character in Latin-1 encoding.
*
- * Prints one character @ch to the current stream.
+ * Prints one character to the current stream. As with all basic functions, the
+ * character is assumed to be in the Latin-1 character encoding.
*/
void
glk_put_char(unsigned char ch)
{
- /* Illegal to print to the current stream if it is NULL */
g_return_if_fail(current_stream != NULL);
glk_put_char_stream(current_stream, ch);
}
* glk_put_string:
* @s: A null-terminated string in Latin-1 encoding.
*
- * Prints @s to the current stream.
+ * Prints a null-terminated string to the current stream. It is exactly
+ * equivalent to:
+ * <informalexample><programlisting>
+ * for (ptr = s; *ptr; ptr++)
+ * glk_put_char(*ptr);
+ * </programlisting></informalexample>
+ * However, it may be more efficient.
*/
void
glk_put_string(char *s)
{
- /* Illegal to print to the current stream if it is NULL */
g_return_if_fail(current_stream != NULL);
glk_put_string_stream(current_stream, s);
}
* @buf: An array of characters in Latin-1 encoding.
* @len: Length of @buf.
*
- * Prints @buf to the current stream.
+ * Prints a block of characters to the current stream. It is exactly equivalent
+ * to:
+ * <informalexample><programlisting>
+ * for (i = 0; i < len; i++)
+ * glk_put_char(buf[i]);
+ * </programlisting></informalexample>
+ * However, it may be more efficient.
*/
void
glk_put_buffer(char *buf, glui32 len)
{
- /* Illegal to print to the current stream if it is NULL */
g_return_if_fail(current_stream != NULL);
glk_put_buffer_stream(current_stream, buf, len);
}
* The data is written to the buffer exactly as it was passed to the printing
* functions (glk_put_char(), etc.); input functions will read the data exactly
* as it exists in memory. No platform-dependent cookery will be done on it.
- * [You can write a disk file in text mode, but a memory stream is effectively
- * always in binary mode.]
+ * (You can write a disk file in text mode, but a memory stream is effectively
+ * always in binary mode.)
*
* Unicode values (characters greater than 255) cannot be written to the buffer.
* If you try, they will be stored as 0x3F ("?") characters.
{
g_return_val_if_fail(fmode != filemode_WriteAppend, NULL);
- strid_t s = g_new0(struct glk_stream_struct, 1);
- s->rock = rock;
- s->file_mode = fmode;
- s->stream_type = STREAM_TYPE_MEMORY;
- s->buffer = buf;
- s->mark = 0;
- s->buflen = buflen;
- s->unicode = FALSE;
+ strid_t str = g_new0(struct glk_stream_struct, 1);
+ str->rock = rock;
+ str->file_mode = fmode;
+ str->type = STREAM_TYPE_MEMORY;
+ str->buffer = buf;
+ str->mark = 0;
+ str->buflen = buflen;
+ str->unicode = FALSE;
/* Add it to the global stream list */
- stream_list = g_list_prepend(stream_list, s);
- s->stream_list = stream_list;
+ stream_list = g_list_prepend(stream_list, str);
+ str->stream_list = stream_list;
- return s;
+ return str;
}
/**
* bytes.
*/
strid_t
-glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 fmode,
- glui32 rock)
+glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 fmode, glui32 rock)
{
g_return_val_if_fail(fmode != filemode_WriteAppend, NULL);
- strid_t s = g_new0(struct glk_stream_struct, 1);
- s->rock = rock;
- s->file_mode = fmode;
- s->stream_type = STREAM_TYPE_MEMORY;
- s->ubuffer = buf;
- s->mark = 0;
- s->buflen = buflen;
- s->unicode = TRUE;
+ strid_t str = g_new0(struct glk_stream_struct, 1);
+ str->rock = rock;
+ str->file_mode = fmode;
+ str->type = STREAM_TYPE_MEMORY;
+ str->ubuffer = buf;
+ str->mark = 0;
+ str->buflen = buflen;
+ str->unicode = TRUE;
/* Add it to the global stream list */
- stream_list = g_list_prepend(stream_list, s);
- s->stream_list = stream_list;
+ stream_list = g_list_prepend(stream_list, str);
+ str->stream_list = stream_list;
- return s;
+ return str;
}
/* Internal function: create a stream using the given parameters. */
}
}
- strid_t s = g_new0(struct glk_stream_struct, 1);
- s->rock = rock;
- s->file_mode = fmode;
- s->stream_type = STREAM_TYPE_FILE;
- s->file_pointer = fp;
- s->binary = binary;
- s->unicode = unicode;
- s->filename = g_filename_to_utf8(fileref->filename, -1, NULL, NULL, NULL);
- if(s->filename == NULL)
- s->filename = g_strdup("Unknown file name"); /* fail silently */
+ strid_t str = g_new0(struct glk_stream_struct, 1);
+ str->rock = rock;
+ str->file_mode = fmode;
+ str->type = STREAM_TYPE_FILE;
+ str->file_pointer = fp;
+ str->binary = binary;
+ str->unicode = unicode;
+ str->filename = g_filename_to_utf8(fileref->filename, -1, NULL, NULL, NULL);
+ if(str->filename == NULL)
+ str->filename = g_strdup("Unknown file name"); /* fail silently */
/* Add it to the global stream list */
- stream_list = g_list_prepend(stream_list, s);
- s->stream_list = stream_list;
+ stream_list = g_list_prepend(stream_list, str);
+ str->stream_list = stream_list;
- return s;
+ return str;
}
/**
g_return_if_fail(str != NULL);
/* Free resources associated with one specific type of stream */
- switch(str->stream_type)
+ switch(str->type)
{
case STREAM_TYPE_WINDOW:
g_warning("%s: Attempted to close a window stream. Use glk_window_"
stream_close_common(str, result);
}
+/* Internal function: Stuff to do upon closing any type of stream. */
void
stream_close_common(strid_t str, stream_result_t *result)
{
/* Remove the stream from the global stream list */
stream_list = g_list_delete_link(stream_list, str->stream_list);
+
/* If it was the current output stream, set that to NULL */
if(current_stream == str)
current_stream = NULL;
+
/* If it was one or more windows' echo streams, set those to NULL */
winid_t win;
for(win = glk_window_iterate(NULL, NULL); win;
win = glk_window_iterate(win, NULL))
if(win->echo_stream == str)
win->echo_stream = NULL;
+
/* Return the character counts */
if(result)
{
glui32 file_mode;
glui32 read_count;
glui32 write_count;
- enum StreamType stream_type;
+ enum StreamType type;
/* Specific to window stream: the window this stream is connected to */
winid_t window;
/* For memory and file streams */
#include <glib.h>
#include <glib/gstdio.h>
-#define min(x,y) ( (x > y)? y : x )
-
/*
*
**************** WRITING FUNCTIONS ********************************************
gdk_threads_leave();
}
-/* Internal function: write a UTF-8 buffer with length to a stream. */
+/* 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->stream_type)
+ switch(str->type)
{
case STREAM_TYPE_WINDOW:
/* Each window type has a different way of printing to it */
- switch(str->window->window_type)
+ switch(str->window->type)
{
/* Printing to these windows' streams does nothing */
case wintype_Blank:
str->write_count += len;
break;
default:
- g_warning("%s: Writing to this kind of window unsupported.",
- __func__);
+ g_warning("%s: Writing to this kind of window unsupported.", __func__);
}
/* Now write the same buffer to the window's echo stream */
}
if(!str->unicode && str->buffer)
{
- int copycount = min(len, str->buflen - str->mark);
+ int copycount = MIN(len, str->buflen - str->mark);
memmove(str->buffer + str->mark, buf, copycount);
str->mark += copycount;
}
str->write_count += len;
break;
default:
- g_warning("%s: Writing to this kind of stream unsupported.",
- __func__);
+ g_warning("%s: Writing to this kind of stream unsupported.", __func__);
}
}
glk_get_char_stream(strid_t str)
{
g_return_val_if_fail(str != NULL, -1);
- g_return_val_if_fail(str->file_mode == filemode_Read
- || str->file_mode == filemode_ReadWrite, -1);
+ g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, -1);
- switch(str->stream_type)
+ switch(str->type)
{
case STREAM_TYPE_MEMORY:
if(str->unicode)
return (ch > 0xFF)? 0x3F : ch;
}
default:
- g_warning("%s: Reading from this kind of stream unsupported.",
- __func__);
+ g_warning("%s: Reading from this kind of stream unsupported.", __func__);
return -1;
}
}
glk_get_buffer_stream(strid_t str, char *buf, glui32 len)
{
g_return_val_if_fail(str != NULL, 0);
- g_return_val_if_fail(str->file_mode == filemode_Read
- || str->file_mode == filemode_ReadWrite, 0);
+ g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0);
g_return_val_if_fail(buf != NULL, 0);
- switch(str->stream_type)
+ switch(str->type)
{
case STREAM_TYPE_MEMORY:
{
int copycount = 0;
if(str->unicode)
{
- while(copycount < len && str->ubuffer
- && str->mark < str->buflen)
+ while(copycount < len && str->ubuffer && str->mark < str->buflen)
{
glui32 ch = str->ubuffer[str->mark++];
buf[copycount++] = (ch > 0xFF)? '?' : (char)ch;
else
{
if(str->buffer) /* if not, copycount stays 0 */
- copycount = min(len, str->buflen - str->mark);
+ copycount = MIN(len, str->buflen - str->mark);
memmove(buf, str->buffer + str->mark, copycount);
str->mark += copycount;
}
{
/* Read len characters of 4 bytes each */
unsigned char *readbuffer = g_new0(unsigned char, 4 * len);
- size_t count = fread(readbuffer, sizeof(unsigned char),
- 4 * len, str->file_pointer);
+ size_t count = fread(readbuffer, sizeof(unsigned char), 4 * len, str->file_pointer);
/* If there was an incomplete character */
if(count % 4 != 0)
{
count -= count % 4;
- g_warning("%s: Incomplete character in binary Unicode "
- "file.", __func__);
+ g_warning("%s: Incomplete character in binary Unicode file.", __func__);
}
str->read_count += count / 4;
}
else /* Regular binary file */
{
- size_t count = fread(buf, sizeof(char), len,
- str->file_pointer);
+ size_t count = fread(buf, sizeof(char), len, str->file_pointer);
str->read_count += count;
return count;
}
return foo;
}
default:
- g_warning("%s: Reading from this kind of stream unsupported.",
- __func__);
+ g_warning("%s: Reading from this kind of stream unsupported.", __func__);
return 0;
}
}
glk_get_line_stream(strid_t str, char *buf, glui32 len)
{
g_return_val_if_fail(str != NULL, 0);
- g_return_val_if_fail(str->file_mode == filemode_Read
- || str->file_mode == filemode_ReadWrite, 0);
+ g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0);
g_return_val_if_fail(buf != NULL, 0);
- switch(str->stream_type)
+ switch(str->type)
{
case STREAM_TYPE_MEMORY:
{
if(str->unicode)
{
/* Do it character-by-character */
- while(copycount < len - 1 && str->ubuffer
- && str->mark < str->buflen)
+ while(copycount < len - 1 && str->ubuffer && str->mark < str->buflen)
{
glui32 ch = str->ubuffer[str->mark++];
/* Check for Unicode newline; slightly different than
in file streams */
- if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028
- || ch == 0x2029)
+ if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029)
{
buf[copycount++] = '\n';
break;
else
{
if(str->buffer) /* if not, copycount stays 0 */
- copycount = min(len - 1, str->buflen - str->mark);
- char *endptr = memccpy(buf, str->buffer + str->mark, '\n',
- copycount);
+ copycount = MIN(len - 1, str->buflen - str->mark);
+ char *endptr = memccpy(buf, str->buffer + str->mark, '\n', copycount);
if(endptr) /* newline was found */
copycount = endptr - buf; /* Real copy count */
buf[copycount] = '\0';
int foo;
for(foo = 0; foo < len - 1; foo++)
{
- glsi32 ch =
- read_ucs4be_char_from_file(str->file_pointer);
+ glsi32 ch = read_ucs4be_char_from_file(str->file_pointer);
if(ch == -1)
{
buf[foo] = '\0';
return foo;
}
default:
- g_warning("%s: Reading from this kind of stream unsupported.",
- __func__);
+ g_warning("%s: Reading from this kind of stream unsupported.", __func__);
return 0;
}
}
{
g_return_val_if_fail(str != NULL, 0);
- switch(str->stream_type)
+ switch(str->type)
{
case STREAM_TYPE_MEMORY:
return str->mark;
g_return_if_fail(!(seekmode == seekmode_Start && pos < 0));
g_return_if_fail(!(seekmode == seekmode_End || pos > 0));
- switch(str->stream_type)
+ switch(str->type)
{
case STREAM_TYPE_MEMORY:
switch(seekmode)
break;
}
default:
- g_warning("%s: Seeking not supported on this type of stream.",
- __func__);
+ g_warning("%s: Seeking not supported on this type of stream.", __func__);
return;
}
}
#include "glk.h"
+/**
+ * glk_set_style:
+ * @val: A style.
+ *
+ * Changes the style of the current output stream. @val should be one of
+ * #style_Normal, #style_Emphasized, #style_Preformatted, #style_Header,
+ * #style_Subheader, #style_Alert, #style_Note, #style_BlockQuote, #style_Input,
+ * #style_User1, or #style_User2. However, any value is actually legal; if the
+ * library does not recognize the style value, it will treat it as
+ * #style_Normal. (This policy allows for the future definition of styles
+ * without breaking old Glk libraries.)
+ */
void
glk_set_style(glui32 val)
{
/**
* glk_window_iterate:
- * @win: A window, or #NULL.
- * @rockptr: Return location for the next window's rock, or #NULL.
+ * @win: A window, or %NULL.
+ * @rockptr: Return location for the next window's rock, or %NULL.
*
- * Iterates over the list of windows; if @win is #NULL, it returns the first
+ * Iterates over the list of windows; if @win is %NULL, it returns the first
* window, otherwise the next window after @win. If there are no more, it
* returns #NULL. The window's rock is stored in @rockptr. If you don't want
- * the rocks to be returned, you may set @rockptr to #NULL.
+ * the rocks to be returned, you may set @rockptr to %NULL.
*
* The order in which windows are returned is arbitrary. The root window is
* not necessarily first, nor is it necessarily last. The order may change
* every time you create or destroy a window, invalidating the iteration.
*
- * Returns: the next window, or #NULL if there are no more.
+ * Returns: the next window, or %NULL if there are no more.
*/
winid_t
glk_window_iterate(winid_t win, glui32 *rockptr)
* glk_window_get_rock:
* @win: A window.
*
- * Returns the window @win's rock value. Pair windows always have rock 0.
+ * Returns @win's rock value. Pair windows always have rock 0; all other windows
+ * have the rock value you created them with.
*
* Returns: A rock value.
*/
* glk_window_get_type:
* @win: A window.
*
- * Returns the window @win's type, one of #wintype_Blank, #wintype_Pair,
+ * Returns @win's type, one of #wintype_Blank, #wintype_Pair,
* #wintype_TextBuffer, #wintype_TextGrid, or #wintype_Graphics.
*
* Returns: The window's type.
glk_window_get_type(winid_t win)
{
g_return_val_if_fail(win != NULL, 0);
- return win->window_type;
+ return win->type;
}
/**
* @win: A window.
*
* Returns the window @win's parent window. If @win is the root window, this
- * returns #NULL, since the root window has no parent. Remember that the parent
+ * returns %NULL, since the root window has no parent. Remember that the parent
* of every window is a pair window; other window types are always childless.
*
* Returns: A window.
* @win: A window.
*
* Returns the other child of the window @win's parent. If @win is the
- * root window, this returns #NULL.
+ * root window, this returns %NULL.
*
- * Returns: A window, or NULL.
+ * Returns: A window, or %NULL.
*/
winid_t
glk_window_get_sibling(winid_t win)
return NULL;
}
/* We only create one window and don't support any more than that */
- winid_t new_window = g_new0(struct glk_window_struct, 1);
- root_window = g_node_new(new_window);
+ winid_t win = g_new0(struct glk_window_struct, 1);
+ root_window = g_node_new(win);
- new_window->rock = rock;
- new_window->window_type = wintype;
+ win->rock = rock;
+ win->type = wintype;
gdk_threads_enter();
case wintype_Blank:
{
/* A blank window will be a label without any text */
- GtkWidget *window = gtk_label_new("");
- gtk_box_pack_end(vbox, window, TRUE, TRUE, 0);
- gtk_widget_show(window);
+ GtkWidget *label = gtk_label_new("");
+ gtk_box_pack_end(vbox, label, TRUE, TRUE, 0);
+ gtk_widget_show(label);
- new_window->widget = window;
+ win->widget = label;
/* You can print to a blank window's stream, but it does nothing */
- new_window->window_stream = window_stream_new(new_window);
- new_window->echo_stream = NULL;
+ win->window_stream = window_stream_new(win);
+ win->echo_stream = NULL;
}
break;
case wintype_TextBuffer:
{
- GtkWidget *scroll_window = gtk_scrolled_window_new(NULL, NULL);
- GtkWidget *window = gtk_text_view_new();
- GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(window) );
+ GtkWidget *scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
+ GtkWidget *textview = gtk_text_view_new();
+ GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(textview) );
- gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(window), GTK_WRAP_WORD_CHAR );
- gtk_text_view_set_editable( GTK_TEXT_VIEW(window), FALSE );
+ gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR );
+ gtk_text_view_set_editable( GTK_TEXT_VIEW(textview), FALSE );
- gtk_container_add( GTK_CONTAINER(scroll_window), window );
- gtk_box_pack_end(vbox, scroll_window, TRUE, TRUE, 0);
- gtk_widget_show_all(scroll_window);
+ gtk_container_add( GTK_CONTAINER(scrolledwindow), textview );
+ gtk_box_pack_end(vbox, scrolledwindow, TRUE, TRUE, 0);
+ gtk_widget_show_all(scrolledwindow);
- new_window->widget = window;
- new_window->window_stream = window_stream_new(new_window);
- new_window->echo_stream = NULL;
- new_window->input_request_type = INPUT_REQUEST_NONE;
- new_window->line_input_buffer = NULL;
- new_window->line_input_buffer_unicode = NULL;
+ win->widget = textview;
+ win->window_stream = window_stream_new(win);
+ win->echo_stream = NULL;
+ win->input_request_type = INPUT_REQUEST_NONE;
+ win->line_input_buffer = NULL;
+ win->line_input_buffer_unicode = NULL;
/* Connect signal handlers */
- new_window->keypress_handler = g_signal_connect( G_OBJECT(window), "key-press-event", G_CALLBACK(on_window_key_press_event), new_window );
- g_signal_handler_block( G_OBJECT(window), new_window->keypress_handler );
+ win->keypress_handler = g_signal_connect( G_OBJECT(textview), "key-press-event", G_CALLBACK(on_window_key_press_event), win );
+ g_signal_handler_block( G_OBJECT(textview), win->keypress_handler );
- new_window->insert_text_handler = g_signal_connect_after( G_OBJECT(buffer), "insert-text", G_CALLBACK(after_window_insert_text), new_window );
- g_signal_handler_block( G_OBJECT(buffer), new_window->insert_text_handler );
+ win->insert_text_handler = g_signal_connect_after( G_OBJECT(textbuffer), "insert-text", G_CALLBACK(after_window_insert_text), win );
+ g_signal_handler_block( G_OBJECT(textbuffer), win->insert_text_handler );
- /* Create an editable tag to indicate editable parts of the window (for line input) */
- gtk_text_buffer_create_tag(buffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL);
+ /* Create an editable tag to indicate uneditable parts of the window
+ (for line input) */
+ gtk_text_buffer_create_tag(textbuffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL);
/* Mark the position where the user will input text */
- GtkTextIter end_iter;
- gtk_text_buffer_get_end_iter(buffer, &end_iter);
- gtk_text_buffer_create_mark(buffer, "input_position", &end_iter, TRUE);
+ GtkTextIter end;
+ gtk_text_buffer_get_end_iter(textbuffer, &end);
+ gtk_text_buffer_create_mark(textbuffer, "input_position", &end, TRUE);
}
break;
default:
- g_warning("glk_window_open: unsupported window type");
- g_free(new_window);
+ g_warning("%s: unsupported window type", __func__);
+ g_free(win);
gdk_threads_leave();
return NULL;
}
- new_window->window_node = root_window;
+ win->window_node = root_window;
gdk_threads_leave();
- return new_window;
+ return win;
}
+/**
+ * glk_window_close:
+ * @win: Window to close.
+ * @result: Pointer to a #stream_result_t in which to store the write count.
+ *
+ * Closes @win, which is pretty much exactly the opposite of opening a window.
+ * It is legal to close all your windows, or to close the root window (which is
+ * the same thing.)
+ *
+ * The @result argument is filled with the output character count of the window
+ * stream.
+ */
void
glk_window_close(winid_t win, stream_result_t *result)
{
g_return_if_fail(win != NULL);
- switch(win->window_type)
+ switch(win->type)
{
case wintype_TextBuffer:
gtk_widget_destroy( gtk_widget_get_parent(win->widget) );
* glk_window_clear:
* @win: A window.
*
- * Erases the window @win.
+ * Erases the window @win. The meaning of this depends on the window type.
+ *
+ * <itemizedlist>
+ * <listitem><para>
+ * Text buffer: This may do any number of things, such as delete all text in
+ * the window, or print enough blank lines to scroll all text beyond
+ * visibility, or insert a page-break marker which is treated specially by the
+ * display part of the library.
+ * </para></listitem>
+ * <listitem><para>
+ * Text grid: This will clear the window, filling all positions with blanks.
+ * The window cursor is moved to the top left corner (position 0,0).
+ * </para></listitem>
+ * <listitem><para>
+ * Graphics: Clears the entire window to its current background color.
+ * </para></listitem>
+ * <listitem><para>
+ * Other window types: No effect.
+ * </para></listitem>
+ * </itemizedlist>
+ *
+ * It is illegal to erase a window which has line input pending.
*/
void
glk_window_clear(winid_t win)
{
g_return_if_fail(win != NULL);
+ g_return_if_fail(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE);
- switch(win->window_type)
+ switch(win->type)
{
case wintype_Blank:
/* do nothing */
{
gdk_threads_enter();
- GtkTextBuffer *buffer =
- gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
+ GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
GtkTextIter start, end;
gtk_text_buffer_get_bounds(buffer, &start, &end);
gtk_text_buffer_delete(buffer, &start, &end);
* glk_set_window:
* @win: A window.
*
- * Sets the current stream to @win's window stream.
+ * Sets the current stream to @win's window stream. It is exactly equivalent to
+ * <informalexample><programlisting>
+ * glk_stream_set_current(glk_window_get_stream(win))
+ * </programlisting></informalexample>
*/
void
glk_set_window(winid_t win)
* glk_window_get_stream:
* @win: A window.
*
- * Gets the stream associated with @win.
+ * Returns the stream which is associated with @win. Every window has a stream
+ * which can be printed to, but this may not be useful, depending on the window
+ * type. (For example, printing to a blank window's stream has no effect.)
*
* Returns: The window stream.
*/
/**
* glk_window_set_echo_stream:
* @win: A window.
- * @str: A stream to attach to the window, or #NULL.
+ * @str: A stream to attach to the window, or %NULL.
*
* Attaches the stream @str to @win as a second stream. Any text printed to the
* window is also echoed to this second stream, which is called the window's
* which would create an infinite loop. It is similarly illegal to create a
* longer loop (two or more windows echoing to each other.)
*
- * You can reset a window to stop echoing by setting @str to #NULL.
+ * You can reset a window to stop echoing by setting @str to %NULL.
*/
void
glk_window_set_echo_stream(winid_t win, strid_t str)
g_return_if_fail(win != NULL);
/* Test for an infinite loop */
- strid_t next_str;
- for(next_str = str;
- next_str != NULL && next_str->stream_type == STREAM_TYPE_WINDOW;
- next_str = next_str->window->echo_stream)
+ strid_t next = str;
+ for(; next && next->type == STREAM_TYPE_WINDOW; next = next->window->echo_stream)
{
- if(next_str == win->window_stream)
+ if(next == win->window_stream)
{
- g_warning("glk_window_set_echo_stream: Infinite loop detected");
+ g_warning("%s: Infinite loop detected", __func__);
win->echo_stream = NULL;
return;
}
}
- win->echo_stream = str;
+ win->echo_stream = str;
}
/**
* @win: A window.
*
* Returns the echo stream of window @win. If the window has no echo stream (as
- * is initially the case) then this returns #NULL.
+ * is initially the case) then this returns %NULL.
*
- * Returns: A stream, or #NULL.
+ * Returns: A stream, or %NULL.
*/
strid_t
glk_window_get_echo_stream(winid_t win)
return win->echo_stream;
}
+/**
+ * glk_window_get_size:
+ * @win: A window.
+ * @widthptr: Pointer to a location to store the window's width, or %NULL.
+ * @heightptr: Pointer to a location to store the window's height, or %NULL.
+ *
+ * Simply returns the actual size of the window, in its measurement system.
+ * Either @widthptr or @heightptr can be %NULL, if you only want one
+ * measurement. (Or, in fact, both, if you want to waste time.)
+ */
void
glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
{
g_return_if_fail(win != NULL);
+ /* TODO: Write this function */
+ /* For a text buffer window: Return the number of rows and columns which
+ would be available _if_ the window was filled with "0" (zero) characters in
+ the "normal" font. */
if(widthptr != NULL) {
*widthptr = 0;
}
}
}
+/**
+ * glk_window_move_cursor:
+ * @win: A text grid window.
+ * @xpos: Horizontal cursor position.
+ * @ypos: Vertical cursor position.
+ *
+ * Sets the cursor position. If you move the cursor right past the end of a
+ * line, it wraps; the next character which is printed will appear at the
+ * beginning of the next line.
+ *
+ * If you move the cursor below the last line, or when the cursor reaches the
+ * end of the last line, it goes "off the screen" and further output has no
+ * effect. You must call glk_window_move_cursor() or glk_window_clear() to move
+ * the cursor back into the visible region.
+ *
+ * <note><para>
+ * Note that the arguments of glk_window_move_cursor() are <type>unsigned
+ * int</type>s. This is okay, since there are no negative positions. If you try
+ * to pass a negative value, Glk will interpret it as a huge positive value,
+ * and it will wrap or go off the last line.
+ * </para></note>
+ *
+ * <note><para>
+ * Also note that the output cursor is not necessarily visible. In particular,
+ * when you are requesting line or character input in a grid window, you cannot
+ * rely on the cursor position to prompt the player where input is indicated.
+ * You should print some character prompt at that spot -- a ">" character, for
+ * example.
+ * </para></note>
+ */
void
glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos)
{
g_return_if_fail(win != NULL);
+ g_return_if_fail(win->type == wintype_TextGrid);
+ /* TODO: write this function */
}
/* Pointer to the node in the global tree that contains this window */
GNode *window_node;
/* Window parameters */
- glui32 window_type;
+ glui32 type;
GtkWidget *widget;
strid_t window_stream;
strid_t echo_stream;