aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gfx.mk3
-rw-r--r--include/gevent.h222
-rw-r--r--include/ginput.h339
-rw-r--r--include/gtimer.h179
-rw-r--r--releases.txt3
-rw-r--r--src/gevent.c216
-rw-r--r--src/ginput.c42
-rw-r--r--src/gtimer.c302
8 files changed, 1306 insertions, 0 deletions
diff --git a/gfx.mk b/gfx.mk
index 5a7e8610..6f199fb0 100644
--- a/gfx.mk
+++ b/gfx.mk
@@ -5,6 +5,9 @@ endif
GFXSRC += $(GFXLIB)/src/gdisp.c \
$(GFXLIB)/src/gdisp_fonts.c \
+ $(GFXLIB)/src/gevent.c \
+ $(GFXLIB)/src/gtimer.c \
+ $(GFXLIB)/src/ginput.c \
$(GFXLIB)/src/gwin.c \
$(GFXLIB)/src/touchscreen.c \
$(GFXLIB)/src/graph.c \
diff --git a/include/gevent.h b/include/gevent.h
new file mode 100644
index 00000000..e7c5dcbf
--- /dev/null
+++ b/include/gevent.h
@@ -0,0 +1,222 @@
+/*
+ ChibiOS/GFX - Copyright (C) 2012
+ Joel Bodenmann aka Tectu <joel@unormal.org>
+
+ This file is part of ChibiOS/GFX.
+
+ ChibiOS/GFX 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.
+
+ ChibiOS/GFX 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file gevent.h
+ * @brief GEVENT GFX User Event subsystem header file.
+ *
+ * @addtogroup GEVENT
+ * @{
+ */
+#ifndef _GEVENT_H
+#define _GEVENT_H
+
+#ifndef GFX_USE_GEVENT
+ #define GFX_USE_GEVENT FALSE
+#endif
+
+#if GFX_USE_GEVENT || defined(__DOXYGEN__)
+
+/**
+ * @name GEVENT macros and more complex functionality to be compiled
+ * @{
+ */
+ /**
+ * @brief Data part of a static GListener initializer.
+ */
+ #define _GLISTENER_DATA(name) { _SEMAPHORE_DATA(name.waitqueue, 0), _BSEMAPHORE_DATA(name.eventlock, FALSE), {0} }
+ /**
+ * @brief Static GListener initializer.
+ */
+ #define GLISTENER_DECL(name) GListener name = _GLISTENER_DATA(name)
+ /**
+ * @brief Defines the maximum size of an event status variable.
+ * @details Defaults to 32 bytes
+ */
+ #ifndef GEVENT_MAXIMUM_STATUS_SIZE
+ #define GEVENT_MAXIMUM_STATUS_SIZE 32
+ #endif
+ /**
+ * @brief Should routines assert() if they run out of resources.
+ * @details Defaults to FALSE.
+ * @details If FALSE the application must be prepared to handle these
+ * failures.
+ */
+ #ifndef GEVENT_ASSERT_NO_RESOURCE
+ #define GEVENT_ASSERT_NO_RESOURCE FALSE
+ #endif
+ /**
+ * @brief Defines the maximum Source/Listener pairs in the system.
+ * @details Defaults to 32
+ */
+ #ifndef MAX_SOURCE_LISTENERS
+ #define MAX_SOURCE_LISTENERS 32
+ #endif
+/** @} */
+
+/*===========================================================================*/
+/* Low Level Driver details and error checks. */
+/*===========================================================================*/
+
+#if !CH_USE_MUTEXES || !CH_USE_SEMAPHORES
+ #error "GEVENT: CH_USE_MUTEXES and CH_USE_SEMAPHORES must be defined in chconf.h"
+#endif
+
+/*===========================================================================*/
+/* Type definitions */
+/*===========================================================================*/
+
+typedef uint16_t GEventType;
+ #define GEVENT_NULL 0x0000 // Null Event - Do nothing
+ #define GEVENT_EXIT 0x0001 // The listener is being forced to exit (someone is destroying the listener)
+
+ /* Other event types are allocated in ranges in their respective include files */
+ #define GEVENT_GINPUT_FIRST 0x0100 // GINPUT events range from 0x0100 to 0x01FF
+ #define GEVENT_GWIN_FIRST 0x0200 // GWIN events range from 0x0200 to 0x02FF
+ #define GEVENT_USER_FIRST 0x8000 // Any application defined events start at 0x8000
+
+// This object can be typecast to any GEventXxxxx type to allow any sub-system (or the application) to create events.
+// The prerequisite is that the new status structure type starts with a field named 'type' of type 'GEventType'.
+// The total status structure also must not exceed GEVENT_MAXIMUM_STATUS_SIZE bytes.
+// For example, this is used by GWIN button events, GINPUT data streams etc.
+typedef union GEvent_u {
+ GEventType type; // The type of this event
+ char pad[GEVENT_MAXIMUM_STATUS_SIZE]; // This is here to allow static initialisation of GEventObject's in the application.
+ } GEvent;
+
+// The Listener Object
+typedef struct GListener {
+ Semaphore waitqueue; // Private: Semaphore for the listener to wait on.
+ BinarySemaphore eventlock; // Private: Protect against more than one sources trying to use this event lock at the same time
+ GEvent event; // Public: The event object into which the event information is stored.
+ } GListener;
+
+// The Source Object
+typedef struct GSource_t GSource, *GSourceHandle;
+
+// This structure is passed to a source to describe a contender listener for sending the current event.
+typedef struct GSourceListener_t {
+ GListener *pListener; // The listener
+ GSource *pSource; // The source
+ unsigned listenflags; // The flags the listener passed when the source was assigned to it.
+ unsigned srcflags; // For the source's exclusive use. Initialised as 0 for a new listener source assignment.
+ } GSourceListener;
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* How to listen for events (act as a Listener)...
+ 1. Get handles for all the event sources you are interested in.
+ 2. Initialise a listener
+ 3. Attach sources to your listener.
+ - Sources can be attached or detached from a listener at any time.
+ - A source can be attached to more than one listener.
+ 4. Loop on getting listener events
+ 5. When finished detach all sources from the listener
+
+ How to create events (act as a Source)...
+ 1. Provide a funtion to the application that returns a GSourceHandle (which can be a pointer to whatever the source wants)
+ 2. Whenever a possible event occurs call geventGetSourceListener to get a pointer to a GSourceListener.
+ This will return NULL when there are no more listeners.
+ For each listener - check the flags to see if an event should be sent.
+ - use geventGetEvent() to get the event buffer supplied by the listener
+ and then call geventSendEvent to send the event.
+ - Note: geventGetEvent() may return FALSE to indicate the listener is currently not listening and
+ therefore no event should be sent. This situation enables the source to (optionally) flag
+ to the listener on its next wait that there have been missed events.
+ - Note: The GSourceListener pointer (and the GEvent buffer) are only valid between
+ the geventGetSourceListener call and either the geventSendEvent call or the next
+ geventGetSourceListener call.
+ - Note: All listeners must be processed for this event before anything else is processed.
+*/
+
+/*---------- Listener Functions --------------------------------------------*/
+
+/* Initialise a Listener.
+ */
+void geventListenerInit(GListener *pl);
+
+/* Attach a source to a listener.
+ * Flags are interpreted by the source when generating events for each listener.
+ * If this source is already assigned to the listener it will update the flags.
+ * If insufficient resources are available it will either assert or return FALSE
+ * depending on the value of GEVENT_ASSERT_NO_RESOURCE.
+ */
+bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags);
+
+/* Detach a source from a listener
+ * If gsh is NULL detach all sources from this listener and if there is still
+ * a thread waiting for events on this listener, it is sent the exit event.
+ */
+void geventDetachSource(GListener *pl, GSourceHandle gsh);
+
+/* Wait for an event on a listener from an assigned source.
+ * The type of the event should be checked (pevent->type) and then pevent should be typecast to the
+ * actual event type if it needs to be processed.
+ * timeout specifies the time to wait in system ticks.
+ * TIME_INFINITE means no timeout - wait forever for an event.
+ * TIME_IMMEDIATE means return immediately
+ * Returns NULL on timeout.
+ * Note: The GEvent buffer is staticly allocated within the GListener so the event does not
+ * need to be dynamicly freed however it will get overwritten by the next call to
+ * this routine.
+ */
+GEvent *geventEventWait(GListener *pl, systime_t timeout);
+
+/*---------- Source Functions --------------------------------------------*/
+
+/* Sources create their own GSourceHandles which are pointers to any arbitrary structure
+ typecast to a GSourceHandle.
+*/
+
+/* Called by a source with a possible event to get a listener record.
+ * 'lastlr' should be NULL on the first call and thereafter the result of the previous call.
+ * It will return NULL when there are no more listeners for this source.
+ */
+GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr);
+
+/* Get the event buffer from the GSourceListener.
+ * Returns NULL if the listener is not currently listening.
+ * A NULL return allows the source to record (perhaps in glr->scrflags) that the listener has missed events.
+ * This can then be notified as part of the next event for the listener.
+ * The buffer can only be accessed untill the next call to geventGetSourceListener or geventSendEvent
+ */
+GEvent *geventGetEventBuffer(GSourceListener *psl);
+
+/* Called by a source to indicate the listener's event buffer has been filled.
+ * After calling this function the source must not reference in fields in the GSourceListener or the event buffer.
+ */
+void geventSendEvent(GSourceListener *psl);
+
+/* Detach any listener that has this source attached */
+void geventDetachSourceListeners(GSourceHandle gsh);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_USE_GEVENT */
+
+#endif /* _GEVENT_H */
+/** @} */
diff --git a/include/ginput.h b/include/ginput.h
new file mode 100644
index 00000000..de2c617b
--- /dev/null
+++ b/include/ginput.h
@@ -0,0 +1,339 @@
+/*
+ ChibiOS/GFX - Copyright (C) 2012
+ Joel Bodenmann aka Tectu <joel@unormal.org>
+
+ This file is part of ChibiOS/GFX.
+
+ ChibiOS/GFX 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.
+
+ ChibiOS/GFX 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file ginput.h
+ * @brief GINPUT GFX User Input subsystem header file.
+ *
+ * @addtogroup GINPUT
+ * @{
+ */
+#ifndef _GINPUT_H
+#define _GINPUT_H
+
+#ifndef GFX_USE_GINPUT
+ #define GFX_USE_GINPUT FALSE
+#endif
+
+#if GFX_USE_GINPUT || defined(__DOXYGEN__)
+
+/**
+ * @name GINPUT more complex functionality to be compiled
+ * @{
+ */
+ /**
+ * @brief Should mouse functions be included.
+ * @details Defaults to FALSE
+ */
+ #ifndef GINPUT_NEED_MOUSE
+ #define GINPUT_NEED_MOUSE FALSE
+ #endif
+ /**
+ * @brief Should touch functions be included.
+ * @details Defaults to FALSE
+ */
+ #ifndef GINPUT_NEED_TOUCH
+ #define GINPUT_NEED_TOUCH FALSE
+ #endif
+ /**
+ * @brief Should keyboard functions be included.
+ * @details Defaults to FALSE
+ */
+ #ifndef GINPUT_NEED_KEYBOARD
+ #define GINPUT_NEED_KEYBOARD FALSE
+ #endif
+ /**
+ * @brief Should hardware toggle/switch/button (pio) functions be included.
+ * @details Defaults to FALSE
+ */
+ #ifndef GINPUT_NEED_TOGGLE
+ #define GINPUT_NEED_TOGGLE FALSE
+ #endif
+ /**
+ * @brief Should analog dial functions be included.
+ * @details Defaults to FALSE
+ */
+ #ifndef GINPUT_NEED_DIAL
+ #define GINPUT_NEED_DIAL FALSE
+ #endif
+/** @} */
+
+/*===========================================================================*/
+/* Low Level Driver details and error checks. */
+/*===========================================================================*/
+
+#ifndef GFX_USE_GDISP
+ #define GFX_USE_GDISP FALSE
+#endif
+#if GINPUT_NEED_TOUCH || !GFX_USE_GDISP
+ #error "GINPUT: GFX_USE_GDISP must be defined for touch functions"
+#endif
+
+#if GFX_USE_GDISP
+ #include "gdisp.h"
+#else
+ // We require some basic type definitions normally kept in gdisp.h
+ typedef int16_t coord_t;
+#endif
+
+#ifndef GFX_USE_GEVENT
+ #define GFX_USE_GEVENT TRUE
+ #include "gevent.h"
+#elif !GFX_USE_GEVENT
+ #error "GINPUT: GFX_USE_GEVENT must be defined"
+#endif
+
+#ifndef GFX_USE_GTIMER
+ #define GFX_USE_GTIMER TRUE
+ #include "gtimer.h"
+#elif !GFX_USE_GTIMER
+ #error "GINPUT: GFX_USE_GTIMER must be defined"
+#endif
+
+/*===========================================================================*/
+/* Type definitions */
+/*===========================================================================*/
+
+// Event types for various ginput sources
+#define GEVENT_MOUSE (GEVENT_GINPUT_FIRST+0)
+#define GEVENT_TOUCH (GEVENT_GINPUT_FIRST+1)
+#define GEVENT_KEYBOARD (GEVENT_GINPUT_FIRST+2)
+#define GEVENT_TOGGLE (GEVENT_GINPUT_FIRST+3)
+#define GEVENT_DIAL (GEVENT_GINPUT_FIRST+4)
+
+#if GINPUT_NEED_MOUSE || GINPUT_NEED_TOUCH
+ typedef struct GEventMouse_t {
+ GEventType type; // The type of this event (GEVENT_MOUSE or GEVENT_TOUCH)
+ uint16_t instance; // The mouse/touch instance
+ coord_t x, y, z; // The position of the mouse.
+ // - For touch devices, Z is the current pressure if supported (otherwise 0)
+ // - For mice, Z is the 3rd dimension if supported (otherwise 0)
+ uint16_t current_buttons; // A bit is set if the button is down.
+ // - For touch only bit 0 is relevant
+ // - For mice the order of the buttons is (from 0 to n) left, right, middle, any other buttons
+ // - Bit 15 being set indicates that an important mouse event has been missed.
+ #define GINPUT_TOUCH_PRESSED 0x0001
+ #define GINPUT_MOUSE_BTN_LEFT 0x0001
+ #define GINPUT_MOUSE_BTN_RIGHT 0x0002
+ #define GINPUT_MOUSE_BTN_MIDDLE 0x0004
+ #define GINPUT_MOUSE_BTN_4 0x0008
+ #define GINPUT_MISSED_MOUSE_EVENT 0x8000
+ uint16_t last_buttons; // The value of current_buttons on the last event
+ enum GMouseMeta_e {
+ GMETA_NONE, // There is no meta event currently happenning
+ GMETA_DOWN, GMETA_UP, // Button 0 has just gone up or down
+ GMETA_CLICK, // Button 0 has just gone through a short down - up cycle
+ GMETA_CXTCLICK // For mice - The right button has just been depressed
+ // For touch - a long press has just occurred
+ } meta;
+ } GEventMouse, GEventTouch;
+
+ // Mouse/Touch Listen Flags - passed to geventAddSourceToListener()
+ #define GLISTEN_MOUSEMETA 0x0001 // Create events for meta events such as CLICK and CXTCLICK
+ #define GLISTEN_MOUSEDOWNMOVES 0x0002 // Creates mouse move events when the primary mouse button is down (touch is on the surface)
+ #define GLISTEN_MOUSEUPMOVES 0x0004 // Creates mouse move events when the primary mouse button is up (touch is off the surface - if the hardware allows).
+ #define GLISTEN_TOUCHMETA 0x0001 // Ditto for touch
+ #define GLISTEN_TOUCHDOWNMOVES 0x0002
+ #define GLISTEN_TOUCHUPMOVES 0x0004
+#endif
+
+#if GINPUT_NEED_KEYBOARD
+ typedef struct GEventKeyboard_t {
+ GEventType type; // The type of this event (GEVENT_KEYBOARD)
+ uint16_t instance; // The keyboard instance
+ char c; // The Ascii code for the current key press.
+ // The only possible values are 0(NUL), 8(BS), 9(TAB), 13(CR), 27(ESC), 32(SPACE) to 126(~), 127(DEL)
+ // 0 indicates an extended only key.
+ uint16_t code; // An extended keyboard code. Codes less than 128 match their ascii equivelent.
+ #define GKEY_NULL 0
+ #define GKEY_BACKSPACE 8
+ #define GKEY_TAB 9
+ #define GKEY_CR 13
+ #define GKEY_ESC 27
+ #define GKEY_SPACE 32
+ #define GKEY_DEL 127
+ #define GKEY_UP 0x0101
+ #define GKEY_DOWN 0x0102
+ #define GKEY_LEFT 0x0103
+ #define GKEY_RIGHT 0x0104
+ #define GKEY_HOME 0x0105
+ #define GKEY_END 0x0106
+ #define GKEY_PAGEUP 0x0107
+ #define GKEY_PAGEDOWN 0x0108
+ #define GKEY_INSERT 0x0109
+ #define GKEY_DELETE 0x010A
+ #define GKEY_SHIFT 0x0201
+ #define GKEY_CNTRL 0x0202
+ #define GKEY_ALT 0x0203
+ #define GKEY_WINKEY 0x0204
+ #define GKEY_RCLKEY 0x0205
+ #define GKEY_FNKEY 0x0206
+ #define GKEY_FN1 0x0301
+ #define GKEY_FN2 0x0302
+ #define GKEY_FN3 0x0303
+ #define GKEY_FN4 0x0304
+ #define GKEY_FN5 0x0305
+ #define GKEY_FN6 0x0306
+ #define GKEY_FN7 0x0307
+ #define GKEY_FN8 0x0308
+ #define GKEY_FN9 0x0309
+ #define GKEY_FN10 0x030A
+ #define GKEY_FN11 0x030B
+ #define GKEY_FN12 0x030C
+ uint16_t current_buttons; // A bit is set to indicate various meta status.
+ #define GMETA_KEYDN 0x0001
+ #define GMETA_SHIFT 0x0002
+ #define GMETA_CNTRL 0x0004
+ #define GMETA_ALT 0x0008
+ #define GMETA_WINKEY 0x0010
+ #define GMETA_RCLKKEY 0x0020
+ #define GMETA_FNKEY 0x0040
+ #define GMETA_MISSED_EVENT 0x8000
+ uint16_t last_buttons; // The value of current_buttons on the last event
+ } GEventKeyboard;
+
+ // Keyboard Listen Flags - passed to geventAddSourceToListener()
+ #define GLISTEN_KEYREPEATS 0x0001 // Return key repeats (where the key is held down to get a repeat character)
+ #define GLISTEN_KEYCODES 0x0002 // Return all key presses including extended code key presses (not just ascii codes)
+ #define GLISTEN_KEYALL 0x0004 // Return keyup's, keydown's and everything in between (but not repeats unless GLISTEN_KEYREPEATS is set).
+ #define GLISTEN_KEYSINGLE 0x8000 // Return only when one particular extended code key is pressed or released. The particular extended code is OR'd into this value
+ // eg. (GLISTEN_KEYSINGLE | GKEY_CR)
+ // No other flags may be set with this flag.
+#endif
+
+#if GINPUT_NEED_TOGGLE
+ typedef struct GEventToggle_t {
+ GEventType type; // The type of this event (GEVENT_TOGGLE)
+ uint16_t instance; // The toggle instance
+ BOOL on; // True if the toggle/button is on
+ } GEventToggle;
+#endif
+
+#if GINPUT_NEED_DIAL
+ typedef struct GEventDial_t {
+ GEventType type; // The type of this event (GEVENT_DIAL)
+ uint16_t instance; // The dial instance
+ uint16_t value; // The dial value
+ } GEventDial;
+#endif
+
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* How to use...
+
+ 1. Get source handles for all the inputs you are interested in.
+ - Attempting to get a handle for one instance of an input more than once will return the same handle
+ 2. Create a listener
+ 3. Assign inputs to your listener.
+ - Inputs can be assigned or released from a listener at any time.
+ - An input can be assigned to more than one listener.
+ 4. Loop on getting listener events
+ 5. When complete destroy the listener
+*/
+
+#if GINPUT_NEED_MOUSE
+ /* Mouse Functions */
+ GSourceHandle ginputGetMouse(uint16_t instance); // Instance = 0 to n-1
+
+ /* Get the current mouse position and button status.
+ * Unlike a listener event, this status cannot record meta events such as "CLICK"
+ * Returns FALSE on error (eg invalid instance)
+ */
+ BOOL ginputGetMouseStatus(uint16_t instance, GEventMouse *pmouse);
+#endif
+
+#if GINPUT_NEED_TOUCH
+ /* Touch Functions */
+ GSourceHandle ginputGetTouch(uint16_t instance); // Instance = 0 to n-1
+
+ /* Get the current touch position and button status.
+ * Unlike a listener event, this status cannot record meta events such as "CLICK"
+ * Returns FALSE on error (eg invalid instance)
+ */
+ BOOL ginputGetTouchStatus(uint16_t instance, GEventTouch *ptouch);
+
+ /* Run a touch calibration.
+ * Returns FALSE if the driver doesn't support it or if the handle is invalid.
+ */
+ BOOL ginputCalibrateTouch(uint16_t instance);
+
+ /* Set the routines to save and fetch calibration data.
+ * This function should be called before first calling ginputGetTouch() for a particular instance
+ * as the gdispGetTouch() routine may attempt to fetch calibration data and perform a startup calibration if there is no way to get it.
+ * If this is called after gdispGetTouch() has been called and the driver requires calibration storage, it will immediately save the data is has already obtained.
+ * The 'requireFree' parameter indicates if the fetch buffer must be free()'d to deallocate the buffer provided by the Fetch routine.
+ */
+ typedef void (*)(uint16_t instance, const uint8_t *calbuf, size_t sz) GTouchCalibrationSaveRoutine; // Save calibration data
+ typedef const char * (*)(uint16_t instance) GTouchCalibrationFetchRoutine; // Fetch calibration data (returns NULL if not data saved)
+ void ginputSetTouchCalibrationRoutines(uint16_t instance, GTouchCalibrationSaveRoutine fnsave, GTouchCalibrationFetchRoutine fnfetch, BOOL requireFree);
+
+ /* Test if a particular touch instance requires routines to save its calibration data. */
+ BOOL ginputRequireTouchCalibrationStorage(uint16_t instance);
+#endif
+
+#if GINPUT_NEED_KEYBOARD
+ /* Keyboard Functions */
+ GSourceHandle ginputGetKeyboard(uint16_t instance); // Instance = 0 to n-1
+
+ /* Get the current keyboard button status.
+ * Returns FALSE on error (eg invalid instance)
+ */
+ BOOL ginputGetKeyboardStatus(uint16_t instance, GEventKeyboard *pkeyboard);
+#endif
+
+#if GINPUT_NEED_TOGGLE
+ /* Hardware Toggle/Switch/Button Functions */
+ GSourceHandle ginputGetToggle(uint16_t instance); // Instance = 0 to n-1
+ void ginputInvertToggle(uint16_t instance, BOOL invert); // If invert is true, invert the on/off sense for the toggle
+
+ /* Get the current toggle status.
+ * Returns FALSE on error (eg invalid instance)
+ */
+ BOOL ginputGetToggleStatus(uint16_t instance, GEventToggle *ptoggle);
+#endif
+
+#if GINPUT_NEED_DIAL
+ /* Dial Functions */
+ GSourceHandle ginputGetDial(uint16_t instance); // Instance = 0 to n-1
+ void ginputResetDialRange(uint16_t instance); // Reset the maximum value back to the hardware default.
+ uint16_t ginputGetDialRange(uint16_t instance); // Get the maximum value. The readings are scaled to be 0...max-1. 0 means over the full uint16_t range.
+ void ginputSetDialRange(uint16_t instance, uint16_t max); // Set the maximum value.
+ void ginputSetDialSensitivity(uint16_t instance, uint16_t diff); // Set the level change required before a dial event is generated.
+ // - This is done after range scaling
+ /* Get the current keyboard button status.
+ * Returns FALSE on error (eg invalid instance)
+ */
+ BOOL ginputGetDialStatus(uint16_t instance, GEventDial *pdial);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_USE_GINPUT */
+
+#endif /* _GINPUT_H */
+/** @} */
diff --git a/include/gtimer.h b/include/gtimer.h
new file mode 100644
index 00000000..2946e0ea
--- /dev/null
+++ b/include/gtimer.h
@@ -0,0 +1,179 @@
+/*
+ ChibiOS/GFX - Copyright (C) 2012
+ Joel Bodenmann aka Tectu <joel@unormal.org>
+
+ This file is part of ChibiOS/GFX.
+
+ ChibiOS/GFX 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.
+
+ ChibiOS/GFX 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 <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @file gtimer.h
+ * @brief GTIMER GFX User Timer subsystem header file.
+ *
+ * @addtogroup GEVENT
+ * @{
+ */
+#ifndef _GTIMER_H
+#define _GTIMER_H
+
+#ifndef GFX_USE_GTIMER
+ #define GFX_USE_GTIMER FALSE
+#endif
+
+#if GFX_USE_GTIMER || defined(__DOXYGEN__)
+
+/**
+ * @name GTIMER macros and more complex functionality to be compiled
+ * @{
+ */
+ /**
+ * @brief Data part of a static GTimer initializer.
+ */
+ #define _GTIMER_DATA() {0}
+ /**
+ * @brief Static GTimer initializer.
+ */
+ #define GTIMER_DECL(name) GTimer name = _GTIMER_DATA()
+ /**
+ * @brief Defines the size of the timer threads work area (stack+structures).
+ * @details Defaults to 512 bytes
+ */
+ #ifndef GTIMER_THREAD_STACK_SIZE
+ #define GTIMER_THREAD_STACK_SIZE 512
+ #endif
+/** @} */
+
+/*===========================================================================*/
+/* Low Level Driver details and error checks. */
+/*===========================================================================*/
+
+#if !CH_USE_MUTEXES || !CH_USE_SEMAPHORES
+ #error "GTIMER: CH_USE_MUTEXES and CH_USE_SEMAPHORES must be defined in chconf.h"
+#endif
+
+/*===========================================================================*/
+/* Type definitions */
+/*===========================================================================*/
+
+// A callback function (executed in a thread context)
+typedef void (*GTimerFunction)(void *param);
+
+// A GTimer structure.
+typedef struct GTimer_t {
+ GTimerFunction fn;
+ void *param;
+ systime_t when;
+ systime_t period;
+ uint16_t flags;
+ struct GTimer_t *next;
+ struct GTimer_t *prev;
+ } GTimer;
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Initialise a timer.
+ *
+ * @param[in] pt pointer to a GTimer structure
+ *
+ * @api
+ */
+void gtimerInit(GTimer *pt);
+
+/**
+ * @brief Set a timer going or alter its properties if it is already going.
+ *
+ * @param[in] pt Pointer to a GTimer structure
+ * @param[in] fn The callback function
+ * @param[in] param The parameter to pass to the callback function
+ * @param[in] periodic Is the timer a periodic timer? FALSE is a once-only timer.
+ * @param[in] millisec The timer period. The following special values are allowed:
+ * TIME_IMMEDIATE causes the callback function to be called asap.
+ * A periodic timer with this value will fire once only.
+ * TIME_INFINITE never timeout (unless triggered by gtimerJab or gtimerJabI)
+ *
+ * @note If the timer is already active its properties are updated with the new parameters.
+ * The current period will be immediately canceled (without the callback function being
+ * called) and the timer will be restart with the new timer properties.
+ * @note The callback function should be careful not to over-run the thread stack.
+ * Define a new value for the macro GTIME_THREAD_STACK_SIZE if you want to
+ * change the default size.
+ * @note The callback function should return as quickly as possible as all
+ * timer callbacks are performed by a single thread. If a callback function
+ * takes too long it could affect the timer response for other timers.
+ * @note A timer callback function is not a replacement for a dedicated thread if the
+ * function wants to perform computationally expensive stuff.
+ * @note As the callback function is called on GTIMER's thread, the function must make sure it uses
+ * appropriate synchronisation controls such as semaphores or mutexes around any data
+ * structures it shares with other threads such as the main application thread.
+ *
+ * @api
+ */
+void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, systime_t millisec);
+
+/**
+ * @brief Stop a timer (periodic or otherwise)
+ *
+ * @param[in] pt Pointer to a GTimer structure
+ *
+ * @note If the timer is not active this does nothing.
+ *
+ * @api
+ */
+void gtimerStop(GTimer *pt);
+
+/**
+ * @brief Jab a timer causing the current period to immediate expire
+ * @details The callback function will be called as soon as possible.
+ *
+ * @pre Use from a normal thread context.
+ *
+ * @param[in] pt Pointer to a GTimer structure
+ *
+ * @note If the timer is not active this does nothing.
+ * @note Repeated Jabs before the callback function actually happens are ignored.
+ *
+ * @api
+ */
+void gtimerJab(GTimer *pt);
+
+/**
+ * @brief Jab a timer causing the current period to immediate expire
+ * @details The callback function will be called as soon as possible.
+ *
+ * @pre Use from an interrupt routine context.
+ *
+ * @param[in] pt Pointer to a GTimer structure
+ *
+ * @note If the timer is not active this does nothing.
+ * @note Repeated Jabs before the callback function actually happens are ignored.
+ *
+ * @api
+ */
+void gtimerJabI(GTimer *pt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GFX_USE_GTIMER */
+
+#endif /* _GTIMER_H */
+/** @} */
diff --git a/releases.txt b/releases.txt
index 83680ae1..caadade2 100644
--- a/releases.txt
+++ b/releases.txt
@@ -14,6 +14,9 @@ FIX: gdisp Win32 driver fixes
DEPRECATE: console deprecated - replaced with gwin functionality
FEATURE: ILI9320 GDISP driver
FEATURE: gdisp Win32 driver - full orientation support
+FEATURE: GEVENT - for passing event structures from Sources to Listeners
+FEATURE: GTIMER - thread context based once-off and periodic timers.
+FEATURE: GINPUT - extensible, multiple device-type, input sub-system.
*** changes after 1.3 ***
diff --git a/src/gevent.c b/src/gevent.c
new file mode 100644
index 00000000..bfda6d53
--- /dev/null
+++ b/src/gevent.c
@@ -0,0 +1,216 @@
+/*
+ ChibiOS/GFX - Copyright (C) 2012
+ Joel Bodenmann aka Tectu <joel@unormal.org>
+
+ This file is part of ChibiOS/GFX.
+
+ ChibiOS/GFX 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.
+
+ ChibiOS/GFX 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file src/gevent.c
+ * @brief GEVENT Driver code.
+ *
+ * @addtogroup GEVENT
+ * @{
+ */
+#include "ch.h"
+#include "hal.h"
+#include "gevent.h"
+
+#ifndef _GEVENT_C
+#define _GEVENT_C
+
+#if GFX_USE_GEVENT || defined(__DOXYGEN__)
+
+#if GEVENT_ASSERT_NO_RESOURCE
+ #define GEVENT_ASSERT(x) assert(x)
+#else
+ #define GEVENT_ASSERT(x)
+#endif
+
+// This mutex protects access to our tables
+static MUTEX_DECL(geventMutex);
+
+// Our table of listener/source pairs
+static GSourceListener Assignments[MAX_SOURCE_LISTENERS];
+
+// Loop through the assignment table deleting this listener/source pair.
+// Null is treated as a wildcard.
+static void deleteAssignments(GListener *pl, GSourceHandle gsh) {
+ GSourceListener *psl;
+
+ for(psl = Assignments; psl < Assignments+MAX_SOURCE_LISTENERS; psl++) {
+ if ((!pl || psl->pListener == pl) && (!gsh || psl->pSource == gsh)) {
+ if (chSemGetCounterI(&psl->pListener->waitqueue) < 0) {
+ chBSemWait(&psl->pListener->eventlock); // Obtain the buffer lock
+ psl->pListener->event.type = GEVENT_EXIT; // Set up the EXIT event
+ chSemSignal(&psl->pListener->waitqueue); // Wake up the listener
+ chBSemSignal(&psl->pListener->eventlock); // Release the buffer lock
+ }
+ psl->pListener = 0;
+ }
+ }
+}
+
+/* Create a Listener.
+ * If insufficient resources are available it will either assert or return NULL
+ * depending on the value of GEVENT_ASSERT_NO_RESOURCE.
+ */
+void geventListenerInit(GListener *pl) {
+ chSemInit(&pl->waitqueue, 0); // Next wait'er will block
+ chBSemInit(&pl->eventlock, FALSE); // Only one thread at a time looking at the event buffer
+ pl->event.type = GEVENT_NULL; // Always safety
+}
+
+/* Attach a source to a listener.
+ * Flags are interpreted by the source when generating events for each listener.
+ * If this source is already assigned to the listener it will update the flags.
+ * If insufficient resources are available it will either assert or return FALSE
+ * depending on the value of GEVENT_ASSERT_NO_RESOURCE.
+ */
+bool_t geventAttachSource(GListener *pl, GSourceHandle gsh, unsigned flags) {
+ GSourceListener *psl, *pslfree;
+
+ // Safety first
+ if (!pl || !gsh) {
+ GEVENT_ASSERT(FALSE);
+ return FALSE;
+ }
+
+ chMtxLock(&geventMutex);
+
+ // Check if this pair is already in the table (scan for a free slot at the same time)
+ pslfree = 0;
+ for(psl = Assignments; psl < Assignments+MAX_SOURCE_LISTENERS; psl++) {
+
+ if (pl == psl->pListener && gsh == psl->pSource) {
+ // Just update the flags
+ chBSemWait(&pl->eventlock); // Safety first - just in case a source is using it
+ psl->listenflags = flags;
+ chBSemSignal(&pl->eventlock); // Release this lock
+ chMtxUnlock();
+ return TRUE;
+ }
+ if (!pslfree && !psl->pListener)
+ pslfree = psl;
+ }
+
+ // A free slot was found - allocate it
+ if (pslfree) {
+ pslfree->pListener = pl;
+ pslfree->pSource = gsh;
+ pslfree->listenflags = flags;
+ pslfree->srcflags = 0;
+ }
+ chMtxUnlock();
+ GEVENT_ASSERT(pslfree != 0);
+ return pslfree != 0;
+}
+
+/* Detach a source from a listener
+ * If gsh is NULL detach all sources from this listener and if there is still
+ * a thread waiting for events on this listener, it is sent the exit event.
+ */
+void geventDetachSource(GListener *pl, GSourceHandle gsh) {
+ if (pl && gsh) {
+ chMtxLock(&geventMutex);
+ deleteAssignments(pl, gsh);
+ if (!gsh && chSemGetCounterI(&pl->waitqueue) < 0) {
+ chBSemWait(&pl->eventlock); // Obtain the buffer lock
+ pl->event.type = GEVENT_EXIT; // Set up the EXIT event
+ chSemSignal(&pl->waitqueue); // Wake up the listener
+ chBSemSignal(&pl->eventlock); // Release the buffer lock
+ }
+ chMtxUnlock();
+ }
+}
+
+/* Wait for an event on a listener from an assigned source.
+ * The type of the event should be checked (pevent->type) and then pevent should be typecast to the
+ * actual event type if it needs to be processed.
+ * timeout specifies the time to wait in system ticks.
+ * TIME_INFINITE means no timeout - wait forever for an event.
+ * TIME_IMMEDIATE means return immediately
+ * Returns NULL on timeout.
+ * Note: The GEvent buffer is staticly allocated within the GListener so the event does not
+ * need to be dynamicly freed however it will get overwritten by the next call to
+ * this routine.
+ */
+GEvent *geventEventWait(GListener *pl, systime_t timeout) {
+ return chSemWaitTimeout(&pl->waitqueue, timeout) == RDY_OK ? &pl->event : 0;
+}
+
+/* Called by a source with a possible event to get a listener record.
+ * 'lastlr' should be NULL on the first call and thereafter the result of the previous call.
+ * It will return NULL when there are no more listeners for this source.
+ */
+GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr) {
+ GSourceListener *psl;
+
+ // Safety first
+ if (!gsh)
+ return 0;
+
+ chMtxLock(&geventMutex);
+
+ // Unlock the last listener event buffer
+ if (lastlr)
+ chBSemSignal(&lastlr->pListener->eventlock);
+
+ // Loop through the table looking for attachments to this source
+ for(psl = lastlr ? (lastlr+1) : Assignments; psl < Assignments+MAX_SOURCE_LISTENERS; psl++) {
+ if (gsh == psl->pSource) {
+ chBSemWait(&psl->pListener->eventlock); // Obtain a lock on the listener event buffer
+ chMtxUnlock();
+ return psl;
+ }
+ }
+ chMtxUnlock();
+ return 0;
+}
+
+/* Get the event buffer from the GSourceListener.
+ * Returns NULL if the listener is not currently listening.
+ * A NULL return allows the source to record (perhaps in glr->scrflags) that the listener has missed events.
+ * This can then be notified as part of the next event for the listener.
+ * The buffer can only be accessed untill the next call to geventGetSourceListener or geventSendEvent
+ */
+GEvent *geventGetEventBuffer(GSourceListener *psl) {
+ // We already know we have the event lock
+ return chSemGetCounterI(&psl->pListener->waitqueue) < 0 ? &psl->pListener->event : 0;
+}
+
+/* Called by a source to indicate the listener's event buffer has been filled.
+ * After calling this function the source must not reference in fields in the GSourceListener or the event buffer.
+ */
+void geventSendEvent(GSourceListener *psl) {
+ chMtxLock(&geventMutex);
+ // Wake up the listener
+ if (chSemGetCounterI(&psl->pListener->waitqueue) < 0)
+ chSemSignal(&psl->pListener->waitqueue);
+ chMtxUnlock();
+}
+
+/* Detach any listener that has this source attached */
+void geventDetachSourceListeners(GSourceHandle gsh) {
+ chMtxLock(&geventMutex);
+ deleteAssignments(0, gsh);
+ chMtxUnlock();
+}
+
+#endif /* GFX_USE_GEVENT */
+
+#endif /* _GEVENT_C */
+/** @} */
diff --git a/src/ginput.c b/src/ginput.c
new file mode 100644
index 00000000..d12bef2b
--- /dev/null
+++ b/src/ginput.c
@@ -0,0 +1,42 @@
+/*
+ ChibiOS/GFX - Copyright (C) 2012
+ Joel Bodenmann aka Tectu <joel@unormal.org>
+
+ This file is part of ChibiOS/GFX.
+
+ ChibiOS/GFX 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.
+
+ ChibiOS/GFX 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file src/ginput.c
+ * @brief GINPUT Driver code.
+ *
+ * @addtogroup GINPUT
+ * @{
+ */
+#include "ch.h"
+#include "hal.h"
+#include "ginput.h"
+
+#ifndef _GINPUT_C
+#define _GINPUT_C
+
+#if GFX_USE_GINPUT || defined(__DOXYGEN__)
+
+#error "GINPUT: Not Implemented Yet"
+
+#endif /* GFX_USE_GINPUT */
+
+#endif /* _GINPUT_C */
+/** @} */
diff --git a/src/gtimer.c b/src/gtimer.c
new file mode 100644
index 00000000..84518a23
--- /dev/null
+++ b/src/gtimer.c
@@ -0,0 +1,302 @@
+/*
+ ChibiOS/GFX - Copyright (C) 2012
+ Joel Bodenmann aka Tectu <joel@unormal.org>
+
+ This file is part of ChibiOS/GFX.
+
+ ChibiOS/GFX 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.
+
+ ChibiOS/GFX 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file src/gtimer.c
+ * @brief GTIMER Driver code.
+ *
+ * @addtogroup GTIMER
+ * @{
+ */
+#include "ch.h"
+#include "hal.h"
+#include "gtimer.h"
+
+#ifndef _GTIMER_C
+#define _GTIMER_C
+
+#if GFX_USE_GTIMER || defined(__DOXYGEN__)
+
+#define GTIMER_FLG_PERIODIC 0x0001
+#define GTIMER_FLG_INFINITE 0x0002
+#define GTIMER_FLG_JABBED 0x0004
+#define GTIMER_FLG_SCHEDULED 0x0008
+
+#define TimeIsWithin(time, start, end) (end > start ? (time >= start && time <= end) : (time >= start || time <= end))
+
+// This mutex protects access to our tables
+static MUTEX_DECL(mutex);
+static Thread *pThread = 0;
+static GTimer *pTimerHead = 0;
+static systime_t lastTime = 0;
+static SEMAPHORE_DECL(waitsem, 0);
+static WORKING_AREA(waTimerThread, GTIMER_THREAD_STACK_SIZE);
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static msg_t GTimerThreadHandler(void *arg) {
+ (void)arg;
+ GTimer *pt;
+ systime_t tm;
+ systime_t nxtTimeout;
+ systime_t tmptime;
+ GTimerFunction fn;
+ void *param;
+
+ #if CH_USE_REGISTRY
+ chRegSetThreadName("GTimer");
+ #endif
+
+ nxtTimeout = TIME_INFINITE;
+ while(1) {
+ /* Wait for work to do. */
+ chSemWaitTimeout(&waitsem, nxtTimeout);
+
+ restartTimerChecks:
+
+ // Our reference time
+ tm = chTimeNow();
+ nxtTimeout = TIME_INFINITE;
+
+ /* We need to obtain the mutex */
+ chMtxLock(&mutex);
+
+ if (pTimerHead) {
+ pt = pTimerHead;
+ do {
+ // Do we have something to do for this timer?
+ if ((pt->flags & GTIMER_FLG_JABBED) || (!(pt->flags & GTIMER_FLG_INFINITE) && TimeIsWithin(pt->when, lastTime, tm))) {
+
+ // Is this timer periodic?
+ if ((pt->flags & GTIMER_FLG_PERIODIC) && pt->period != TIME_IMMEDIATE) {
+ // Yes - Update ready for the next period
+ if (!(pt->flags & GTIMER_FLG_INFINITE)) {
+ do {
+ pt->when += pt->period; // We may have skipped a period
+ } while (TimeIsWithin(pt->when, lastTime, tm));
+ }
+
+ // We are definitely no longer jabbed
+ pt->flags &= ~GTIMER_FLG_JABBED;
+
+ } else {
+ // No - get us off the timers list
+ if (pt->next == pt->prev)
+ pTimerHead = 0;
+ else {
+ pt->next->prev = pt->prev;
+ pt->prev->next = pt->next;
+ if (pTimerHead == pt)
+ pTimerHead = pt->next;
+ }
+ pt->flags = 0;
+ }
+
+ // Call the callback function
+ fn = pt->fn;
+ param = pt->param;
+ chMtxUnlock();
+ fn(param);
+
+ // We no longer hold the mutex, the callback function may have taken a while
+ // and our list may have been altered so start again!
+ goto restartTimerChecks;
+ }
+
+ // Find when we next need to wake up
+ if (!(pt->flags & GTIMER_FLG_INFINITE)) {
+ tmptime = pt->when - tm;
+ if (tmptime < nxtTimeout) nxtTimeout = tmptime;
+ }
+ pt = pt->next;
+ } while(pt != pTimerHead);
+ }
+
+ // Ready for the next loop
+ lastTime = tm;
+ chMtxUnlock();
+ }
+ return 0;
+}
+
+/**
+ * @brief Initialise a timer.
+ *
+ * @param[in] pt pointer to a GTimer structure
+ *
+ * @api
+ */
+void gtimerInit(GTimer *pt) {
+ pt->flags = 0;
+}
+
+/**
+ * @brief Set a timer going or alter its properties if it is already going.
+ *
+ * @param[in] pt Pointer to a GTimer structure
+ * @param[in] fn The callback function
+ * @param[in] param The parameter to pass to the callback function
+ * @param[in] periodic Is the timer a periodic timer? FALSE is a once-only timer.
+ * @param[in] millisec The timer period. The following special values are allowed:
+ * TIME_IMMEDIATE causes the callback function to be called asap.
+ * A periodic timer with this value will fire once only.
+ * TIME_INFINITE never timeout (unless triggered by gtimerJab or gtimerJabI)
+ *
+ * @note If the timer is already active its properties are updated with the new parameters.
+ * The current period will be immediately canceled (without the callback function being
+ * called) and the timer will be restart with the new timer properties.
+ * @note The callback function should be careful not to over-run the thread stack.
+ * Define a new value for the macro GTIME_THREAD_STACK_SIZE if you want to
+ * change the default size.
+ * @note The callback function should return as quickly as possible as all
+ * timer callbacks are performed by a single thread. If a callback function
+ * takes too long it could affect the timer response for other timers.
+ * @note A timer callback function is not a replacement for a dedicated thread if the
+ * function wants to perform computationally expensive stuff.
+ * @note As the callback function is called on GTIMER's thread, the function must make sure it uses
+ * appropriate synchronisation controls such as semaphores or mutexes around any data
+ * structures it shares with other threads such as the main application thread.
+ *
+ * @api
+ */
+void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, systime_t millisec) {
+ chMtxLock(&mutex);
+
+ // Start our thread if not already going
+ if (!pThread)
+ pThread = chThdCreateStatic(waTimerThread, sizeof(waTimerThread), HIGHPRIO, GTimerThreadHandler, NULL);
+
+ // Is this already scheduled?
+ if (pt->flags & GTIMER_FLG_SCHEDULED) {
+ // Cancel it!
+ if (pt->next == pt->prev)
+ pTimerHead = 0;
+ else {
+ pt->next->prev = pt->prev;
+ pt->prev->next = pt->next;
+ if (pTimerHead == pt)
+ pTimerHead = pt->next;
+ }
+ }
+
+ // Set up the timer structure
+ pt->fn = fn;
+ pt->param = param;
+ pt->flags = GTIMER_FLG_SCHEDULED;
+ if (periodic)
+ pt->flags |= GTIMER_FLG_PERIODIC;
+ if (millisec != TIME_INFINITE) {
+ pt->period = MS2ST(millisec);
+ pt->when = chTimeNow() + pt->period;
+ } else
+ pt->flags |= GTIMER_FLG_INFINITE;
+
+ // Just pop it on the end of the queue
+ if (pTimerHead) {
+ pt->next = pTimerHead;
+ pt->prev = pTimerHead->prev;
+ pt->prev->next = pt;
+ pt->next->prev = pt;
+ } else
+ pt->next = pt->prev = pTimerHead = pt;
+
+ // Bump the thread
+ chSemSignal(&waitsem);
+ chMtxUnlock();
+}
+
+/**
+ * @brief Stop a timer (periodic or otherwise)
+ *
+ * @param[in] pt Pointer to a GTimer structure
+ *
+ * @note If the timer is not active this does nothing.
+ *
+ * @api
+ */
+void gtimerStop(GTimer *pt) {
+ chMtxLock(&mutex);
+ if (pt->flags & GTIMER_FLG_SCHEDULED) {
+ // Cancel it!
+ if (pt->next == pt->prev)
+ pTimerHead = 0;
+ else {
+ pt->next->prev = pt->prev;
+ pt->prev->next = pt->next;
+ if (pTimerHead == pt)
+ pTimerHead = pt->next;
+ }
+ // Make sure we know the structure is dead!
+ pt->flags = 0;
+ }
+ chMtxUnlock();
+}
+
+/**
+ * @brief Jab a timer causing the current period to immediate expire
+ * @details The callback function will be called as soon as possible.
+ *
+ * @pre Use from a normal thread context.
+ *
+ * @param[in] pt Pointer to a GTimer structure
+ *
+ * @note If the timer is not active this does nothing.
+ * @note Repeated Jabs before the callback function actually happens are ignored.
+ *
+ * @api
+ */
+void gtimerJab(GTimer *pt) {
+ chMtxLock(&mutex);
+
+ // Jab it!
+ pt->flags |= GTIMER_FLG_JABBED;
+
+ // Bump the thread
+ chSemSignal(&waitsem);
+ chMtxUnlock();
+}
+
+/**
+ * @brief Jab a timer causing the current period to immediate expire
+ * @details The callback function will be called as soon as possible.
+ *
+ * @pre Use from an interrupt routine context.
+ *
+ * @param[in] pt Pointer to a GTimer structure
+ *
+ * @note If the timer is not active this does nothing.
+ * @note Repeated Jabs before the callback function actually happens are ignored.
+ *
+ * @api
+ */
+void gtimerJabI(GTimer *pt) {
+ // Jab it!
+ pt->flags |= GTIMER_FLG_JABBED;
+
+ // Bump the thread
+ chSemSignalI(&waitsem);
+}
+
+#endif /* GFX_USE_GTIMER */
+
+#endif /* _GTIMER_C */
+/** @} */