/* * Helpers for implementing a rapid-update counter display in Motif. * Copyright © 2022-2023 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 . */ #ifndef XCOUNTER_H_ #define XCOUNTER_H_ /* * A set of functions to implement a simple decimal counter display supporting * rapid updates. This is intended to avoid two specific practical problems * with the Motif text widgets: * * (1) Updating the text involves expensive(?) XmString operations, and * (2) Updating the text causes the label to be cleared and redrawn. * * Point #2 in particular results in very annoying flicker. * * These functions are intended for dealing with strings that consist of * "static" text interspersed with digits. For example, a string such as: * * "The time is 12:30:57." * * On a call to xcounter_init, a string like this is provided as the template. * The "static" elements are all the maximal nondigit substrings, which in this * instance are "The time is ", ":" (twice) and ".". These static elements * are pre-rendered to an X pixmap along with all possible decimal digits. * * A subsequent call to xcounter_update will change the displayed string by * combining the pre-rendered images in various ways. This takes a strings in * the same format the original template to modify the digit sequences. * * This solves the practical problems by significantly simplifying the actual * X drawing process (just a single operation to update any particular pixel). * This avoids the flicker inherent to a clear+redraw, and a simple change * tracking scheme is employed to avoid redundant drawing for a smoother * update. * * To simplify the implementation, some arbitrary limits are baked in: * * - maximum number of static elements in a template: 10 * - maximum number of digits plus static elements in an update: 23 * * Which should be more than enough to handle the RRace displays. */ /* * Create xcounter based on template. * * The provided template must be writeable as it is internally modified by * this function. However, it is restored to its original value upon return. */ struct xcounter *xcounter_init(Widget w, char *template); /* * Update xcounter segments according to str. * * Generally, the string should be in the same format as the original * template, with only the digits changing between calls. However, there * are two allowed exceptions: * * - It does not matter what exact text is placed in the static portions * between digits, provided they are nonempty. These are used only to * separate groups of digits. The pre-rendered text from the original * template is always used. * * - The updated string need not include all the elements of the original * template. Omitted elements from the update string are not drawn. */ void xcounter_update(struct xcounter *xc, const char *str); /* * Redraw the counter in response to an expose event. */ void xcounter_expose(struct xcounter *xc, XExposeEvent *e); /* * Resize and expose callbacks that can be registered on an XmDrawingArea. */ void xcounter_resize_cb(Widget w, void *data, void *cb_data); void xcounter_expose_cb(Widget w, void *data, void *cb_data); /* * Call after the widget is resized to update the internal dimensions, * and clear the margins. * * Since the pre-rendered text never changes, this is all that is needed: * existing portions of the window do not need to be redrawn (except when * shrinking the margins over previously-drawn text), and newly-revealed * areas will get expose events to trigger redraw. */ static inline void xcounter_resize(struct xcounter *xc) { xcounter_resize_cb(0, xc, 0); } static inline struct xcounter *xcounter_simple_init(Widget w, char *template) { struct xcounter *xc = xcounter_init(w, template); XtAddCallback(w, XmNresizeCallback, xcounter_resize_cb, xc); XtAddCallback(w, XmNexposeCallback, xcounter_expose_cb, xc); return xc; } #endif