dist_man_MANS = doc/rrace-motif.1
endif
+noinst_HEADERS = conf_post.h
+
rrace_motif_SOURCES = src/game.c src/x11.c src/game.h src/motif.h \
src/colour.h src/ewmhicon.c src/ewmhicon.h \
- src/xcounter.c src/xcounter.h
+ src/version.c src/version.h src/xcounter.c \
+ src/xcounter.h
rrace_motif_LDADD = $(libmotifmain_a_OBJECTS) $(libmotifui_a_OBJECTS) \
$(libglohelp_a_OBJECTS) libgnu.a $(MOTIF_LIBS) \
$(LIB_CLOCK_GETTIME) $(LIB_GETHRXTIME)
--- /dev/null
+/* Allow ENABLE_NLS to be used in C conditionals. */
+#ifndef ENABLE_NLS
+# define ENABLE_NLS 0
+#endif
AC_CACHE_SAVE
m4_include([lib/gnulib.mk])
+dnl We will provide our own makefile rules for gettext. Disable tracing of
+dnl AM_GNU_GETTEXT to prevent autoreconf from running autopoint, and to
+dnl prevent automake from growing gratuitous error conditions.
+m4_traceoff([AM_GNU_GETTEXT])
+AM_GNU_GETTEXT([external])
+AH_BOTTOM([#include <conf_post.h>])
+
AC_PATH_XTRA
AS_IF([test x"$no_x" != x"yes"],
[AC_CACHE_CHECK([for Motif], [dx_cv_have_motif],
/00gnulib.m4
/absolute-header.m4
/clock_time.m4
+/codeset.m4
/extensions.m4
/extern-inline.m4
/flexmember.m4
+/free.m4
/gethrxtime.m4
/getopt.m4
/gettime.m4
/gnulib-common.m4
/gnulib-comp.m4
/gnulib-tool.m4
+/host-cpu-c-abi.m4
+/iconv.m4
+/iconv_h.m4
+/iconv_open.m4
/include_next.m4
/inline.m4
+/inttypes.m4
+/lib-ld.m4
+/lib-link.m4
+/lib-prefix.m4
/libtool.m4
+/libunistring-base.m4
+/limits-h.m4
+/localcharset.m4
+/locale-fr.m4
+/locale-ja.m4
+/locale-zh.m4
+/locale_h.m4
/ltoptions.m4
/ltsugar.m4
/ltversion.m4
/lt~obsolete.m4
+/mbrtowc.m4
+/mbsinit.m4
+/mbstate_t.m4
+/mbswidth.m4
+/multiarch.m4
/nocrash.m4
/off_t.m4
/pid_t.m4
+/setlocale_null.m4
/ssize_t.m4
+/std-gnu11.m4
+/stdbool.m4
/stddef_h.m4
+/stdint.m4
+/stdlib_h.m4
+/string_h.m4
/sys_socket_h.m4
/sys_time_h.m4
/sys_types_h.m4
+/threadlib.m4
/time_h.m4
/timespec.m4
/unistd_h.m4
/vararrays.m4
+/visibility.m4
/warn-on-use.m4
+/wchar_h.m4
/wchar_t.m4
+/wctype_h.m4
+/wcwidth.m4
+/wint_t.m4
/zzgnulib.m4
# flexmember \
# gethrxtime \
# getopt-gnu \
-# inline
+# gettext-h \
+# inline \
+# localcharset \
+# mbswidth \
+# striconv
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([])
flexmember
gethrxtime
getopt-gnu
+ gettext-h
inline
+ localcharset
+ mbswidth
+ striconv
])
gl_AVOID([])
gl_SOURCE_BASE([lib])
#include "motif.h"
#include "motifopt.h"
#include "game.h"
+#include "version.h"
#define TIMER_UPDATE_MS 33
"*gameExit.accelerator: Ctrl<Key>Q",
"*gameExit.acceleratorText: Ctrl+Q",
+ "*aboutDialog*pixmapTextPadding: 10",
+
NULL
};
static void print_version(void)
{
- printf("%s %s\n", PROGNAME, PACKAGE_VERSION);
- printf("Copyright (C) 2022 Nick Bowler\n");
+ version_print_head("rrace-motif", stdout);
puts("License GPLv3+: GNU GPL version 3 or any later version");
puts("This is free software: you are free to change and redistribute it.");
puts("There is NO WARRANTY, to the extent permitted by law.");
static struct app_state state;
+static Widget get_shell(Widget w)
+{
+ Widget shell;
+
+ for (shell = w; !XtIsWMShell(shell);)
+ shell = XtParent(shell);
+
+ return shell;
+}
+
static void proc_exit(Widget w, XEvent *e, String *argv, Cardinal *argc)
{
XtAppSetExitFlag(XtWidgetToApplicationContext(w));
static void proc_new_game(Widget w, XEvent *e, String *argv, Cardinal *argc)
{
- Widget shell;
-
- for (shell = w; !XtIsWMShell(shell);)
- shell = XtParent(shell);
-
game_reset(&state.board);
x11_redraw_goal(&state, -1);
x11_redraw_game(&state, -1);
- x11_redraw_icon(&state, shell);
+ x11_redraw_icon(&state, get_shell(w));
if (!state.timer_tick) {
XtAppContext app = XtWidgetToApplicationContext(w);
game_begin(&state.board);
}
+static void proc_about(Widget w, XEvent *e, String *argv, Cardinal *argc)
+{
+ ui_show_about(&state, get_shell(w));
+}
+
static const XtActionsRec menu_actions[] = {
{ "gameNew", proc_new_game },
- { "gameExit", proc_exit }
+ { "gameExit", proc_exit },
+ { "helpAbout", proc_about }
};
static XtAppContext app_initialize(int argc, char **argv)
};
void ui_initialize(struct app_state *state, Widget shell);
+void ui_show_about(struct app_state *state, Widget shell);
void ui_timer_update(struct app_state *state, int_fast32_t elapsed);
void x11_initialize(struct app_state *state, Widget shell);
#include "motifstr.h"
#include "motifgui.h"
#include "xcounter.h"
+#include "version.h"
#define SPLIT_NUMERATOR 75
#define SPLIT_DENOMINATOR 100
widgetMax,
/* Pseudo-widgets */
- widgetMenuBar = widgetMax
+ widgetMenuBar = widgetMax,
+ widgetMessageDialog,
+ widgetScrolledText,
+
+ widgetUnmanaged = 128
};
static WidgetClass * const widgets[widgetMax] = {
ArgList args, Cardinal num_args)
{
String name = (void *)&tree_strtab[item->name];
- WidgetClass class;
+ unsigned type = item->widget_type & widgetUnmanaged-1;
- if (item->widget_type == widgetMenuBar)
+ switch (type) {
+ case widgetMenuBar:
return XmCreateMenuBar(parent, name, args, num_args);
+ case widgetMessageDialog:
+ return XmCreateMessageDialog(parent, name, args, num_args);
+ case widgetScrolledText:
+ return XmCreateScrolledText(parent, name, args, num_args);
+ }
- assert(item->widget_type < widgetMax);
- class = *widgets[item->widget_type];
- return XtCreateWidget(name, class, parent, args, num_args);
+ assert(type < widgetMax);
+ return XtCreateWidget(name, *widgets[type], parent, args, num_args);
}
static void
if (item->subtree)
construct_widgets(root, w, item->subtree);
- XtManageChild(w);
+ if (!(item->widget_type & widgetUnmanaged))
+ XtManageChild(w);
}
}
XtAddCallback(state->goal, XmNresizeCallback, goal_resize, state);
XtAddCallback(state->goal, XmNexposeCallback, goal_expose, state);
}
+
+static void dialog_close(Widget w, void *data, void *cb_data)
+{
+ XtDestroyWidget(w);
+}
+
+/*
+ * Expands to an XtVaSetValues argument list to set the given resource with
+ * a typed string value, which is internally converted to the appropriate
+ * resource type.
+ *
+ * Note that str is expanded twice and thus should not have side effects.
+ */
+#define STRING_ARG(resource, str) \
+ XtVaTypedArg, (resource), XtRString, (str), strlen((str))+1
+
+void ui_show_about(struct app_state *state, Widget shell)
+{
+ static const struct ui_widget dialog[] = { ABOUTDIALOG_INITIALIZER };
+ Widget w, l;
+ char *msg;
+
+ construct_widgets(dialog, shell, 0);
+
+ w = XtNameToWidget(shell, "*aboutDialog");
+ XtUnmanageChild(XmMessageBoxGetChild(w, XmDIALOG_CANCEL_BUTTON));
+
+ XtAddCallback(w, XmNunmapCallback, dialog_close, NULL);
+
+ XtVaSetValues(w, STRING_ARG(XmNokLabelString, "Close"), (char *)NULL);
+
+ msg = version_format_head("rrace-motif");
+ l = XmMessageBoxGetChild(w, XmDIALOG_MESSAGE_LABEL);
+ XtVaSetValues(l, XmNlabelType, XmPIXMAP_AND_STRING,
+ XmNlabelPixmap, state->icon_pixmap,
+ STRING_ARG(XmNlabelString, msg),
+ (char *)NULL);
+ free(msg);
+
+ l = XtNameToWidget(w, "*licenseBlurb");
+ XmTextSetString(l,
+"This program is free software: you can redistribute it and/or modify\n"
+"it under the terms of the GNU General Public License as published by\n"
+"the Free Software Foundation, either version 3 of the License, or\n"
+"(at your option) any later version.\n\n"
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+"GNU General Public License for more details.\n\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this program. If not, see <https://www.gnu.org/licenses/>.");
+
+ XtVaSetValues(l, XmNeditMode, XmMULTI_LINE_EDIT,
+ XmNeditable, FALSE,
+ XmNresizeWidth, TRUE,
+ XmNrows, 5,
+ (char *)NULL);
+
+ XtManageChild(w);
+}
goalCanvas, 0, widgetDrawingArea
timeDisplay, 0, widgetDrawingArea
+ABOUTDIALOG
+ aboutDialog, aboutDialog_OFFSET, widgetUnmanaged|widgetMessageDialog
+ licenseBlurb, 0, widgetScrolledText
+
MAINMENU
gameMenu, gameMenu_OFFSET, widgetCascadeButton, gameMenuLabel
gameNew, 0, widgetPushButton, gameNewLabel
--- /dev/null
+#include <config.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <striconv.h>
+#include <localcharset.h>
+
+#include "version.h"
+
+const char *init_copysign(char **alloc)
+{
+ *alloc = NULL;
+
+ if (ENABLE_NLS) {
+ char *buf = str_iconv("\xc2\xa9", "UTF-8", locale_charset());
+ if (buf)
+ return *alloc = buf;
+ }
+
+ return "(C)";
+}
+
+#define VERSION_HEAD_FMT "%s (RRace) %s\nCopyright %s 2022 Nick Bowler"
+#define VERSION_HEAD_ARGS progname, PACKAGE_VERSION, copysign
+
+void version_print_head(const char *progname, FILE *f)
+{
+ const char *copysign;
+ char *copybuf;
+
+ copysign = init_copysign(©buf);
+
+ printf(VERSION_HEAD_FMT, VERSION_HEAD_ARGS);
+ putc('\n', f);
+
+ free(copybuf);
+}
+
+char *version_format_head(const char *progname)
+{
+ const char *copysign;
+ char *copybuf, *ret;
+
+ copysign = init_copysign(©buf);
+
+ ret = malloc(sizeof VERSION_HEAD_FMT + 100);
+ if (ret)
+ sprintf(ret, VERSION_HEAD_FMT, VERSION_HEAD_ARGS);
+
+ free(copybuf);
+ return ret;
+}
--- /dev/null
+#ifndef RRACE_VERSION_H_
+#define RRACE_VERSION_H_
+
+#include <stdio.h>
+
+/*
+ * Print a line containing the name of the program and the version of the
+ * package, followed by a line for each copyright notice to the specified file.
+ */
+void version_print_head(const char *progname, FILE *f);
+
+/*
+ * As with the above, but the string is put in a newly-allocated string which
+ * must be freed by the caller. The resulting string has newline characters
+ * between each line but there is no newline after the final line.
+ */
+char *version_format_head(const char *progname);
+
+#endif