Fix GTK casts to restore correct compilation on GTK 2.0 after r6022.
[sgt/puzzles] / gtk.c
diff --git a/gtk.c b/gtk.c
index 7a92d66..05d7ad4 100644 (file)
--- a/gtk.c
+++ b/gtk.c
 
 #include "puzzles.h"
 
+#if GTK_CHECK_VERSION(2,0,0)
+#define USE_PANGO
+#endif
+
+#ifdef DEBUGGING
+static FILE *debug_fp = NULL;
+
+void dputs(char *buf)
+{
+    if (!debug_fp) {
+        debug_fp = fopen("debug.log", "w");
+    }
+
+    fputs(buf, stderr);
+
+    if (debug_fp) {
+        fputs(buf, debug_fp);
+        fflush(debug_fp);
+    }
+}
+
+void debug_printf(char *fmt, ...)
+{
+    char buf[4096];
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsprintf(buf, fmt, ap);
+    dputs(buf);
+    va_end(ap);
+}
+#endif
+
 /* ----------------------------------------------------------------------
  * Error reporting functions used elsewhere.
  */
@@ -44,7 +77,11 @@ void fatal(char *fmt, ...)
  */
 
 struct font {
+#ifdef USE_PANGO
+    PangoFontDescription *desc;
+#else
     GdkFont *font;
+#endif
     int type;
     int size;
 };
@@ -79,6 +116,8 @@ struct frontend {
     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 +209,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,24 +247,54 @@ 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 +327,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)
@@ -293,6 +363,66 @@ void draw_polygon(frontend *fe, int *coords, int npoints,
     sfree(points);
 }
 
+void draw_circle(frontend *fe, int cx, int cy, int radius,
+                 int fill, int colour)
+{
+    gdk_gc_set_foreground(fe->gc, &fe->colours[colour]);
+    gdk_draw_arc(fe->pixmap, fe->gc, fill,
+                 cx - radius, cy - radius,
+                 2 * radius, 2 * radius, 0, 360 * 64);
+}
+
+struct blitter {
+    GdkPixmap *pixmap;
+    int w, h, x, y;
+};
+
+blitter *blitter_new(int w, int h)
+{
+    /*
+     * We can't create the pixmap right now, because fe->window
+     * might not yet exist. So we just cache w and h and create it
+     * during the firs call to blitter_save.
+     */
+    blitter *bl = snew(blitter);
+    bl->pixmap = NULL;
+    bl->w = w;
+    bl->h = h;
+    return bl;
+}
+
+void blitter_free(blitter *bl)
+{
+    if (bl->pixmap)
+        gdk_pixmap_unref(bl->pixmap);
+    sfree(bl);
+}
+
+void blitter_save(frontend *fe, blitter *bl, int x, int y)
+{
+    if (!bl->pixmap)
+        bl->pixmap = gdk_pixmap_new(fe->area->window, bl->w, bl->h, -1);
+    bl->x = x;
+    bl->y = y;
+    gdk_draw_pixmap(bl->pixmap,
+                    fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
+                    fe->pixmap,
+                    x, y, 0, 0, bl->w, bl->h);
+}
+
+void blitter_load(frontend *fe, blitter *bl, int x, int y)
+{
+    assert(bl->pixmap);
+    if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
+        x = bl->x;
+        y = bl->y;
+    }
+    gdk_draw_pixmap(fe->pixmap,
+                    fe->area->style->fg_gc[GTK_WIDGET_STATE(fe->area)],
+                    bl->pixmap,
+                    0, 0, x, y, bl->w, bl->h);
+}
+
 void draw_update(frontend *fe, int x, int y, int w, int h)
 {
     if (fe->bbox_l > x  ) fe->bbox_l = x  ;
@@ -311,7 +441,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 +517,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 +552,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 +568,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 +595,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);
@@ -816,6 +957,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 +994,15 @@ 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);
+    {
+        GtkRequisition req;
+        gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
+        gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
+    }
 }
 
 GdkAtom compound_text_atom, utf8_string_atom;
@@ -969,10 +1143,15 @@ 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);
+    {
+        GtkRequisition req;
+        gtk_widget_size_request(GTK_WIDGET(fe->window), &req);
+        gtk_window_resize(GTK_WINDOW(fe->window), req.width, req.height);
+    }
 }
 
 static void menu_about_event(GtkMenuItem *menuitem, gpointer data)
@@ -1201,12 +1380,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;
@@ -1246,6 +1425,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;
 }