From 6a5f37b58f4e1b7ff1063864eded5661b080b2e5 Mon Sep 17 00:00:00 2001 From: rodin Date: Thu, 28 Jan 2010 23:41:06 +0000 Subject: [PATCH] Beginning work on graphic windows. Hopelessly broken still.... --- libchimara/Makefile.am | 4 +- libchimara/chimara-glk-private.h | 5 + libchimara/chimara-glk.c | 8 ++ libchimara/gestalt.c | 12 +- libchimara/gi_blorb.c | 2 - libchimara/graphics.c | 195 +++++++++++++++++++++++++++++++ libchimara/graphics.h | 19 +++ libchimara/hyperlink.c | 3 - libchimara/image.c | 40 ------- libchimara/input.c | 2 +- libchimara/mouse.c | 16 ++- libchimara/resource.c | 78 +++++++++++++ libchimara/resource.h | 3 + libchimara/window.c | 50 ++++++++ libchimara/window.h | 4 + 15 files changed, 387 insertions(+), 54 deletions(-) create mode 100644 libchimara/graphics.c create mode 100644 libchimara/graphics.h delete mode 100644 libchimara/image.c diff --git a/libchimara/Makefile.am b/libchimara/Makefile.am index 69c5922..7283ed2 100644 --- a/libchimara/Makefile.am +++ b/libchimara/Makefile.am @@ -16,13 +16,13 @@ libchimara_la_SOURCES = \ 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 \ @@ -33,7 +33,7 @@ libchimara_la_SOURCES = \ 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"\" \ diff --git a/libchimara/chimara-glk-private.h b/libchimara/chimara-glk-private.h index 903d353..99e8589 100644 --- a/libchimara/chimara-glk-private.h +++ b/libchimara/chimara-glk-private.h @@ -64,6 +64,11 @@ struct _ChimaraGlkPrivate { /* 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 */ diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index 8d1e655..4f1d391 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -107,6 +107,9 @@ chimara_glk_init(ChimaraGlk *self) 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; @@ -215,6 +218,11 @@ chimara_glk_finalize(GObject *object) 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); diff --git a/libchimara/gestalt.c b/libchimara/gestalt.c index a9412c3..a9128ac 100644 --- a/libchimara/gestalt.c +++ b/libchimara/gestalt.c @@ -115,15 +115,21 @@ glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) /* 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; diff --git a/libchimara/gi_blorb.c b/libchimara/gi_blorb.c index a37c8a8..cdee5f7 100644 --- a/libchimara/gi_blorb.c +++ b/libchimara/gi_blorb.c @@ -597,5 +597,3 @@ static void giblorb_free(void *ptr) { g_free(ptr); } - - diff --git a/libchimara/graphics.c b/libchimara/graphics.c new file mode 100644 index 0000000..48b3a12 --- /dev/null +++ b/libchimara/graphics.c @@ -0,0 +1,195 @@ +#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) +{ +} diff --git a/libchimara/graphics.h b/libchimara/graphics.h new file mode 100644 index 0000000..cf81d59 --- /dev/null +++ b/libchimara/graphics.h @@ -0,0 +1,19 @@ +#ifndef GRAPHICS_H +#define GRAPHICS_H + +#include +#include + +#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 diff --git a/libchimara/hyperlink.c b/libchimara/hyperlink.c index 6a0426a..aa9f426 100644 --- a/libchimara/hyperlink.c +++ b/libchimara/hyperlink.c @@ -83,8 +83,6 @@ glk_set_hyperlink_stream(strid_t str, glui32 linkval) 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); @@ -100,7 +98,6 @@ hyperlink_unblock_event_handler(gpointer key, gpointer value, gpointer user_data { 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 */ diff --git a/libchimara/image.c b/libchimara/image.c deleted file mode 100644 index 9a902aa..0000000 --- a/libchimara/image.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include - -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) -{ -} diff --git a/libchimara/input.c b/libchimara/input.c index 86a9ff5..2730e23 100644 --- a/libchimara/input.c +++ b/libchimara/input.c @@ -436,7 +436,7 @@ on_char_input_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win /* 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; } diff --git a/libchimara/mouse.c b/libchimara/mouse.c index 98d522f..3fd464f 100644 --- a/libchimara/mouse.c +++ b/libchimara/mouse.c @@ -6,7 +6,7 @@ glk_request_mouse_event(winid_t win) { 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); } @@ -16,7 +16,7 @@ glk_cancel_mouse_event(winid_t win) { 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); } @@ -27,7 +27,17 @@ on_window_button_press(GtkWidget *widget, GdkEventButton *event, winid_t win) 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; } diff --git a/libchimara/resource.c b/libchimara/resource.c index b3c98ec..9c40723 100644 --- a/libchimara/resource.c +++ b/libchimara/resource.c @@ -37,6 +37,8 @@ giblorb_set_resource_map(strid_t file) glk_data->resource_map = newmap; glk_data->resource_file = file; + + //giblorb_print_contents(newmap); return giblorb_err_None; } @@ -59,3 +61,79 @@ giblorb_get_resource_map() 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; inumresources; i++) { + giblorb_resdesc_t *resource = map->ressorted[i]; + printf("Resource #%d, chunknum: %d\n", resource->resnum, resource->chunknum); + } + + printf("\n-------\n"); + + for(i=0; inumresources; 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"; + } +} diff --git a/libchimara/resource.h b/libchimara/resource.h index 7db741e..a732541 100644 --- a/libchimara/resource.h +++ b/libchimara/resource.h @@ -7,4 +7,7 @@ #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 diff --git a/libchimara/window.c b/libchimara/window.c index b9c9c14..209cda6 100644 --- a/libchimara/window.c +++ b/libchimara/window.c @@ -558,6 +558,28 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, 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(); @@ -660,6 +682,7 @@ destroy_windows_below(winid_t win, stream_result_t *result) case wintype_Blank: case wintype_TextGrid: case wintype_TextBuffer: + case wintype_Graphics: gtk_widget_unparent(win->frame); break; @@ -914,6 +937,18 @@ glk_window_clear(winid_t win) 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); @@ -1079,6 +1114,21 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) *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: diff --git a/libchimara/window.h b/libchimara/window.h index 1d183a3..571d02a 100644 --- a/libchimara/window.h +++ b/libchimara/window.h @@ -10,6 +10,7 @@ #include "style.h" #include "hyperlink.h" #include "mouse.h" +#include "graphics.h" enum InputRequestType @@ -75,11 +76,14 @@ struct glk_window_struct 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 -- 2.30.2