GEVENT, GTIMER & GINPUT subsystems
GEVENT - for passing event structures from Sources to Listeners GTIMER - thread context based once-off and periodic timers. GINPUT - extensible, multiple device-type, input sub-system. gevent & gtimer are code complete, ginput is definition complete but not code complete.remotes/origin_old/ugfx_release_2.6
parent
633d64b393
commit
a762a629aa
3
gfx.mk
3
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 \
|
||||
|
|
|
@ -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 */
|
||||
/** @} */
|
|
@ -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 */
|
||||
/** @} */
|
|
@ -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 */
|
||||
/** @} */
|
|
@ -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 ***
|
||||
|
|
|
@ -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 */
|
||||
/** @} */
|
|
@ -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 */
|
||||
/** @} */
|
|
@ -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 */
|
||||
/** @} */
|
Loading…
Reference in New Issue