/* Raw Xt: one Core widget, with drawing and interaction supplied by us.
 * Build: cc -std=c99 -Wall -Wextra -o xt-counter xt-counter.c -lXt -lX11
 */
#include <stdio.h>
#include <string.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Core.h>

static unsigned int clicks;
static GC gc;
static Atom wm_protocols, wm_delete;

static void
draw(Widget widget)
{
    Display *display = XtDisplay(widget);
    Window window = XtWindow(widget);
    char text[64];
    const char *lines[] = {
        "Raw Xt / Core widget",
        text,
        "Click the canvas or press Space to count.",
        "R resets. Q quits."
    };
    unsigned int i;

    if (!XtIsRealized(widget))
        return;
    snprintf(text, sizeof text, "Clicks: %u", clicks);
    XClearWindow(display, window);
    for (i = 0; i < XtNumber(lines); ++i)
        XDrawString(display, window, gc, 18, 30 + (int)i * 30,
                    lines[i], (int)strlen(lines[i]));
}

static void
count_action(Widget widget, XEvent *event, String *params, Cardinal *num_params)
{
    (void)event; (void)params; (void)num_params;
    ++clicks;
    draw(widget);
}

static void
reset_action(Widget widget, XEvent *event, String *params, Cardinal *num_params)
{
    (void)event; (void)params; (void)num_params;
    clicks = 0;
    draw(widget);
}

static void
quit_action(Widget widget, XEvent *event, String *params, Cardinal *num_params)
{
    (void)event; (void)params; (void)num_params;
    XtAppSetExitFlag(XtWidgetToApplicationContext(widget));
}

static void
expose(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch)
{
    (void)data; (void)dispatch;
    if (event->type == Expose && event->xexpose.count == 0)
        draw(widget);
}

static void
close_window(Widget widget, XtPointer data, XEvent *event, Boolean *dispatch)
{
    (void)data; (void)dispatch;
    if (event->type == ClientMessage && event->xclient.format == 32 &&
        event->xclient.message_type == wm_protocols &&
        (Atom)event->xclient.data.l[0] == wm_delete)
        XtAppSetExitFlag(XtWidgetToApplicationContext(widget));
}

int
main(int argc, char **argv)
{
    XtAppContext app;
    Widget shell, canvas;
    Display *display;
    XFontStruct *font;
    XGCValues values;
    XtActionsRec actions[] = {
        {"count", count_action},
        {"reset", reset_action},
        {"quit", quit_action}
    };
    String fallback[] = {"*title: Raw Xt", "*canvas.background: #e8e8e8", NULL};

    XtSetLanguageProc(NULL, NULL, NULL);
    shell = XtVaAppInitialize(&app, "XtCounter", NULL, 0,
                             &argc, argv, fallback, NULL);
    canvas = XtVaCreateManagedWidget("canvas", coreWidgetClass, shell,
                                     XtNwidth, 350, XtNheight, 150,
                                     XtNborderWidth, 0, NULL);
    display = XtDisplay(shell);
    font = XLoadQueryFont(display, "fixed");
    if (!font)
        XtAppError(app, "The X server has no 'fixed' core font");
    values.foreground = BlackPixelOfScreen(XtScreen(canvas));
    values.font = font->fid;
    gc = XtGetGC(canvas, GCForeground | GCFont, &values);

    XtAppAddActions(app, actions, XtNumber(actions));
    XtOverrideTranslations(canvas, XtParseTranslationTable(
        "<Btn1Up>: count()\n"
        "<Key>space: count()\n"
        "<Key>r: reset()\n"
        "<Key>q: quit()"));
    XtAddEventHandler(canvas, ExposureMask, False, expose, NULL);
    XtSetKeyboardFocus(shell, canvas);
    XtRealizeWidget(shell);

    wm_protocols = XInternAtom(display, "WM_PROTOCOLS", False);
    wm_delete = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, XtWindow(shell), &wm_delete, 1);
    XtAddEventHandler(shell, NoEventMask, True, close_window, NULL);
    XtAppMainLoop(app);

    XtReleaseGC(canvas, gc);
    XtDestroyWidget(shell);
    XFreeFont(display, font);
    XtCloseDisplay(display);
    XtDestroyApplicationContext(app);
    return 0;
}
