#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.
*/
*/
struct font {
+#ifdef USE_PANGO
+ PangoFontDescription *desc;
+#else
GdkFont *font;
+#endif
int type;
int size;
};
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)
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.
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.
*/
}
/*
- * 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)
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 ;
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);
}
}
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;
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;
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);
}
{
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);
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();
}
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();
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;
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;
void init_paste()
{
+ unsigned char empty[] = { 0 };
+
if (paste_initialised)
return;
* 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. */
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)
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;
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;
}