/* * 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 // 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 */