Merge branch 'sound'
[projects/chimara/chimara.git] / libchimara / garglk.c
1 #include <glib.h>
2 #include <glib/gi18n.h>
3 #include <glib/gprintf.h>
4 #include <libchimara/glk.h>
5 #include "chimara-glk-private.h"
6 #include "stream.h"
7 #include "fileref.h"
8 #include "style.h"
9 #include "garglk.h"
10
11 extern GPrivate *glk_data_key;
12
13 /**
14  * garglk_fileref_get_name:
15  * @fref: A file reference.
16  *
17  * Gets the actual disk filename that @fref refers to, in the platform's
18  * native filename encoding. The string is owned by @fref and must not be
19  * changed or freed.
20  *
21  * Returns: a string in filename encoding.
22  */
23 char * 
24 garglk_fileref_get_name(frefid_t fref)
25 {
26         VALID_FILEREF(fref, return NULL);
27         return fref->filename;
28 }
29
30 /**
31  * garglk_set_program_name:
32  * @name: Name of the Glk program that is running.
33  *
34  * This function is used to let the library know the name of the currently
35  * running Glk program, in case it wants to display this information somewhere
36  * &mdash; for example, in the title bar of a window. A typical use of this
37  * function would be:
38  * |[ garglk_set_program_name("SuperGlkFrotz 0.1"); ]|
39  */
40 void 
41 garglk_set_program_name(const char *name)
42 {
43         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
44         glk_data->program_name = g_strdup(name);
45         g_object_notify(G_OBJECT(glk_data->self), "program-name");
46 }
47
48 /**
49  * garglk_set_program_info:
50  * @info: Information about the Glk program that is running.
51  *
52  * This function is used to provide the library with additional information
53  * about the currently running Glk program, in case it wants to display this
54  * information somewhere &mdash; for example, in an About box. A typical use of
55  * this function would be:
56  * |[ 
57  * garglk_set_program_info("SuperGlkFrotz, version 0.1\n"
58  *     "Original Frotz by Stefan Jokisch\n"
59  *     "Unix port by Jim Dunleavy and David Griffith\n"
60  *     "Glk port by Tor Andersson\n"
61  *     "Animation, networking, and evil AI by Sven Metcalfe");
62  * ]|
63  */
64 void 
65 garglk_set_program_info(const char *info)
66 {
67         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
68         glk_data->program_info = g_strdup(info);
69         g_object_notify(G_OBJECT(glk_data->self), "program-info");
70 }
71
72 /**
73  * garglk_set_story_name:
74  * @name: Name of the story that the Glk program is currently interpreting.
75  *
76  * If the Glk program running is an interactive fiction interpreter, then this
77  * function can be used to let the library know the name of the story currently
78  * loaded in the interpreter, in case it wants to display this information
79  * anywhere &mdash; for example, in the title bar of a window. A typical use of
80  * this function would be:
81  * |[ garglk_set_story_name("Lighan Ses Lion, el Zarf"); ]|
82  */
83 void 
84 garglk_set_story_name(const char *name)
85 {
86         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
87         glk_data->story_name = g_strdup(name);
88         g_object_notify(G_OBJECT(glk_data->self), "story-name");
89 }
90
91 /**
92  * garglk_set_line_terminators:
93  * @win: A window.
94  * @keycodes: An array of <code>keycode_</code> constants.
95  * @numkeycodes: The length of @keycodes.
96  *
97  * Amends the current line input request of @win to include terminating key
98  * codes. Any of the specified key codes will terminate the line input request 
99  * (without printing a newline). 
100  *
101  * Usually, in the event structure returned from a line input request, @val2 is
102  * zero, but if garglk_set_line_terminators() has been called during that input
103  * request, @val2 will be filled in with the key code that terminated the input
104  * request.
105  *
106  * This function only applies to one input request; any subsequent line input
107  * requests on that window are treated normally.
108  *
109  * If @numkeycodes is zero, then any previous call to 
110  * garglk_set_line_terminators() is cancelled and the input request is treated
111  * normally.
112  *
113  * <warning><para>This function is not currently implemented.</para></warning>
114  */
115 void 
116 garglk_set_line_terminators(winid_t win, const glui32 *keycodes, glui32 numkeycodes)
117 {
118         VALID_WINDOW(win, return);
119         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
120         
121         if(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE) {
122                 ILLEGAL(_("Tried to set the line terminators on a window without a line input request."));
123                 return;
124         }
125
126         WARNING(_("Not implemented"));
127 }
128
129 /**
130  * garglk_unput_string:
131  * @str: a null-terminated string.
132  *
133  * Removes @str from the end of the current stream, if indeed it is there. The
134  * stream's write count is decreased accordingly, and the stream's echo stream
135  * is also modified, if it has one.
136  *
137  * <warning><para>This function is not currently implemented.</para></warning>
138  */
139 void 
140 garglk_unput_string(char *str)
141 {
142         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
143         g_return_if_fail(glk_data->current_stream != NULL);
144
145         WARNING(_("Not implemented"));
146 }
147
148 /**
149  * garglk_unput_string_uni:
150  * @str: a zero-terminated array of Unicode code points.
151  *
152  * Like garglk_unput_string(), but for Unicode streams.
153  *
154  * <warning><para>This function is not currently implemented.</para></warning>
155  */
156 void 
157 garglk_unput_string_uni(glui32 *str)
158 {
159         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
160         g_return_if_fail(glk_data->current_stream != NULL);
161         
162         WARNING(_("Not implemented"));
163 }
164
165 /* TODO document */
166 void
167 garglk_set_zcolors_stream(strid_t str, glui32 fg, glui32 bg)
168 {
169 #ifdef DEBUG_STYLES
170         g_printf("garglk_set_zcolors_stream(str->rock=%d, fg=%08X, bg=%08X)\n", str->rock, fg, bg);
171 #endif
172
173         VALID_STREAM(str, return);
174         g_return_if_fail(str->window != NULL);
175
176         winid_t window = str->window;
177
178         gdk_threads_enter();
179
180         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(window->widget) );
181         GtkTextTagTable *tags = gtk_text_buffer_get_tag_table(buffer);
182         GdkColor fore, back;
183         GdkColor *fore_pointer = NULL;
184         GdkColor *back_pointer = NULL;
185         gchar *fore_name;
186         gchar *back_name;
187
188         switch(fg) {
189         case zcolor_Transparent:
190         case zcolor_Cursor:
191                 WARNING(_("zcolor_Transparent, zcolor_Cursor not implemented"));
192                 // Fallthrough to default
193         case zcolor_Default:
194                 fore_name = g_strdup("default");
195                 break;
196         case zcolor_Current:
197         {
198                 if(window->zcolor) {
199                         // Get the current foreground color
200                         GdkColor *current_color;
201                         g_object_get(window->zcolor, "foreground-gdk", &current_color, NULL);
202                         fore_name = gdkcolor_to_hex(current_color);
203
204                         // Copy the color and use it
205                         fore.red = current_color->red;
206                         fore.green = current_color->green;
207                         fore.blue = current_color->blue;
208                         fore_pointer = &fore;
209                 } else {
210                         fore_name = g_strdup("default");
211                 }
212                 break;
213         }
214         default:
215                 glkcolor_to_gdkcolor(fg, &fore);
216                 fore_pointer = &fore;
217                 fore_name = glkcolor_to_hex(fg);
218         }
219
220         switch(bg) {
221         case zcolor_Transparent:
222         case zcolor_Cursor:
223                 WARNING(_("zcolor_Transparent, zcolor_Cursor not implemented"));
224                 // Fallthrough to default
225         case zcolor_Default:
226                 back_name = g_strdup("default");
227                 break;
228         case zcolor_Current:
229         {
230                 if(window->zcolor) {
231                         // Get the current background color
232                         GdkColor *current_color;
233                         g_object_get(window->zcolor, "background-gdk", &current_color, NULL);
234                         back_name = gdkcolor_to_hex(current_color);
235
236                         // Copy the color and use it
237                         back.red = current_color->red;
238                         back.green = current_color->green;
239                         back.blue = current_color->blue;
240                         back_pointer = &back;
241                 } else {
242                         back_name = g_strdup("default");
243                 }
244                 break;
245         }
246         default:
247                 glkcolor_to_gdkcolor(bg, &back);
248                 back_pointer = &back;
249                 back_name = glkcolor_to_hex(bg);
250         }
251
252         if(fore_pointer == NULL && back_pointer == NULL) {
253                 // NULL value means to ignore the zcolor property altogether
254                 window->zcolor = NULL;
255         } else {
256                 char *name = g_strdup_printf("zcolor:#%s/#%s", fore_name, back_name);
257                 g_free(fore_name);
258                 g_free(back_name);
259
260                 // See if we have used this color combination before
261                 GtkTextTag *tag = gtk_text_tag_table_lookup(tags, name);
262
263                 if(tag == NULL) {
264                         // Create a new texttag with the specified colors
265                         tag = gtk_text_buffer_create_tag(
266                                 buffer,
267                                 name,
268                                 "foreground-gdk", fore_pointer,
269                                 "foreground-set", fore_pointer != NULL,
270                                 "background-gdk", back_pointer,
271                                 "background-set", back_pointer != NULL,
272                                 NULL
273                         );
274                 }
275
276                 // From now on, text will be drawn in the specified colors
277                 window->zcolor = tag;
278
279                 // Update the reversed version if necessary
280                 if(str->window->zcolor_reversed) {
281                         gint reversed = GPOINTER_TO_INT( g_object_get_data( G_OBJECT(str->window->zcolor_reversed), "reverse-color" ) );
282
283                         gdk_threads_leave();
284                         garglk_set_reversevideo_stream(str, reversed != 0);
285                         gdk_threads_enter();
286                 }
287         
288         }
289
290         gdk_threads_leave();
291 }
292
293 /**
294  * garglk_set_zcolors:
295  * @fg: one of the <code>zcolor_</code> constants.
296  * @bg: one of the <code>zcolor_</code> constants.
297  *
298  * Glk works with styles, not specific colors. This is not quite compatible with
299  * the Z-machine, so this Glk extension implements Z-machine style colors.
300  *
301  * This function changes the foreground color of the current stream to @fg and 
302  * the background color to @bg.
303  *
304  * <warning><para>This function is not currently implemented.</para></warning>
305  */
306 void 
307 garglk_set_zcolors(glui32 fg, glui32 bg)
308 {
309         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
310         g_return_if_fail(glk_data->current_stream != NULL);
311
312         garglk_set_zcolors_stream(glk_data->current_stream, fg, bg);
313 }
314
315 /* TODO document */
316 void
317 garglk_set_reversevideo_stream(strid_t str, glui32 reverse)
318 {
319 #ifdef DEBUG_STYLES
320         g_printf("garglk_set_reversevideo_stream(str->rock=%d, reverse=%d)\n", str->rock, reverse);
321 #endif
322
323         VALID_STREAM(str, return);
324         g_return_if_fail(str->window != NULL);
325         g_return_if_fail(str->window->type != wintype_TextBuffer || str->window->type != wintype_TextGrid);
326
327         // Determine the current colors
328         
329         // If all fails, use black/white
330         // FIXME: Use system theme here
331         GdkColor foreground, background;
332         gdk_color_parse("black", &foreground);
333         gdk_color_parse("white", &background);
334         GdkColor *current_foreground = &foreground;
335         GdkColor *current_background = &background;
336
337         gdk_threads_enter();
338
339         style_stream_colors(str, &current_foreground, &current_background);
340
341         if(reverse) {
342                 GdkColor *temp = current_foreground;
343                 current_foreground = current_background;
344                 current_background = temp;
345         }
346
347         // Name the color
348         gchar *name = g_strdup_printf(
349                 "zcolor:#%04X%04X%04X/#%04X%04X%04X",
350                 current_foreground->red,
351                 current_foreground->green,
352                 current_foreground->blue,
353                 current_background->red,
354                 current_background->green,
355                 current_background->blue
356         );
357
358         // Create a tag for the new colors if it doesn't exist yet
359         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(str->window->widget) ); 
360         GtkTextTagTable *tags = gtk_text_buffer_get_tag_table(buffer);
361         GtkTextTag *tag = gtk_text_tag_table_lookup(tags, name);
362         if(tag == NULL) {
363                 tag = gtk_text_buffer_create_tag(
364                         buffer,
365                         name,
366                         "foreground-gdk", current_foreground,
367                         "foreground-set", TRUE,
368                         "background-gdk", current_background,
369                         "background-set", TRUE,
370                         NULL
371                 );
372                 g_object_set_data( G_OBJECT(tag), "reverse-color", GINT_TO_POINTER(reverse) );
373         }
374
375         // From now on, text will be drawn in the specified colors
376         str->window->zcolor_reversed = tag;
377
378         // Update the background of the gtktextview to correspond with the current background color
379         if(current_background != NULL) {
380                 gtk_widget_modify_base(str->window->widget, GTK_STATE_NORMAL, current_background);
381         }
382
383         gdk_threads_leave();
384 }
385
386 /**
387  * garglk_set_reversevideo:
388  * @reverse: nonzero for reverse colors, zero for normal colors.
389  *
390  * If @reverse is not zero, uses the foreground color of the current stream as
391  * its background and vice versa. If @reverse is zero, changes the colors of the
392  * current stream back to normal.
393  */
394 void 
395 garglk_set_reversevideo(glui32 reverse)
396 {
397         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
398         g_return_if_fail(glk_data->current_stream != NULL);
399         g_return_if_fail(glk_data->current_stream->window != NULL);
400
401         garglk_set_reversevideo_stream(glk_data->current_stream, reverse);
402 }