/*
* X11 GUI for slide puzzle game
* Copyright © 2022 Nick Bowler
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include "motif.h"
#include "motifgui.h"
#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,
widgetForm,
widgetFrame,
widgetDrawingArea,
widgetCascadeButton,
widgetPushButton,
widgetMax,
/* Pseudo-widgets */
widgetMenuBar = widgetMax,
widgetMessageDialog,
widgetScrolledText,
widgetUnmanaged = 128
};
static WidgetClass * const widgets[widgetMax] = {
&xmMainWindowWidgetClass,
&xmFormWidgetClass,
&xmFrameWidgetClass,
&xmDrawingAreaWidgetClass,
&xmCascadeButtonWidgetClass,
&xmPushButtonWidgetClass
};
static const struct ui_widget {
uint_least16_t name;
uint_least8_t subtree;
uint_least8_t widget_type;
} mainwin[] = { MAINWIN_INITIALIZER };
static const struct ui_menuitem {
struct ui_widget w;
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;
sec = ms / 1000, ms %= 1000;
min = sec / 60, sec %= 60;
sprintf(buf, "Time: %u:%.2u.%.3u", min, sec, (unsigned)ms);
return buf;
}
void ui_timer_update(struct app_state *state, int_fast32_t ms)
{
char buf[100];
if (ms < 0) {
xcounter_simple_update(state->timer, "\n");
return;
}
xcounter_simple_update(state->timer, timer_text(ms, buf));
}
static void configure_mainwin(struct app_state *state, Widget form)
{
Widget gamearea = XtNameToWidget(form, &tree_strtab[gameArea]);
Widget goalarea = XtNameToWidget(form, &tree_strtab[goalArea]);
XtActionsRec resize_rec;
char xc_template[100];
assert(gamearea && goalarea);
XtVaSetValues(form, XmNfractionBase, SPLIT_DENOMINATOR, (char *)NULL);
state->game = XtNameToWidget(gamearea, &tree_strtab[gameCanvas]);
XtVaSetValues(gamearea, XmNleftAttachment, XmATTACH_FORM,
XmNtopAttachment, XmATTACH_FORM,
(char *)NULL);
state->goal = XtNameToWidget(goalarea, &tree_strtab[goalCanvas]);
XtVaSetValues(goalarea, XmNleftAttachment, XmATTACH_WIDGET,
XmNleftWidget, gamearea,
XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET,
XmNtopWidget, gamearea,
(char *)NULL);
state->timer = XtNameToWidget(form, &tree_strtab[timeDisplay]);
XtVaSetValues(state->timer, XmNleftAttachment, XmATTACH_WIDGET,
XmNleftWidget, gamearea,
XmNtopAttachment, XmATTACH_WIDGET,
XmNtopWidget, goalarea,
XmNrightAttachment, XmATTACH_FORM,
(char *)NULL);
xcounter_simple_setup(state->timer, timer_text(20000, xc_template));
ui_timer_update(state, -1);
resize_rec.string = "ResizeGameArea";
resize_rec.proc = ResizeGameArea;
XtAppAddActions(XtWidgetToApplicationContext(form), &resize_rec, 1);
XtOverrideTranslations(form, XtParseTranslationTable(
": ResizeGameArea()\n"
"