ugfx/src/ginput/ginput_mouse.c
Joel Bodenmann 9c0678a291 Avoid duplicate const specifier compiler warnings
The original code is perfectly valid standard C. However, some compilers (especially GCC) complain about duplicate const specifiers anyway.
At this point we cave in as there doesn't seem to be any efforts to fix this problem by the corresponding compiler vendors.

uGFX v3 will no longer suffer from this problem as the driver interface works differently in this area.
2021-08-12 12:20:07 +02:00

839 lines
26 KiB
C

/*
* This file is subject to the terms of the GFX License. If a copy of
* the license was not distributed with this file, you can obtain one at:
*
* http://ugfx.io/license.html
*/
/**
* @file src/ginput/ginput_mouse.c
* @brief GINPUT mouse/touch code.
*/
#include "../../gfx.h"
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
// Just to make code easier
#if !GFX_USE_GDISP
#define GDISP 0
#endif
// Local Settings
#define CALIBRATION_POLL_PERIOD 20 // milliseconds
#define CALIBRATION_MINPRESS_PERIOD 300 // milliseconds
#define CALIBRATION_MAXPRESS_PERIOD 5000 // milliseconds
#ifdef GINPUT_TOUCH_CALIBRATION_FONT1
#define CALIBRATION_FONT1 GINPUT_TOUCH_CALIBRATION_FONT1
#else
#define CALIBRATION_FONT1 "* Double"
#endif
#ifdef GINPUT_TOUCH_CALIBRATION_FONT2
#define CALIBRATION_FONT2 GINPUT_TOUCH_CALIBRATION_FONT2
#else
#define CALIBRATION_FONT2 "* Narrow"
#endif
#define CALIBRATION_BACKGROUND GFX_BLUE
#define CALIBRATION_CROSS_COLOR1 GFX_WHITE
#define CALIBRATION_CROSS_COLOR2 RGB2COLOR(184,158,131)
#define CALIBRATION_CROSS_INNERGAP 2
#define CALIBRATION_CROSS_RADIUS 15
#ifdef GINPUT_TOUCH_CALIBRATION_TITLE
#define CALIBRATION_TITLE GINPUT_TOUCH_CALIBRATION_TITLE
#else
#define CALIBRATION_TITLE "Calibration"
#endif
#define CALIBRATION_TITLE_Y 5
#define CALIBRATION_TITLE_HEIGHT 30
#define CALIBRATION_TITLE_COLOR GFX_WHITE
#define CALIBRATION_TITLE_BACKGROUND GFX_BLUE
#ifdef GINPUT_TOUCH_CALIBRATION_ERROR
#define CALIBRATION_ERROR_TEXT GINPUT_TOUCH_CALIBRATION_ERROR
#else
#define CALIBRATION_ERROR_TEXT "Calibration Failed!"
#endif
#define CALIBRATION_ERROR_DELAY 3000
#define CALIBRATION_ERROR_COLOR GFX_RED
#define CALIBRATION_ERROR_BACKGROUND GFX_YELLOW
#define CALIBRATION_ERROR_Y 35
#define CALIBRATION_ERROR_HEIGHT 40
// Get the mouse driver interface
#include "ginput_driver_mouse.h"
// The mouse poll timer
static GTIMER_DECL(MouseTimer);
// Calibration application
#if !GINPUT_TOUCH_NOCALIBRATE
#include <string.h> // Required for memcpy
static GFXINLINE void CalibrationTransform(GMouseReading *pt, const GMouseCalibration *c) {
gCoord x, y;
x = (gCoord) (c->ax * pt->x + c->bx * pt->y + c->cx);
y = (gCoord) (c->ay * pt->x + c->by * pt->y + c->cy);
pt->x = x;
pt->y = y;
}
#endif
static void SendMouseEvent(GSourceListener *psl, GMouse *m, GMouseReading *r) {
GEventMouse *pe;
// If there is no event buffer just mark a missed event
if (!(pe = (GEventMouse *)geventGetEventBuffer(psl))) {
// This listener is missing - save the meta events that have happened
psl->srcflags |= ((r->buttons & GMETA_MASK)|GINPUT_MISSED_MOUSE_EVENT);
return;
}
// If we haven't really moved (and there are no meta events) don't bother sending the event
if (!(r->buttons & GMETA_MASK) && !psl->srcflags && !(psl->listenflags & GLISTEN_MOUSENOFILTER)
&& r->x == m->r.x && r->y == m->r.y && (r->buttons & GINPUT_MOUSE_BTN_MASK) == (m->r.buttons & GINPUT_MOUSE_BTN_MASK))
return;
// Send the event only 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))
&& !((r->buttons & GMETA_MASK) && (psl->listenflags & GLISTEN_MOUSEMETA)))
return;
#if !GINPUT_TOUCH_NOTOUCH
pe->type = (gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH) ? GEVENT_TOUCH : GEVENT_MOUSE;
#else
pe->type = GEVENT_MOUSE;
#endif
pe->x = r->x;
pe->y = r->y;
pe->z = r->z;
pe->buttons = r->buttons | psl->srcflags;
psl->srcflags = 0;
pe->display = m->display;
geventSendEvent(psl);
}
static void GetMouseReading(GMouse *m) {
GMouseReading r;
// Step 1 - Get the Raw Reading
{
m->flags &= ~GMOUSE_FLG_NEEDREAD;
if (!gmvmt(m)->get(m, &r))
return;
}
// Step 2 - Handle touch and button 0 debouncing
{
// Clean off button garbage
r.buttons &= GINPUT_MOUSE_BTN_MASK;
#if !GINPUT_TOUCH_NOTOUCH
// 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
} else {
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
}
}
// Devices with poor button 0 transitioning need debouncing
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_POORUPDOWN)) {
// Are we in a transition test
if ((m->flags & GMOUSE_FLG_INDELTA)) {
if (!((r.buttons ^ m->r.buttons) & GINPUT_MOUSE_BTN_LEFT)) {
// Transition failed
m->flags &= ~GMOUSE_FLG_INDELTA;
return;
}
// Transition succeeded
m->flags &= ~GMOUSE_FLG_INDELTA;
// Should we start a transition test
} else if (((r.buttons ^ m->r.buttons) & GINPUT_MOUSE_BTN_LEFT)) {
m->flags |= GMOUSE_FLG_INDELTA;
return;
}
}
#endif
#if !GINPUT_TOUCH_NOCALIBRATE_GUI
// Stop here with just the raw x,y reading during calibration
if ((m->flags & GMOUSE_FLG_IN_CAL)) {
if ((r.buttons & GINPUT_MOUSE_BTN_LEFT)) {
m->r.x = r.x;
m->r.y = r.y;
}
m->r.buttons = r.buttons;
return;
}
#endif
}
// Step 3 - Apply calibration, rotation and display clipping
{
// If the mouse is up we may need to keep our previous position
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_ONLY_DOWN) && !(r.buttons & GINPUT_MOUSE_BTN_LEFT)) {
r.x = m->r.x;
r.y = m->r.y;
} else {
#if !GINPUT_TOUCH_NOCALIBRATE
// Do we need to calibrate the reading?
if ((m->flags & GMOUSE_FLG_CALIBRATE))
CalibrationTransform(&r, &m->caldata);
#endif
// We can't clip or rotate if we don't have a display
if (m->display) {
gCoord w, h;
// 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)) {
gCoord t;
switch(gdispGGetOrientation(m->display)) {
case gOrientation0:
break;
case gOrientation90:
t = r.x;
r.x = w - 1 - r.y;
r.y = t;
break;
case gOrientation180:
r.x = w - 1 - r.x;
r.y = h - 1 - r.y;
break;
case gOrientation270:
t = r.y;
r.y = h - 1 - r.x;
r.x = t;
break;
default:
break;
}
}
#endif
// Do we need to clip the reading to the display
if ((m->flags & GMOUSE_FLG_CLIP)) {
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;
}
}
}
}
// Step 4 - Apply jitter detection
#if !GINPUT_TOUCH_NOTOUCH
{
const GMouseJitter *pj;
gU32 diff;
// Are we in pen or finger mode
pj = (m->flags & GMOUSE_FLG_FINGERMODE) ? &gmvmt(m)->finger_jitter : &gmvmt(m)->pen_jitter;
// Is this just movement jitter
if (pj->move > 0) {
diff = (gU32)(r.x - m->r.x) * (gU32)(r.x - m->r.x) + (gU32)(r.y - m->r.y) * (gU32)(r.y - m->r.y);
if (diff < (gU32)pj->move * (gU32)pj->move) {
r.x = m->r.x;
r.y = m->r.y;
}
}
// Check if the click has moved outside the click area and if so cancel the click
if (pj->click > 0 && (m->flags & GMOUSE_FLG_CLICK_TIMER)) {
diff = (gU32)(r.x - m->clickpos.x) * (gU32)(r.x - m->clickpos.x) + (gU32)(r.y - m->clickpos.y) * (gU32)(r.y - m->clickpos.y);
if (diff > (gU32)pj->click * (gU32)pj->click)
m->flags &= ~GMOUSE_FLG_CLICK_TIMER;
}
}
#endif
// Step 5 - Click, context-click and other meta event detection
{
gU16 upbtns, dnbtns;
// Calculate button transitions
dnbtns = r.buttons & ~m->r.buttons;
upbtns = ~r.buttons & m->r.buttons;
// Left mouse down generates the Mouse-down meta event
if ((dnbtns & GINPUT_MOUSE_BTN_LEFT))
r.buttons |= GMETA_MOUSE_DOWN;
// Left mouse up generates the Mouse-up meta event
if ((upbtns & GINPUT_MOUSE_BTN_LEFT))
r.buttons |= GMETA_MOUSE_UP;
// Left/Right mouse down starts the click timer
if ((dnbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) {
m->clickpos.x = r.x;
m->clickpos.y = r.y;
m->clicktime = gfxSystemTicks();
m->flags |= GMOUSE_FLG_CLICK_TIMER;
}
// Left/Right mouse up with the click timer still running may generate a click or context click
if ((upbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT)) && (m->flags & GMOUSE_FLG_CLICK_TIMER)) {
m->flags &= ~GMOUSE_FLG_CLICK_TIMER;
m->clicktime = gfxSystemTicks() - m->clicktime;
// Was this a short click?
if (m->clicktime <= gfxMillisecondsToTicks(GINPUT_MOUSE_CLICK_TIME)) {
if ((upbtns & GINPUT_MOUSE_BTN_RIGHT))
r.buttons |= GMETA_MOUSE_CXTCLICK;
if ((upbtns & GINPUT_MOUSE_BTN_LEFT))
r.buttons |= GMETA_MOUSE_CLICK;
}
#if !GINPUT_TOUCH_NOTOUCH
// Was this a long click on a touch device?
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH) && m->clicktime >= gfxMillisecondsToTicks(GINPUT_TOUCH_CXTCLICK_TIME))
r.buttons |= GMETA_MOUSE_CXTCLICK;
#endif
}
}
// Step 6 - Send the event to the listeners that are interested.
{
GSourceListener *psl;
// Send to the "All Mice" source listeners
psl = 0;
while ((psl = geventGetSourceListener((GSourceHandle)&MouseTimer, psl)))
SendMouseEvent(psl, m, &r);
// Send to the mouse specific source listeners
psl = 0;
while ((psl = geventGetSourceListener((GSourceHandle)m, psl)))
SendMouseEvent(psl, m, &r);
}
// Step 7 - Finally save the results
m->r.x = r.x;
m->r.y = r.y;
m->r.z = r.z;
m->r.buttons = r.buttons;
}
static void MousePoll(void *param) {
GMouse * m;
(void) param;
for(m = (GMouse *)gdriverGetNext(GDRIVER_TYPE_MOUSE, 0); m; m = (GMouse *)gdriverGetNext(GDRIVER_TYPE_MOUSE, (GDriver *)m)) {
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_NOPOLL) || (m->flags & GMOUSE_FLG_NEEDREAD))
GetMouseReading(m);
}
}
// Calibration user interface
#if !GINPUT_TOUCH_NOCALIBRATE_GUI
#if !defined(GFX_USE_GDISP) || !GFX_USE_GDISP
#error "GINPUT: GFX_USE_GDISP must be defined when calibration is required"
#endif
static GFXINLINE void CalibrationCrossDraw(GMouse *m, const gPoint *pp) {
gdispGDrawLine(m->display, pp->x-CALIBRATION_CROSS_RADIUS, pp->y, pp->x-CALIBRATION_CROSS_INNERGAP, pp->y, CALIBRATION_CROSS_COLOR1);
gdispGDrawLine(m->display, pp->x+CALIBRATION_CROSS_INNERGAP, pp->y, pp->x+CALIBRATION_CROSS_RADIUS, pp->y, CALIBRATION_CROSS_COLOR1);
gdispGDrawLine(m->display, pp->x, pp->y-CALIBRATION_CROSS_RADIUS, pp->x, pp->y-CALIBRATION_CROSS_INNERGAP, CALIBRATION_CROSS_COLOR1);
gdispGDrawLine(m->display, pp->x, pp->y+CALIBRATION_CROSS_INNERGAP, pp->x, pp->y+CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR1);
gdispGDrawLine(m->display, pp->x-CALIBRATION_CROSS_RADIUS, pp->y+CALIBRATION_CROSS_RADIUS, pp->x-CALIBRATION_CROSS_RADIUS/2, pp->y+CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR2);
gdispGDrawLine(m->display, pp->x-CALIBRATION_CROSS_RADIUS, pp->y+CALIBRATION_CROSS_RADIUS/2, pp->x-CALIBRATION_CROSS_RADIUS, pp->y+CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR2);
gdispGDrawLine(m->display, pp->x-CALIBRATION_CROSS_RADIUS, pp->y-CALIBRATION_CROSS_RADIUS, pp->x-CALIBRATION_CROSS_RADIUS/2, pp->y-CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR2);
gdispGDrawLine(m->display, pp->x-CALIBRATION_CROSS_RADIUS, pp->y-CALIBRATION_CROSS_RADIUS/2, pp->x-CALIBRATION_CROSS_RADIUS, pp->y-CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR2);
gdispGDrawLine(m->display, pp->x+CALIBRATION_CROSS_RADIUS/2, pp->y+CALIBRATION_CROSS_RADIUS, pp->x+CALIBRATION_CROSS_RADIUS, pp->y+CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR2);
gdispGDrawLine(m->display, pp->x+CALIBRATION_CROSS_RADIUS, pp->y+CALIBRATION_CROSS_RADIUS/2, pp->x+CALIBRATION_CROSS_RADIUS, pp->y+CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR2);
gdispGDrawLine(m->display, pp->x+CALIBRATION_CROSS_RADIUS/2, pp->y-CALIBRATION_CROSS_RADIUS, pp->x+CALIBRATION_CROSS_RADIUS, pp->y-CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR2);
gdispGDrawLine(m->display, pp->x+CALIBRATION_CROSS_RADIUS, pp->y-CALIBRATION_CROSS_RADIUS, pp->x+CALIBRATION_CROSS_RADIUS, pp->y-CALIBRATION_CROSS_RADIUS/2, CALIBRATION_CROSS_COLOR2);
}
static GFXINLINE void CalibrationCrossClear(GMouse *m, const gPoint *pp) {
gdispGFillArea(m->display, pp->x - CALIBRATION_CROSS_RADIUS, pp->y - CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_RADIUS*2+1, CALIBRATION_CROSS_RADIUS*2+1, CALIBRATION_BACKGROUND);
}
static GFXINLINE void CalibrationCalculate(GMouse *m, const gPoint *cross, const gPoint *points) {
float dx;
gCoord c0, c1, c2;
(void) m;
// Work on x values
c0 = cross[0].x;
c1 = cross[1].x;
c2 = cross[2].x;
#if GDISP_NEED_CONTROL
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
/* Convert all cross points back to gOrientation0 convention
* before calculating the calibration matrix.
*/
switch(gdispGGetOrientation(m->display)) {
case gOrientation90:
c0 = cross[0].y;
c1 = cross[1].y;
c2 = cross[2].y;
break;
case gOrientation180:
c0 = c1 = c2 = gdispGGetWidth(m->display) - 1;
c0 -= cross[0].x;
c1 -= cross[1].x;
c2 -= cross[2].x;
break;
case gOrientation270:
c0 = c1 = c2 = gdispGGetHeight(m->display) - 1;
c0 -= cross[0].y;
c1 -= cross[1].y;
c2 -= cross[2].y;
break;
default:
break;
}
}
#endif
/* 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);
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;
#if GDISP_NEED_CONTROL
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
switch(gdispGGetOrientation(m->display)) {
case gOrientation90:
c0 = c1 = c2 = gdispGGetWidth(m->display) - 1;
c0 -= cross[0].x;
c1 -= cross[1].x;
c2 -= cross[2].x;
break;
case gOrientation180:
c0 = c1 = c2 = gdispGGetHeight(m->display) - 1;
c0 -= cross[0].y;
c1 -= cross[1].y;
c2 -= cross[2].y;
break;
case gOrientation270:
c0 = cross[0].x;
c1 = cross[1].x;
c2 = cross[2].x;
break;
default:
break;
}
}
#endif
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;
}
static gU32 CalibrateMouse(GMouse *m) {
gCoord w, h;
gPoint cross[4]; // The locations of the test points on the display
gPoint points[4]; // The x, y readings obtained from the mouse for each test point
gU32 err;
#if GDISP_NEED_TEXT
gFont font1, font2;
#endif
#if GDISP_NEED_TEXT
font1 = gdispOpenFont(CALIBRATION_FONT1);
if (!font1) font1 = gdispOpenFont("*");
font2 = gdispOpenFont(CALIBRATION_FONT2);
if (!font2) font2 = gdispOpenFont("*");
#endif
err = 0;
w = gdispGGetWidth(m->display);
h = gdispGGetHeight(m->display);
#if GDISP_NEED_CLIP
gdispGSetClip(m->display, 0, 0, w, h);
#endif
// Ensure we get minimally processed readings for the calibration
m->flags |= GMOUSE_FLG_IN_CAL;
// Set up our calibration locations
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_CAL_EXTREMES)) {
cross[0].x = 0; cross[0].y = 0;
cross[1].x = w-1; cross[1].y = 0;
cross[2].x = w-1; cross[2].y = h-1;
cross[3].x = w/2; cross[3].y = h/2;
} else {
cross[0].x = w/4; cross[0].y = h/4;
cross[1].x = w-w/4; cross[1].y = h/4;
cross[2].x = w-w/4; cross[2].y = h-h/4;
cross[3].x = w/2; cross[3].y = h/2;
}
// Set up the calibration display
gdispGClear(m->display, CALIBRATION_BACKGROUND);
#if GDISP_NEED_TEXT
gdispGFillStringBox(m->display,
0, CALIBRATION_TITLE_Y, w, CALIBRATION_TITLE_HEIGHT,
CALIBRATION_TITLE, font1, CALIBRATION_TITLE_COLOR, CALIBRATION_TITLE_BACKGROUND,
gJustifyCenter);
#endif
// Calculate the calibration
{
unsigned i, maxpoints;
maxpoints = (gmvmt(m)->d.flags & GMOUSE_VFLG_CAL_TEST) ? 4 : 3;
// Loop through the calibration points
for(i = 0; i < maxpoints; i++) {
gI32 px, py;
unsigned j;
// Draw the current calibration point
CalibrationCrossDraw(m, &cross[i]);
// Get a valid "point pressed" average reading
do {
// Wait for the mouse to be pressed
while(!(m->r.buttons & GINPUT_MOUSE_BTN_LEFT))
gfxSleepMilliseconds(CALIBRATION_POLL_PERIOD);
// Sum samples taken every CALIBRATION_POLL_PERIOD milliseconds while the mouse is down
px = py = j = 0;
while((m->r.buttons & GINPUT_MOUSE_BTN_LEFT)) {
// Limit sampling period to prevent overflow
if (j < CALIBRATION_MAXPRESS_PERIOD/CALIBRATION_POLL_PERIOD) {
px += m->r.x;
py += m->r.y;
j++;
}
gfxSleepMilliseconds(CALIBRATION_POLL_PERIOD);
}
// Ignore presses less than CALIBRATION_MINPRESS_PERIOD milliseconds
} while(j < CALIBRATION_MINPRESS_PERIOD/CALIBRATION_POLL_PERIOD);
points[i].x = px / j;
points[i].y = py / j;
// Clear the current calibration point
CalibrationCrossClear(m, &cross[i]);
}
}
// Apply 3 point calibration algorithm
CalibrationCalculate(m, cross, points);
/* 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 return the error.
*/
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_CAL_TEST)) {
const GMouseJitter *pj;
// Are we in pen or finger mode
pj = (m->flags & GMOUSE_FLG_FINGERMODE) ? &gmvmt(m)->finger_jitter : &gmvmt(m)->pen_jitter;
// Transform the co-ordinates
CalibrationTransform((GMouseReading *)&points[3], &m->caldata);
// Do we need to rotate the reading to match the display
#if GDISP_NEED_CONTROL
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
gCoord t;
switch(gdispGGetOrientation(m->display)) {
case gOrientation0:
break;
case gOrientation90:
t = points[3].x;
points[3].x = w - 1 - points[3].y;
points[3].y = t;
break;
case gOrientation180:
points[3].x = w - 1 - points[3].x;
points[3].y = h - 1 - points[3].y;
break;
case gOrientation270:
t = points[3].y;
points[3].y = h - 1 - points[3].x;
points[3].x = t;
break;
default:
break;
}
}
#endif
// Is this accurate enough?
err = (points[3].x - cross[3].x) * (points[3].x - cross[3].x) + (points[3].y - cross[3].y) * (points[3].y - cross[3].y);
if (err > (gU32)pj->calibrate * (gU32)pj->calibrate) {
#if GDISP_NEED_TEXT
// No - Display error and return
gdispGFillStringBox(m->display,
0, CALIBRATION_ERROR_Y, w, CALIBRATION_ERROR_HEIGHT,
CALIBRATION_ERROR_TEXT, font2, CALIBRATION_ERROR_COLOR, CALIBRATION_ERROR_BACKGROUND,
gJustifyCenter);
gfxSleepMilliseconds(CALIBRATION_ERROR_DELAY);
#endif
} else
err = 0;
}
// We are done calibrating
#if GDISP_NEED_TEXT
gdispCloseFont(font1);
gdispCloseFont(font2);
#endif
m->flags &= ~GMOUSE_FLG_IN_CAL;
m->flags |= GMOUSE_FLG_CLIP;
// Save the calibration data (if possible)
if (!err) {
m->flags |= GMOUSE_FLG_CALIBRATE;
#if GINPUT_TOUCH_USER_CALIBRATION_SAVE
SaveMouseCalibration(gdriverGetDriverInstanceNumber((GDriver *)m), &m->caldata, sizeof(GMouseCalibration));
#endif
if (gmvmt(m)->calsave)
gmvmt(m)->calsave(m, &m->caldata, sizeof(GMouseCalibration));
}
// Force an initial reading
m->r.buttons = 0;
GetMouseReading(m);
// Clear the screen using the GWIN default background color
#if GFX_USE_GWIN
gdispGClear(m->display, gwinGetDefaultBgColor());
#else
gdispGClear(m->display, GDISP_STARTUP_COLOR);
#endif
return err;
}
#endif
void _gmouseInit(void) {
// GINPUT_MOUSE_DRIVER_LIST is defined - create each driver instance
#if defined(GINPUT_MOUSE_DRIVER_LIST)
{
int i;
typedef const GMouseVMT const GMOUSEVMTLIST[1];
extern GMOUSEVMTLIST GINPUT_MOUSE_DRIVER_LIST;
static const GMouseVMT * const dclist[] = {GINPUT_MOUSE_DRIVER_LIST};
for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) {
if (!(dclist[i]->d.flags & GMOUSE_VFLG_DYNAMICONLY))
gdriverRegister(&dclist[i]->d, GDISP);
}
}
// One and only one mouse
#else
{
/*
* This should be: extern const GMouseVMT const GMOUSEVMT_OnlyOne[1];
* However, some major compilers complain about the duplicate const specifier even though this is perfectly valid standard C.
*/
extern const GMouseVMT GMOUSEVMT_OnlyOne[1];
if (!(GMOUSEVMT_OnlyOne->d.flags & GMOUSE_VFLG_DYNAMICONLY))
gdriverRegister(&GMOUSEVMT_OnlyOne->d, GDISP);
}
#endif
}
void _gmouseDeinit(void) {
gtimerDeinit(&MouseTimer);
}
gBool _gmouseInitDriver(GDriver *g, void *display, unsigned driverinstance, unsigned systeminstance) {
#define m ((GMouse *)g)
(void) systeminstance;
// The initial display is passed in the parameter for mice
m->display = display;
#if !GINPUT_TOUCH_NOTOUCH
// Should this mouse start in finger mode? (according to the VMT)
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_DEFAULTFINGER))
m->flags |= GMOUSE_FLG_FINGERMODE;
#endif
// Init the mouse
if (!gmvmt(m)->init((GMouse *)g, driverinstance))
return gFalse;
// Ensure the Poll timer is started
if (!gtimerIsActive(&MouseTimer))
gtimerStart(&MouseTimer, MousePoll, 0, gTrue, GINPUT_MOUSE_POLL_PERIOD);
return gTrue;
#undef m
}
void _gmousePostInitDriver(GDriver *g) {
#define m ((GMouse *)g)
#if !GINPUT_TOUCH_STARTRAW
m->flags |= GMOUSE_FLG_CLIP;
#endif
#if !GINPUT_TOUCH_NOCALIBRATE && !GINPUT_TOUCH_STARTRAW
if ((gmvmt(m)->d.flags & GMOUSE_VFLG_CALIBRATE)) {
#if GINPUT_TOUCH_USER_CALIBRATION_LOAD
if (LoadMouseCalibration(gdriverGetDriverInstanceNumber((GDriver *)m), &m->caldata, sizeof(GMouseCalibration)))
m->flags |= GMOUSE_FLG_CALIBRATE;
else
#endif
if (gmvmt(m)->calload && gmvmt(m)->calload(m, &m->caldata, sizeof(GMouseCalibration)))
m->flags |= GMOUSE_FLG_CALIBRATE;
#if !GINPUT_TOUCH_NOCALIBRATE_GUI
else
while (CalibrateMouse(m));
#endif
}
#endif
// Get the first reading
GetMouseReading(m);
#undef m
}
void _gmouseDeInitDriver(GDriver *g) {
(void) g;
}
GSourceHandle ginputGetMouse(unsigned instance) {
if (instance == GMOUSE_ALL_INSTANCES)
return (GSourceHandle)&MouseTimer;
return (GSourceHandle)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance);
}
void ginputSetMouseDisplay(unsigned instance, GDisplay *g) {
GMouse *m;
if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
return;
m->display = g ? g : GDISP;
}
GDisplay *ginputGetMouseDisplay(unsigned instance) {
GMouse *m;
if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
return 0;
return m->display;
}
gBool ginputGetMouseStatus(unsigned instance, GEventMouse *pe) {
GMouse *m;
// 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);
if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
return gFalse;
#if !GINPUT_TOUCH_NOCALIBRATE_GUI
if ((m->flags & GMOUSE_FLG_IN_CAL))
return gFalse;
#endif
#if !GINPUT_TOUCH_NOTOUCH
pe->type = (gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH) ? GEVENT_TOUCH : GEVENT_MOUSE;
#else
pe->type = GEVENT_MOUSE;
#endif
pe->x = m->r.x;
pe->y = m->r.y;
pe->z = m->r.z;
pe->buttons = m->r.buttons;
pe->display = m->display;
return gTrue;
}
#if !GINPUT_TOUCH_NOTOUCH
void ginputSetFingerMode(unsigned instance, gBool on) {
GMouse *m;
if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
return;
if (on)
m->flags |= GMOUSE_FLG_FINGERMODE;
else
m->flags &= ~GMOUSE_FLG_FINGERMODE;
}
#endif
#if !GINPUT_TOUCH_NOCALIBRATE_GUI
gU32 ginputCalibrateMouse(unsigned instance) {
GMouse *m;
// Find the instance
if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
return 0;
// Check it needs calibration
if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_CALIBRATE))
return 0;
return CalibrateMouse(m);
}
#endif
/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */
void _gmouseWakeup(GMouse *m) {
if (m)
m->flags |= GMOUSE_FLG_NEEDREAD;
gtimerJab(&MouseTimer);
}
/* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */
void _gmouseWakeupI(GMouse *m) {
if (m)
m->flags |= GMOUSE_FLG_NEEDREAD;
gtimerJabI(&MouseTimer);
}
#endif /* GFX_USE_GINPUT && GINPUT_NEED_MOUSE */