X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/puzzles/blobdiff_plain/f5f4a1f13cefaf8b3637fd6e7a248b2babf675cb..5d8c6c556982ae44f786d14a6d641182ced15043:/gtk.c diff --git a/gtk.c b/gtk.c index deeb7a1..5f016ad 100644 --- a/gtk.c +++ b/gtk.c @@ -21,6 +21,10 @@ #include "puzzles.h" +#if GTK_CHECK_VERSION(2,0,0) +#define USE_PANGO +#endif + /* ---------------------------------------------------------------------- * Error reporting functions used elsewhere. */ @@ -44,7 +48,11 @@ void fatal(char *fmt, ...) */ struct font { +#ifdef USE_PANGO + PangoFontDescription *desc; +#else GdkFont *font; +#endif int type; int size; }; @@ -76,9 +84,11 @@ struct frontend { config_item *cfg; int cfg_which, cfgret; GtkWidget *cfgbox; - char *paste_data; + void *paste_data; int paste_data_len; char *laststatus; + int pw, ph; /* pixmap size (w, h are area size) */ + int ox, oy; /* offset of pixmap in drawing area */ }; void get_random_seed(void **randseed, int *randseedsize) @@ -170,7 +180,7 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, fe->fonts[i].type = fonttype; fe->fonts[i].size = fontsize; -#if GTK_CHECK_VERSION(2,0,0) +#ifdef USE_PANGO /* * Use Pango to find the closest match to the requested * font. @@ -208,25 +218,55 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, pango_font_description_set_size(fd, resolution * fontsize); } #endif - fe->fonts[i].font = gdk_font_from_description(fd); - pango_font_description_free(fd); + fe->fonts[i].desc = fd; } - if (!fe->fonts[i].font) +#else + /* + * In GTK 1.2, I don't know of any plausible way to + * pick a suitable font, so I'm just going to be + * tedious. + */ + fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ? + "fixed" : "variable"); #endif - /* - * In GTK 1.2, I don't know of any plausible way to - * pick a suitable font, so I'm just going to be - * tedious. - * - * This is also fallback code called if the Pango - * approach fails to find an appropriate font. - */ - fe->fonts[i].font = gdk_font_load(fonttype == FONT_FIXED ? - "fixed" : "variable"); + } /* + * Set the colour. + */ + gdk_gc_set_foreground(fe->gc, &fe->colours[colour]); + +#ifdef USE_PANGO + + { + PangoLayout *layout; + PangoRectangle rect; + + /* + * Create a layout. + */ + layout = pango_layout_new(gtk_widget_get_pango_context(fe->area)); + pango_layout_set_font_description(layout, fe->fonts[i].desc); + pango_layout_set_text(layout, text, strlen(text)); + pango_layout_get_pixel_extents(layout, NULL, &rect); + + if (align & ALIGN_VCENTRE) + rect.y -= rect.height / 2; + + if (align & ALIGN_HCENTRE) + rect.x -= rect.width / 2; + else if (align & ALIGN_HRIGHT) + rect.x -= rect.width; + + gdk_draw_layout(fe->pixmap, fe->gc, rect.x + x, rect.y + y, layout); + + g_object_unref(layout); + } + +#else + /* * Find string dimensions and process alignment. */ { @@ -258,10 +298,11 @@ void draw_text(frontend *fe, int x, int y, int fonttype, int fontsize, } /* - * Set colour and actually draw text. + * Actually draw the text. */ - gdk_gc_set_foreground(fe->gc, &fe->colours[colour]); gdk_draw_string(fe->pixmap, fe->fonts[i].font, fe->gc, x, y, text); +#endif + } void draw_rect(frontend *fe, int x, int y, int w, int h, int colour) @@ -311,7 +352,7 @@ void end_draw(frontend *fe) fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)], fe->pixmap, fe->bbox_l, fe->bbox_u, - fe->bbox_l, fe->bbox_u, + fe->ox + fe->bbox_l, fe->oy + fe->bbox_u, fe->bbox_r - fe->bbox_l, fe->bbox_d - fe->bbox_u); } } @@ -387,17 +428,18 @@ static gint button_event(GtkWidget *widget, GdkEventButton *event, if (event->button == 2 || (event->state & GDK_SHIFT_MASK)) button = MIDDLE_BUTTON; + else if (event->button == 3 || (event->state & GDK_MOD1_MASK)) + button = RIGHT_BUTTON; else if (event->button == 1) button = LEFT_BUTTON; - else if (event->button == 3) - button = RIGHT_BUTTON; else return FALSE; /* don't even know what button! */ if (event->type == GDK_BUTTON_RELEASE) button += LEFT_RELEASE - LEFT_BUTTON; - if (!midend_process_key(fe->me, event->x, event->y, button)) + if (!midend_process_key(fe->me, event->x - fe->ox, + event->y - fe->oy, button)) gtk_widget_destroy(fe->window); return TRUE; @@ -421,7 +463,8 @@ static gint motion_event(GtkWidget *widget, GdkEventMotion *event, else return FALSE; /* don't even know what button! */ - if (!midend_process_key(fe->me, event->x, event->y, button)) + if (!midend_process_key(fe->me, event->x - fe->ox, + event->y - fe->oy, button)) gtk_widget_destroy(fe->window); return TRUE; @@ -436,7 +479,7 @@ static gint expose_area(GtkWidget *widget, GdkEventExpose *event, gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], fe->pixmap, - event->area.x, event->area.y, + event->area.x - fe->ox, event->area.y - fe->oy, event->area.x, event->area.y, event->area.width, event->area.height); } @@ -463,15 +506,24 @@ static gint configure_area(GtkWidget *widget, { frontend *fe = (frontend *)data; GdkGC *gc; + int x, y; if (fe->pixmap) gdk_pixmap_unref(fe->pixmap); - fe->pixmap = gdk_pixmap_new(widget->window, fe->w, fe->h, -1); + x = fe->w = event->width; + y = fe->h = event->height; + midend_size(fe->me, &x, &y, TRUE); + fe->pw = x; + fe->ph = y; + fe->ox = (fe->w - fe->pw) / 2; + fe->oy = (fe->h - fe->ph) / 2; + + fe->pixmap = gdk_pixmap_new(widget->window, fe->pw, fe->ph, -1); gc = gdk_gc_new(fe->area->window); gdk_gc_set_foreground(gc, &fe->colours[0]); - gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->w, fe->h); + gdk_draw_rectangle(fe->pixmap, gc, 1, 0, 0, fe->pw, fe->ph); gdk_gc_unref(gc); midend_force_redraw(fe->me); @@ -566,7 +618,7 @@ void message_box(GtkWidget *parent, char *title, char *msg, int centre) GTK_SIGNAL_FUNC(win_key_press), ok); gtk_window_set_modal(GTK_WINDOW(window), TRUE); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent)); - //set_transient_window_pos(parent, window); + /* set_transient_window_pos(parent, window); */ gtk_widget_show(window); gtk_main(); } @@ -798,7 +850,7 @@ static int get_config(frontend *fe, int which) gtk_window_set_modal(GTK_WINDOW(fe->cfgbox), TRUE); gtk_window_set_transient_for(GTK_WINDOW(fe->cfgbox), GTK_WINDOW(fe->window)); - //set_transient_window_pos(fe->window, fe->cfgbox); + /* set_transient_window_pos(fe->window, fe->cfgbox); */ gtk_widget_show(fe->cfgbox); gtk_main(); @@ -816,6 +868,34 @@ static void menu_key_event(GtkMenuItem *menuitem, gpointer data) gtk_widget_destroy(fe->window); } +static void get_size(frontend *fe, int *px, int *py) +{ + int x, y; + + /* + * Currently I don't want to make the GTK port scale large + * puzzles to fit on the screen. This is because X does permit + * extremely large windows and many window managers provide a + * means of navigating round them, and the users I consulted + * before deciding said that they'd rather have enormous puzzle + * windows spanning multiple screen pages than have them + * shrunk. I could change my mind later or introduce + * configurability; this would be the place to do so, by + * replacing the initial values of x and y with the screen + * dimensions. + */ + x = INT_MAX; + y = INT_MAX; + midend_size(fe->me, &x, &y, FALSE); + *px = x; + *py = y; +} + +#if !GTK_CHECK_VERSION(2,0,0) +#define gtk_window_resize(win, x, y) \ + gdk_window_resize(GTK_WIDGET(win)->window, x, y) +#endif + static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) { frontend *fe = (frontend *)data; @@ -825,10 +905,11 @@ static void menu_preset_event(GtkMenuItem *menuitem, gpointer data) midend_set_params(fe->me, params); midend_new_game(fe->me); - midend_size(fe->me, &x, &y); - gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); + get_size(fe, &x, &y); fe->w = x; fe->h = y; + gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); + gtk_window_resize(GTK_WINDOW(fe->window), 1, 1); } GdkAtom compound_text_atom, utf8_string_atom; @@ -836,6 +917,8 @@ int paste_initialised = FALSE; void init_paste() { + unsigned char empty[] = { 0 }; + if (paste_initialised) return; @@ -849,21 +932,21 @@ void init_paste() * ICCCM, we must do this before we start using cut buffers. */ XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, "", 0); + XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0); XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, "", 0); + XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0); XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, "", 0); + XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0); XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, "", 0); + XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0); XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, "", 0); + XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0); XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, "", 0); + XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0); XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, "", 0); + XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0); XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), - XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, "", 0); + XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0); } /* Store data in a cut-buffer. */ @@ -967,10 +1050,11 @@ static void menu_config_event(GtkMenuItem *menuitem, gpointer data) return; midend_new_game(fe->me); - midend_size(fe->me, &x, &y); - gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); + get_size(fe, &x, &y); fe->w = x; fe->h = y; + gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); + gtk_window_resize(GTK_WINDOW(fe->window), 1, 1); } static void menu_about_event(GtkMenuItem *menuitem, gpointer data) @@ -1199,12 +1283,12 @@ static frontend *new_window(char *game_id, char **error) fe->statusbar = NULL; fe->area = gtk_drawing_area_new(); - midend_size(fe->me, &x, &y); + get_size(fe, &x, &y); gtk_drawing_area_size(GTK_DRAWING_AREA(fe->area), x, y); fe->w = x; fe->h = y; - gtk_box_pack_end(vbox, fe->area, FALSE, FALSE, 0); + gtk_box_pack_end(vbox, fe->area, TRUE, TRUE, 0); fe->pixmap = NULL; fe->fonts = NULL; @@ -1244,6 +1328,9 @@ static frontend *new_window(char *game_id, char **error) gtk_widget_show(fe->area); gtk_widget_show(fe->window); + gdk_window_set_background(fe->area->window, &fe->colours[0]); + gdk_window_set_background(fe->window->window, &fe->colours[0]); + return fe; }