aboutsummaryrefslogtreecommitdiffstats
path: root/src/ginput/ginput_mouse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ginput/ginput_mouse.c')
-rw-r--r--src/ginput/ginput_mouse.c679
1 files changed, 679 insertions, 0 deletions
diff --git a/src/ginput/ginput_mouse.c b/src/ginput/ginput_mouse.c
new file mode 100644
index 00000000..a0daba98
--- /dev/null
+++ b/src/ginput/ginput_mouse.c
@@ -0,0 +1,679 @@
+/*
+ * 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.org/license.html
+ */
+
+/**
+ * @file src/ginput/ginput_mouse.c
+ * @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"
+
+#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
+
+ #include <string.h> // Required for memcpy
+
+ #define GINPUT_MOUSE_CALIBRATION_FONT "* Double"
+ #define GINPUT_MOUSE_CALIBRATION_FONT2 "* Narrow"
+ #define GINPUT_MOUSE_CALIBRATION_TEXT "Calibration"
+ #define GINPUT_MOUSE_CALIBRATION_ERROR_TEXT "Failed - Please try again!"
+ #define GINPUT_MOUSE_CALIBRATION_SAME_TEXT "Error: Same Reading - Check Driver!"
+
+ #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;
+ systemticks_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
+ #define FLG_CAL_RAW 0x0100
+ #if GINPUT_MOUSE_NEED_CALIBRATION
+ GMouseCalibrationSaveRoutine fnsavecal;
+ GMouseCalibrationLoadRoutine fnloadcal;
+ Calibration caldata;
+ #endif
+ GDisplay * display;
+} MouseConfig;
+
+void _tsOrientClip(MouseReading *pt, GDisplay *g, bool_t doClip) {
+ coord_t w, h;
+
+ w = gdispGGetWidth(g);
+ h = gdispGGetHeight(g);
+
+ #if GDISP_NEED_CONTROL && !GINPUT_MOUSE_NO_ROTATION
+ switch(gdispGGetOrientation(g)) {
+ case GDISP_ROTATE_0:
+ break;
+ case GDISP_ROTATE_90:
+ {
+ coord_t t = pt->x;
+ pt->x = w - 1 - pt->y;
+ pt->y = 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->y;
+ pt->y = h - 1 - pt->x;
+ pt->x = t;
+ }
+ break;
+ default:
+ break;
+ }
+ #endif
+
+ if (doClip) {
+ 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;
+ }
+}
+
+#if GINPUT_MOUSE_NEED_CALIBRATION
+ static inline void _tsSetIdentity(Calibration *c) {
+ c->ax = 1;
+ c->bx = 0;
+ c->cx = 0;
+ c->ay = 0;
+ c->by = 1;
+ c->cy = 0;
+ }
+
+ static inline void _tsDrawCross(const MousePoint *pp) {
+ gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y, pp->x-2, pp->y, White);
+ gdispGDrawLine(MouseConfig.display, pp->x+2, pp->y, pp->x+15, pp->y, White);
+ gdispGDrawLine(MouseConfig.display, pp->x, pp->y-15, pp->x, pp->y-2, White);
+ gdispGDrawLine(MouseConfig.display, pp->x, pp->y+2, pp->x, pp->y+15, White);
+
+ gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y+15, pp->x-7, pp->y+15, RGB2COLOR(184,158,131));
+ gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y+7, pp->x-15, pp->y+15, RGB2COLOR(184,158,131));
+
+ gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y-15, pp->x-7, pp->y-15, RGB2COLOR(184,158,131));
+ gdispGDrawLine(MouseConfig.display, pp->x-15, pp->y-7, pp->x-15, pp->y-15, RGB2COLOR(184,158,131));
+
+ gdispGDrawLine(MouseConfig.display, pp->x+7, pp->y+15, pp->x+15, pp->y+15, RGB2COLOR(184,158,131));
+ gdispGDrawLine(MouseConfig.display, pp->x+15, pp->y+7, pp->x+15, pp->y+15, RGB2COLOR(184,158,131));
+
+ gdispGDrawLine(MouseConfig.display, pp->x+7, pp->y-15, pp->x+15, pp->y-15, RGB2COLOR(184,158,131));
+ gdispGDrawLine(MouseConfig.display, pp->x+15, pp->y-15, pp->x+15, pp->y-7, RGB2COLOR(184,158,131));
+ }
+
+ static inline void _tsClearCross(const MousePoint *pp) {
+ gdispGFillArea(MouseConfig.display, 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, GDisplay *g, Calibration *c) {
+ float dx;
+ coord_t c0, c1, c2;
+
+ #if GDISP_NEED_CONTROL
+ /* Convert all cross points back to GDISP_ROTATE_0 convention
+ * before calculating the calibration matrix.
+ */
+ switch(gdispGGetOrientation(g)) {
+ 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(g) - 1;
+ c0 -= cross[0].x;
+ c1 -= cross[1].x;
+ c2 -= cross[2].x;
+ break;
+ case GDISP_ROTATE_270:
+ c0 = c1 = c2 = gdispGGetHeight(g) - 1;
+ c0 -= cross[0].y;
+ c1 -= cross[1].y;
+ c2 -= cross[2].y;
+ break;
+ case GDISP_ROTATE_0:
+ default:
+ c0 = cross[0].x;
+ c1 = cross[1].x;
+ c2 = cross[2].x;
+ break;
+ }
+ #else
+ (void) g;
+
+ c0 = cross[0].x;
+ c1 = cross[1].x;
+ c2 = cross[2].x;
+ #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);
+
+ c->ax = ((float)(c0 - c2) * (float)(points[1].y - points[2].y)
+ - (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx;
+ c->bx = ((float)(c1 - c2) * (float)(points[0].x - points[2].x)
+ - (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx;
+ c->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;
+
+ #if GDISP_NEED_CONTROL
+ switch(gdispGGetOrientation(g)) {
+ case GDISP_ROTATE_90:
+ c0 = c1 = c2 = gdispGGetWidth(g) - 1;
+ c0 -= cross[0].x;
+ c1 -= cross[1].x;
+ c2 -= cross[2].x;
+ break;
+ case GDISP_ROTATE_180:
+ c0 = c1 = c2 = gdispGGetHeight(g) - 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;
+ case GDISP_ROTATE_0:
+ default:
+ c0 = cross[0].y;
+ c1 = cross[1].y;
+ c2 = cross[2].y;
+ break;
+ }
+ #else
+ c0 = cross[0].y;
+ c1 = cross[1].y;
+ c2 = cross[2].y;
+ #endif
+
+ c->ay = ((float)(c0 - c2) * (float)(points[1].y - points[2].y)
+ - (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx;
+ c->by = ((float)(c1 - c2) * (float)(points[0].x - points[2].x)
+ - (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx;
+ c->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;
+ }
+#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) {
+ get_raw_reading(pt);
+
+ #if GINPUT_MOUSE_NEED_CALIBRATION
+ _tsTransform(pt, &MouseConfig.caldata);
+ #endif
+
+ _tsOrientClip(pt, MouseConfig.display, !(MouseConfig.flags & FLG_CAL_RAW));
+}
+
+static void MousePoll(void *param) {
+ (void) param;
+ GSourceListener *psl;
+ GEventMouse *pe;
+ unsigned meta;
+ uint16_t upbtns, dnbtns;
+ 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
+ dnbtns = MouseConfig.t.buttons & ~MouseConfig.last_buttons;
+ upbtns = ~MouseConfig.t.buttons & MouseConfig.last_buttons;
+ meta = GMETA_NONE;
+
+ // As the touch moves up we need to return a point at the old position because some
+ // controllers return garbage with the mouse up
+ if ((upbtns & GINPUT_MOUSE_BTN_LEFT)) {
+ MouseConfig.t.x = MouseConfig.movepos.x;
+ MouseConfig.t.y = MouseConfig.movepos.y;
+ }
+
+ // 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
+ if ((dnbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) {
+ MouseConfig.clickpos.x = MouseConfig.t.x;
+ MouseConfig.clickpos.y = MouseConfig.t.y;
+ MouseConfig.clicktime = gfxSystemTicks();
+ MouseConfig.flags |= FLG_CLICK_TIMER;
+ 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;
+ if ((MouseConfig.flags & FLG_CLICK_TIMER)) {
+ if ((upbtns & GINPUT_MOUSE_BTN_LEFT)
+ #if GINPUT_MOUSE_CLICK_TIME != TIME_INFINITE
+ && gfxSystemTicks() - MouseConfig.clicktime < gfxMillisecondsToTicks(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 && MouseConfig.last_buttons == MouseConfig.t.buttons && !(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;
+ }
+ pe->display = MouseConfig.display;
+ geventSendEvent(psl);
+ }
+ }
+}
+
+GSourceHandle ginputGetMouse(uint16_t instance) {
+ #if GINPUT_MOUSE_NEED_CALIBRATION
+ Calibration *pc;
+ #endif
+
+ // We only support a single mouse instance currently
+ // Instance 9999 is the same as instance 0 except that it installs
+ // a special "raw" calibration if there isn't one we can load.
+ if (instance && instance != 9999)
+ return 0;
+
+ // Make sure we have a valid mouse display
+ if (!MouseConfig.display)
+ MouseConfig.display = GDISP;
+
+ // 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))) {
+ memcpy(&MouseConfig.caldata, pc, sizeof(MouseConfig.caldata));
+ MouseConfig.flags |= (FLG_CAL_OK|FLG_CAL_SAVED);
+ if ((MouseConfig.flags & FLG_CAL_FREE))
+ gfxFree((void *)pc);
+ } else if (instance == 9999) {
+ _tsSetIdentity(&MouseConfig.caldata);
+ MouseConfig.flags |= (FLG_CAL_OK|FLG_CAL_SAVED|FLG_CAL_RAW);
+ } 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;
+}
+
+void ginputSetMouseDisplay(uint16_t instance, GDisplay *g) {
+ if (instance)
+ return;
+
+ MouseConfig.display = g ? g : GDISP;
+}
+
+GDisplay *ginputGetMouseDisplay(uint16_t instance) {
+ if (instance)
+ return 0;
+
+ return MouseConfig.display;
+}
+
+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);
+
+ 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;
+}
+
+bool_t ginputCalibrateMouse(uint16_t instance) {
+ #if !GINPUT_MOUSE_NEED_CALIBRATION
+ (void) instance;
+
+ 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
+ MousePoint points[GINPUT_MOUSE_CALIBRATION_POINTS];
+ 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);
+
+ #if GDISP_NEED_CLIP
+ gdispGSetClip(MouseConfig.display, 0, 0, width, height);
+ #endif
+
+ #if GINPUT_MOUSE_MAX_CALIBRATION_ERROR >= 0
+ while(1) {
+ #endif
+ gdispGClear(MouseConfig.display, Blue);
+
+ gdispGFillStringBox(MouseConfig.display, 0, 5, width, 30, GINPUT_MOUSE_CALIBRATION_TEXT, font1, White, Blue, justifyCenter);
+
+ 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))
+ gfxSleepMilliseconds(20);
+
+ /* Average all the samples while the mouse is down */
+ for(px = py = 0, j = 0;
+ gfxSleepMilliseconds(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);
+
+ 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);
+ }
+
+ }
+
+ /* Apply 3 point calibration algorithm */
+ _tsDo3PointCalibration(cross, points, MouseConfig.display, &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);
+ _tsOrientClip(&MouseConfig.t, MouseConfig.display, FALSE);
+
+ /* 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);
+ }
+ #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);
+
+ // 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
+
+ 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.
+ * 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 /* GFX_USE_GINPUT && GINPUT_NEED_MOUSE */
+/** @} */