From 065c6efa774ac144665f340f6c3578ab74e05c7b Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Sat, 21 Dec 2002 19:54:15 +0000 Subject: [PATCH] otktimer works. imagecontrol is underway! --- otk_c/Makefile | 6 +- otk_c/color.c | 2 +- otk_c/display.c | 1 + otk_c/display.h | 2 +- otk_c/font.c | 4 +- otk_c/font.h | 2 +- otk_c/gccache.c | 6 +- otk_c/gccache.h | 2 +- otk_c/imagecontrol.c | 325 +++++++++++++++++++++++++++++++++++++++++++ otk_c/imagecontrol.h | 115 +++++++++++++++ otk_c/init.c | 7 + otk_c/screeninfo.c | 2 +- otk_c/screeninfo.h | 1 + otk_c/timer.c | 79 +++++++++++ otk_c/timer.h | 52 +++++++ otk_c/timerqueue.c | 98 +++++++++++++ otk_c/timerqueue.h | 27 ++++ 17 files changed, 719 insertions(+), 12 deletions(-) create mode 100644 otk_c/imagecontrol.c create mode 100644 otk_c/imagecontrol.h create mode 100644 otk_c/timer.c create mode 100644 otk_c/timer.h create mode 100644 otk_c/timerqueue.c create mode 100644 otk_c/timerqueue.h diff --git a/otk_c/Makefile b/otk_c/Makefile index 397227ef..f6cb0c54 100644 --- a/otk_c/Makefile +++ b/otk_c/Makefile @@ -3,8 +3,10 @@ exec_prefix = $(prefix) 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` diff --git a/otk_c/color.c b/otk_c/color.c index 732f7b7d..81f5b76a 100644 --- a/otk_c/color.c +++ b/otk_c/color.c @@ -1,4 +1,4 @@ -// -*- mode: C; indent-tabs-mode: nil; -*- +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- #include "../config.h" #include "color.h" diff --git a/otk_c/display.c b/otk_c/display.c index 3ce7f06b..c0cae13c 100644 --- a/otk_c/display.c +++ b/otk_c/display.c @@ -148,6 +148,7 @@ void OtkDisplay_Ungrab(OtkDisplay *self) OtkScreenInfo *OtkDisplay_ScreenInfo(OtkDisplay *self, int num) { + assert(num >= 0); return (OtkScreenInfo*)PyList_GetItem((PyObject*)self->screenInfoList, num); } diff --git a/otk_c/display.h b/otk_c/display.h index aa55e0b9..32e92c4a 100644 --- a/otk_c/display.h +++ b/otk_c/display.h @@ -1,4 +1,4 @@ -// -*- mode: C; indent-tabs-mode: nil; -*- +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- #ifndef __display_h #define __display_h diff --git a/otk_c/font.c b/otk_c/font.c index 7c9b4f5f..a709f587 100644 --- a/otk_c/font.c +++ b/otk_c/font.c @@ -1,4 +1,4 @@ -// -*- mode: C; indent-tabs-mode: nil; -*- +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- #include "../config.h" #include "font.h" @@ -135,7 +135,7 @@ static PyObject *otkfont_getattr(PyObject *obj, char *name) return Py_FindMethod(get_methods, obj, name); } -PyTypeObject Otkfont_Type = { +PyTypeObject OtkFont_Type = { PyObject_HEAD_INIT(NULL) 0, "OtkFont", diff --git a/otk_c/font.h b/otk_c/font.h index 637a215d..a1b24490 100644 --- a/otk_c/font.h +++ b/otk_c/font.h @@ -1,4 +1,4 @@ -// -*- mode: C; indent-tabs-mode: nil; -*- +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- #ifndef __font_h #define __font_h diff --git a/otk_c/gccache.c b/otk_c/gccache.c index f8b40f31..85ab9f9e 100644 --- a/otk_c/gccache.c +++ b/otk_c/gccache.c @@ -1,4 +1,4 @@ -// -*- mode: C; indent-tabs-mode: nil; -*- +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- #include "../config.h" #include "gccache.h" @@ -40,7 +40,7 @@ void OtkGCCacheContext_Set(OtkGCCacheContext *self, 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; @@ -156,7 +156,7 @@ static void OtkGCCache_InternalRelease(OtkGCCacheContext *ctx) 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; diff --git a/otk_c/gccache.h b/otk_c/gccache.h index d7318260..119c256b 100644 --- a/otk_c/gccache.h +++ b/otk_c/gccache.h @@ -1,4 +1,4 @@ -// -*- mode: C; indent-tabs-mode: nil; -*- +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- #ifndef __gccache_h #define __gccache_h diff --git a/otk_c/imagecontrol.c b/otk_c/imagecontrol.c new file mode 100644 index 00000000..93eddc41 --- /dev/null +++ b/otk_c/imagecontrol.c @@ -0,0 +1,325 @@ +// -*- 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 */ +}; diff --git a/otk_c/imagecontrol.h b/otk_c/imagecontrol.h new file mode 100644 index 00000000..7ed6aec7 --- /dev/null +++ b/otk_c/imagecontrol.h @@ -0,0 +1,115 @@ +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- +#ifndef __imagecontrol_h +#define __imagecontrol_h + +#include +#include +#include + +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 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 diff --git a/otk_c/init.c b/otk_c/init.c index 8ea5d4a1..32a651bc 100644 --- a/otk_c/init.c +++ b/otk_c/init.c @@ -6,6 +6,10 @@ #include "color.h" #include "gccache.h" #include "font.h" +#include "rect.h" +#include "timer.h" +#include "timerqueue.h" +#include "imagecontrol.h" #include #include @@ -24,9 +28,12 @@ void initotk(char *display) 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(); diff --git a/otk_c/screeninfo.c b/otk_c/screeninfo.c index 62d65eb5..2f11cfd9 100644 --- a/otk_c/screeninfo.c +++ b/otk_c/screeninfo.c @@ -1,4 +1,4 @@ -// -*- mode: C; indent-tabs-mode: nil; -*- +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- #include "../config.h" #include "screeninfo.h" diff --git a/otk_c/screeninfo.h b/otk_c/screeninfo.h index 745d62ad..70ce7d3a 100644 --- a/otk_c/screeninfo.h +++ b/otk_c/screeninfo.h @@ -10,6 +10,7 @@ extern PyTypeObject OtkScreenInfo_Type; struct OtkRect; typedef struct OtkScreenInfo { + PyObject_HEAD int screen; Window root_window; diff --git a/otk_c/timer.c b/otk_c/timer.c new file mode 100644 index 00000000..8764a0e0 --- /dev/null +++ b/otk_c/timer.c @@ -0,0 +1,79 @@ +// -*- 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 */ +}; diff --git a/otk_c/timer.h b/otk_c/timer.h new file mode 100644 index 00000000..8e5399ec --- /dev/null +++ b/otk_c/timer.h @@ -0,0 +1,52 @@ +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- +#ifndef __timer_h +#define __timer_h + +#include +#include + +#ifdef TIME_WITH_SYS_TIME +# include +# include +#else // !TIME_WITH_SYS_TIME +# ifdef HAVE_SYS_TIME_H +# include +# else // !HAVE_SYS_TIME_H +# include +# 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 diff --git a/otk_c/timerqueue.c b/otk_c/timerqueue.c new file mode 100644 index 00000000..25fd8501 --- /dev/null +++ b/otk_c/timerqueue.c @@ -0,0 +1,98 @@ +// -*- mode: C; indent-tabs-mode: nil; c-basic-offset: 2; -*- + +#include "../config.h" +#include "timerqueue.h" +#include "display.h" + +#include +#include + +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); +} diff --git a/otk_c/timerqueue.h b/otk_c/timerqueue.h new file mode 100644 index 00000000..151aea07 --- /dev/null +++ b/otk_c/timerqueue.h @@ -0,0 +1,27 @@ +// -*- 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 -- 2.45.2