#define TIMER_UPDATE_MS 33
+#define SPLIT_NUMERATOR 75
+#define SPLIT_DENOMINATOR 100
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
#define PROGNAME "rrace"
static const char *progname = PROGNAME;
static const struct option lopts[] = { LOPTS_INITIALIZER, {0} };
XmDrawingAreaCallbackStruct *cbs = cb_data;
XButtonEvent *b = &cbs->event->xbutton;
struct app_state *state = data;
- Dimension width, height;
switch (cbs->event->type) {
case ButtonPress:
if (b->state & Button3Mask)
break;
- XtVaGetValues(w, XmNwidth, &width,
- XmNheight, &height,
- (char *)NULL);
-
- do_input_move(state, b->x / (width / 5),
- b->y / (height / 5));
+ do_input_move(state, b->x / state->game_tile_sz,
+ b->y / state->game_tile_sz);
break;
case Button3:
set_view_goal(state, 1);
ui_show_about(&state, get_shell(w));
}
-static const XtActionsRec menu_actions[] = {
+static void proc_resize(Widget form, XEvent *e, String *argv, Cardinal *argc)
+{
+ Widget game = XtParent(state.game);
+ Widget goal = XtParent(state.goal);
+ Dimension w, h, gamesz, gameborder, goalsz, goalborder;
+ int x, y, gap;
+
+ XtVaGetValues(form, XmNwidth, &w, XmNheight, &h, (char *)NULL);
+ XtVaGetValues(game, XmNshadowThickness, &gameborder, (char *)NULL);
+ XtVaGetValues(goal, XmNshadowThickness, &goalborder,
+ XmNleftOffset, &gap,
+ (char *)NULL);
+
+ gamesz = MIN(h, w * SPLIT_NUMERATOR / SPLIT_DENOMINATOR);
+ state.game_tile_sz = (gamesz - 2*gameborder) / 5;
+ gamesz = 5*state.game_tile_sz + 2*gameborder;
+
+ goalsz = MIN(gamesz*3/5, w - gamesz - gap);
+ state.goal_tile_sz = (goalsz - 2*goalborder) / 3;
+ goalsz = 3*state.goal_tile_sz + 2*goalborder;
+
+ x = (w - gamesz - goalsz - gap) / 2;
+ if (x < 2) x = 0;
+
+ y = (h - gamesz) / 2;
+ if (y < 3) y = 0;
+
+ XtVaSetValues(game, XmNleftOffset, x, XmNtopOffset, y, (char *)NULL);
+ XtVaSetValues(game, XmNwidth, gamesz, XmNheight, gamesz, (char *)NULL);
+ XtVaSetValues(goal, XmNwidth, goalsz, XmNheight, goalsz, (char *)NULL);
+}
+
+static const XtActionsRec app_actions[] = {
{ "gameNew", proc_new_game },
{ "gameExit", proc_exit },
- { "helpAbout", proc_about }
+ { "helpAbout", proc_about },
+
+ { "ResizeGameArea", proc_resize }
};
static XtAppContext app_initialize(int argc, char **argv)
progname = argv[0];
shell = early_setup(&app, argc, argv);
- XtAppAddActions(app, (void *)menu_actions, XtNumber(menu_actions));
+ XtAppAddActions(app, (void *)app_actions, XtNumber(app_actions));
ui_initialize(&state, shell);
x11_initialize(&state, shell);
- XtAddCallback(state.game, XmNinputCallback, game_input, &state);
+ XtAddCallback(state.game, XmNinputCallback, game_input, &state);
/* Begin with the game in winning state */
game_reset(&state.board);
XtIntervalId render_tick;
XtWorkProcId render_proc;
- uint_fast32_t render_game_mask;
- uint_fast16_t render_goal_mask;
+
+ uint_least32_t render_game_mask;
+ uint_least16_t render_goal_mask;
+ uint_least16_t game_tile_sz, goal_tile_sz;
/* If true, the goal will be displayed over the main play area. */
int view_goal_on_game;
#include "xcounter.h"
#include "version.h"
-#define SPLIT_NUMERATOR 75
-#define SPLIT_DENOMINATOR 100
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
/* XXX generate this list? */
enum {
widgetMainWindow,
uint_least16_t label;
} mainmenu[] = { MAINMENU_INITIALIZER };
-static void
-ResizeGameArea(Widget form, XEvent *e, String *args, Cardinal *num_args)
-{
- Widget game = XtNameToWidget(form, &tree_strtab[gameArea]);
- Widget goal = XtNameToWidget(form, &tree_strtab[goalArea]);
- Dimension w, h, gamesz, gameborder, goalsz, goalborder;
- int x, y, gap;
-
- XtVaGetValues(form, XmNwidth, &w, XmNheight, &h, (char *)NULL);
- XtVaGetValues(game, XmNshadowThickness, &gameborder, (char *)NULL);
- XtVaGetValues(goal, XmNshadowThickness, &goalborder,
- XmNleftOffset, &gap,
- (char *)NULL);
-
- gamesz = MIN(h, w * SPLIT_NUMERATOR / SPLIT_DENOMINATOR);
- gamesz = 5*( (gamesz - 2*gameborder)/5 ) + 2*gameborder;
-
- goalsz = MIN(gamesz*3/5, w - gamesz - gap);
- goalsz = 3*( (goalsz - 2*goalborder)/3 ) + 2*goalborder;
-
- x = (w - gamesz - goalsz - gap) / 2;
- if (x < 2) x = 0;
-
- y = (h - gamesz) / 2;
- if (y < 3) y = 0;
-
- XtVaSetValues(game, XmNleftOffset, x, XmNtopOffset, y, (char *)NULL);
- XtVaSetValues(game, XmNwidth, gamesz, XmNheight, gamesz, (char *)NULL);
- XtVaSetValues(goal, XmNwidth, goalsz, XmNheight, goalsz, (char *)NULL);
-}
-
static char *timer_text(int_fast32_t ms, char *buf)
{
unsigned min, sec;
char xc_template[100];
assert(gamearea && goalarea && timer);
- XtVaSetValues(form, XmNfractionBase, SPLIT_DENOMINATOR, (char *)NULL);
state->game = XtNameToWidget(gamearea, &tree_strtab[gameCanvas]);
XtVaSetValues(gamearea, XmNleftAttachment, XmATTACH_FORM,
state->timer = xcounter_simple_init(timer, timer_text(0, xc_template));
ui_timer_update(state, -1);
- resize_rec.string = "ResizeGameArea";
- resize_rec.proc = ResizeGameArea;
- XtAppAddActions(XtWidgetToApplicationContext(form), &resize_rec, 1);
XtOverrideTranslations(form, XtParseTranslationTable(
"<Configure>: ResizeGameArea()\n"
"<Map>: ResizeGameArea()\n"
/*
* Performing the initial update of the layout seems to avoid
- * some weird problems on Motif 2.1
+ * some weird initial sizing problems on Motif 2.1
*/
- ResizeGameArea(form, 0, 0, 0);
+ XtCallActionProc(form, "ResizeGameArea", 0, 0, 0);
}
static Widget create_widget(const struct ui_widget *item, Widget parent,
}
/* Figure out which tiles intersect a rectangle. */
-static uint_fast32_t x11_expose_mask(XExposeEvent *e, int tile_w, int tile_h)
+static uint_fast32_t x11_expose_mask(XExposeEvent *e, int tile_sz)
{
- return board_rect( e->x/tile_w,
- e->y/tile_h,
- (e->x+e->width-1)/tile_w,
- (e->y+e->height-1)/tile_h );
+ return board_rect( e->x/tile_sz,
+ e->y/tile_sz,
+ (e->x+e->width-1)/tile_sz,
+ (e->y+e->height-1)/tile_sz );
}
static void resize(Widget w, void *data, void *cb_data)
XmDrawingAreaCallbackStruct *cbs = cb_data;
XExposeEvent *e = &cbs->event->xexpose;
struct app_state *state = data;
- Dimension tile_w, tile_h;
uint_fast32_t mask;
+ Dimension tile_sz;
- XtVaGetValues(w, XmNwidth, &tile_w, XmNheight, &tile_h, (char *)NULL);
if (w == state->game) {
uint_least32_t *gp = state->board.game;
- if (!(tile_w /= 5) || !(tile_h /= 5))
- return;
-
/*
* Only draw exposed nonempty tiles; exposed areas are filled
* with the background automatically and thus exposed empty
* spaces don't need to be drawn again.
*/
+ tile_sz = state->game_tile_sz;
mask = gp[0] | gp[1] | gp[2];
} else {
- if (!(tile_w /= 3) || !(tile_h /= 3))
- return;
-
/* Goal area never has empty tiles. */
+ tile_sz = state->goal_tile_sz;
mask = -1;
}
- mask &= x11_expose_mask(e, tile_w, tile_h);
+ if (tile_sz)
+ mask &= x11_expose_mask(e, tile_sz);
x11_queue_render(state, mask, 1<<(w == state->game));
}
XFillRectangle(display, d, state->tile_gc, tx+2, ty+2, tw-4, th-4);
}
-static void clear_rect(struct app_state *state, Display *display, Drawable d,
- int x, int y, int w, int h)
+static void
+clear_rect(Display *display, Drawable d, int x, int y, int w, int h)
{
#if X11_RENDER_DEBUG
XRectangle r = { x, y, w, h };
+ XGCValues gcv;
+ GC gc;
+
+ gcv.foreground = 0xff0000;
+ gc = XCreateGC(display, d, GCForeground, &gcv);
+ XFillRectangles(display, d, gc, &r, 1);
+ XFreeGC(display, gc);
- XSetForeground(display, state->tile_gc, 0xff0000);
- XFillRectangles(display, d, state->tile_gc, &r, 1);
XFlush(display);
usleep(70000);
#endif
* which tiles need clearing, but for the border clear it is safe to wipe an
* entire row or column of the border using a single XClearArea.
*/
-static void clear_border(struct app_state *state, Display *display, Drawable d,
- Dimension w, Dimension h, uint_fast32_t mask)
+static void clear_border(Display *display, Drawable d, unsigned sz)
{
- if (!(mask &= ~GOAL_MASK & 0x1ffffff))
- return;
-
- clear_rect(state, display, d, 0, 0, 5*w, h);
- clear_rect(state, display, d, 0, 4*h, 5*w, h);
- clear_rect(state, display, d, 0, 0, w, 5*h);
- clear_rect(state, display, d, 4*w, 0, w, 5*h);
+ clear_rect(display, d, 0, 0, -1, sz);
+ clear_rect(display, d, 0, 4*sz, -1, sz);
+ clear_rect(display, d, 0, 0, sz, -1);
+ clear_rect(display, d, 4*sz, 0, sz, -1);
}
static int
redraw_tile(struct app_state *state, Display *display, Drawable d,
- uint_least32_t *gp, int x, int y, Dimension w, Dimension h)
+ uint_least32_t *gp, int x, int y, unsigned sz)
{
unsigned tile = board_tile(gp, 5*y+x);
+ unsigned w = sz, h = sz;
#if X11_RENDER_DEBUG
if (d == XtWindow(state->game) || d == XtWindow(state->goal)) {
{
Display *display = XtDisplay(state->goal);
Window goal = XtWindow(state->goal);
+ unsigned sz = state->goal_tile_sz;
unsigned long ewmhseq[9];
- Dimension w, h;
int i;
uint_least32_t gp[3] = {
state->board.goal[2]
};
- XtVaGetValues(state->goal, XtNwidth, &w, XtNheight, &h, (char *)NULL);
- w /= 3; h /= 3;
-
for (i = 0; i < 9; i++) {
int x = i%3, y = i/3;
unsigned tile;
tile = redraw_tile(state, display, state->icon_pixmap,
- gp, x, y, ICON_SIZE/3, ICON_SIZE/3);
+ gp, x, y, ICON_SIZE/3);
ewmhseq[i] = COLOUR_MAX*0x10101 * (tile-1) + 0x20100;
}
if (mask & 1)
- redraw_tile(state, display, goal, gp, x, y, w, h);
+ redraw_tile(state, display, goal, gp, x, y, sz);
/*
* Goal bitmaps have a gap of 2 tiles between each row.
Display *display = XtDisplay(state->goal);
Window game = XtWindow(state->game);
uint_least32_t buf[3], *gp = state->board.game;
- Dimension w, h;
+ unsigned sz = state->game_tile_sz;
int i;
- XtVaGetValues(state->game, XtNwidth, &w, XtNheight, &h, (char *)NULL);
- w /= 5; h /= 5;
-
if (state->view_goal_on_game) {
for (i = 0; i < 3; i++) {
buf[i] = state->board.goal[i];
/* Optimize the game end case where the outer frame is cleared */
if (((gp[0] | gp[1] | gp[2]) & ~GOAL_MASK) == 0) {
- clear_border(state, display, game, w, h, mask);
+ clear_border(display, game, sz);
mask &= GOAL_MASK;
}
for (i = 0; i < 25; i++) {
if (mask & 1)
- redraw_tile(state, display, game, gp, i%5, i/5, w, h);
+ redraw_tile(state, display, game, gp, i%5, i/5, sz);
mask >>= 1;
}
}