diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gdisp.c | 10 | ||||
| -rw-r--r-- | src/gdisp_fonts.c | 3 | ||||
| -rw-r--r-- | src/gevent.c | 54 | ||||
| -rw-r--r-- | src/ginput/dial.c (renamed from src/ginput.c) | 12 | ||||
| -rw-r--r-- | src/ginput/ginput.mk | 4 | ||||
| -rw-r--r-- | src/ginput/keyboard.c | 35 | ||||
| -rw-r--r-- | src/ginput/mouse.c | 554 | ||||
| -rw-r--r-- | src/ginput/toggle.c | 161 | ||||
| -rw-r--r-- | src/gtimer.c | 54 | ||||
| -rw-r--r-- | src/gwin.c | 368 | ||||
| -rw-r--r-- | src/gwin/button.c | 331 | ||||
| -rw-r--r-- | src/gwin/console.c | 210 | ||||
| -rw-r--r-- | src/gwin/gwin.mk | 2 | ||||
| -rw-r--r-- | src/gwin/gwin_internal.h | 53 | 
14 files changed, 1450 insertions, 401 deletions
diff --git a/src/gdisp.c b/src/gdisp.c index 92bb060d..51a43b49 100644 --- a/src/gdisp.c +++ b/src/gdisp.c @@ -32,7 +32,7 @@  #if GFX_USE_GDISP || defined(__DOXYGEN__)
  #ifdef GDISP_NEED_TEXT
 -	#include "gdisp_fonts.h"
 +	#include "gdisp/fonts.h"
  #endif
  /*===========================================================================*/
 @@ -487,13 +487,13 @@  	 *
  	 * @api
  	 */
 -	void gdispDrawArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) {
 +	void gdispDrawArc(coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) {
  		chMtxLock(&gdispMutex);
  		GDISP_LLD(drawarc)(x, y, radius, start, end, color);
  		chMtxUnlock();
  	}
  #elif GDISP_NEED_ARC && GDISP_NEED_ASYNC
 -	void gdispDrawArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) {
 +	void gdispDrawArc(coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) {
  		gdisp_lld_msg_t *p = gdispAllocMsg(GDISP_LLD_MSG_DRAWARC);
  		p->drawarc.x = x;
  		p->drawarc.y = y;
 @@ -518,13 +518,13 @@  	 *
  	 * @api
  	 */
 -	void gdispFillArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) {
 +	void gdispFillArc(coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) {
  		chMtxLock(&gdispMutex);
  		GDISP_LLD(fillarc)(x, y, radius, start, end, color);
  		chMtxUnlock();
  	}
  #elif GDISP_NEED_ARC && GDISP_NEED_ASYNC
 -	void gdispFillArc(coord_t x, coord_t y, coord_t radius, uint16_t start, uint16_t end, color_t color) {
 +	void gdispFillArc(coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) {
  		gdisp_lld_msg_t *p = gdispAllocMsg(GDISP_LLD_MSG_FILLARC);
  		p->fillarc.x = x;
  		p->fillarc.y = y;
 diff --git a/src/gdisp_fonts.c b/src/gdisp_fonts.c index 4384a72e..6a6cd910 100644 --- a/src/gdisp_fonts.c +++ b/src/gdisp_fonts.c @@ -29,7 +29,8 @@  #if GDISP_NEED_TEXT
 -#include "gdisp_fonts.h"
 +#include "gdisp/fonts.h"
 +
  /* fontSmall - for side buttons */
  #if 1
 diff --git a/src/gevent.c b/src/gevent.c index d9c7e4f1..0a18eca2 100644 --- a/src/gevent.c +++ b/src/gevent.c @@ -71,6 +71,7 @@ static void deleteAssignments(GListener *pl, GSourceHandle gsh) {  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->callback = 0;						// No callback active
  	pl->event.type = GEVENT_NULL;			// Always safety
  }
 @@ -165,9 +166,45 @@ void geventDetachSource(GListener *pl, GSourceHandle gsh) {   * @return	NULL on timeout
   */
  GEvent *geventEventWait(GListener *pl, systime_t timeout) {
 +	if (pl->callback || chSemGetCounterI(&pl->waitqueue) < 0)
 +		return 0;
  	return chSemWaitTimeout(&pl->waitqueue, timeout) == RDY_OK ? &pl->event : 0;
  }
 +/* @brief	Register a callback for an event on a listener from an assigned source.
 + * @details	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.
 + *
 + * @params[in] pl		The Listener
 + * @params[in] fn		The function to call back
 + * @params[in] param	A parameter to pass the callback function
 + *
 + * @note	The GEvent buffer is valid only during the time of the callback. The callback MUST NOT save
 + * 			a pointer to the buffer for use outside the callback.
 + * @note	An existing callback function is de-registered by passing a NULL for 'fn'. Any existing
 + * 			callback function is replaced. Any thread currently waiting using geventEventWait will be sent the exit event.
 + * @note	Callbacks occur in a thread context but stack space must be kept to a minumum and
 + * 			the callback must process quickly as all other events are performed on a single thread.
 + * @note	In the callback function you should never call ANY event functions using your own GListener handle
 + * 			as it WILL create a deadlock and lock the system up.
 + * @note	Applications should not use this call - geventEventWait() is the preferred mechanism for an
 + * 			application. This call is provided for GUI objects that may not have their own thread.
 + */
 +void geventRegisterCallback(GListener *pl, GEventCallbackFn fn, void *param) {
 +	if (pl) {
 +		chMtxLock(&geventMutex);
 +		chBSemWait(&pl->eventlock);				// Obtain the buffer lock
 +		pl->param = param;						// Set the param
 +		pl->callback = fn;						// Set the callback function
 +		if (chSemGetCounterI(&pl->waitqueue) < 0) {
 +			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();
 +	}
 +}
 +
  /**
   * @brief	Called by a source with a possible event to get a listener record.
   * @details	@p lastlr should be NULL on the first call and thereafter the result of the previous call.
 @@ -215,7 +252,7 @@ GSourceListener *geventGetSourceListener(GSourceHandle gsh, GSourceListener *las   */
  GEvent *geventGetEventBuffer(GSourceListener *psl) {
  	// We already know we have the event lock
 -	return chSemGetCounterI(&psl->pListener->waitqueue) < 0 ? &psl->pListener->event : 0;
 +	return &psl->pListener->callback || chSemGetCounterI(&psl->pListener->waitqueue) < 0 ? &psl->pListener->event : 0;
  }
  /** 
 @@ -226,10 +263,17 @@ GEvent *geventGetEventBuffer(GSourceListener *psl) {   */
  void geventSendEvent(GSourceListener *psl) {
  	chMtxLock(&geventMutex);
 -	// Wake up the listener
 -	if (chSemGetCounterI(&psl->pListener->waitqueue) < 0)
 -		chSemSignal(&psl->pListener->waitqueue);
 -	chMtxUnlock();
 +	if (psl->pListener->callback) {				// This test needs to be taken inside the mutex
 +		chMtxUnlock();
 +		// We already know we have the event lock
 +		psl->pListener->callback(psl->pListener->param, &psl->pListener->event);
 +
 +	} else {
 +		// Wake up the listener
 +		if (chSemGetCounterI(&psl->pListener->waitqueue) < 0)
 +			chSemSignal(&psl->pListener->waitqueue);
 +		chMtxUnlock();
 +	}
  }
  /**
 diff --git a/src/ginput.c b/src/ginput/dial.c index 9b6b180a..cb6799a9 100644 --- a/src/ginput.c +++ b/src/ginput/dial.c @@ -19,8 +19,8 @@  */
  /**
 - * @file    src/ginput.c
 - * @brief   GINPUT Driver code.
 + * @file    src/ginput/dial.c
 + * @brief   GINPUT dial code.
   *
   * @addtogroup GINPUT
   * @{
 @@ -29,9 +29,7 @@  #include "hal.h"
  #include "ginput.h"
 -#if GFX_USE_GINPUT || defined(__DOXYGEN__)
 -
 -#error "GINPUT: Not Implemented Yet"
 -
 -#endif /* GFX_USE_GINPUT */
 +#if GINPUT_NEED_DIAL || defined(__DOXYGEN__)
 +	#error "GINPUT: GINPUT_NEED_DIAL - Not Implemented Yet"
 +#endif /* GINPUT_NEED_DIAL */
  /** @} */
 diff --git a/src/ginput/ginput.mk b/src/ginput/ginput.mk new file mode 100644 index 00000000..06bcfc07 --- /dev/null +++ b/src/ginput/ginput.mk @@ -0,0 +1,4 @@ +GFXSRC +=   $(GFXLIB)/src/ginput/mouse.c \
 +			$(GFXLIB)/src/ginput/keyboard.c \
 +			$(GFXLIB)/src/ginput/toggle.c \
 +			$(GFXLIB)/src/ginput/dial.c
 diff --git a/src/ginput/keyboard.c b/src/ginput/keyboard.c new file mode 100644 index 00000000..1c38a408 --- /dev/null +++ b/src/ginput/keyboard.c @@ -0,0 +1,35 @@ +/*
 +    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/keyboard.c
 + * @brief   GINPUT keyboard code.
 + *
 + * @addtogroup GINPUT
 + * @{
 + */
 +#include "ch.h"
 +#include "hal.h"
 +#include "ginput.h"
 +
 +#if GINPUT_NEED_KEYBOARD || defined(__DOXYGEN__)
 +	#error "GINPUT: GINPUT_NEED_KEYBOARD - Not Implemented Yet"
 +#endif /* GINPUT_NEED_KEYBOARD */
 +/** @} */
 diff --git a/src/ginput/mouse.c b/src/ginput/mouse.c new file mode 100644 index 00000000..35872a6c --- /dev/null +++ b/src/ginput/mouse.c @@ -0,0 +1,554 @@ +/*
 +    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/mouse.c
 + * @brief   GINPUT mouse/touch code.
 + *
 + * @addtogroup GINPUT_MOUSE
 + * @{
 + */
 +#include "ch.h"
 +#include "hal.h"
 +#include "gtimer.h"
 +#include "ginput.h"
 +
 +#if GINPUT_NEED_MOUSE || defined(__DOXYGEN__)
 +
 +#include "lld/ginput/mouse.h"
 +
 +#if GINPUT_MOUSE_NEED_CALIBRATION
 +	#if !defined(GFX_USE_GDISP) || !GFX_USE_GDISP
 +		#error "GINPUT: GFX_USE_GDISP must be defined when mouse or touch calibration is required"
 +	#endif
 +
 +	#define GINPUT_MOUSE_CALIBRATION_FONT		&fontUI2Double
 +	#define GINPUT_MOUSE_CALIBRATION_TEXT		"Calibration"
 +
 +	#if GINPUT_MOUSE_MAX_CALIBRATION_ERROR < 0
 +		#define GINPUT_MOUSE_CALIBRATION_POINTS		3
 +	#else
 +		#define GINPUT_MOUSE_CALIBRATION_POINTS		4
 +	#endif
 +
 +	typedef struct Calibration_t {
 +	    float ax;
 +	    float bx;
 +	    float cx;
 +	    float ay;
 +	    float by;
 +	    float cy;
 +	} Calibration;
 +#endif
 +
 +typedef struct MousePoint_t {
 +	coord_t		x, y;
 +	} MousePoint;
 +
 +static GTIMER_DECL(MouseTimer);
 +
 +static struct MouseConfig_t {
 +	MouseReading					t;
 +	MousePoint						movepos;
 +	MousePoint						clickpos;
 +	systime_t						clicktime;
 +	uint16_t						last_buttons;
 +	uint16_t						flags;
 +			#define FLG_INIT_DONE		0x8000
 +			#define FLG_CLICK_TIMER		0x0001
 +			#define FLG_IN_CAL			0x0010
 +			#define FLG_CAL_OK			0x0020
 +			#define FLG_CAL_SAVED		0x0040
 +			#define FLG_CAL_FREE		0x0080
 +	#if GINPUT_MOUSE_NEED_CALIBRATION
 +		GMouseCalibrationSaveRoutine	fnsavecal;
 +		GMouseCalibrationLoadRoutine	fnloadcal;
 +		Calibration						caldata;
 +	#endif
 +	} MouseConfig;
 +
 +#if GINPUT_MOUSE_NEED_CALIBRATION
 +	static __inline void _tsDrawCross(const MousePoint *pp) {
 +		gdispDrawLine(pp->x-15, pp->y, pp->x-2, pp->y, White);
 +		gdispDrawLine(pp->x+2, pp->y, pp->x+15, pp->y, White);
 +		gdispDrawLine(pp->x, pp->y-15, pp->x, pp->y-2, White);
 +		gdispDrawLine(pp->x, pp->y+2, pp->x, pp->y+15, White);
 +
 +		gdispDrawLine(pp->x-15, pp->y+15, pp->x-7, pp->y+15, RGB2COLOR(184,158,131));
 +		gdispDrawLine(pp->x-15, pp->y+7, pp->x-15, pp->y+15, RGB2COLOR(184,158,131));
 +
 +		gdispDrawLine(pp->x-15, pp->y-15, pp->x-7, pp->y-15, RGB2COLOR(184,158,131));
 +		gdispDrawLine(pp->x-15, pp->y-7, pp->x-15, pp->y-15, RGB2COLOR(184,158,131));
 +
 +		gdispDrawLine(pp->x+7, pp->y+15, pp->x+15, pp->y+15, RGB2COLOR(184,158,131));
 +		gdispDrawLine(pp->x+15, pp->y+7, pp->x+15, pp->y+15, RGB2COLOR(184,158,131));
 +
 +		gdispDrawLine(pp->x+7, pp->y-15, pp->x+15, pp->y-15, RGB2COLOR(184,158,131));
 +		gdispDrawLine(pp->x+15, pp->y-15, pp->x+15, pp->y-7, RGB2COLOR(184,158,131));
 +	}
 +
 +	static __inline void _tsClearCross(const MousePoint *pp) {
 +		gdispFillArea(pp->x - 15, pp->y - 15, 42, 42, Blue);
 +	}
 +
 +	static __inline void _tsTransform(MouseReading *pt, const Calibration *c) {
 +		pt->x = (coord_t) (c->ax * pt->x + c->bx * pt->y + c->cx);
 +		pt->y = (coord_t) (c->ay * pt->x + c->by * pt->y + c->cy);
 +	}
 +
 +	static __inline void _tsDo3PointCalibration(const MousePoint *cross, const MousePoint *points, Calibration *c) {
 +		float dx, dx0, dx1, dx2, dy0, dy1, dy2;
 +
 +		/* Compute all the required determinants */
 +		dx = ((float)(points[0].x - points[2].x)) * ((float)(points[1].y - points[2].y))
 +			- ((float)(points[1].x - points[2].x)) * ((float)(points[0].y - points[2].y));
 +
 +		dx0 = ((float)(cross[0].x - cross[2].x)) * ((float)(points[1].y - points[2].y))
 +			- ((float)(cross[1].x - cross[2].x)) * ((float)(points[0].y - points[2].y));
 +
 +		dx1 = ((float)(cross[1].x - cross[2].x)) * ((float)(points[0].x - points[2].x))
 +			- ((float)(cross[0].x - cross[2].x)) * ((float)(points[1].x - points[2].x));
 +
 +		dx2 = cross[0].x * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y) -
 +			cross[1].x * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y) +
 +			cross[2].x * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y);
 +
 +		dy0 = ((float)(cross[0].y - cross[2].y))  *  ((float)(points[1].y - points[2].y))
 +			- ((float)(cross[1].y - cross[2].y))  *  ((float)(points[0].y - points[2].y));
 +
 +		dy1  = ((float)(cross[1].y - cross[2].y)) * ((float)(points[0].x - points[2].x))
 +			- ((float)(cross[0].y - cross[2].y)) * ((float)(points[1].x - points[2].x));
 +
 +		dy2  = cross[0].y * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y) -
 +			cross[1].y * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y) +
 +			cross[2].y * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y);
 +
 +		/* Now, calculate all the required coefficients */
 +		c->ax = dx0 / dx;
 +		c->bx = dx1 / dx;
 +		c->cx = dx2 / dx;
 +
 +		c->ay = dy0 / dx;
 +		c->by = dy1 / dx;
 +		c->cy = dy2 / dx;
 +	}
 +#endif
 +
 +#if GINPUT_MOUSE_READ_CYCLES > 1
 +	static void get_raw_reading(MouseReading *pt) {
 +		int32_t x, y, z;
 +		unsigned i;
 +
 +		x = y = z = 0;
 +		for(i = 0; i < GINPUT_MOUSE_READ_CYCLES; i++) {
 +			ginput_lld_mouse_get_reading(pt);
 +			x += pt->x;
 +			y += pt->y;
 +			z += pt->z;
 +		}
 +
 +		/* Take the average of the readings */
 +		pt->x = x / GINPUT_MOUSE_READ_CYCLES;
 +		pt->y = y / GINPUT_MOUSE_READ_CYCLES;
 +		pt->z = z / GINPUT_MOUSE_READ_CYCLES;
 +	}
 +#else
 +	#define get_raw_reading(pt)		ginput_lld_mouse_get_reading(pt)
 +#endif
 +
 +static void get_calibrated_reading(MouseReading *pt) {
 +	#if GINPUT_MOUSE_NEED_CALIBRATION || GDISP_NEED_CONTROL
 +		coord_t		w, h;
 +	#endif
 +
 +	get_raw_reading(pt);
 +
 +	#if GINPUT_MOUSE_NEED_CALIBRATION || GDISP_NEED_CONTROL
 +		w = gdispGetWidth();
 +		h = gdispGetHeight();
 +	#endif
 +
 +	#if GINPUT_MOUSE_NEED_CALIBRATION
 +		_tsTransform(pt, &MouseConfig.caldata);
 +	#endif
 +
 +	#if GDISP_NEED_CONTROL
 +		switch(gdispGetOrientation()) {
 +			case GDISP_ROTATE_0:
 +				break;
 +			case GDISP_ROTATE_90:
 +				{
 +					coord_t t = pt->y;
 +					pt->y = h - 1 - pt->x;
 +					pt->x = t;
 +				}
 +				break;
 +			case GDISP_ROTATE_180:
 +				pt->x = w - 1 - pt->x;
 +				pt->y = h - 1 - pt->y;
 +				break;
 +			case GDISP_ROTATE_270:
 +				{
 +					coord_t t = pt->x;
 +					pt->x = w - 1 - pt->y;
 +					pt->y = t;
 +				}
 +				break;
 +		}
 +	#endif
 +
 +	#if GINPUT_MOUSE_NEED_CALIBRATION
 +		if (pt->x < 0)	pt->x = 0;
 +		else if (pt->x >= w) pt->x = w-1;
 +		if (pt->y < 0)	pt->y = 0;
 +		else if (pt->y >= h) pt->y = h-1;
 +	#endif
 +}
 +
 +static void MousePoll(void *param) {
 +	(void) param;
 +	GSourceListener	*psl;
 +	GEventMouse		*pe;
 +	unsigned 		meta;
 +	uint16_t		tbtns;
 +	uint32_t		cdiff;
 +	uint32_t		mdiff;
 +
 +	// Save the last mouse state
 +	MouseConfig.last_buttons = MouseConfig.t.buttons;
 +
 +	// Get the new mouse reading
 +	get_calibrated_reading(&MouseConfig.t);
 +
 +	// Calculate out new event meta value and handle CLICK and CXTCLICK
 +	meta = GMETA_NONE;
 +
 +	// Calculate the position difference from our movement reference (update the reference if out of range)
 +	mdiff = (MouseConfig.t.x - MouseConfig.movepos.x) * (MouseConfig.t.x - MouseConfig.movepos.x) +
 +		(MouseConfig.t.y - MouseConfig.movepos.y) * (MouseConfig.t.y - MouseConfig.movepos.y);
 +	if (mdiff > GINPUT_MOUSE_MAX_MOVE_JITTER * GINPUT_MOUSE_MAX_MOVE_JITTER) {
 +		MouseConfig.movepos.x = MouseConfig.t.x;
 +		MouseConfig.movepos.y = MouseConfig.t.y;
 +	}
 +	
 +	// Check if the click has moved outside the click area and if so cancel the click
 +	if ((MouseConfig.flags & FLG_CLICK_TIMER)) {
 +		cdiff = (MouseConfig.t.x - MouseConfig.clickpos.x) * (MouseConfig.t.x - MouseConfig.clickpos.x) +
 +			(MouseConfig.t.y - MouseConfig.clickpos.y) * (MouseConfig.t.y - MouseConfig.clickpos.y);
 +		if (cdiff > GINPUT_MOUSE_MAX_CLICK_JITTER * GINPUT_MOUSE_MAX_CLICK_JITTER)
 +			MouseConfig.flags &= ~FLG_CLICK_TIMER;
 +	}
 +
 +	// Mouse down
 +	tbtns = MouseConfig.t.buttons & ~MouseConfig.last_buttons;
 +	if ((tbtns & GINPUT_MOUSE_BTN_LEFT))
 +		meta |= GMETA_MOUSE_DOWN;
 +	if ((tbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) {
 +		MouseConfig.clickpos.x = MouseConfig.t.x;
 +		MouseConfig.clickpos.y = MouseConfig.t.y;
 +		MouseConfig.clicktime = chTimeNow();
 +		MouseConfig.flags |= FLG_CLICK_TIMER;
 +	}
 +
 +	// Mouse up
 +	tbtns = ~MouseConfig.t.buttons & MouseConfig.last_buttons;
 +	if ((tbtns & GINPUT_MOUSE_BTN_LEFT))
 +		meta |= GMETA_MOUSE_UP;
 +	if ((tbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) {
 +		if ((MouseConfig.flags & FLG_CLICK_TIMER)) {
 +			if ((tbtns & GINPUT_MOUSE_BTN_LEFT)
 +					#if GINPUT_MOUSE_CLICK_TIME != TIME_INFINITE
 +						&& chTimeNow() - MouseConfig.clicktime < MS2ST(GINPUT_MOUSE_CLICK_TIME)
 +					#endif
 +					)
 +				meta |= GMETA_MOUSE_CLICK;
 +			else
 +				meta |= GMETA_MOUSE_CXTCLICK;
 +			MouseConfig.flags &= ~FLG_CLICK_TIMER;
 +		}
 +	}
 +
 +	// Send the event to the listeners that are interested.
 +	psl = 0;
 +	while ((psl = geventGetSourceListener((GSourceHandle)(&MouseConfig), psl))) {
 +		if (!(pe = (GEventMouse *)geventGetEventBuffer(psl))) {
 +			// This listener is missing - save the meta events that have happened
 +			psl->srcflags |= meta;
 +			continue;
 +		}
 +
 +		// If we haven't really moved (and there are no meta events) don't bother sending the event
 +		if (mdiff <= GINPUT_MOUSE_MAX_MOVE_JITTER * GINPUT_MOUSE_MAX_MOVE_JITTER && !psl->srcflags && !meta && !(psl->listenflags & GLISTEN_MOUSENOFILTER))
 +			continue;
 +
 +		// Send the event if we are listening for it
 +		if (((MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEDOWNMOVES))
 +				|| (!(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEUPMOVES))
 +				|| (meta && (psl->listenflags & GLISTEN_MOUSEMETA))) {
 +			pe->type = GINPUT_MOUSE_EVENT_TYPE;
 +			pe->instance = 0;
 +			pe->x = MouseConfig.t.x;
 +			pe->y = MouseConfig.t.y;
 +			pe->z = MouseConfig.t.z;
 +			pe->current_buttons = MouseConfig.t.buttons;
 +			pe->last_buttons = MouseConfig.last_buttons;
 +			pe->meta = meta;
 +			if (psl->srcflags) {
 +				pe->current_buttons |= GINPUT_MISSED_MOUSE_EVENT;
 +				pe->meta |= psl->srcflags;
 +				psl->srcflags = 0;
 +			}
 +			geventSendEvent(psl);
 +		}
 +	}
 +}
 +
 +/* Mouse Functions */
 +GSourceHandle ginputGetMouse(uint16_t instance) {
 +	#if GINPUT_MOUSE_NEED_CALIBRATION
 +		Calibration		*pc;
 +	#endif
 +
 +	// We only support a single mouse instance currently
 +	if (instance)
 +		return 0;
 +
 +	// Do we need to initialise the mouse subsystem?
 +	if (!(MouseConfig.flags & FLG_INIT_DONE)) {
 +		ginput_lld_mouse_init();
 +
 +		#if GINPUT_MOUSE_NEED_CALIBRATION
 +			#if GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE
 +				if (!MouseConfig.fnloadcal) {
 +					MouseConfig.fnloadcal = ginput_lld_mouse_calibration_load;
 +					MouseConfig.flags &= ~FLG_CAL_FREE;
 +				}
 +				if (!MouseConfig.fnsavecal)
 +					MouseConfig.fnsavecal = ginput_lld_mouse_calibration_save;
 +			#endif
 +			if (MouseConfig.fnloadcal && (pc = (Calibration *)MouseConfig.fnloadcal(instance))) {
 +				MouseConfig.caldata = pc[0];
 +				MouseConfig.flags |= (FLG_CAL_OK|FLG_CAL_SAVED);
 +				if ((MouseConfig.flags & FLG_CAL_FREE))
 +					chHeapFree((void *)pc);
 +			} else
 +				ginputCalibrateMouse(instance);
 +		#endif
 +
 +		// Get the first reading
 +		MouseConfig.last_buttons = 0;
 +		get_calibrated_reading(&MouseConfig.t);
 +
 +		// Mark init as done and start the Poll timer
 +		MouseConfig.flags |= FLG_INIT_DONE;
 +		gtimerStart(&MouseTimer, MousePoll, 0, TRUE, GINPUT_MOUSE_POLL_PERIOD);
 +	}
 +
 +	// Return our structure as the handle
 +	return (GSourceHandle)&MouseConfig;
 +}
 +
 +/* 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_t ginputGetMouseStatus(uint16_t instance, GEventMouse *pe) {
 +	if (instance || (MouseConfig.flags & (FLG_INIT_DONE|FLG_IN_CAL)) != FLG_INIT_DONE)
 +		return FALSE;
 +
 +	pe->type = GINPUT_MOUSE_EVENT_TYPE;
 +	pe->instance = instance;
 +	pe->x = MouseConfig.t.x;
 +	pe->y = MouseConfig.t.y;
 +	pe->z = MouseConfig.t.z;
 +	pe->current_buttons = MouseConfig.t.buttons;
 +	pe->last_buttons = MouseConfig.last_buttons;
 +	if (pe->current_buttons & ~pe->last_buttons & GINPUT_MOUSE_BTN_LEFT)
 +		pe->meta = GMETA_MOUSE_DOWN;
 +	else if (~pe->current_buttons & pe->last_buttons & GINPUT_MOUSE_BTN_LEFT)
 +		pe->meta = GMETA_MOUSE_UP;
 +	else
 +		pe->meta = GMETA_NONE;
 +	return TRUE;
 +}
 +
 +/* Run a mouse calibration.
 + *	Returns FALSE if the driver doesn't support it or if the handle is invalid.
 + */
 +bool_t ginputCalibrateMouse(uint16_t instance) {
 +	#if !GINPUT_MOUSE_NEED_CALIBRATION
 +		(void) instance;
 +		
 +		return FALSE;
 +	#else
 +
 +		const coord_t height  =  gdispGetHeight();
 +		const coord_t width  =  gdispGetWidth();
 +		const MousePoint cross[]  =  {{(width / 4), (height / 4)},
 +										{(width - (width / 4)) , (height / 4)},
 +										{(width - (width / 4)) , (height - (height / 4))},
 +										{(width / 2), (height / 2)}}; /* Check point */
 +		MousePoint points[GINPUT_MOUSE_CALIBRATION_POINTS];
 +		const MousePoint	*pc;
 +		MousePoint *pt;
 +		int32_t px, py;
 +		unsigned i, j;
 +
 +		if (instance || (MouseConfig.flags & FLG_IN_CAL))
 +			return FALSE;
 +
 +		MouseConfig.flags |= FLG_IN_CAL;
 +		gtimerStop(&MouseTimer);
 +		MouseConfig.flags &= ~(FLG_CAL_OK|FLG_CAL_SAVED);
 +
 +		#if GDISP_NEED_CONTROL
 +			gdispSetOrientation(GDISP_ROTATE_0);
 +		#endif
 +
 +		gdispClear(Blue);
 +
 +		gdispFillStringBox(0, 5, width, 30, GINPUT_MOUSE_CALIBRATION_TEXT, GINPUT_MOUSE_CALIBRATION_FONT,  White, Blue, justifyCenter);
 +
 +		#if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0
 +			do {
 +		#endif
 +				for(i = 0, pt = points, pc = cross; i < GINPUT_MOUSE_CALIBRATION_POINTS; i++, pt++, pc++) {
 +					_tsDrawCross(pc);
 +
 +					do {
 +
 +						/* Wait for the mouse to be pressed */
 +						while(get_raw_reading(&MouseConfig.t), !(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT))
 +							chThdSleepMilliseconds(20);
 +
 +						/* Average all the samples while the mouse is down */
 +						for(px = py = 0, j = 0;
 +								chThdSleepMilliseconds(20),			/* Settling time between readings */
 +								get_raw_reading(&MouseConfig.t),
 +								(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT);
 +								j++) {
 +							px += MouseConfig.t.x;
 +							py += MouseConfig.t.y;
 +						}
 +
 +					} while(!j);
 +
 +					pt->x = px / j;
 +					pt->y = py / j;
 +
 +					_tsClearCross(pc);
 +				}
 +
 +				/* Apply 3 point calibration algorithm */
 +				_tsDo3PointCalibration(cross, points, &MouseConfig.caldata);
 +
 +				 /* Verification of correctness of calibration (optional) :
 +				 *  See if the 4th point (Middle of the screen) coincides with the calibrated
 +				 *  result. If point is within +/- Squareroot(ERROR) pixel margin, then successful calibration
 +				 *  Else, start from the beginning.
 +				 */
 +		#if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0
 +				/* Transform the co-ordinates */
 +				MouseConfig.t.x = points[3].x;
 +				MouseConfig.t.y = points[3].y;
 +				_tsTransform(&MouseConfig.t, &MouseConfig.caldata);
 +
 +				/* Calculate the delta */
 +				px = (MouseConfig.t.x - cross[3].x) * (MouseConfig.t.x - cross[3].x) +
 +					(MouseConfig.t.y - cross[3].y) * (MouseConfig.t.y - cross[3].y);
 +
 +			} while (px > GINPUT_MOUSE_MAX_CALIBRATION_ERROR * GINPUT_MOUSE_MAX_CALIBRATION_ERROR);
 +		#endif
 +
 +		// Restart everything
 +		MouseConfig.flags |= FLG_CAL_OK;
 +		MouseConfig.last_buttons = 0;
 +		get_calibrated_reading(&MouseConfig.t);
 +		MouseConfig.flags &= ~FLG_IN_CAL;
 +		if ((MouseConfig.flags & FLG_INIT_DONE))
 +			gtimerStart(&MouseTimer, MousePoll, 0, TRUE, GINPUT_MOUSE_POLL_PERIOD);
 +		
 +		// Save the calibration data (if possible)
 +		if (MouseConfig.fnsavecal) {
 +			MouseConfig.fnsavecal(instance, (const uint8_t *)&MouseConfig.caldata, sizeof(MouseConfig.caldata));
 +			MouseConfig.flags |= FLG_CAL_SAVED;
 +		}
 +		return TRUE;
 +	#endif
 +}
 +
 +/* Set the routines to save and fetch calibration data.
 + * This function should be called before first calling ginputGetMouse() for a particular instance
 + *	as the gdispGetMouse() 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 gdispGetMouse() 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.
 + */
 +void ginputSetMouseCalibrationRoutines(uint16_t instance, GMouseCalibrationSaveRoutine fnsave, GMouseCalibrationLoadRoutine fnload, bool_t requireFree) {
 +	#if GINPUT_MOUSE_NEED_CALIBRATION
 +		if (instance)
 +			return;
 +
 +		MouseConfig.fnloadcal = fnload;
 +		MouseConfig.fnsavecal = fnsave;
 +		if (requireFree)
 +			MouseConfig.flags |= FLG_CAL_FREE;
 +		else
 +			MouseConfig.flags &= ~FLG_CAL_FREE;
 +		#if GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE
 +			if (!MouseConfig.fnloadcal) {
 +				MouseConfig.fnloadcal = ginput_lld_mouse_calibration_load;
 +				MouseConfig.flags &= ~FLG_CAL_FREE;
 +			}
 +			if (!MouseConfig.fnsavecal)
 +				MouseConfig.fnsavecal = ginput_lld_mouse_calibration_save;
 +		#endif
 +		if (MouseConfig.fnsavecal && (MouseConfig.flags & (FLG_CAL_OK|FLG_CAL_SAVED)) == FLG_CAL_OK) {
 +			MouseConfig.fnsavecal(instance, (const uint8_t *)&MouseConfig.caldata, sizeof(MouseConfig.caldata));
 +			MouseConfig.flags |= FLG_CAL_SAVED;
 +		}
 +	#else
 +		(void)instance, (void)fnsave, (void)fnload, (void)requireFree;
 +	#endif
 +}
 +
 +/* Test if a particular mouse instance requires routines to save its calibration data. */
 +bool_t ginputRequireMouseCalibrationStorage(uint16_t instance) {
 +	if (instance)
 +		return FALSE;
 +
 +	#if GINPUT_MOUSE_NEED_CALIBRATION && !GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE
 +		return TRUE;
 +	#else
 +		return FALSE;
 +	#endif
 +}
 +
 +/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */
 +void ginputMouseWakeup(void) {
 +	gtimerJab(&MouseTimer);
 +}
 +
 +/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */
 +void ginputMouseWakeupI(void) {
 +	gtimerJabI(&MouseTimer);
 +}
 +
 +#endif /* GINPUT_NEED_MOUSE */
 +/** @} */
 diff --git a/src/ginput/toggle.c b/src/ginput/toggle.c new file mode 100644 index 00000000..a49ebfd3 --- /dev/null +++ b/src/ginput/toggle.c @@ -0,0 +1,161 @@ +/*
 +    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/toggle.c
 + * @brief   GINPUT toggle code.
 + *
 + * @addtogroup GINPUT_TOGGLE
 + * @{
 + */
 +#include "ch.h"
 +#include "hal.h"
 +#include "gtimer.h"
 +#include "ginput.h"
 +
 +#if GINPUT_NEED_TOGGLE || defined(__DOXYGEN__)
 +
 +#include "lld/ginput/toggle.h"
 +
 +#ifndef GINPUT_TOGGLE_POLL_PERIOD
 +	#define GINPUT_TOGGLE_POLL_PERIOD	250
 +#endif
 +
 +#define GINPUT_TOGGLE_ISON		0x01
 +#define GINPUT_TOGGLE_INVERT	0x02
 +
 +static GTIMER_DECL(ToggleTimer);
 +static struct GEventToggleStatus_t {
 +	uint8_t		status;
 +} ToggleStatus[GINPUT_TOGGLE_NUM_PORTS];
 +
 +// Our polling function
 +static void TogglePoll(void *param) {
 +	(void) param;
 +
 +	const GToggleConfig	*ptc;
 +	GSourceListener	*psl;
 +	GEventToggle	*pe;
 +	unsigned		i, bits, mask;
 +	uint8_t 		state;
 +	
 +	// Loop while there are bits to get
 +	for(ptc = GInputToggleConfigTable, i=0; i < GINPUT_TOGGLE_NUM_PORTS; ptc++) {
 +	
 +		// Get the next block of bits
 +		bits = ginput_lld_toggle_getbits(ptc) ^ ptc->invert;
 +
 +		// Extract the bits of use
 +		for(mask = ptc->mask; i < GINPUT_TOGGLE_NUM_PORTS && mask; mask >>= 1, bits >>= 1) {
 +			// Ignore bits not in our mask
 +			if (!(mask & 1))
 +				continue;
 +		
 +			// Calculate our new state
 +			state = ToggleStatus[i].status & ~GINPUT_TOGGLE_ISON;
 +			if (state & GINPUT_TOGGLE_INVERT)
 +				bits ^= 1;
 +			if (bits & 1)
 +				state |= GINPUT_TOGGLE_ISON;
 +
 +			// Has it changed?
 +			if ((state ^ ToggleStatus[i].status) & GINPUT_TOGGLE_ISON) {
 +			
 +				// Save the new state
 +				ToggleStatus[i].status = state;
 +				
 +				// Send the event to the listeners that are interested.
 +				psl = 0;
 +				while ((psl = geventGetSourceListener((GSourceHandle)(ToggleStatus+i), psl))) {
 +					if (!(pe = (GEventToggle *)geventGetEventBuffer(psl)))
 +						continue;
 +					if ((state & GINPUT_TOGGLE_ISON)) {
 +						if ((psl->listenflags & GLISTEN_TOGGLE_ON)) {
 +							pe->type = GEVENT_TOGGLE;
 +							pe->instance = i;
 +							pe->on = TRUE;
 +							geventSendEvent(psl);
 +						}
 +					} else {
 +						if ((psl->listenflags & GLISTEN_TOGGLE_OFF)) {
 +							pe->type = GEVENT_TOGGLE;
 +							pe->instance = i;
 +							pe->on = FALSE;
 +							geventSendEvent(psl);
 +						}
 +					}
 +				}
 +			}
 +
 +			// Next toggle switch
 +			i++;
 +		}
 +	}
 +}
 +
 +/* Hardware Toggle/Switch/Button Functions */
 +GSourceHandle ginputGetToggle(uint16_t instance) {
 +	const GToggleConfig	*ptc;
 +
 +	if (instance >= GINPUT_TOGGLE_NUM_PORTS)
 +		return 0;
 +
 +	// Do we need to initialise the toggle subsystem?
 +	if (!gtimerIsActive(&ToggleTimer)) {
 +		for(ptc = GInputToggleConfigTable; ptc < GInputToggleConfigTable+sizeof(GInputToggleConfigTable)/sizeof(GInputToggleConfigTable[0]); ptc++)
 +			ginput_lld_toggle_init(ptc);
 +		gtimerStart(&ToggleTimer, TogglePoll, 0, TRUE, GINPUT_TOGGLE_POLL_PERIOD);
 +	}
 +		
 +	// OK - return this input
 +	return (GSourceHandle)(ToggleStatus+instance);
 +}
 +
 +// If invert is true, invert the on/off sense for the toggle
 +void ginputInvertToggle(uint16_t instance, bool_t invert) {
 +	if (instance >= GINPUT_TOGGLE_NUM_PORTS)
 +		return;
 +	if (invert) {
 +		if (!(ToggleStatus[instance].status & GINPUT_TOGGLE_INVERT)) {
 +			ToggleStatus[instance].status |= GINPUT_TOGGLE_INVERT;
 +			ToggleStatus[instance].status ^= GINPUT_TOGGLE_ISON;
 +		}
 +	} else {
 +		if ((ToggleStatus[instance].status & GINPUT_TOGGLE_INVERT)) {
 +			ToggleStatus[instance].status &= ~GINPUT_TOGGLE_INVERT;
 +			ToggleStatus[instance].status ^= GINPUT_TOGGLE_ISON;
 +		}
 +	}
 +}
 +
 +/* Get the current toggle status.
 + *	Returns FALSE on error (eg invalid instance)
 + */
 +bool_t ginputGetToggleStatus(uint16_t instance, GEventToggle *ptoggle) {
 +	if (instance >= GINPUT_TOGGLE_NUM_PORTS)
 +		return FALSE;
 +	ptoggle->type = GEVENT_TOGGLE;
 +	ptoggle->instance = instance;
 +	ptoggle->on = (ToggleStatus[instance].status & GINPUT_TOGGLE_ISON) ? TRUE : FALSE;
 +	return TRUE;
 +}
 +
 +#endif /* GINPUT_NEED_TOGGLE */
 +/** @} */
 diff --git a/src/gtimer.c b/src/gtimer.c index 98556607..8956a190 100644 --- a/src/gtimer.c +++ b/src/gtimer.c @@ -36,14 +36,14 @@  #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))
 +/* Don't rework this macro to use a ternary operator - the gcc compiler stuffs it up */
 +#define TimeIsWithin(x, start, end)	((end >= start && x >= start && x <= end) || (end < start && (x >= start || x <= 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 BSEMAPHORE_DECL(waitsem, TRUE);
  static WORKING_AREA(waTimerThread, GTIMER_THREAD_STACK_SIZE);
  /*===========================================================================*/
 @@ -55,7 +55,7 @@ static msg_t GTimerThreadHandler(void *arg) {  	GTimer			*pt;
  	systime_t		tm;
  	systime_t		nxtTimeout;
 -	systime_t		tmptime;
 +	systime_t		lastTime;
  	GTimerFunction	fn;
  	void			*param;
 @@ -64,9 +64,11 @@ static msg_t GTimerThreadHandler(void *arg) {  	#endif
  	nxtTimeout = TIME_INFINITE;
 +	lastTime = 0;
  	while(1) {
  		/* Wait for work to do. */
 -		chSemWaitTimeout(&waitsem, nxtTimeout);
 +		chThdYield();					// Give someone else a go no matter how busy we are
 +		chBSemWaitTimeout(&waitsem, nxtTimeout);
  	restartTimerChecks:
 @@ -87,11 +89,13 @@ static msg_t GTimerThreadHandler(void *arg) {  					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 may have skipped a period.
 +							// We use this complicated formulae rather than a loop
 +							//	because the gcc compiler stuffs up the loop so that it
 +							//	either loops forever or doesn't get executed at all.
 +							pt->when += ((tm + pt->period - pt->when) / pt->period) * pt->period;
  						}
 -						
 +
  						// We are definitely no longer jabbed
  						pt->flags &= ~GTIMER_FLG_JABBED;
 @@ -120,10 +124,8 @@ static msg_t GTimerThreadHandler(void *arg) {  				}
  				// Find when we next need to wake up
 -				if (!(pt->flags & GTIMER_FLG_INFINITE)) {
 -					tmptime = pt->when - tm;
 -					if (tmptime < nxtTimeout) nxtTimeout = tmptime;
 -				}
 +				if (!(pt->flags & GTIMER_FLG_INFINITE) && pt->when - tm < nxtTimeout)
 +					nxtTimeout = pt->when - tm;
  				pt = pt->next;
  			} while(pt != pTimerHead);
  		}
 @@ -201,11 +203,13 @@ void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, sy  	pt->flags = GTIMER_FLG_SCHEDULED;
  	if (periodic)
  		pt->flags |= GTIMER_FLG_PERIODIC;
 -	if (millisec != TIME_INFINITE) {
 +	if (millisec == TIME_INFINITE) {
 +		pt->flags |= GTIMER_FLG_INFINITE;
 +		pt->period = TIME_INFINITE;
 +	} else {
  		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) {
 @@ -217,7 +221,8 @@ void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, sy  		pt->next = pt->prev = pTimerHead = pt;
  	// Bump the thread
 -	chSemSignal(&waitsem);
 +	if (!(pt->flags & GTIMER_FLG_INFINITE))
 +		chBSemSignal(&waitsem);
  	chMtxUnlock();
  }
 @@ -249,6 +254,17 @@ void gtimerStop(GTimer *pt) {  }
  /**
 + * @brief   Test if a timer is currently active
 + *
 + * @param[in] pt		Pointer to a GTimer structure
 + *
 + * @api
 + */
 +bool_t gtimerIsActive(GTimer *pt) {
 +	return (pt->flags & GTIMER_FLG_SCHEDULED) ? TRUE : FALSE;
 +}
 +
 +/**
   * @brief   			Jab a timer causing the current period to immediate expire
   * @details				The callback function will be called as soon as possible.
   *
 @@ -268,7 +284,7 @@ void gtimerJab(GTimer *pt) {  	pt->flags |= GTIMER_FLG_JABBED;
  	// Bump the thread
 -	chSemSignal(&waitsem);
 +	chBSemSignal(&waitsem);
  	chMtxUnlock();
  }
 @@ -291,7 +307,7 @@ void gtimerJabI(GTimer *pt) {  	pt->flags |= GTIMER_FLG_JABBED;
  	// Bump the thread
 -	chSemSignalI(&waitsem);
 +	chBSemSignalI(&waitsem);
  }
  #endif /* GFX_USE_GTIMER */
 @@ -31,17 +31,11 @@  #if GFX_USE_GWIN || defined(__DOXYGEN__)
 -#include <string.h>
 -
 -#define GWIN_CONSOLE_USE_CLEAR_LINES			TRUE
 -#define GWIN_CONSOLE_USE_FILLED_CHARS			FALSE
 -
 -#define GWIN_FLG_DYNAMIC				0x0001
 -#define GWIN_FIRST_CONTROL_FLAG			0x0002
 -#define GBTN_FLG_ALLOCTXT				(GWIN_FIRST_CONTROL_FLAG<<0)
 +#include "gwin/gwin_internal.h"
 +// Internal routine for use by GWIN components only
  // Initialise a window creating it dynamicly if required.
 -static GHandle gwinInit(GWindowObject *gw, coord_t x, coord_t y, coord_t width, coord_t height, size_t size) {
 +GHandle _gwinInit(GWindowObject *gw, coord_t x, coord_t y, coord_t width, coord_t height, size_t size) {
  	coord_t	w, h;
  	// Check the window size against the screen size
 @@ -90,7 +84,7 @@ static GHandle gwinInit(GWindowObject *gw, coord_t x, coord_t y, coord_t width,   * @api
   */
  GHandle gwinCreateWindow(GWindowObject *gw, coord_t x, coord_t y, coord_t width, coord_t height) {
 -	if (!(gw = (GWindowObject *)gwinInit((GWindowObject *)gw, x, y, width, height, sizeof(GWindowObject))))
 +	if (!(gw = (GWindowObject *)_gwinInit((GWindowObject *)gw, x, y, width, height, sizeof(GWindowObject))))
  		return 0;
  	gw->type = GW_WINDOW;
  	return (GHandle)gw;
 @@ -536,360 +530,6 @@ void gwinFillStringBox(GHandle gh, coord_t x, coord_t y, coord_t cx, coord_t cy,  }
  #endif
 -/*------------------------------------------------------------------------------------------------------------------------*/
 -
 -#if GWIN_NEED_CONSOLE || defined(__DOXYGEN__)
 -
 -/*
 - * Stream interface implementation. The interface is write only
 - */
 -
 -#define Stream2GWindow(ip)		((GHandle)(((char *)(ip)) - (size_t)(&(((GConsoleObject *)0)->stream))))
 -
 -static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; }
 -static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) {	(void)ip; (void)bp; (void)n; return 0; }
 -static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; }
 -static msg_t GWinStreamGet(void *ip) {(void)ip; return RDY_OK; }
 -static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; }
 -static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return RDY_OK; }
 -static size_t GWinStreamWriteTimed(void *ip, const uint8_t *bp, size_t n, systime_t time) { (void)time; gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; }
 -static size_t GWinStreamReadTimed(void *ip, uint8_t *bp, size_t n, systime_t time) { (void)ip; (void)bp; (void)n; (void)time; return 0; }
 -
 -struct GConsoleWindowVMT_t {
 -	_base_asynchronous_channel_methods
 -};
 -
 -static const struct GConsoleWindowVMT_t GWindowConsoleVMT = {
 -	GWinStreamWrite,
 -	GWinStreamRead,
 -	GWinStreamPut,
 -	GWinStreamGet,
 -	GWinStreamPutTimed,
 -	GWinStreamGetTimed,
 -	GWinStreamWriteTimed,
 -	GWinStreamReadTimed
 -};
 -
 -/**
 - * @brief   Create a console window.
 - * @details	A console window allows text to be written using chprintf() (and the console functions defined here).
 - * @brief	Text in a console window supports newlines and will wrap text as required.
 - * @return  NULL if there is no resultant drawing area, otherwise a window handle.
 - *
 - * @param[in] gc		The GConsoleObject structure to initialise. If this is NULL the structure is dynamically allocated.
 - * @param[in] x,y		The screen co-ordinates for the bottom left corner of the window
 - * @param[in] width		The width of the window
 - * @param[in] height	The height of the window
 - * @param[in] font		The font to use
 - * @note				The console is not automatically cleared on creation. You must do that by calling gwinClear() (possibly after changing your background color)
 - * @note				If the dispay does not support scrolling, the window will be cleared when the bottom line is reached.
 - * @note				The default drawing color gets set to White and the background drawing color to Black.
 - * @note				The dimensions and position may be changed to fit on the real screen.
 - *
 - * @api
 - */
 -GHandle gwinCreateConsole(GConsoleObject *gc, coord_t x, coord_t y, coord_t width, coord_t height, font_t font) {
 -	if (!(gc = (GConsoleObject *)gwinInit((GWindowObject *)gc, x, y, width, height, sizeof(GConsoleObject))))
 -		return 0;
 -	gc->gwin.type = GW_CONSOLE;
 -	gwinSetFont(&gc->gwin, font);
 -	gc->stream.vmt = &GWindowConsoleVMT;
 -	gc->cx = 0;
 -	gc->cy = 0;
 -	return (GHandle)gc;
 -}
 -
 -/**
 - * @brief   Get a stream from a console window suitable for use with chprintf().
 - * @return	The stream handle or NULL if this is not a console window.
 - *
 - * @param[in] gh	The window handle (must be a console window)
 - *
 - * @api
 - */
 -BaseSequentialStream *gwinGetConsoleStream(GHandle gh) {
 -	if (gh->type != GW_CONSOLE)
 -		return 0;
 -	return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream);
 -}
 -
 -/**
 - * @brief   Put a character at the cursor position in the window.
 - * @note	Uses the current foreground color to draw the character and fills the background using the background drawing color
 - *
 - * @param[in] gh	The window handle (must be a console window)
 - * @param[in] c		The character to draw
 - *
 - * @api
 - */
 -void gwinPutChar(GHandle gh, char c) {
 -	uint8_t			width;
 -	#define gcw		((GConsoleObject *)gh)
 -
 -	if (gh->type != GW_CONSOLE || !gh->font) return;
 -
 -	#if GDISP_NEED_CLIP
 -		gdispSetClip(gh->x, gh->y, gh->width, gh->height);
 -	#endif
 -	
 -	if (c == '\n') {
 -		gcw->cx = 0;
 -		gcw->cy += gcw->fy;
 -		// We use lazy scrolling here and only scroll when the next char arrives
 -	} else if (c == '\r') {
 -		// gcw->cx = 0;
 -	} else {
 -		width = gdispGetCharWidth(c, gh->font) + gcw->fp;
 -		if (gcw->cx + width >= gh->width) {
 -			gcw->cx = 0;
 -			gcw->cy += gcw->fy;
 -		}
 -
 -		if (gcw->cy + gcw->fy > gh->height) {
 -#if GDISP_NEED_SCROLL
 -			/* scroll the console */
 -			gdispVerticalScroll(gh->x, gh->y, gh->width, gh->height, gcw->fy, gh->bgcolor);
 -			/* reset the cursor to the start of the last line */
 -			gcw->cx = 0;
 -			gcw->cy = (((coord_t)(gh->height/gcw->fy))-1)*gcw->fy;
 -#else
 -			/* clear the console */
 -			gdispFillArea(gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
 -			/* reset the cursor to the top of the window */
 -			gcw->cx = 0;
 -			gcw->cy = 0;
 -#endif
 -		}
 -
 -#if GWIN_CONSOLE_USE_CLEAR_LINES
 -		/* clear to the end of the line */
 -		if (gcw->cx == 0)
 -			gdispFillArea(gh->x, gh->y + gcw->cy, gh->width, gcw->fy, gh->bgcolor);
 -#endif
 -#if GWIN_CONSOLE_USE_FILLED_CHARS
 -		gdispFillChar(gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color, gh->bgcolor);
 -#else
 -		gdispDrawChar(gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color);
 -#endif
 -
 -		/* update cursor */
 -		gcw->cx += width;
 -	}
 -	#undef gcw
 -}
 -
 -/**
 - * @brief   Put a string at the cursor position in the window. It will wrap lines as required.
 - * @note	Uses the current foreground color to draw the string and fills the background using the background drawing color
 - *
 - * @param[in] gh	The window handle (must be a console window)
 - * @param[in] str	The string to draw
 - *
 - * @api
 - */
 -void gwinPutString(GHandle gh, const char *str) {
 -	while(*str)
 -		gwinPutChar(gh, *str++);
 -}
 -
 -/**
 - * @brief   Put the character array at the cursor position in the window. It will wrap lines as required.
 - * @note	Uses the current foreground color to draw the string and fills the background using the background drawing color
 - *
 - * @param[in] gh	The window handle (must be a console window)
 - * @param[in] str	The string to draw
 - * @param[in] n		The number of characters to draw
 - *
 - * @api
 - */
 -void gwinPutCharArray(GHandle gh, const char *str, size_t n) {
 -	while(n--)
 -		gwinPutChar(gh, *str++);
 -}
 -#endif
 -
 -/*------------------------------------------------------------------------------------------------------------------------*/
 -
 -#if GWIN_NEED_BUTTON || defined(__DOXYGEN__)
 -
 -static const GButtonStyle GButtonDefaultStyle = {
 -	GBTN_3D,
 -	HTML2COLOR(0x404040),		// color_up_edge;
 -	HTML2COLOR(0xE0E0E0),		// color_up_fill;
 -	HTML2COLOR(0x000000),		// color_up_txt;
 -	HTML2COLOR(0x404040),		// color_dn_edge;
 -	HTML2COLOR(0x808080),		// color_dn_fill;
 -	HTML2COLOR(0x404040),		// color_dn_txt;
 -	};
 -
 -/**
 - * @brief   Create a button window.
 - * @return  NULL if there is no resultant drawing area, otherwise a window handle.
 - *
 - * @param[in] gb		The GConsoleWindow structure to initialise. If this is NULL the structure is dynamically allocated.
 - * @param[in] x,y		The screen co-ordinates for the bottom left corner of the window
 - * @param[in] width		The width of the window
 - * @param[in] height	The height of the window
 - * @param[in] font		The font to use
 - * @param[in] type		The type of button
 - * @note				The drawing color gets set to White and the background drawing color to Black.
 - * @note				The dimensions and position may be changed to fit on the real screen.
 - * @note				The button is not automatically drawn. Call gwinButtonDraw() after changing the button style or setting the text.
 - *
 - * @api
 - */
 -GHandle gwinCreateButton(GButtonObject *gb, coord_t x, coord_t y, coord_t width, coord_t height, font_t font, GButtonType type) {
 -	if (!(gb = (GButtonObject *)gwinInit((GWindowObject *)gb, x, y, width, height, sizeof(GButtonObject))))
 -		return 0;
 -	gb->gwin.type = GW_BUTTON;
 -	gwinSetFont(&gb->gwin, font);
 -	gwinSetButtonStyle(&gb->gwin, &GButtonDefaultStyle);
 -	gb->type = type;
 -	gb->state = GBTN_UP;
 -	gb->txt = "";
 -	gb->callback = 0;
 -	gb->inputsrc = 0;
 -	return (GHandle)gb;
 -}
 -
 -/**
 - * @brief   Set the style of a button.
 - * @details	The button style is defined by its shape and colours.
 - *
 - * @param[in] gh		The window handle (must be a button window)
 - * @param[in] style		The button style to set.
 - * @note				The button is not automatically redrawn. Call gwinButtonDraw() after changing the button style
 - *
 - * @api
 - */
 -void gwinSetButtonStyle(GHandle gh, const GButtonStyle *style) {
 -	#define gbw		((GButtonObject *)gh)
 -	if (gh->type != GW_BUTTON)
 -		return;
 -	
 -	gbw->style.shape = style->shape;
 -	gbw->style.color_up_edge = style->color_up_edge;
 -	gbw->style.color_up_fill = style->color_up_fill;
 -	gbw->style.color_dn_edge = style->color_dn_edge;
 -	gbw->style.color_dn_fill = style->color_dn_fill;
 -	gbw->style.color_up_txt = style->color_up_txt;
 -	gbw->style.color_dn_txt = style->color_dn_txt;
 -	#undef gbw
 -}
 -
 -/**
 - * @brief   Set the text of a button.
 - *
 - * @param[in] gh		The window handle (must be a button window)
 - * @param[in] txt		The button text to set. This must be a constant string unless useAlloc is set.
 - * @param[in] useAlloc	If TRUE the string specified will be copied into dynamically allocated memory.
 - * @note				The button is not automatically redrawn. Call gwinButtonDraw() after changing the button text.
 - *
 - * @api
 - */
 -void gwinSetButtonText(GHandle gh, const char *txt, bool_t useAlloc) {
 -	#define gbw		((GButtonObject *)gh)
 -	if (gh->type != GW_BUTTON)
 -		return;
 -
 -	// Dispose of the old string
 -	if ((gh->flags & GBTN_FLG_ALLOCTXT)) {
 -		gh->flags &= ~GBTN_FLG_ALLOCTXT;
 -		if (gbw->txt) {
 -			chHeapFree((void *)gbw->txt);
 -			gbw->txt = "";
 -		}
 -	}
 -	// Alloc the new text if required
 -	if (txt && useAlloc) {
 -		char *str;
 -		
 -		if ((str = (char *)chHeapAlloc(NULL, strlen(txt)+1))) {
 -			gh->flags |= GBTN_FLG_ALLOCTXT;
 -			strcpy(str, txt);
 -		}
 -		txt = (const char *)str;
 -	}
 -	
 -	gbw->txt = txt ? txt : "";
 -	#undef gbw
 -}
 -
 -/**
 - * @brief   Redraw the button.
 - *
 - * @param[in] gh		The window handle (must be a button window)
 - *
 - * @api
 - */
 -void gwinButtonDraw(GHandle gh) {
 -	color_t			cedge;
 -	color_t			cfill;
 -	color_t			ctxt;
 -	const char *	txt;
 -	#define gbw		((GButtonObject *)gh)
 -	#define RND_CNR_SIZE	5
 -	
 -	if (gh->type != GW_BUTTON)
 -		return;
 -
 -	#if GDISP_NEED_CLIP
 -		gdispSetClip(gh->x, gh->y, gh->width, gh->height);
 -	#endif
 -	
 -	// Get the text (safely)
 -	txt = gh->font && gbw->txt ? gbw->txt : "";
 -	
 -	// Determine the colors to use
 -	switch(gbw->state) {
 -	case GBTN_DOWN:
 -		cedge = gbw->style.color_dn_edge;
 -		cfill = gbw->style.color_dn_fill;
 -		ctxt = gbw->style.color_dn_txt;
 -		break;
 -	case GBTN_UP:	default:
 -		cedge = gbw->style.color_up_edge;
 -		cfill = gbw->style.color_up_fill;
 -		ctxt = gbw->style.color_up_txt;
 -		break;
 -	}
 -	
 -	// Draw according to the shape specified.
 -	switch(gbw->style.shape) {
 -#if GDISP_NEED_ARC
 -	case GBTN_ROUNDED:
 -		if (gh->width >= 2*RND_CNR_SIZE+10) {
 -			gdispFillRoundedBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, RND_CNR_SIZE-1, cfill);
 -			gdispDrawStringBox(gh->x+1, gh->y+RND_CNR_SIZE, gh->width-2, gh->height-(2*RND_CNR_SIZE), txt, gh->font, ctxt, justifyCenter);
 -			gdispDrawRoundedBox(gh->x, gh->y, gh->width, gh->height, RND_CNR_SIZE, cedge);
 -			break;
 -		}
 -		/* Fall Through */
 -#endif
 -	case GBTN_SQUARE:
 -		gdispFillStringBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, txt, gh->font, ctxt, cfill, justifyCenter);
 -		gdispDrawBox(gh->x, gh->y, gh->width, gh->height, cedge);
 -		break;
 -#if GDISP_NEED_ELLIPSE
 -	case GBTN_ELLIPSE:
 -		gdispFillEllipse(gh->x+1, gh->y+1, gh->width/2-1, gh->height/2-1, cfill);
 -		gdispDrawStringBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, txt, gh->font, ctxt, justifyCenter);
 -		gdispDrawEllipse(gh->x, gh->y, gh->width/2, gh->height/2, cedge);
 -		break;
 -#endif
 -	case GBTN_3D:	default:
 -		gdispFillStringBox(gh->x, gh->y, gh->width-1, gh->height-1, txt, gh->font, ctxt, cfill, justifyCenter);
 -		gdispDrawLine(gh->x+gh->width-1, gh->y, gh->x+gh->width-1, gh->y+gh->height-1, cedge);
 -		gdispDrawLine(gh->x, gh->y+gh->height-1, gh->x+gh->width-2, gh->y+gh->height-1, cedge);
 -		break;
 -	}
 -	#undef gbw
 -}
 -
 -//void gwinSetButtonCallback(GHandle gh, ????);
 -//void gwinSetButtonInput(GHandle gh, ????);
 -#endif
 -
  #endif /* GFX_USE_GWIN */
  /** @} */
 diff --git a/src/gwin/button.c b/src/gwin/button.c new file mode 100644 index 00000000..f62a86a4 --- /dev/null +++ b/src/gwin/button.c @@ -0,0 +1,331 @@ +/*
 +    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/gwin/button.c
 + * @brief   GWIN Driver code.
 + *
 + * @addtogroup GWIN_BUTTON
 + * @{
 + */
 +#include "ch.h"
 +#include "hal.h"
 +#include "gwin.h"
 +#include "ginput.h"
 +
 +#if !defined(GFX_USE_GINPUT) || !GFX_USE_GINPUT
 +	#error "GWIN Buttons require GFX_USE_GINPUT"
 +#endif
 +
 +#if (GFX_USE_GWIN && GWIN_NEED_BUTTON) || defined(__DOXYGEN__)
 +
 +#include <string.h>
 +#include "gwin_internal.h"
 +
 +static const GButtonStyle GButtonDefaultStyle = {
 +	GBTN_3D,
 +	HTML2COLOR(0x404040),		// color_up_edge;
 +	HTML2COLOR(0xE0E0E0),		// color_up_fill;
 +	HTML2COLOR(0x000000),		// color_up_txt;
 +	HTML2COLOR(0x404040),		// color_dn_edge;
 +	HTML2COLOR(0x808080),		// color_dn_fill;
 +	HTML2COLOR(0x404040),		// color_dn_txt;
 +	};
 +
 +// Process an event callback
 +static void gwinButtonCallback(void *param, GEvent *pe) {
 +	GSourceListener	*psl;
 +	#define gh		((GHandle)param)
 +	#define gbw		((GButtonObject *)param)
 +	#define gsh		((GSourceHandle)param)
 +	#define pme		((GEventMouse *)pe)
 +	#define pte		((GEventTouch *)pe)
 +	#define pxe		((GEventToggle *)pe)
 +	#define pbe		((GEventGWinButton *)pe)
 +
 +	switch (pe->type) {
 +	#if defined(GINPUT_NEED_MOUSE) && GINPUT_NEED_MOUSE
 +		case GEVENT_MOUSE:
 +		case GEVENT_TOUCH:
 +			// Ignore anything other than the primary mouse button going up or down
 +			if (!((pme->current_buttons ^ pme->last_buttons) & GINPUT_MOUSE_BTN_LEFT))
 +				return;
 +
 +			if (gbw->state == GBTN_UP) {
 +				// Our button is UP: Test for button down over the button
 +				if ((pme->current_buttons & GINPUT_MOUSE_BTN_LEFT)
 +						&& pme->x >= gbw->gwin.x && pme->x < gbw->gwin.x + gbw->gwin.width
 +						&& pme->y >= gbw->gwin.y && pme->y < gbw->gwin.y + gbw->gwin.height) {
 +					gbw->state = GBTN_DOWN;
 +					gwinButtonDraw((GHandle)param);
 +				}
 +				return;
 +			}
 +
 +			// Our button is DOWN
 +
 +			// Skip more mouse downs
 +			if ((pme->current_buttons & GINPUT_MOUSE_BTN_LEFT))
 +				return;
 +
 +			// This must be a mouse up - set the button as UP
 +			gbw->state = GBTN_UP;
 +			gwinButtonDraw((GHandle)param);
 +
 +			// If the mouse up was over the button then create the event
 +			if (pme->x >= gbw->gwin.x && pme->x < gbw->gwin.x + gbw->gwin.width
 +					&& pme->y >= gbw->gwin.y && pme->y < gbw->gwin.y + gbw->gwin.height)
 +				break;
 +
 +			return;
 +	#endif
 +
 +	#if defined(GINPUT_NEED_TOGGLE) && GINPUT_NEED_TOGGLE
 +		case GEVENT_TOGGLE:
 +			// State has changed - update the button
 +			gbw->state = pxe->on ? GBTN_DOWN : GBTN_UP;
 +			gwinButtonDraw((GHandle)param);
 +
 +			// Trigger the event on button down (different than for mouse/touch)
 +			if (gbw->state == GBTN_DOWN)
 +				break;
 +
 +			return;
 +	#endif
 +
 +	default:
 +		return;
 +	}
 +
 +	// Trigger a GWIN Button Event
 +	psl = 0;
 +	while ((psl = geventGetSourceListener(gsh, psl))) {
 +		if (!(pe = geventGetEventBuffer(psl)))
 +			continue;
 +		pbe->type = GEVENT_GWIN_BUTTON;
 +		pbe->button = gh;
 +		geventSendEvent(psl);
 +	}
 +
 +	#undef pbe
 +	#undef pme
 +	#undef pte
 +	#undef pxe
 +	#undef gsh
 +	#undef gbw
 +	#undef gh
 +}
 +
 +/**
 + * @brief   Create a button window.
 + * @return  NULL if there is no resultant drawing area, otherwise a window handle.
 + *
 + * @param[in] gb		The GConsoleWindow structure to initialise. If this is NULL the structure is dynamically allocated.
 + * @param[in] x,y		The screen co-ordinates for the bottom left corner of the window
 + * @param[in] width		The width of the window
 + * @param[in] height	The height of the window
 + * @param[in] font		The font to use
 + * @param[in] type		The type of button
 + * @note				The drawing color gets set to White and the background drawing color to Black.
 + * @note				The dimensions and position may be changed to fit on the real screen.
 + * @note				The button is not automatically drawn. Call gwinButtonDraw() after changing the button style or setting the text.
 + *
 + * @api
 + */
 +GHandle gwinCreateButton(GButtonObject *gb, coord_t x, coord_t y, coord_t width, coord_t height, font_t font, GButtonType type) {
 +	if (!(gb = (GButtonObject *)_gwinInit((GWindowObject *)gb, x, y, width, height, sizeof(GButtonObject))))
 +		return 0;
 +	gb->gwin.type = GW_BUTTON;
 +	gwinSetFont(&gb->gwin, font);
 +	gwinSetButtonStyle(&gb->gwin, &GButtonDefaultStyle);
 +	gb->type = type;
 +	gb->state = GBTN_UP;
 +	gb->txt = "";
 +	geventListenerInit(&gb->listener);
 +	geventRegisterCallback(&gb->listener, gwinButtonCallback, gb);
 +	return (GHandle)gb;
 +}
 +
 +/**
 + * @brief   Set the style of a button.
 + * @details	The button style is defined by its shape and colours.
 + *
 + * @param[in] gh		The window handle (must be a button window)
 + * @param[in] style		The button style to set.
 + * @note				The button is not automatically redrawn. Call gwinButtonDraw() after changing the button style
 + *
 + * @api
 + */
 +void gwinSetButtonStyle(GHandle gh, const GButtonStyle *style) {
 +	#define gbw		((GButtonObject *)gh)
 +	if (gh->type != GW_BUTTON)
 +		return;
 +	
 +	gbw->style.shape = style->shape;
 +	gbw->style.color_up_edge = style->color_up_edge;
 +	gbw->style.color_up_fill = style->color_up_fill;
 +	gbw->style.color_dn_edge = style->color_dn_edge;
 +	gbw->style.color_dn_fill = style->color_dn_fill;
 +	gbw->style.color_up_txt = style->color_up_txt;
 +	gbw->style.color_dn_txt = style->color_dn_txt;
 +	#undef gbw
 +}
 +
 +/**
 + * @brief   Set the text of a button.
 + *
 + * @param[in] gh		The window handle (must be a button window)
 + * @param[in] txt		The button text to set. This must be a constant string unless useAlloc is set.
 + * @param[in] useAlloc	If TRUE the string specified will be copied into dynamically allocated memory.
 + * @note				The button is not automatically redrawn. Call gwinButtonDraw() after changing the button text.
 + *
 + * @api
 + */
 +void gwinSetButtonText(GHandle gh, const char *txt, bool_t useAlloc) {
 +	#define gbw		((GButtonObject *)gh)
 +	if (gh->type != GW_BUTTON)
 +		return;
 +
 +	// Dispose of the old string
 +	if ((gh->flags & GBTN_FLG_ALLOCTXT)) {
 +		gh->flags &= ~GBTN_FLG_ALLOCTXT;
 +		if (gbw->txt) {
 +			chHeapFree((void *)gbw->txt);
 +			gbw->txt = "";
 +		}
 +	}
 +	// Alloc the new text if required
 +	if (txt && useAlloc) {
 +		char *str;
 +		
 +		if ((str = (char *)chHeapAlloc(NULL, strlen(txt)+1))) {
 +			gh->flags |= GBTN_FLG_ALLOCTXT;
 +			strcpy(str, txt);
 +		}
 +		txt = (const char *)str;
 +	}
 +	
 +	gbw->txt = txt ? txt : "";
 +	#undef gbw
 +}
 +
 +/**
 + * @brief   Redraw the button.
 + *
 + * @param[in] gh		The window handle (must be a button window)
 + *
 + * @api
 + */
 +void gwinButtonDraw(GHandle gh) {
 +	color_t			cedge;
 +	color_t			cfill;
 +	color_t			ctxt;
 +	const char *	txt;
 +	#define gbw		((GButtonObject *)gh)
 +	#define RND_CNR_SIZE	5
 +	
 +	if (gh->type != GW_BUTTON)
 +		return;
 +
 +	#if GDISP_NEED_CLIP
 +		gdispSetClip(gh->x, gh->y, gh->width, gh->height);
 +	#endif
 +	
 +	// Get the text (safely)
 +	txt = gh->font && gbw->txt ? gbw->txt : "";
 +	
 +	// Determine the colors to use
 +	switch(gbw->state) {
 +	case GBTN_DOWN:
 +		cedge = gbw->style.color_dn_edge;
 +		cfill = gbw->style.color_dn_fill;
 +		ctxt = gbw->style.color_dn_txt;
 +		break;
 +	case GBTN_UP:	default:
 +		cedge = gbw->style.color_up_edge;
 +		cfill = gbw->style.color_up_fill;
 +		ctxt = gbw->style.color_up_txt;
 +		break;
 +	}
 +	
 +	// Draw according to the shape specified.
 +	switch(gbw->style.shape) {
 +#if GDISP_NEED_ARC
 +	case GBTN_ROUNDED:
 +		if (gh->width >= 2*RND_CNR_SIZE+10) {
 +			gdispFillRoundedBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, RND_CNR_SIZE-1, cfill);
 +			gdispDrawStringBox(gh->x+1, gh->y+RND_CNR_SIZE, gh->width-2, gh->height-(2*RND_CNR_SIZE), txt, gh->font, ctxt, justifyCenter);
 +			gdispDrawRoundedBox(gh->x, gh->y, gh->width, gh->height, RND_CNR_SIZE, cedge);
 +			break;
 +		}
 +		/* Fall Through */
 +#endif
 +	case GBTN_SQUARE:
 +		gdispFillStringBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, txt, gh->font, ctxt, cfill, justifyCenter);
 +		gdispDrawBox(gh->x, gh->y, gh->width, gh->height, cedge);
 +		break;
 +#if GDISP_NEED_ELLIPSE
 +	case GBTN_ELLIPSE:
 +		gdispFillEllipse(gh->x+1, gh->y+1, gh->width/2-1, gh->height/2-1, cfill);
 +		gdispDrawStringBox(gh->x+1, gh->y+1, gh->width-2, gh->height-2, txt, gh->font, ctxt, justifyCenter);
 +		gdispDrawEllipse(gh->x, gh->y, gh->width/2, gh->height/2, cedge);
 +		break;
 +#endif
 +	case GBTN_3D:	default:
 +		gdispFillStringBox(gh->x, gh->y, gh->width-1, gh->height-1, txt, gh->font, ctxt, cfill, justifyCenter);
 +		gdispDrawLine(gh->x+gh->width-1, gh->y, gh->x+gh->width-1, gh->y+gh->height-1, cedge);
 +		gdispDrawLine(gh->x, gh->y+gh->height-1, gh->x+gh->width-2, gh->y+gh->height-1, cedge);
 +		break;
 +	}
 +	#undef gbw
 +}
 +
 +// Attach a source to this button. Sources recognised: Mouse, Touch and Toggle - others are ignored (returns false).
 +bool_t gwinAttachButtonSource(GHandle gh, GSourceHandle gsh, GEventType type) {
 +	#define gbw		((GButtonObject *)gh)
 +	unsigned	flags;
 +
 +	switch (type) {
 +	#if defined(GINPUT_NEED_MOUSE) && GINPUT_NEED_MOUSE
 +		case GEVENT_MOUSE:
 +			flags = 0;
 +			break;
 +	#endif
 +	#if defined(GINPUT_NEED_TOUCH) && GINPUT_NEED_TOUCH
 +		case GEVENT_TOUCH:
 +			flags = 0;
 +			break;
 +	#endif
 +	#if defined(GINPUT_NEED_TOGGLE) && GINPUT_NEED_TOGGLE
 +		case GEVENT_TOGGLE:
 +			flags = GLISTEN_TOGGLE_OFF|GLISTEN_TOGGLE_ON;
 +			break;
 +	#endif
 +	default:
 +		return FALSE;
 +	}
 +	return geventAttachSource(&gbw->listener, gsh, flags);
 +
 +	#undef gbw
 +}
 +
 +#endif /* GFX_USE_GWIN && GWIN_NEED_BUTTON */
 +/** @} */
 +
 diff --git a/src/gwin/console.c b/src/gwin/console.c new file mode 100644 index 00000000..63960c23 --- /dev/null +++ b/src/gwin/console.c @@ -0,0 +1,210 @@ +/*
 +    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/gwin/console.c
 + * @brief   GWIN Driver code.
 + *
 + * @addtogroup GWIN_CONSOLE
 + * @{
 + */
 +#include "ch.h"
 +#include "hal.h"
 +#include "gwin.h"
 +
 +#if (GFX_USE_GWIN && GWIN_NEED_CONSOLE) || defined(__DOXYGEN__)
 +
 +#include <string.h>
 +#include "gwin_internal.h"
 +
 +
 +#define GWIN_CONSOLE_USE_CLEAR_LINES			TRUE
 +#define GWIN_CONSOLE_USE_FILLED_CHARS			FALSE
 +
 +/*
 + * Stream interface implementation. The interface is write only
 + */
 +
 +#define Stream2GWindow(ip)		((GHandle)(((char *)(ip)) - (size_t)(&(((GConsoleObject *)0)->stream))))
 +
 +static size_t GWinStreamWrite(void *ip, const uint8_t *bp, size_t n) { gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; }
 +static size_t GWinStreamRead(void *ip, uint8_t *bp, size_t n) {	(void)ip; (void)bp; (void)n; return 0; }
 +static msg_t GWinStreamPut(void *ip, uint8_t b) { gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; }
 +static msg_t GWinStreamGet(void *ip) {(void)ip; return RDY_OK; }
 +static msg_t GWinStreamPutTimed(void *ip, uint8_t b, systime_t time) { (void)time; gwinPutChar(Stream2GWindow(ip), (char)b); return RDY_OK; }
 +static msg_t GWinStreamGetTimed(void *ip, systime_t timeout) { (void)ip; (void)timeout; return RDY_OK; }
 +static size_t GWinStreamWriteTimed(void *ip, const uint8_t *bp, size_t n, systime_t time) { (void)time; gwinPutCharArray(Stream2GWindow(ip), (const char *)bp, n); return RDY_OK; }
 +static size_t GWinStreamReadTimed(void *ip, uint8_t *bp, size_t n, systime_t time) { (void)ip; (void)bp; (void)n; (void)time; return 0; }
 +
 +struct GConsoleWindowVMT_t {
 +	_base_asynchronous_channel_methods
 +};
 +
 +static const struct GConsoleWindowVMT_t GWindowConsoleVMT = {
 +	GWinStreamWrite,
 +	GWinStreamRead,
 +	GWinStreamPut,
 +	GWinStreamGet,
 +	GWinStreamPutTimed,
 +	GWinStreamGetTimed,
 +	GWinStreamWriteTimed,
 +	GWinStreamReadTimed
 +};
 +
 +/**
 + * @brief   Create a console window.
 + * @details	A console window allows text to be written using chprintf() (and the console functions defined here).
 + * @brief	Text in a console window supports newlines and will wrap text as required.
 + * @return  NULL if there is no resultant drawing area, otherwise a window handle.
 + *
 + * @param[in] gc		The GConsoleObject structure to initialise. If this is NULL the structure is dynamically allocated.
 + * @param[in] x,y		The screen co-ordinates for the bottom left corner of the window
 + * @param[in] width		The width of the window
 + * @param[in] height	The height of the window
 + * @param[in] font		The font to use
 + * @note				The console is not automatically cleared on creation. You must do that by calling gwinClear() (possibly after changing your background color)
 + * @note				If the dispay does not support scrolling, the window will be cleared when the bottom line is reached.
 + * @note				The default drawing color gets set to White and the background drawing color to Black.
 + * @note				The dimensions and position may be changed to fit on the real screen.
 + *
 + * @api
 + */
 +GHandle gwinCreateConsole(GConsoleObject *gc, coord_t x, coord_t y, coord_t width, coord_t height, font_t font) {
 +	if (!(gc = (GConsoleObject *)_gwinInit((GWindowObject *)gc, x, y, width, height, sizeof(GConsoleObject))))
 +		return 0;
 +	gc->gwin.type = GW_CONSOLE;
 +	gwinSetFont(&gc->gwin, font);
 +	gc->stream.vmt = &GWindowConsoleVMT;
 +	gc->cx = 0;
 +	gc->cy = 0;
 +	return (GHandle)gc;
 +}
 +
 +/**
 + * @brief   Get a stream from a console window suitable for use with chprintf().
 + * @return	The stream handle or NULL if this is not a console window.
 + *
 + * @param[in] gh	The window handle (must be a console window)
 + *
 + * @api
 + */
 +BaseSequentialStream *gwinGetConsoleStream(GHandle gh) {
 +	if (gh->type != GW_CONSOLE)
 +		return 0;
 +	return (BaseSequentialStream *)&(((GConsoleObject *)(gh))->stream);
 +}
 +
 +/**
 + * @brief   Put a character at the cursor position in the window.
 + * @note	Uses the current foreground color to draw the character and fills the background using the background drawing color
 + *
 + * @param[in] gh	The window handle (must be a console window)
 + * @param[in] c		The character to draw
 + *
 + * @api
 + */
 +void gwinPutChar(GHandle gh, char c) {
 +	uint8_t			width;
 +	#define gcw		((GConsoleObject *)gh)
 +
 +	if (gh->type != GW_CONSOLE || !gh->font) return;
 +
 +	#if GDISP_NEED_CLIP
 +		gdispSetClip(gh->x, gh->y, gh->width, gh->height);
 +	#endif
 +	
 +	if (c == '\n') {
 +		gcw->cx = 0;
 +		gcw->cy += gcw->fy;
 +		// We use lazy scrolling here and only scroll when the next char arrives
 +	} else if (c == '\r') {
 +		// gcw->cx = 0;
 +	} else {
 +		width = gdispGetCharWidth(c, gh->font) + gcw->fp;
 +		if (gcw->cx + width >= gh->width) {
 +			gcw->cx = 0;
 +			gcw->cy += gcw->fy;
 +		}
 +
 +		if (gcw->cy + gcw->fy > gh->height) {
 +#if GDISP_NEED_SCROLL
 +			/* scroll the console */
 +			gdispVerticalScroll(gh->x, gh->y, gh->width, gh->height, gcw->fy, gh->bgcolor);
 +			/* reset the cursor to the start of the last line */
 +			gcw->cx = 0;
 +			gcw->cy = (((coord_t)(gh->height/gcw->fy))-1)*gcw->fy;
 +#else
 +			/* clear the console */
 +			gdispFillArea(gh->x, gh->y, gh->width, gh->height, gh->bgcolor);
 +			/* reset the cursor to the top of the window */
 +			gcw->cx = 0;
 +			gcw->cy = 0;
 +#endif
 +		}
 +
 +#if GWIN_CONSOLE_USE_CLEAR_LINES
 +		/* clear to the end of the line */
 +		if (gcw->cx == 0)
 +			gdispFillArea(gh->x, gh->y + gcw->cy, gh->width, gcw->fy, gh->bgcolor);
 +#endif
 +#if GWIN_CONSOLE_USE_FILLED_CHARS
 +		gdispFillChar(gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color, gh->bgcolor);
 +#else
 +		gdispDrawChar(gh->x + gcw->cx, gh->y + gcw->cy, c, gh->font, gh->color);
 +#endif
 +
 +		/* update cursor */
 +		gcw->cx += width;
 +	}
 +	#undef gcw
 +}
 +
 +/**
 + * @brief   Put a string at the cursor position in the window. It will wrap lines as required.
 + * @note	Uses the current foreground color to draw the string and fills the background using the background drawing color
 + *
 + * @param[in] gh	The window handle (must be a console window)
 + * @param[in] str	The string to draw
 + *
 + * @api
 + */
 +void gwinPutString(GHandle gh, const char *str) {
 +	while(*str)
 +		gwinPutChar(gh, *str++);
 +}
 +
 +/**
 + * @brief   Put the character array at the cursor position in the window. It will wrap lines as required.
 + * @note	Uses the current foreground color to draw the string and fills the background using the background drawing color
 + *
 + * @param[in] gh	The window handle (must be a console window)
 + * @param[in] str	The string to draw
 + * @param[in] n		The number of characters to draw
 + *
 + * @api
 + */
 +void gwinPutCharArray(GHandle gh, const char *str, size_t n) {
 +	while(n--)
 +		gwinPutChar(gh, *str++);
 +}
 +
 +#endif /* GFX_USE_GWIN && GWIN_NEED_CONSOLE */
 +/** @} */
 +
 diff --git a/src/gwin/gwin.mk b/src/gwin/gwin.mk new file mode 100644 index 00000000..cf952580 --- /dev/null +++ b/src/gwin/gwin.mk @@ -0,0 +1,2 @@ +GFXSRC +=   $(GFXLIB)/src/gwin/console.c \
 +			$(GFXLIB)/src/gwin/button.c
 diff --git a/src/gwin/gwin_internal.h b/src/gwin/gwin_internal.h new file mode 100644 index 00000000..13401a6f --- /dev/null +++ b/src/gwin/gwin_internal.h @@ -0,0 +1,53 @@ +/*
 +    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    gwin_internal.h
 + * @brief   GWIN Graphic window subsystem header file.
 + *
 + * @addtogroup GWIN
 + * @{
 + */
 +#ifndef _GWIN_INTERNAL_H
 +#define _GWIN_INTERNAL_H
 +
 +#if GFX_USE_GWIN || defined(__DOXYGEN__)
 +
 +/*===========================================================================*/
 +/* Sub-system constants.													 */
 +/*===========================================================================*/
 +
 +#define GWIN_FLG_DYNAMIC				0x0001
 +#define GWIN_FIRST_CONTROL_FLAG			0x0002
 +#define GBTN_FLG_ALLOCTXT				(GWIN_FIRST_CONTROL_FLAG<<0)
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +GHandle _gwinInit(GWindowObject *gw, coord_t x, coord_t y, coord_t width, coord_t height, size_t size);
 +
 +#ifdef __cplusplus
 +}
 +#endif
 +
 +#endif /* GFX_USE_GWIN */
 +
 +#endif /* _GWIN_INTERNAL_H */
 +/** @} */
  | 
