Merge pull request #20 from inmarket/master

GEVENT, GTIMER & GINPUT subsystems
ugfx_release_2.6
Tectu 2012-11-16 10:08:48 -08:00
commit 1ea82a4d37
8 changed files with 1306 additions and 0 deletions

3
gfx.mk
View File

@ -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 \

222
include/gevent.h 100644
View File

@ -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 */
/** @} */

339
include/ginput.h 100644
View File

@ -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 */
/** @} */

179
include/gtimer.h 100644
View File

@ -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 */
/** @} */

View File

@ -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 ***

216
src/gevent.c 100644
View File

@ -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 */
/** @} */

42
src/ginput.c 100644
View File

@ -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 */
/** @} */

302
src/gtimer.c 100644
View File

@ -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 */
/** @} */