fileref.c fileref.h \
garglk.c garglk.h \
gestalt.c \
+ graphics.c graphics.h \
gi_blorb.c gi_blorb.h \
gi_dispa.c gi_dispa.h \
glk.c glk.h \
glkstart.h \
glkunix.c glkunix.h \
hyperlink.c hyperlink.h \
- image.c \
init.c init.h \
input.c input.h \
magic.c magic.h \
strio.c strio.h \
style.c style.h \
timer.c timer.h \
- window.c window.h
+ window.c window.h
libchimara_la_CPPFLAGS = \
-DG_LOG_DOMAIN=\"Chimara\" \
-DLOCALEDIR=\""$(datadir)/locale"\" \
/* Input queues */
GAsyncQueue *char_input_queue;
GAsyncQueue *line_input_queue;
+ /* Resource loading locks */
+ GMutex *resource_lock;
+ GCond *resource_loaded;
+ GCond *resource_info_available;
+ guint32 resource_available;
/* *** Glk library data *** */
/* User-defined interrupt handler */
priv->ignore_next_arrange_event = FALSE;
priv->char_input_queue = g_async_queue_new();
priv->line_input_queue = g_async_queue_new();
+ priv->resource_lock = g_mutex_new();
+ priv->resource_loaded = g_cond_new();
+ priv->resource_info_available = g_cond_new();
/* Should be g_async_queue_new_full(g_free); but only in GTK >= 2.16 */
priv->interrupt_handler = NULL;
priv->root_window = NULL;
g_mutex_unlock(priv->arrange_lock);
g_mutex_free(priv->arrange_lock);
priv->arrange_lock = NULL;
+ g_mutex_lock(priv->resource_lock);
+ g_cond_free(priv->resource_loaded);
+ g_cond_free(priv->resource_info_available);
+ g_mutex_unlock(priv->resource_lock);
+ g_mutex_free(priv->resource_lock);
/* Unref input queues (this should destroy them since any Glk thread has stopped by now */
g_async_queue_unref(priv->char_input_queue);
g_async_queue_unref(priv->line_input_queue);
/* Mouse support present in textgrids */
case gestalt_MouseInput:
return val == wintype_TextGrid;
-
- /* Unsupported capabilities */
+
case gestalt_Graphics:
+ return 1;
+
case gestalt_DrawImage:
+ return val == wintype_Graphics;
+
+ case gestalt_GraphicsTransparency:
+ return 1;
+
+ /* Unsupported capabilities */
case gestalt_Sound:
case gestalt_SoundVolume:
case gestalt_SoundNotify:
case gestalt_SoundMusic:
- case gestalt_GraphicsTransparency:
/* Selector not supported */
default:
return 0;
--- /dev/null
+#include "graphics.h"
+#include "chimara-glk-private.h"
+#include "magic.h"
+
+#define BUFFER_SIZE (1024)
+
+extern GPrivate *glk_data_key;
+void on_size_prepared(GdkPixbufLoader *loader, gint width, gint height, struct image_info *info);
+
+static gboolean size_determined;
+
+glui32
+glk_image_get_info(glui32 image, glui32 *width, glui32 *height)
+{
+ ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
+ giblorb_result_t res;
+ giblorb_err_t blorb_error = 0;
+ GError *pixbuf_error = NULL;
+ struct image_info *info = g_new0(struct image_info, 1);
+ info->resource_number = image;
+ guchar *buffer;
+
+ //printf("glk_image_get_info(%d)\n", image);
+
+ /* Lookup the proper resource */
+ blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
+ if(blorb_error != giblorb_err_None) {
+ WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
+ return FALSE;
+ }
+
+ if(width == NULL && height == NULL) {
+ /* No size requested, don't bother loading the image */
+ giblorb_unload_chunk(glk_data->resource_map, image);
+ return TRUE;
+ }
+
+ /* Load the resource */
+ GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+ g_signal_connect( loader, "size-prepared", G_CALLBACK(on_size_prepared), info );
+ glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
+ buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
+
+ guint32 total_read = 0;
+ size_determined = FALSE;
+ while(total_read < res.length && !size_determined) {
+ guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
+
+ if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
+ WARNING_S("Cannot read image", pixbuf_error->message);
+ giblorb_unload_chunk(glk_data->resource_map, image);
+ gdk_pixbuf_loader_close(loader, &pixbuf_error);
+ g_free(buffer);
+ return FALSE;
+ }
+
+ total_read += num_read;
+ }
+ giblorb_unload_chunk(glk_data->resource_map, image);
+ gdk_pixbuf_loader_close(loader, &pixbuf_error);
+ g_free(buffer);
+
+ /* Determine the image dimensions */
+ if(!size_determined) {
+ WARNING("Cannot read image size: file data trimmed?");
+ g_free(info);
+ return FALSE;
+ }
+
+ if(width != NULL)
+ *width = info->width;
+ if(height != NULL)
+ *height =info->height;
+ g_free(info);
+
+ //printf("size loaded: %d x %d\n", (int) *width, (int) *height);
+
+ return TRUE;
+}
+
+void
+on_size_prepared(GdkPixbufLoader *loader, gint width, gint height, struct image_info *info)
+{
+ info->width = width;
+ info->height = height;
+ size_determined = TRUE;
+}
+
+void
+on_graphics_size_allocate(GtkWidget *widget, GtkAllocation *allocation, winid_t *win)
+{
+ printf("size allocated: %dx%d\n", allocation->width, allocation->height);
+}
+
+glui32
+glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2) {
+ VALID_WINDOW(win, return FALSE);
+ g_return_val_if_fail(win->type == wintype_Graphics, FALSE);
+ printf("Drawing image %d\n", (int)image);
+
+ ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
+ giblorb_result_t res;
+ giblorb_err_t blorb_error = 0;
+ GError *pixbuf_error = NULL;
+ struct image_info *info = g_new0(struct image_info, 1);
+ info->resource_number = image;
+ guchar *buffer;
+ GdkPixmap *canvas;
+
+ //printf("glk_image_get_info(%d)\n", image);
+
+ /* Lookup the proper resource */
+ blorb_error = giblorb_load_resource(glk_data->resource_map, giblorb_method_FilePos, &res, giblorb_ID_Pict, image);
+ if(blorb_error != giblorb_err_None) {
+ WARNING_S( "Error loading resource", giblorb_get_error_message(blorb_error) );
+ return FALSE;
+ }
+
+ /* Load the resource */
+ GdkPixbufLoader *loader = gdk_pixbuf_loader_new();
+ glk_stream_set_position(glk_data->resource_file, res.data.startpos, seekmode_Start);
+ buffer = g_malloc( BUFFER_SIZE * sizeof(guchar) );
+
+ guint32 total_read = 0;
+ while(total_read < res.length) {
+ guint32 num_read = glk_get_buffer_stream(glk_data->resource_file, (char *) buffer, BUFFER_SIZE);
+
+ if( !gdk_pixbuf_loader_write(loader, buffer, MIN(BUFFER_SIZE, num_read), &pixbuf_error) ) {
+ WARNING_S("Cannot read image", pixbuf_error->message);
+ giblorb_unload_chunk(glk_data->resource_map, image);
+ gdk_pixbuf_loader_close(loader, &pixbuf_error);
+ g_free(buffer);
+ return FALSE;
+ }
+
+ total_read += num_read;
+ }
+ giblorb_unload_chunk(glk_data->resource_map, image);
+ g_free(buffer);
+
+ gtk_image_get_pixmap( GTK_IMAGE(win->widget), &canvas, NULL );
+ if(canvas == NULL) {
+ WARNING("Could not get pixmap");
+ gdk_pixbuf_loader_close(loader, &pixbuf_error);
+ return FALSE;
+ }
+
+ GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+ if(pixbuf == NULL) {
+ WARNING("Could not read image");
+ gdk_pixbuf_loader_close(loader, &pixbuf_error);
+ return FALSE;
+ }
+
+ gdk_draw_pixbuf( GDK_DRAWABLE(canvas), NULL, pixbuf, 0, 0, val1, val2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0 );
+ gdk_pixbuf_loader_close(loader, &pixbuf_error);
+
+ /* Update the screen */
+ gtk_widget_queue_draw(win->widget);
+ return TRUE;
+}
+
+glui32
+glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui32 width, glui32 height)
+{
+ return TRUE;
+}
+
+void
+glk_window_set_background_color(winid_t win, glui32 color) {
+ win->background_color = color;
+}
+
+void
+glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top, glui32 width, glui32 height)
+{
+ VALID_WINDOW(win, return);
+ g_return_if_fail(win->type == wintype_Graphics);
+
+ GdkPixmap *map;
+ gtk_image_get_pixmap( GTK_IMAGE(win->widget), &map, NULL );
+ gdk_draw_rectangle( GDK_DRAWABLE(map), win->widget->style->white_gc, TRUE, left, top, width, height);
+ gtk_widget_queue_draw(win->widget);
+}
+
+void
+glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height)
+{
+ printf("erasing rect: %d %d %d %d\n", left, top, width, height);
+ glk_window_fill_rect(win, win->background_color, left, top, width, height);
+}
+
+void glk_window_flow_break(winid_t win)
+{
+}
--- /dev/null
+#ifndef GRAPHICS_H
+#define GRAPHICS_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "glk.h"
+#include "gi_blorb.h"
+#include "resource.h"
+#include "window.h"
+
+struct image_info {
+ guint32 resource_number;
+ gint width, height;
+};
+
+void on_graphics_size_allocate(GtkWidget *widget, GtkAllocation *allocation, winid_t *win);
+
+#endif
GtkTextTagTable *tags = gtk_text_buffer_get_tag_table(textbuffer);
gtk_text_tag_table_add(tags, new_hyperlink->tag);
- printf("inserting link %d\n", linkval);
-
gint *linkval_pointer = g_new0(gint, 1);
*linkval_pointer = linkval;
g_hash_table_insert(str->window->hyperlinks, linkval_pointer, new_hyperlink);
{
hyperlink_t *link = (hyperlink_t *) value;
g_signal_handler_unblock(link->tag, link->event_handler);
- printf("unblocking link %d\n", link->value);
}
/* Internal function used to iterate over all the hyperlinks, blocking the event handler */
+++ /dev/null
-#include <glib.h>
-#include <libchimara/glk.h>
-
-glui32
-glk_image_draw(winid_t win, glui32 image, glsi32 val1, glsi32 val2)
-{
- return FALSE;
-}
-
-glui32
-glk_image_draw_scaled(winid_t win, glui32 image, glsi32 val1, glsi32 val2, glui32 width, glui32 height)
-{
- return FALSE;
-}
-
-glui32
-glk_image_get_info(glui32 image, glui32 *width, glui32 *height)
-{
- return FALSE;
-}
-
-void
-glk_window_flow_break(winid_t win)
-{
-}
-
-void
-glk_window_erase_rect(winid_t win, glsi32 left, glsi32 top, glui32 width, glui32 height)
-{
-}
-
-void
-glk_window_fill_rect(winid_t win, glui32 color, glsi32 left, glsi32 top, glui32 width, glui32 height)
-{
-}
-
-void
-glk_window_set_background_color(winid_t win, glui32 color)
-{
-}
/* Only one keypress will be handled */
win->input_request_type = INPUT_REQUEST_NONE;
- g_signal_handler_block( win->widget, win->char_input_keypress_handler );
+ g_signal_handler_block(win->widget, win->char_input_keypress_handler);
return TRUE;
}
{
VALID_WINDOW(win, return);
g_return_if_fail(win != NULL);
- g_return_if_fail(win->type == wintype_TextGrid);
+ g_return_if_fail(win->type == wintype_TextGrid || win->type == wintype_Graphics);
g_signal_handler_unblock(win->widget, win->button_press_event_handler);
}
{
VALID_WINDOW(win, return);
g_return_if_fail(win != NULL);
- g_return_if_fail(win->type == wintype_TextGrid);
+ g_return_if_fail(win->type == wintype_TextGrid || win->type == wintype_Graphics);
g_signal_handler_block(win->widget, win->button_press_event_handler);
}
ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
g_assert(glk);
- event_throw(glk, evtype_MouseInput, win, event->x/win->unit_width, event->y/win->unit_height);
+ switch(win->type)
+ {
+ case wintype_TextGrid:
+ event_throw(glk, evtype_MouseInput, win, event->x/win->unit_width, event->y/win->unit_height);
+ break;
+ case wintype_Graphics:
+ event_throw(glk, evtype_MouseInput, win, event->x, event->y);
+ break;
+ default:
+ ILLEGAL_PARAM("Unknown window type: %u", win->type);
+ }
return TRUE;
}
glk_data->resource_map = newmap;
glk_data->resource_file = file;
+
+ //giblorb_print_contents(newmap);
return giblorb_err_None;
}
return glk_data->resource_map;
}
+
+/* giblorb_chunkdesc_t: Describes one chunk of the Blorb file. */
+typedef struct giblorb_chunkdesc_struct {
+ glui32 type;
+ glui32 len;
+ glui32 startpos; /* start of chunk header */
+ glui32 datpos; /* start of data (either startpos or startpos+8) */
+
+ void *ptr; /* pointer to malloc'd data, if loaded */
+ int auxdatnum; /* entry in the auxsound/auxpict array; -1 if none.
+ This only applies to chunks that represent resources; */
+
+} giblorb_chunkdesc_t;
+
+/* giblorb_resdesc_t: Describes one resource in the Blorb file. */
+typedef struct giblorb_resdesc_struct {
+ glui32 usage;
+ glui32 resnum;
+ glui32 chunknum;
+} giblorb_resdesc_t;
+
+/* giblorb_map_t: Holds the complete description of an open Blorb file. */
+struct giblorb_map_struct {
+ glui32 inited; /* holds giblorb_Inited_Magic if the map structure is
+ valid */
+ strid_t file;
+
+ int numchunks;
+ giblorb_chunkdesc_t *chunks; /* list of chunk descriptors */
+
+ int numresources;
+ giblorb_resdesc_t *resources; /* list of resource descriptors */
+ giblorb_resdesc_t **ressorted; /* list of pointers to descriptors
+ in map->resources -- sorted by usage and resource number. */
+};
+
+void
+giblorb_print_contents(giblorb_map_t *map)
+{
+ int i;
+ for(i=0; i<map->numresources; i++) {
+ giblorb_resdesc_t *resource = map->ressorted[i];
+ printf("Resource #%d, chunknum: %d\n", resource->resnum, resource->chunknum);
+ }
+
+ printf("\n-------\n");
+
+ for(i=0; i<map->numresources; i++) {
+ giblorb_chunkdesc_t chunk = map->chunks[i];
+ printf("Chunk #%d, type: %d\n", i, chunk.type);
+ }
+}
+
+gchar*
+giblorb_get_error_message(giblorb_err_t err)
+{
+ switch(err)
+ {
+ case giblorb_err_None:
+ return "not BLORB's fault";
+ case giblorb_err_CompileTime:
+ return "BLORB compile time error";
+ case giblorb_err_Alloc:
+ return "memory allocation failed";
+ case giblorb_err_Read:
+ return "error during reading of file";
+ case giblorb_err_NotAMap:
+ return "invalid resource map supplied";
+ case giblorb_err_Format:
+ return "invalid format of resource";
+ case giblorb_err_NotFound:
+ return "resource not found";
+ default:
+ return "Unknown error code";
+ }
+}
#include "chimara-glk-private.h"
#include "magic.h"
+void giblorb_print_contents(giblorb_map_t *map);
+gchar* giblorb_get_error_message(giblorb_err_t err);
+
#endif
gtk_text_buffer_create_mark(textbuffer, "input_position", &end, TRUE);
}
break;
+
+ case wintype_Graphics:
+ {
+ // TODO: Find real size
+ GdkPixmap *newmap = gdk_pixmap_new(NULL, 800, 600, 24);
+ GtkWidget *image = gtk_image_new_from_pixmap(newmap, NULL);
+ g_object_unref(newmap);
+
+ gtk_widget_show(image);
+
+ win->widget = image;
+ win->frame = image;
+
+
+ /* Connect signal handlers */
+ win->button_press_event_handler = g_signal_connect(image, "button-press-event", G_CALLBACK(on_window_button_press), win);
+ g_signal_handler_block(image, win->button_press_event_handler);
+ win->shutdown_keypress_handler = g_signal_connect(image, "key-press-event", G_CALLBACK(on_shutdown_key_press_event), win);
+ g_signal_handler_block(image, win->shutdown_keypress_handler);
+ win->size_allocate_handler = g_signal_connect(image, "size-allocate", G_CALLBACK(on_graphics_size_allocate), win);
+ }
+ break;
default:
gdk_threads_leave();
case wintype_Blank:
case wintype_TextGrid:
case wintype_TextBuffer:
+ case wintype_Graphics:
gtk_widget_unparent(win->frame);
break;
gdk_threads_leave();
}
break;
+
+ case wintype_Graphics:
+ {
+ /* Wait for the window's size to be updated */
+ 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);
+
+ glk_window_erase_rect(win, 0, 0, win->widget->allocation.width, win->widget->allocation.height);
+ }
+ break;
default:
ILLEGAL_PARAM("Unknown window type: %d", win->type);
*heightptr = (glui32)(win->widget->allocation.height / win->unit_height);
gdk_threads_leave();
+ break;
+
+ case wintype_Graphics:
+ 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);
+
+ gdk_threads_enter();
+ if(widthptr != NULL)
+ *widthptr = (glui32)(win->widget->allocation.width);
+ if(heightptr != NULL)
+ *heightptr = (glui32)(win->widget->allocation.height);
+ gdk_threads_leave();
+
break;
default:
#include "style.h"
#include "hyperlink.h"
#include "mouse.h"
+#include "graphics.h"
enum InputRequestType
gulong tag_event_handler;
gulong shutdown_keypress_handler;
gulong button_press_event_handler;
+ gulong size_allocate_handler;
/* Window buffer */
GString *buffer;
/* Hyperlinks */
GHashTable *hyperlinks;
struct hyperlink *current_hyperlink;
+ /* Graphics */
+ glui32 background_color;
};
#endif