]> git.draconx.ca Git - rrace.git/commitdiff
motif: Avoid constantly recalculating tile size.
authorNick Bowler <nbowler@draconx.ca>
Fri, 30 Dec 2022 17:16:13 +0000 (12:16 -0500)
committerNick Bowler <nbowler@draconx.ca>
Sat, 7 Jan 2023 16:43:12 +0000 (11:43 -0500)
Instead of calculating tile size every time any drawing is needed,
we can just store the value calculated during window layout.  This
involves some code reorganization to give the ResizeGameArea action
implementation direct access to the state structure.

Moreover, avoid dealing with width/height separately in the drawing
functions, as by design the game/goal areas (and therefore the tiles)
are always square.

src/motif.c
src/motif.h
src/motif_ui.c
src/x11.c

index fe030799984fc3442aa4b35917cbdb5039eba160..c4ea62dbbd25b5d5e4a4b7d6ceec50a3d8f4e40d 100644 (file)
 
 #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} };
@@ -182,7 +187,6 @@ static void game_input(Widget w, void *data, void *cb_data)
        XmDrawingAreaCallbackStruct *cbs = cb_data;
        XButtonEvent *b = &cbs->event->xbutton;
        struct app_state *state = data;
-       Dimension width, height;
 
        switch (cbs->event->type) {
        case ButtonPress:
@@ -191,12 +195,8 @@ static void game_input(Widget w, void *data, void *cb_data)
                        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);
@@ -250,10 +250,44 @@ static void proc_about(Widget w, XEvent *e, String *argv, Cardinal *argc)
        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)
@@ -266,11 +300,11 @@ 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);
index cb07ec3d7c1dc9f5a2979b96e5861330706a14db..a008306f5bb44f69eaf63120674c333677341c58 100644 (file)
@@ -37,8 +37,10 @@ struct app_state {
 
        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;
index 6abdbc9d6db5dbe0aca21eab9cb6d28a358056bf..5941834c86ae8b7bcba3ef35290615e5a068ccc0 100644 (file)
 #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,
@@ -69,37 +64,6 @@ static const struct ui_menuitem {
        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;
@@ -132,7 +96,6 @@ static void configure_mainwin(struct app_state *state, Widget form)
        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,
@@ -155,9 +118,6 @@ static void configure_mainwin(struct app_state *state, Widget 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"
@@ -165,9 +125,9 @@ static void configure_mainwin(struct app_state *state, Widget form)
 
        /*
         * 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,
@@ -257,12 +217,12 @@ construct_menu(const struct ui_menuitem *root, Widget parent, unsigned i)
 }
 
 /* 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)
@@ -277,31 +237,27 @@ static void expose(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));
 }
 
index b15e7d67c18b4dd9d084c4f061b27d6b4cbddd24..6d09a7dcfc09f7359db1daeba62d980bae65cfd4 100644 (file)
--- a/src/x11.c
+++ b/src/x11.c
@@ -140,14 +140,19 @@ static void draw_tile(struct app_state *state, Display *display, Drawable d,
        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
@@ -160,23 +165,20 @@ static void clear_rect(struct app_state *state, Display *display, Drawable d,
  * 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)) {
@@ -235,8 +237,8 @@ x11_redraw_goal(struct app_state *state, uint_fast32_t mask, Widget icon_shell)
 {
        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] = {
@@ -245,9 +247,6 @@ x11_redraw_goal(struct app_state *state, uint_fast32_t mask, Widget icon_shell)
                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;
 
@@ -255,12 +254,12 @@ x11_redraw_goal(struct app_state *state, uint_fast32_t mask, Widget icon_shell)
                        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.
@@ -278,12 +277,9 @@ void x11_redraw_game(struct app_state *state, uint_fast32_t mask)
        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];
@@ -294,13 +290,13 @@ void x11_redraw_game(struct app_state *state, uint_fast32_t mask)
 
        /* 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;
        }
 }