libdir = $(exec_prefix)/lib
targets = libotk.so libotk.a
-sources = init.c display.c screeninfo.c rect.c gccache.c color.c font.c
-headers = init.h display.h screeninfo.h rect.h gccache.h color.h font.h
+sources = init.c display.c screeninfo.c rect.c gccache.c color.c font.c \
+ timer.c timerqueue.c imagecontrol.c
+headers = init.h display.h screeninfo.h rect.h gccache.h color.h font.h \
+ timer.h timerqueue.h imagecontrol.h
CFLAGS += -g -W -Wall -I/usr/gwar/include/python2.2 `pkg-config --cflags xft`
LDFLAGS += `pkg-config --libs xft`
-// -*- mode: C; indent-tabs-mode: nil; -*-
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#include "../config.h"
#include "color.h"
OtkScreenInfo *OtkDisplay_ScreenInfo(OtkDisplay *self, int num)
{
+ assert(num >= 0);
return (OtkScreenInfo*)PyList_GetItem((PyObject*)self->screenInfoList, num);
}
-// -*- mode: C; indent-tabs-mode: nil; -*-
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#ifndef __display_h
#define __display_h
-// -*- mode: C; indent-tabs-mode: nil; -*-
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#include "../config.h"
#include "font.h"
return Py_FindMethod(get_methods, obj, name);
}
-PyTypeObject Otkfont_Type = {
+PyTypeObject OtkFont_Type = {
PyObject_HEAD_INIT(NULL)
0,
"OtkFont",
-// -*- mode: C; indent-tabs-mode: nil; -*-
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#ifndef __font_h
#define __font_h
-// -*- mode: C; indent-tabs-mode: nil; -*-
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#include "../config.h"
#include "gccache.h"
XGCValues gcv;
unsigned long mask;
- self->pixel = gcv.foreground = OtkColor_Pixel(color);
+ self->pixel = gcv.foreground = color->pixel;
self->function = gcv.function = function;
self->subwindow = gcv.subwindow_mode = subwindow;
self->linewidth = gcv.line_width = linewidth;
OtkGCCacheItem *OtkGCCache_Find(OtkColor *color, XFontStruct *font,
int function, int subwindow, int linewidth)
{
- const unsigned long pixel = OtkColor_Pixel(color);
+ const unsigned long pixel = color->pixel;
const int screen = color->screen;
const int key = color->red ^ color->green ^ color->blue;
int k = (key % gccache->cache_size) * gccache->cache_buckets;
-// -*- mode: C; indent-tabs-mode: nil; -*-
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#ifndef __gccache_h
#define __gccache_h
--- /dev/null
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+
+#include "../config.h"
+#include "imagecontrol.h"
+#include "timer.h"
+#include "screeninfo.h"
+#include "display.h"
+
+typedef struct CachedImage {
+ Pixmap pixmap;
+
+ unsigned int count, width, height;
+ unsigned long pixel1, pixel2, texture;
+} CachedImage;
+
+static void timeout(OtkImageControl *self);
+static void initColors(OtkImageControl *self, Visual *visual);
+
+PyObject *OtkImageControl_New(int screen)
+{
+ OtkImageControl *self;
+ int count, i;
+ XPixmapFormatValues *pmv;
+
+ self = PyObject_New(OtkImageControl, &OtkImageControl_Type);
+
+ self->screen = OtkDisplay_ScreenInfo(OBDisplay, screen);
+
+ self->timer = (OtkTimer*)OtkTimer_New((OtkTimeoutHandler)timeout, self);
+ self->timer->timeout = 300000;
+ OtkTimer_Start(self->timer);
+ self->cache_max = 200;
+
+ self->dither = True; // default value
+ self->cpc = 4; // default value
+
+ // get the BPP from the X server
+ self->bpp = 0;
+ if ((pmv = XListPixmapFormats(OBDisplay->display, &count))) {
+ for (i = 0; i < count; i++)
+ if (pmv[i].depth == self->screen->depth) {
+ self->bpp = pmv[i].bits_per_pixel;
+ break;
+ }
+ XFree(pmv);
+ }
+ if (!self->bpp) self->bpp = self->screen->depth;
+ if (self->bpp >= 24) self->dither = False; // don't need dither at >= 24 bpp
+
+ self->grad_xbuffer = self->grad_ybuffer = NULL;
+ self->grad_buffer_width = self->grad_buffer_height = 0;
+ self->sqrt_table = NULL;
+
+ initColors(self, self->screen->visual);
+
+ return (PyObject*)self;
+}
+
+static void initColors(OtkImageControl *self, Visual *visual)
+{
+ // these are not used for !TrueColor
+ self->red_offset = self->green_offset = self->blue_offset = 0;
+ // these are not used for TrueColor
+ self->colors = NULL;
+ self->ncolors = 0;
+
+ // figure out all our color settings based on the visual type
+ switch (visual->class) {
+ case TrueColor: {
+ int i;
+ unsigned long red_mask, green_mask, blue_mask;
+
+ // find the offsets for each color in the visual's masks
+ red_mask = visual->red_mask;
+ green_mask = visual->green_mask;
+ blue_mask = visual->blue_mask;
+
+ while (! (red_mask & 1)) { self->red_offset++; red_mask >>= 1; }
+ while (! (green_mask & 1)) { self->green_offset++; green_mask >>= 1; }
+ while (! (blue_mask & 1)) { self->blue_offset++; blue_mask >>= 1; }
+
+ // use the mask to determine the number of bits for each shade of color
+ // so, best case, red_mask == 0xff (255), and so each bit is a different
+ // shade!
+ self->red_bits = 255 / red_mask;
+ self->green_bits = 255 / green_mask;
+ self->blue_bits = 255 / blue_mask;
+
+ // compute color tables, based on the number of bits for each shade
+ for (i = 0; i < 256; i++) {
+ self->red_color_table[i] = i / self->red_bits;
+ self->green_color_table[i] = i / self->green_bits;
+ self->blue_color_table[i] = i / self->blue_bits;
+ }
+ break;
+ }
+/*
+ case PseudoColor:
+ case StaticColor: {
+ ncolors = self->cpc * self->cpc * self->cpc; // cpc ^ 3
+
+ if (ncolors > (1 << self->screen->depth)) {
+ self->cpc = (1 << self->screen->depth) / 3;
+ ncolors = self->cpc * self->cpc * self->cpc; // cpc ^ 3
+ }
+
+ if (self->cpc < 2 || self->ncolors > (1 << self->screen->depth)) {
+ fprintf(stderr,
+ "OtkImageControl_New: invalid colormap size %d "
+ "(%d/%d/%d) - reducing",
+ self->ncolors, self->cpc, self->cpc, self->cpc);
+
+ self->cpc = (1 << self->screen->depth) / 3;
+ }
+
+ self->colors = malloc(sizeof(XColor) * self->ncolors);
+ if (! self->colors) {
+ fprintf(stderr, "OtkImageControl_New: error allocating colormap\n");
+ exit(1);
+ }
+
+ int i = 0, ii, p, r, g, b,
+ bits = 255 / (colors_per_channel - 1);
+
+ red_bits = green_bits = blue_bits = bits;
+
+ for (i = 0; i < 256; i++)
+ red_color_table[i] = green_color_table[i] = blue_color_table[i] =
+ i / bits;
+
+ for (r = 0, i = 0; r < colors_per_channel; r++)
+ for (g = 0; g < colors_per_channel; g++)
+ for (b = 0; b < colors_per_channel; b++, i++) {
+ colors[i].red = (r * 0xffff) / (colors_per_channel - 1);
+ colors[i].green = (g * 0xffff) / (colors_per_channel - 1);
+ colors[i].blue = (b * 0xffff) / (colors_per_channel - 1);;
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ }
+
+ for (i = 0; i < ncolors; i++) {
+ if (! XAllocColor(OBDisplay::display, colormap, &colors[i])) {
+ fprintf(stderr, "couldn't alloc color %i %i %i\n",
+ colors[i].red, colors[i].green, colors[i].blue);
+ colors[i].flags = 0;
+ } else {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ }
+ }
+
+ XColor icolors[256];
+ int incolors = (((1 << screen_depth) > 256) ? 256 : (1 << screen_depth));
+
+ for (i = 0; i < incolors; i++)
+ icolors[i].pixel = i;
+
+ XQueryColors(OBDisplay::display, colormap, icolors, incolors);
+ for (i = 0; i < ncolors; i++) {
+ if (! colors[i].flags) {
+ unsigned long chk = 0xffffffff, pixel, close = 0;
+
+ p = 2;
+ while (p--) {
+ for (ii = 0; ii < incolors; ii++) {
+ r = (colors[i].red - icolors[i].red) >> 8;
+ g = (colors[i].green - icolors[i].green) >> 8;
+ b = (colors[i].blue - icolors[i].blue) >> 8;
+ pixel = (r * r) + (g * g) + (b * b);
+
+ if (pixel < chk) {
+ chk = pixel;
+ close = ii;
+ }
+
+ colors[i].red = icolors[close].red;
+ colors[i].green = icolors[close].green;
+ colors[i].blue = icolors[close].blue;
+
+ if (XAllocColor(OBDisplay::display, colormap,
+ &colors[i])) {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ case GrayScale:
+ case StaticGray: {
+ if (visual->c_class == StaticGray) {
+ ncolors = 1 << screen_depth;
+ } else {
+ ncolors = colors_per_channel * colors_per_channel * colors_per_channel;
+
+ if (ncolors > (1 << screen_depth)) {
+ colors_per_channel = (1 << screen_depth) / 3;
+ ncolors =
+ colors_per_channel * colors_per_channel * colors_per_channel;
+ }
+ }
+
+ if (colors_per_channel < 2 || ncolors > (1 << screen_depth)) {
+ fprintf(stderr,
+ "BImageControl::BImageControl: invalid colormap size %d "
+ "(%d/%d/%d) - reducing",
+ ncolors, colors_per_channel, colors_per_channel,
+ colors_per_channel);
+
+ colors_per_channel = (1 << screen_depth) / 3;
+ }
+
+ colors = new XColor[ncolors];
+ if (! colors) {
+ fprintf(stderr,
+ "BImageControl::BImageControl: error allocating colormap\n");
+ exit(1);
+ }
+
+ int i = 0, ii, p, bits = 255 / (colors_per_channel - 1);
+ red_bits = green_bits = blue_bits = bits;
+
+ for (i = 0; i < 256; i++)
+ red_color_table[i] = green_color_table[i] = blue_color_table[i] =
+ i / bits;
+
+ for (i = 0; i < ncolors; i++) {
+ colors[i].red = (i * 0xffff) / (colors_per_channel - 1);
+ colors[i].green = (i * 0xffff) / (colors_per_channel - 1);
+ colors[i].blue = (i * 0xffff) / (colors_per_channel - 1);;
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+
+ if (! XAllocColor(OBDisplay::display, colormap,
+ &colors[i])) {
+ fprintf(stderr, "couldn't alloc color %i %i %i\n",
+ colors[i].red, colors[i].green, colors[i].blue);
+ colors[i].flags = 0;
+ } else {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ }
+ }
+
+ XColor icolors[256];
+ int incolors = (((1 << screen_depth) > 256) ? 256 :
+ (1 << screen_depth));
+
+ for (i = 0; i < incolors; i++)
+ icolors[i].pixel = i;
+
+ XQueryColors(OBDisplay::display, colormap, icolors, incolors);
+ for (i = 0; i < ncolors; i++) {
+ if (! colors[i].flags) {
+ unsigned long chk = 0xffffffff, pixel, close = 0;
+
+ p = 2;
+ while (p--) {
+ for (ii = 0; ii < incolors; ii++) {
+ int r = (colors[i].red - icolors[i].red) >> 8;
+ int g = (colors[i].green - icolors[i].green) >> 8;
+ int b = (colors[i].blue - icolors[i].blue) >> 8;
+ pixel = (r * r) + (g * g) + (b * b);
+
+ if (pixel < chk) {
+ chk = pixel;
+ close = ii;
+ }
+
+ colors[i].red = icolors[close].red;
+ colors[i].green = icolors[close].green;
+ colors[i].blue = icolors[close].blue;
+
+ if (XAllocColor(OBDisplay::display, colormap,
+ &colors[i])) {
+ colors[i].flags = DoRed|DoGreen|DoBlue;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+*/
+ default:
+ fprintf(stderr, "OtkImageControl: unsupported visual class: %d\n",
+ visual->class);
+ exit(1);
+ }
+}
+
+
+static void timeout(OtkImageControl *self)
+{
+ (void)self;
+}
+
+
+
+static void otkimagecontrol_dealloc(OtkImageControl* self)
+{
+ Py_DECREF(self->screen);
+ Py_DECREF(self->timer);
+ PyObject_Del((PyObject*)self);
+}
+
+PyTypeObject OtkImageControl_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "OtkImageControl",
+ sizeof(OtkImageControl),
+ 0,
+ (destructor)otkimagecontrol_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
--- /dev/null
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+#ifndef __imagecontrol_h
+#define __imagecontrol_h
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <Python.h>
+
+struct OtkScreenInfo;
+struct OtkTimer;
+
+extern PyTypeObject OtkImageControl_Type;
+
+typedef struct OtkImageControl {
+ struct OtkScreenInfo *screen;
+
+ // for the pixmap cache
+ struct OtkTimer *timer;
+ unsigned long cache_max;
+
+ Bool dither;
+
+ int cpc; // colors-per-channel: must be a value between [2,6]
+ int bpp; // bits-per-pixel
+
+ unsigned int *grad_xbuffer;
+ unsigned int *grad_ybuffer;
+ unsigned int grad_buffer_width;
+ unsigned int grad_buffer_height;
+
+ unsigned long *sqrt_table;
+
+ // These values are all determined based on a visual
+
+ int red_bits; // the number of bits (1-255) that each shade of color
+ int green_bits; // spans across. best case is 1, which gives 255 shades.
+ int blue_bits;
+ unsigned char red_color_table[256];
+ unsigned char green_color_table[256];
+ unsigned char blue_color_table[256];
+
+ // These are only used for TrueColor visuals
+ int red_offset; // the offset of each color in a color mask
+ int green_offset;
+ int blue_offset;
+
+ // These are only used for !TrueColor visuals
+ XColor *colors;
+ int ncolors;
+
+} OtkImageControl;
+
+PyObject *OtkImageControl_New(int screen);
+
+
+/*
+ inline bool doDither(void) { return dither; }
+
+ inline const ScreenInfo* getScreenInfo() const { return screeninfo; }
+
+ inline Window getDrawable(void) const { return window; }
+
+ inline Visual *getVisual(void) { return screeninfo->visual(); }
+
+ inline int getBitsPerPixel(void) const { return bits_per_pixel; }
+ inline int getDepth(void) const { return screen_depth; }
+ inline int getColorsPerChannel(void) const
+ { return colors_per_channel; }
+
+ unsigned long getSqrt(unsigned int x);
+
+ Pixmap renderImage(unsigned int width, unsigned int height,
+ const BTexture &texture);
+
+ void installRootColormap(void);
+ void removeImage(Pixmap pixmap);
+ void getColorTables(unsigned char **rmt, unsigned char **gmt,
+ unsigned char **bmt,
+ int *roff, int *goff, int *boff,
+ int *rbit, int *gbit, int *bbit);
+ void getXColorTable(XColor **c, int *n);
+ void getGradientBuffers(unsigned int w, unsigned int h,
+ unsigned int **xbuf, unsigned int **ybuf);
+ void setDither(bool d) { dither = d; }
+
+ static void timeout(BImageControl *t);
+
+private:
+ bool dither;
+ const ScreenInfo *screeninfo;
+ OBTimer *timer;
+
+ Colormap colormap;
+
+ Window window;
+ XColor *colors;
+ int colors_per_channel, ncolors, screen_number, screen_depth,
+ bits_per_pixel, red_offset, green_offset, blue_offset,
+ red_bits, green_bits, blue_bits;
+ unsigned char red_color_table[256], green_color_table[256],
+ blue_color_table[256];
+ unsigned int *grad_xbuffer, *grad_ybuffer, grad_buffer_width,
+ grad_buffer_height;
+ unsigned long *sqrt_table, cache_max;
+
+ typedef std::list<CachedImage> CacheContainer;
+ CacheContainer cache;
+
+ Pixmap searchCache(const unsigned int width, const unsigned int height,
+ const unsigned long texture,
+ const BColor &c1, const BColor &c2);
+};
+*/
+
+#endif // __imagecontrol_h
#include "color.h"
#include "gccache.h"
#include "font.h"
+#include "rect.h"
+#include "timer.h"
+#include "timerqueue.h"
+#include "imagecontrol.h"
#include <X11/Xlib.h>
#include <Python.h>
OtkScreenInfo_Type.ob_type = &PyType_Type;
OtkColor_Type.ob_type = &PyType_Type;
OtkFont_Type.ob_type = &PyType_Type;
+ OtkTimer_Type.ob_type = &PyType_Type;
+ OtkImageControl_Type.ob_type = &PyType_Type;
Py_InitModule("otk", otk_methods);
+ OtkTimerQueue_Initialize();
OtkDisplay_Initialize(display);
assert(OBDisplay);
OtkGCCache_Initialize();
-// -*- mode: C; indent-tabs-mode: nil; -*-
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#include "../config.h"
#include "screeninfo.h"
struct OtkRect;
typedef struct OtkScreenInfo {
+ PyObject_HEAD
int screen;
Window root_window;
--- /dev/null
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+
+#include "../config.h"
+#include "timer.h"
+#include "timerqueue.h"
+
+PyObject *OtkTimer_New(OtkTimeoutHandler handler, OtkTimeoutData data)
+{
+ OtkTimer *self = PyObject_New(OtkTimer, &OtkTimer_Type);
+
+ assert(handler); assert(data);
+ self->handler = handler;
+ self->data = data;
+ self->recur = self->timing = False;
+
+ return (PyObject*)self;
+}
+
+void OtkTimer_Start(OtkTimer *self)
+{
+ gettimeofday(&(self->start), 0);
+
+ self->end.tv_sec = self->start.tv_sec + self->timeout / 1000;
+ self->end.tv_usec = self->start.tv_usec + self->timeout % 1000 * 1000;
+
+ if (! self->timing) {
+ self->timing = True;
+ OtkTimerQueue_Add(self);
+ }
+}
+
+void OtkTimer_Stop(OtkTimer *self)
+{
+ if (self->timing) {
+ self->timing = False;
+ OtkTimerQueue_Remove(self);
+ }
+}
+
+
+
+
+
+static void otktimer_dealloc(OtkTimer* self)
+{
+ OtkTimer_Stop(self);
+ // when this is called, the color has already been cleaned out of the cache
+ PyObject_Del((PyObject*)self);
+}
+
+static int otktimer_compare(OtkTimer *t1, OtkTimer *t2)
+{
+ if (t1->end.tv_sec == t2->end.tv_sec && t1->end.tv_usec == t2->end.tv_usec)
+ return 0;
+ else if ((t1->end.tv_sec < t2->end.tv_sec) ||
+ (t1->end.tv_sec == t2->end.tv_sec &&
+ t1->end.tv_usec < t2->end.tv_usec))
+ return -1;
+ else
+ return 1;
+}
+
+PyTypeObject OtkTimer_Type = {
+ PyObject_HEAD_INIT(NULL)
+ 0,
+ "OtkTimer",
+ sizeof(OtkTimer),
+ 0,
+ (destructor)otktimer_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ (cmpfunc)otktimer_compare, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+};
--- /dev/null
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+#ifndef __timer_h
+#define __timer_h
+
+#include <X11/Xlib.h>
+#include <Python.h>
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else // !TIME_WITH_SYS_TIME
+# ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else // !HAVE_SYS_TIME_H
+# include <time.h>
+# endif // HAVE_SYS_TIME_H
+#endif // TIME_WITH_SYS_TIME
+
+extern PyTypeObject OtkTimer_Type;
+
+//! The data passed to the OtkTimeoutHandler function.
+/*!
+ Note: this is a very useful place to put an object instance, and set the
+ event handler to a static function in the same class.
+*/
+typedef void *OtkTimeoutData;
+//! The type of function which can be set as the callback for an OtkTimer
+//! firing
+typedef void (*OtkTimeoutHandler)(OtkTimeoutData);
+
+typedef struct OtkTimer {
+ PyObject_HEAD
+ OtkTimeoutHandler handler;
+ OtkTimeoutData data;
+ Bool recur;
+ long timeout;
+
+ // don't edit these
+ Bool timing;
+ struct timeval start;
+ struct timeval end;
+} OtkTimer;
+
+PyObject *OtkTimer_New(OtkTimeoutHandler handler, OtkTimeoutData data);
+
+//! Causes the timer to begin
+void OtkTimer_Start(OtkTimer *self);
+
+//! Causes the timer to stop
+void OtkTimer_Stop(OtkTimer *self);
+
+#endif // __timer_h
--- /dev/null
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+
+#include "../config.h"
+#include "timerqueue.h"
+#include "display.h"
+
+#include <X11/Xlib.h>
+#include <Python.h>
+
+static PyObject *list = NULL; // PyListObject
+
+void OtkTimerQueue_Initialize()
+{
+ list = PyList_New(0);
+}
+
+void OtkTimerQueue_Add(OtkTimer* timer)
+{
+ PyList_Append(list, (PyObject*)timer);
+ PyList_Sort(list);
+}
+
+void OtkTimerQueue_Remove(OtkTimer* timer)
+{
+ int index;
+
+ index = PySequence_Index(list, (PyObject*)timer);
+ if (index >= 0)
+ PySequence_DelItem(list, index);
+}
+
+static Bool shouldFire(OtkTimer *timer, const struct timeval *now)
+{
+ return ! ((now->tv_sec < timer->end.tv_sec) ||
+ (now->tv_sec == timer->end.tv_sec &&
+ now->tv_usec < timer->end.tv_usec));
+}
+
+static void normalizeTimeval(struct timeval *time)
+{
+ while (time->tv_usec < 0) {
+ if (time->tv_sec > 0) {
+ --time->tv_sec;
+ time->tv_usec += 1000000;
+ } else {
+ time->tv_usec = 0;
+ }
+ }
+
+ if (time->tv_usec >= 1000000) {
+ time->tv_sec += time->tv_usec / 1000000;
+ time->tv_usec %= 1000000;
+ }
+
+ if (time->tv_sec < 0) time->tv_sec = 0;
+}
+
+void OtkTimerQueue_Fire()
+{
+ fd_set rfds;
+ struct timeval now, tm, *timeout = NULL;
+
+ const int xfd = ConnectionNumber(OBDisplay->display);
+
+ FD_ZERO(&rfds);
+ FD_SET(xfd, &rfds); // break on any x events
+
+ // check for timer timeout
+ gettimeofday(&now, 0);
+
+ // there is a small chance for deadlock here:
+ // *IF* the timer list keeps getting refreshed *AND* the time between
+ // timer->start() and timer->shouldFire() is within the timer's period
+ // then the timer will keep firing. This should be VERY near impossible.
+ while (PyList_Size(list)) {
+ OtkTimer *timer = (OtkTimer*)PyList_GetItem(list, 0);
+
+ if (! shouldFire(timer, &now)) {
+ tm.tv_sec = timer->end.tv_sec - now.tv_sec;
+ tm.tv_usec = timer->end.tv_usec - now.tv_usec;
+ normalizeTimeval(&tm);
+ timeout = &tm; // set the timeout for the select
+ break; // go on and wait
+ }
+
+ // stop and remove the timer from the queue
+ PySequence_DelItem(list, 0);
+ timer->timing = False;
+
+ if (timer->handler)
+ timer->handler(timer->data);
+
+ if (timer->recur)
+ OtkTimer_Start(timer);
+ }
+
+ select(xfd + 1, &rfds, 0, 0, timeout);
+}
--- /dev/null
+// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*-
+#ifndef __timerqueue_h
+#define __timerqueue_h
+
+#include "timer.h"
+
+void OtkTimerQueue_Initialize();
+
+//! Will wait for and fire the next timer in the queue.
+/*!
+ The function will stop waiting if an event is received from the X server.
+*/
+void OtkTimerQueue_Fire();
+
+//! Adds a new timer to the queue
+/*!
+ @param timer An OtkTimer to add to the queue
+*/
+void OtkTimerQueue_Add(OtkTimer* timer);
+
+//! Removes a timer from the queue
+/*!
+ @param timer An OtkTimer already in the queue to remove
+*/
+void OtkTimerQueue_Remove(OtkTimer* timer);
+
+#endif // __timerqueue_h