ugfx/src/ginput/ginput_mouse.c

698 lines
22 KiB
C
Raw Normal View History

2013-05-03 14:36:17 +00:00
/*
2013-06-15 11:37:22 +00:00
* This file is subject to the terms of the GFX License. If a copy of
2013-05-03 14:36:17 +00:00
* the license was not distributed with this file, you can obtain one at:
*
2013-07-21 20:20:37 +00:00
* http://ugfx.org/license.html
2013-05-03 14:36:17 +00:00
*/
2013-05-20 03:59:28 +00:00
/**
* @file src/ginput/ginput_mouse.c
2013-05-20 03:59:28 +00:00
* @brief GINPUT mouse/touch code.
*
* @defgroup Mouse Mouse
* @ingroup GINPUT
* @{
*/
#include "gfx.h"
#if (GFX_USE_GINPUT && GINPUT_NEED_MOUSE) || defined(__DOXYGEN__)
#include "driver_mouse.h"
2013-05-20 03:59:28 +00:00
2014-07-01 23:40:01 +00:00
#if !GINPUT_TOUCH_NOCALIBRATE
2013-05-20 03:59:28 +00:00
#if !defined(GFX_USE_GDISP) || !GFX_USE_GDISP
#error "GINPUT: GFX_USE_GDISP must be defined when mouse or touch calibration is required"
#endif
#include <string.h> // Required for memcpy
2014-09-16 00:06:59 +00:00
#define CALIBRATION_FONT "* Double"
#define CALIBRATION_FONT2 "* Narrow"
#define CALIBRATION_TEXT "Calibration"
#define CALIBRATION_ERROR_TEXT "Failed - Please try again!"
#define CALIBRATION_SAME_TEXT "Error: Same Reading - Check Driver!"
#define CALIBRATION_BACKGROUND Blue
#define CALIBRATION_COLOR1 White
#define CALIBRATION_COLOR2 RGB2COLOR(184,158,131)
2013-05-20 03:59:28 +00:00
2014-09-16 00:06:59 +00:00
#endif
2013-05-20 03:59:28 +00:00
static GTIMER_DECL(MouseTimer);
2014-07-01 23:40:01 +00:00
#if !GINPUT_TOUCH_NOCALIBRATE
2014-09-16 00:06:59 +00:00
/*
static inline void CalibrationSetIdentity(MouseCalibration *c) {
c->ax = 1;
c->bx = 0;
c->cx = 0;
c->ay = 0;
c->by = 1;
c->cy = 0;
}
2014-09-16 00:06:59 +00:00
*/
static inline void CalibrationCrossDraw(GMouse *m, const point *pp) {
gdispGDrawLine(m->display, pp->x-15, pp->y, pp->x-2, pp->y, CALIBRATION_COLOR1);
gdispGDrawLine(m->display, pp->x+2, pp->y, pp->x+15, pp->y, CALIBRATION_COLOR1);
gdispGDrawLine(m->display, pp->x, pp->y-15, pp->x, pp->y-2, CALIBRATION_COLOR1);
gdispGDrawLine(m->display, pp->x, pp->y+2, pp->x, pp->y+15, CALIBRATION_COLOR1);
gdispGDrawLine(m->display, pp->x-15, pp->y+15, pp->x-7, pp->y+15, CALIBRATION_COLOR2);
gdispGDrawLine(m->display, pp->x-15, pp->y+7, pp->x-15, pp->y+15, CALIBRATION_COLOR2);
gdispGDrawLine(m->display, pp->x-15, pp->y-15, pp->x-7, pp->y-15, CALIBRATION_COLOR2);
gdispGDrawLine(m->display, pp->x-15, pp->y-7, pp->x-15, pp->y-15, CALIBRATION_COLOR2);
gdispGDrawLine(m->display, pp->x+7, pp->y+15, pp->x+15, pp->y+15, CALIBRATION_COLOR2);
gdispGDrawLine(m->display, pp->x+15, pp->y+7, pp->x+15, pp->y+15, CALIBRATION_COLOR2);
gdispGDrawLine(m->display, pp->x+7, pp->y-15, pp->x+15, pp->y-15, CALIBRATION_COLOR2);
gdispGDrawLine(m->display, pp->x+15, pp->y-15, pp->x+15, pp->y-7, CALIBRATION_COLOR2);
2013-05-20 03:59:28 +00:00
}
2014-09-16 00:06:59 +00:00
static inline void CalibrationCrossClear(GMouse *m, const point *pp) {
gdispGFillArea(m->display, pp->x - 15, pp->y - 15, 32, 32, CALIBRATION_BACKGROUND);
2013-05-20 03:59:28 +00:00
}
2014-09-16 00:06:59 +00:00
static inline void CalibrationTransform(GMouseReading *pt, const GMouseCalibration *c) {
2013-05-20 03:59:28 +00:00
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);
}
2014-09-16 22:44:31 +00:00
static inline void CalibrationCalculate(GMouse *m, const point *cross, const point *points) {
float dx;
coord_t c0, c1, c2;
2014-09-16 00:06:59 +00:00
(void) m;
// Work on x values
c0 = cross[0].x;
c1 = cross[1].x;
c2 = cross[2].x;
2013-05-20 03:59:28 +00:00
#if GDISP_NEED_CONTROL
2014-09-16 00:06:59 +00:00
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
/* Convert all cross points back to GDISP_ROTATE_0 convention
* before calculating the calibration matrix.
*/
switch(gdispGGetOrientation(m->display)) {
case GDISP_ROTATE_90:
c0 = cross[0].y;
c1 = cross[1].y;
c2 = cross[2].y;
break;
case GDISP_ROTATE_180:
c0 = c1 = c2 = gdispGGetWidth(m->display) - 1;
c0 -= cross[0].x;
c1 -= cross[1].x;
c2 -= cross[2].x;
break;
case GDISP_ROTATE_270:
c0 = c1 = c2 = gdispGGetHeight(m->display) - 1;
c0 -= cross[0].y;
c1 -= cross[1].y;
c2 -= cross[2].y;
break;
2014-09-16 22:44:31 +00:00
default:
break;
2014-09-16 00:06:59 +00:00
}
}
#endif
2013-05-20 03:59:28 +00:00
/* 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);
2013-05-20 03:59:28 +00:00
2014-09-16 00:06:59 +00:00
m->caldata.ax = ((float)(c0 - c2) * (float)(points[1].y - points[2].y)
- (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx;
m->caldata.bx = ((float)(c1 - c2) * (float)(points[0].x - points[2].x)
- (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx;
m->caldata.cx = (c0 * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y)
- c1 * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y)
+ c2 * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y)) / dx;
// Work on y values
c0 = cross[0].y;
c1 = cross[1].y;
c2 = cross[2].y;
2013-05-20 03:59:28 +00:00
#if GDISP_NEED_CONTROL
2014-09-16 00:06:59 +00:00
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
switch(gdispGGetOrientation(m->display)) {
case GDISP_ROTATE_90:
c0 = c1 = c2 = gdispGGetWidth(m->display) - 1;
c0 -= cross[0].x;
c1 -= cross[1].x;
c2 -= cross[2].x;
break;
case GDISP_ROTATE_180:
c0 = c1 = c2 = gdispGGetHeight(m->display) - 1;
c0 -= cross[0].y;
c1 -= cross[1].y;
c2 -= cross[2].y;
break;
case GDISP_ROTATE_270:
c0 = cross[0].x;
c1 = cross[1].x;
c2 = cross[2].x;
break;
2014-09-16 22:44:31 +00:00
default:
break;
2014-09-16 00:06:59 +00:00
}
}
#endif
2013-05-20 03:59:28 +00:00
2014-09-16 00:06:59 +00:00
m->caldata.ay = ((float)(c0 - c2) * (float)(points[1].y - points[2].y)
- (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx;
m->caldata.by = ((float)(c1 - c2) * (float)(points[0].x - points[2].x)
- (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx;
m->caldata.cy = (c0 * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y)
- c1 * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y)
+ c2 * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y)) / dx;
2013-05-20 03:59:28 +00:00
}
#endif
2014-09-16 00:06:59 +00:00
static void GetMouseReading(GMouse *m) {
GMouseReading r;
2014-07-01 23:40:01 +00:00
2014-09-16 00:06:59 +00:00
// Get the raw reading.
gmvmt(m)->get(m, &r);
m->flags &= ~GMOUSE_FLG_NEEDREAD;
2014-07-01 23:40:01 +00:00
2014-09-16 00:06:59 +00:00
// If touch then calculate button 0 from z
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH)) {
if (gmvmt(m)->z_min <= gmvmt(m)->z_max) {
if (r.z >= gmvmt(m)->z_touchon) r.buttons |= GINPUT_MOUSE_BTN_LEFT;
else if (r.z <= gmvmt(m)->z_touchoff) r.buttons &= ~GINPUT_MOUSE_BTN_LEFT;
else return; // bad transitional reading
2014-07-01 23:40:01 +00:00
} else {
2014-09-16 00:06:59 +00:00
if (r.z <= gmvmt(m)->z_touchon) r.buttons |= GINPUT_MOUSE_BTN_LEFT;
else if (r.z >= gmvmt(m)->z_touchoff) r.buttons &= ~GINPUT_MOUSE_BTN_LEFT;
else return; // bad transitional reading
2013-05-20 03:59:28 +00:00
}
2014-07-01 23:40:01 +00:00
}
// Double check up & down events if needed
2014-09-16 00:06:59 +00:00
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_POORUPDOWN)) {
2014-07-01 23:40:01 +00:00
// Are we in a transition test
2014-09-16 22:44:31 +00:00
if ((m->flags & GMOUSE_FLG_INDELTA)) {
if (!((r.buttons ^ m->r.buttons) & GINPUT_MOUSE_BTN_LEFT)) {
2014-07-01 23:40:01 +00:00
// Transition failed
2014-09-16 22:44:31 +00:00
m->flags &= ~GMOUSE_FLG_INDELTA;
2014-07-01 23:40:01 +00:00
return;
}
// Transition succeeded
2014-09-16 22:44:31 +00:00
m->flags &= ~GMOUSE_FLG_INDELTA;
2013-05-20 03:59:28 +00:00
2014-07-01 23:40:01 +00:00
// Should we start a transition test
2014-09-16 22:44:31 +00:00
} else if (((r.buttons ^ m->r.buttons) & GINPUT_MOUSE_BTN_LEFT)) {
m->flags |= GMOUSE_FLG_INDELTA;
2014-07-01 23:40:01 +00:00
return;
}
2013-05-20 03:59:28 +00:00
}
2014-07-01 23:40:01 +00:00
// If the mouse is up we may need to keep our previous position
2014-09-16 00:06:59 +00:00
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_ONLY_DOWN) && !(r.buttons & GINPUT_MOUSE_BTN_LEFT)) {
2014-09-16 22:44:31 +00:00
r.x = m->r.x;
r.y = m->r.y;
2013-05-20 03:59:28 +00:00
2014-07-01 23:40:01 +00:00
} else {
coord_t w, h;
2013-05-20 03:59:28 +00:00
2014-07-01 23:40:01 +00:00
#if !GINPUT_TOUCH_NOCALIBRATE
// Do we need to calibrate the reading?
2014-09-16 22:44:31 +00:00
if ((m->flags & GMOUSE_FLG_CALIBRATE))
2014-09-16 00:06:59 +00:00
CalibrationTransform(&r, &m->caldata);
2014-07-01 23:40:01 +00:00
#endif
2013-05-20 03:59:28 +00:00
2014-09-16 00:06:59 +00:00
// We can't clip or rotate if we don't have a display
if (m->display) {
// We now need display information
w = gdispGGetWidth(m->display);
h = gdispGGetHeight(m->display);
#if GDISP_NEED_CONTROL
// Do we need to rotate the reading to match the display
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
coord_t t;
switch(gdispGGetOrientation(m->display)) {
case GDISP_ROTATE_0:
break;
case GDISP_ROTATE_90:
t = r.x;
r.x = w - 1 - r.y;
r.y = t;
break;
case GDISP_ROTATE_180:
r.x = w - 1 - r.x;
r.y = h - 1 - r.y;
break;
case GDISP_ROTATE_270:
t = r.y;
r.y = h - 1 - r.x;
r.x = t;
break;
default:
break;
}
2014-07-01 23:40:01 +00:00
}
2014-09-16 00:06:59 +00:00
#endif
2013-05-20 03:59:28 +00:00
2014-09-16 00:06:59 +00:00
// Do we need to clip the reading to the display
2014-09-16 22:44:31 +00:00
if ((m->flags & GMOUSE_FLG_CLIP)) {
2014-09-16 00:06:59 +00:00
if (r.x < 0) r.x = 0;
else if (r.x >= w) r.x = w-1;
if (r.y < 0) r.y = 0;
else if (r.y >= h) r.y = h-1;
}
2014-07-01 23:40:01 +00:00
}
2013-05-20 03:59:28 +00:00
}
2014-07-01 23:40:01 +00:00
{
2014-09-16 00:06:59 +00:00
const GMouseJitter *pj;
uint32_t diff;
2014-07-01 23:40:01 +00:00
// Are we in pen or finger mode
2014-09-16 00:06:59 +00:00
pj = (m->flags & GMOUSE_FLG_FINGERMODE) ? &gmvmt(m)->finger_jitter : &gmvmt(m)->pen_jitter;
2014-07-01 23:40:01 +00:00
// Is this just movement jitter
if (pj->move > 0) {
2014-09-16 00:06:59 +00:00
diff = (uint32_t)(r.x - m->r.x) * (uint32_t)(r.x - m->r.x) + (uint32_t)(r.y - m->r.y) * (uint32_t)(r.y - m->r.y);
2014-07-01 23:40:01 +00:00
if (diff > (uint32_t)pj->move * (uint32_t)pj->move) {
2014-09-16 00:06:59 +00:00
r.x = m->r.x;
r.y = m->r.y;
2014-07-01 23:40:01 +00:00
}
}
// Check if the click has moved outside the click area and if so cancel the click
2014-09-16 00:06:59 +00:00
if (pj->click > 0 && (m->flags & GMOUSE_FLG_CLICK_TIMER)) {
diff = (uint32_t)(r.x - m->clickpos.x) * (uint32_t)(r.x - m->clickpos.x) + (uint32_t)(r.y - m->clickpos.y) * (uint32_t)(r.y - m->clickpos.y);
2014-07-01 23:40:01 +00:00
if (diff > (uint32_t)pj->click * (uint32_t)pj->click)
2014-09-16 00:06:59 +00:00
m->flags &= ~GMOUSE_FLG_CLICK_TIMER;
2013-05-20 03:59:28 +00:00
}
}
2014-07-01 23:40:01 +00:00
{
GSourceListener *psl;
GEventMouse *pe;
unsigned meta;
uint16_t upbtns, dnbtns;
// Calculate out new event meta value and handle CLICK and CXTCLICK
2014-09-16 00:06:59 +00:00
dnbtns = r.buttons & ~m->r.buttons;
upbtns = ~r.buttons & m->r.buttons;
2014-07-01 23:40:01 +00:00
meta = GMETA_NONE;
// Mouse down
if ((dnbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) {
2014-09-16 00:06:59 +00:00
m->clickpos.x = r.x;
m->clickpos.y = r.y;
m->clicktime = gfxSystemTicks();
m->flags |= GMOUSE_FLG_CLICK_TIMER;
2014-07-01 23:40:01 +00:00
if ((dnbtns & GINPUT_MOUSE_BTN_LEFT))
meta |= GMETA_MOUSE_DOWN;
}
// Mouse up
if ((upbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) {
if ((upbtns & GINPUT_MOUSE_BTN_LEFT))
meta |= GMETA_MOUSE_UP;
2014-09-16 00:06:59 +00:00
if ((m->flags & GMOUSE_FLG_CLICK_TIMER)) {
2014-07-01 23:40:01 +00:00
if ((upbtns & GINPUT_MOUSE_BTN_LEFT)
#if GINPUT_TOUCH_CLICK_TIME != TIME_INFINITE
2014-09-16 00:06:59 +00:00
&& gfxSystemTicks() - m->clicktime < gfxMillisecondsToTicks(GINPUT_TOUCH_CLICK_TIME)
2014-07-01 23:40:01 +00:00
#endif
)
meta |= GMETA_MOUSE_CLICK;
else
meta |= GMETA_MOUSE_CXTCLICK;
2014-09-16 00:06:59 +00:00
m->flags &= ~GMOUSE_FLG_CLICK_TIMER;
2014-07-01 23:40:01 +00:00
}
2013-05-20 03:59:28 +00:00
}
2014-07-01 23:40:01 +00:00
// Send the event to the listeners that are interested.
psl = 0;
2014-09-16 22:44:31 +00:00
while ((psl = geventGetSourceListener((GSourceHandle)m, psl))) {
2014-07-01 23:40:01 +00:00
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 (!meta && !psl->srcflags && !(psl->listenflags & GLISTEN_MOUSENOFILTER)
2014-09-16 00:06:59 +00:00
&& r.x == m->r.x && r.y == m->r.y && r.buttons == m->r.buttons)
2014-07-01 23:40:01 +00:00
continue;
// Send the event if we are listening for it
if (((r.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEDOWNMOVES))
|| (!(r.buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEUPMOVES))
|| (meta && (psl->listenflags & GLISTEN_MOUSEMETA))) {
2014-09-16 22:44:31 +00:00
pe->type = (gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH) ? GEVENT_TOUCH : GEVENT_MOUSE;
2014-07-01 23:40:01 +00:00
pe->x = r.x;
pe->y = r.y;
pe->z = r.z;
pe->current_buttons = r.buttons;
2014-09-16 00:06:59 +00:00
pe->last_buttons = m->r.buttons;
2014-07-01 23:40:01 +00:00
pe->meta = meta;
if (psl->srcflags) {
pe->current_buttons |= GINPUT_MISSED_MOUSE_EVENT;
pe->meta |= psl->srcflags;
psl->srcflags = 0;
}
2014-09-16 00:06:59 +00:00
pe->display = m->display;
2014-07-01 23:40:01 +00:00
geventSendEvent(psl);
2013-05-20 03:59:28 +00:00
}
}
}
2014-07-01 23:40:01 +00:00
// Finally save the results
2014-09-16 00:06:59 +00:00
m->r.x = r.x;
m->r.y = r.y;
m->r.z = r.z;
m->r.buttons = r.buttons;
2014-07-01 23:40:01 +00:00
}
static void MousePoll(void *param) {
2014-09-16 00:06:59 +00:00
GMouse * m;
(void) param;
2014-09-16 22:44:31 +00:00
for(m = (GMouse *)gdriverGetNext(GDRIVER_TYPE_MOUSE, 0); m; m = (GMouse *)gdriverGetNext(GDRIVER_TYPE_MOUSE, (GDriver *)m)) {
2014-09-16 00:06:59 +00:00
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_NOPOLL) || (m->flags & GMOUSE_FLG_NEEDREAD))
GetMouseReading(m);
}
2013-05-20 03:59:28 +00:00
}
2014-09-16 22:44:31 +00:00
void _gmouseInit(void) {
// GINPUT_MOUSE_DRIVER_LIST is defined - create each driver instance
#if defined(GINPUT_MOUSE_DRIVER_LIST)
{
int i;
extern GDriverVMTList GINPUT_MOUSE_DRIVER_LIST;
static const struct GDriverVMT const * dclist[] = {GINPUT_MOUSE_DRIVER_LIST};
static const unsigned dnlist[] = {GDISP_CONTROLLER_DISPLAYS};
for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++)
gdriverRegister(dclist[i]);
}
// One and only one display
#else
{
extern GDriverVMTList GINPUTMOUSEVMT_OnlyOne;
gdriverRegister(GINPUTMOUSEVMT_OnlyOne);
}
#endif
}
void _gmouseDeinit(void) {
}
2013-05-20 03:59:28 +00:00
GSourceHandle ginputGetMouse(uint16_t instance) {
2014-09-16 22:44:31 +00:00
GMouse *m;
2013-05-20 03:59:28 +00:00
#if GINPUT_MOUSE_NEED_CALIBRATION
2014-09-16 22:44:31 +00:00
GCalibration *pc;
2013-05-20 03:59:28 +00:00
#endif
2014-09-16 22:44:31 +00:00
if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
return 0;
2013-05-20 03:59:28 +00:00
// Make sure we have a valid mouse display
2014-09-16 22:44:31 +00:00
if (!m->display)
m->display = GDISP;
2013-05-20 03:59:28 +00:00
// Do we need to initialise the mouse subsystem?
2014-09-16 22:44:31 +00:00
if (!(m->flags & FLG_INIT_DONE)) {
2013-05-20 03:59:28 +00:00
ginput_lld_mouse_init();
#if GINPUT_MOUSE_NEED_CALIBRATION
#if GINPUT_MOUSE_LLD_CALIBRATION_LOADSAVE
2014-09-16 22:44:31 +00:00
if (!m->fnloadcal) {
m->fnloadcal = ginput_lld_mouse_calibration_load;
m->flags &= ~FLG_CAL_FREE;
2013-05-20 03:59:28 +00:00
}
2014-09-16 22:44:31 +00:00
if (!m->fnsavecal)
m->fnsavecal = ginput_lld_mouse_calibration_save;
2013-05-20 03:59:28 +00:00
#endif
2014-09-16 22:44:31 +00:00
if (m->fnloadcal && (pc = (Calibration *)m->fnloadcal(instance))) {
memcpy(&m->caldata, pc, sizeof(m->caldata));
m->flags |= (FLG_CAL_OK|FLG_CAL_SAVED);
if ((m->flags & FLG_CAL_FREE))
gfxFree((void *)pc);
2013-05-20 03:59:28 +00:00
} else if (instance == 9999) {
2014-09-16 22:44:31 +00:00
CalibrationSetIdentity(&m->caldata);
m->flags |= (FLG_CAL_OK|FLG_CAL_SAVED|FLG_CAL_RAW);
2013-05-20 03:59:28 +00:00
} else
ginputCalibrateMouse(instance);
#endif
// Get the first reading
2014-09-16 22:44:31 +00:00
m->last_buttons = 0;
get_calibrated_reading(&m->t);
2013-05-20 03:59:28 +00:00
// Mark init as done and start the Poll timer
2014-09-16 22:44:31 +00:00
m->flags |= FLG_INIT_DONE;
2013-05-20 03:59:28 +00:00
gtimerStart(&MouseTimer, MousePoll, 0, TRUE, GINPUT_MOUSE_POLL_PERIOD);
}
// Return our structure as the handle
2014-09-16 22:44:31 +00:00
return (GSourceHandle)m;
2013-05-20 03:59:28 +00:00
}
void ginputSetMouseDisplay(uint16_t instance, GDisplay *g) {
if (instance)
return;
2014-09-16 22:44:31 +00:00
m->display = g ? g : GDISP;
}
GDisplay *ginputGetMouseDisplay(uint16_t instance) {
if (instance)
return 0;
2014-09-16 22:44:31 +00:00
return m->display;
}
2013-05-20 03:59:28 +00:00
bool_t ginputGetMouseStatus(uint16_t instance, GEventMouse *pe) {
// Win32 threads don't seem to recognise priority and/or pre-emption
// so we add a sleep here to prevent 100% polled applications from locking up.
gfxSleepMilliseconds(1);
2013-05-20 03:59:28 +00:00
2014-09-16 22:44:31 +00:00
if (instance || (m->flags & (FLG_INIT_DONE|FLG_IN_CAL)) != FLG_INIT_DONE)
2013-05-20 03:59:28 +00:00
return FALSE;
pe->type = GINPUT_MOUSE_EVENT_TYPE;
2014-09-16 22:44:31 +00:00
pe->x = m->t.x;
pe->y = m->t.y;
pe->z = m->t.z;
pe->current_buttons = m->t.buttons;
pe->last_buttons = m->last_buttons;
2013-05-20 03:59:28 +00:00
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;
}
bool_t ginputCalibrateMouse(uint16_t instance) {
2014-07-01 23:40:01 +00:00
#if GINPUT_TOUCH_NOCALIBRATE
2013-05-20 03:59:28 +00:00
(void) instance;
2014-09-16 22:44:31 +00:00
2013-05-20 03:59:28 +00:00
return FALSE;
#else
const coord_t height = gdispGGetHeight(MouseConfig.display);
const coord_t width = gdispGGetWidth(MouseConfig.display);
#if GINPUT_MOUSE_CALIBRATE_EXTREMES
const MousePoint cross[] = {{0, 0},
{(width - 1) , 0},
{(width - 1) , (height - 1)},
{(width / 2), (height / 2)}}; /* Check point */
#else
const MousePoint cross[] = {{(width / 4), (height / 4)},
{(width - (width / 4)) , (height / 4)},
{(width - (width / 4)) , (height - (height / 4))},
{(width / 2), (height / 2)}}; /* Check point */
#endif
2014-09-16 00:06:59 +00:00
MousePoint points[4];
2013-05-20 03:59:28 +00:00
const MousePoint *pc;
MousePoint *pt;
int32_t px, py;
unsigned i, j;
font_t font1, font2;
#if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0
unsigned err;
#endif
if (instance || (MouseConfig.flags & FLG_IN_CAL))
return FALSE;
font1 = gdispOpenFont(GINPUT_MOUSE_CALIBRATION_FONT);
font2 = gdispOpenFont(GINPUT_MOUSE_CALIBRATION_FONT2);
MouseConfig.flags |= FLG_IN_CAL;
gtimerStop(&MouseTimer);
MouseConfig.flags &= ~(FLG_CAL_OK|FLG_CAL_SAVED|FLG_CAL_RAW);
2013-05-20 03:59:28 +00:00
#if GDISP_NEED_CLIP
gdispGSetClip(MouseConfig.display, 0, 0, width, height);
2013-05-20 03:59:28 +00:00
#endif
#if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0
while(1) {
#endif
gdispGClear(MouseConfig.display, Blue);
2013-05-20 03:59:28 +00:00
gdispGFillStringBox(MouseConfig.display, 0, 5, width, 30, GINPUT_MOUSE_CALIBRATION_TEXT, font1, White, Blue, justifyCenter);
2013-05-20 03:59:28 +00:00
for(i = 0, pt = points, pc = cross; i < GINPUT_MOUSE_CALIBRATION_POINTS; i++, pt++, pc++) {
2014-09-16 00:06:59 +00:00
CalibrationCrossDraw(pc);
2013-05-20 03:59:28 +00:00
do {
/* Wait for the mouse to be pressed */
while(get_raw_reading(&MouseConfig.t), !(MouseConfig.t.buttons & GINPUT_MOUSE_BTN_LEFT))
gfxSleepMilliseconds(20);
2013-05-20 03:59:28 +00:00
/* Average all the samples while the mouse is down */
for(px = py = 0, j = 0;
gfxSleepMilliseconds(20), /* Settling time between readings */
2013-05-20 03:59:28 +00:00
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;
2014-09-16 00:06:59 +00:00
CalibrationCrossClear(pc);
2013-05-20 03:59:28 +00:00
if (i >= 1 && pt->x == (pt-1)->x && pt->y == (pt-1)->y) {
gdispGFillStringBox(MouseConfig.display, 0, 35, width, 40, GINPUT_MOUSE_CALIBRATION_SAME_TEXT, font2, Red, Yellow, justifyCenter);
gfxSleepMilliseconds(5000);
gdispGFillArea(MouseConfig.display, 0, 35, width, 40, Blue);
2013-05-20 03:59:28 +00:00
}
}
/* Apply 3 point calibration algorithm */
2014-09-16 00:06:59 +00:00
CalibrationCalculate(&MouseConfig, cross, points);
2013-05-20 03:59:28 +00:00
/* 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;
2014-09-16 00:06:59 +00:00
CalibrationTransform(&MouseConfig.t, &MouseConfig.caldata);
_tsOrientClip(&MouseConfig.t, MouseConfig.display, FALSE);
2013-05-20 03:59:28 +00:00
/* Calculate the delta */
err = (MouseConfig.t.x - cross[3].x) * (MouseConfig.t.x - cross[3].x) +
(MouseConfig.t.y - cross[3].y) * (MouseConfig.t.y - cross[3].y);
if (err <= GINPUT_MOUSE_MAX_CALIBRATION_ERROR * GINPUT_MOUSE_MAX_CALIBRATION_ERROR)
break;
gdispGFillStringBox(MouseConfig.display, 0, 35, width, 40, GINPUT_MOUSE_CALIBRATION_ERROR_TEXT, font2, Red, Yellow, justifyCenter);
gfxSleepMilliseconds(5000);
2013-05-20 03:59:28 +00:00
}
#endif
// Restart everything
gdispCloseFont(font1);
gdispCloseFont(font2);
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);
2014-09-16 22:44:31 +00:00
2013-05-20 03:59:28 +00:00
// 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;
}
// Clear the screen using the GWIN default background color
#if GFX_USE_GWIN
gdispGClear(MouseConfig.display, gwinGetDefaultBgColor());
#else
gdispGClear(MouseConfig.display, Black);
#endif
2014-09-16 22:44:31 +00:00
2013-05-20 03:59:28 +00:00
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 ginputGetMouse() 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 ginputGetMouse() has been called and the driver requires calibration storage, it will immediately save the data is has already obtained.
2013-05-20 03:59:28 +00:00
* 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) */
2014-09-16 00:06:59 +00:00
void ginputMouseWakeup(GMouse *m) {
m->flags |= GMOUSE_FLG_NEEDREAD;
2013-05-20 03:59:28 +00:00
gtimerJab(&MouseTimer);
}
/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */
2014-09-16 00:06:59 +00:00
void ginputMouseWakeupI(GMouse *m) {
m->flags |= GMOUSE_FLG_NEEDREAD;
2013-05-20 03:59:28 +00:00
gtimerJabI(&MouseTimer);
}
#endif /* GFX_USE_GINPUT && GINPUT_NEED_MOUSE */
/** @} */