µGFX  2.9
version 2.9
ginput_mouse.c
Go to the documentation of this file.
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.io/license.html
6  */
7 
8 /**
9  * @file src/ginput/ginput_mouse.c
10  * @brief GINPUT mouse/touch code.
11  */
12 #include "../../gfx.h"
13 
14 #if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
15 
16 // Just to make code easier
17 #if !GFX_USE_GDISP
18  #define GDISP 0
19 #endif
20 
21 // Local Settings
22 #define CALIBRATION_POLL_PERIOD 20 // milliseconds
23 #define CALIBRATION_MINPRESS_PERIOD 300 // milliseconds
24 #define CALIBRATION_MAXPRESS_PERIOD 5000 // milliseconds
25 
26 #ifdef GINPUT_TOUCH_CALIBRATION_FONT1
27  #define CALIBRATION_FONT1 GINPUT_TOUCH_CALIBRATION_FONT1
28 #else
29  #define CALIBRATION_FONT1 "* Double"
30 #endif
31 #ifdef GINPUT_TOUCH_CALIBRATION_FONT2
32  #define CALIBRATION_FONT2 GINPUT_TOUCH_CALIBRATION_FONT2
33 #else
34  #define CALIBRATION_FONT2 "* Narrow"
35 #endif
36 #define CALIBRATION_BACKGROUND GFX_BLUE
37 
38 #define CALIBRATION_CROSS_COLOR1 GFX_WHITE
39 #define CALIBRATION_CROSS_COLOR2 RGB2COLOR(184,158,131)
40 #define CALIBRATION_CROSS_INNERGAP 2
41 #define CALIBRATION_CROSS_RADIUS 15
42 
43 #ifdef GINPUT_TOUCH_CALIBRATION_TITLE
44  #define CALIBRATION_TITLE GINPUT_TOUCH_CALIBRATION_TITLE
45 #else
46  #define CALIBRATION_TITLE "Calibration"
47 #endif
48 #define CALIBRATION_TITLE_Y 5
49 #define CALIBRATION_TITLE_HEIGHT 30
50 #define CALIBRATION_TITLE_COLOR GFX_WHITE
51 #define CALIBRATION_TITLE_BACKGROUND GFX_BLUE
52 
53 #ifdef GINPUT_TOUCH_CALIBRATION_ERROR
54  #define CALIBRATION_ERROR_TEXT GINPUT_TOUCH_CALIBRATION_ERROR
55 #else
56  #define CALIBRATION_ERROR_TEXT "Calibration Failed!"
57 #endif
58 #define CALIBRATION_ERROR_DELAY 3000
59 #define CALIBRATION_ERROR_COLOR GFX_RED
60 #define CALIBRATION_ERROR_BACKGROUND GFX_YELLOW
61 #define CALIBRATION_ERROR_Y 35
62 #define CALIBRATION_ERROR_HEIGHT 40
63 
64 // Get the mouse driver interface
65 #include "ginput_driver_mouse.h"
66 
67 // The mouse poll timer
68 static GTIMER_DECL(MouseTimer);
69 
70 // Calibration application
71 #if !GINPUT_TOUCH_NOCALIBRATE
72  #include <string.h> // Required for memcpy
73 
74  static GFXINLINE void CalibrationTransform(GMouseReading *pt, const GMouseCalibration *c) {
75  gCoord x, y;
76 
77  x = (gCoord) (c->ax * pt->x + c->bx * pt->y + c->cx);
78  y = (gCoord) (c->ay * pt->x + c->by * pt->y + c->cy);
79 
80  pt->x = x;
81  pt->y = y;
82  }
83 #endif
84 
85 static void SendMouseEvent(GSourceListener *psl, GMouse *m, GMouseReading *r) {
86  GEventMouse *pe;
87 
88  // If there is no event buffer just mark a missed event
89  if (!(pe = (GEventMouse *)geventGetEventBuffer(psl))) {
90  // This listener is missing - save the meta events that have happened
91  psl->srcflags |= ((r->buttons & GMETA_MASK)|GINPUT_MISSED_MOUSE_EVENT);
92  return;
93  }
94 
95  // If we haven't really moved (and there are no meta events) don't bother sending the event
96  if (!(r->buttons & GMETA_MASK) && !psl->srcflags && !(psl->listenflags & GLISTEN_MOUSENOFILTER)
97  && r->x == m->r.x && r->y == m->r.y && (r->buttons & GINPUT_MOUSE_BTN_MASK) == (m->r.buttons & GINPUT_MOUSE_BTN_MASK))
98  return;
99 
100  // Send the event only if we are listening for it
101  if (!((r->buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEDOWNMOVES))
102  && !(!(r->buttons & GINPUT_MOUSE_BTN_LEFT) && (psl->listenflags & GLISTEN_MOUSEUPMOVES))
103  && !((r->buttons & GMETA_MASK) && (psl->listenflags & GLISTEN_MOUSEMETA)))
104  return;
105 
106  #if !GINPUT_TOUCH_NOTOUCH
107  pe->type = (gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH) ? GEVENT_TOUCH : GEVENT_MOUSE;
108  #else
109  pe->type = GEVENT_MOUSE;
110  #endif
111  pe->x = r->x;
112  pe->y = r->y;
113  pe->z = r->z;
114  pe->buttons = r->buttons | psl->srcflags;
115  psl->srcflags = 0;
116  pe->display = m->display;
117  geventSendEvent(psl);
118 }
119 
120 static void GetMouseReading(GMouse *m) {
121  GMouseReading r;
122 
123  // Step 1 - Get the Raw Reading
124  {
125  m->flags &= ~GMOUSE_FLG_NEEDREAD;
126  if (!gmvmt(m)->get(m, &r))
127  return;
128  }
129 
130  // Step 2 - Handle touch and button 0 debouncing
131  {
132  // Clean off button garbage
133  r.buttons &= GINPUT_MOUSE_BTN_MASK;
134 
135  #if !GINPUT_TOUCH_NOTOUCH
136  // If touch then calculate button 0 from z
137  if ((gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH)) {
138  if (gmvmt(m)->z_min <= gmvmt(m)->z_max) {
139  if (r.z >= gmvmt(m)->z_touchon) r.buttons |= GINPUT_MOUSE_BTN_LEFT;
140  else if (r.z <= gmvmt(m)->z_touchoff) r.buttons &= ~GINPUT_MOUSE_BTN_LEFT;
141  else return; // bad transitional reading
142  } else {
143  if (r.z <= gmvmt(m)->z_touchon) r.buttons |= GINPUT_MOUSE_BTN_LEFT;
144  else if (r.z >= gmvmt(m)->z_touchoff) r.buttons &= ~GINPUT_MOUSE_BTN_LEFT;
145  else return; // bad transitional reading
146  }
147  }
148 
149  // Devices with poor button 0 transitioning need debouncing
150  if ((gmvmt(m)->d.flags & GMOUSE_VFLG_POORUPDOWN)) {
151  // Are we in a transition test
152  if ((m->flags & GMOUSE_FLG_INDELTA)) {
153  if (!((r.buttons ^ m->r.buttons) & GINPUT_MOUSE_BTN_LEFT)) {
154  // Transition failed
155  m->flags &= ~GMOUSE_FLG_INDELTA;
156  return;
157  }
158  // Transition succeeded
159  m->flags &= ~GMOUSE_FLG_INDELTA;
160 
161  // Should we start a transition test
162  } else if (((r.buttons ^ m->r.buttons) & GINPUT_MOUSE_BTN_LEFT)) {
163  m->flags |= GMOUSE_FLG_INDELTA;
164  return;
165  }
166  }
167  #endif
168 
169  #if !GINPUT_TOUCH_NOCALIBRATE_GUI
170  // Stop here with just the raw x,y reading during calibration
171  if ((m->flags & GMOUSE_FLG_IN_CAL)) {
172  if ((r.buttons & GINPUT_MOUSE_BTN_LEFT)) {
173  m->r.x = r.x;
174  m->r.y = r.y;
175  }
176  m->r.buttons = r.buttons;
177  return;
178  }
179  #endif
180  }
181 
182  // Step 3 - Apply calibration, rotation and display clipping
183  {
184  // If the mouse is up we may need to keep our previous position
185  if ((gmvmt(m)->d.flags & GMOUSE_VFLG_ONLY_DOWN) && !(r.buttons & GINPUT_MOUSE_BTN_LEFT)) {
186  r.x = m->r.x;
187  r.y = m->r.y;
188 
189  } else {
190 
191  #if !GINPUT_TOUCH_NOCALIBRATE
192  // Do we need to calibrate the reading?
193  if ((m->flags & GMOUSE_FLG_CALIBRATE))
194  CalibrationTransform(&r, &m->caldata);
195  #endif
196 
197  // We can't clip or rotate if we don't have a display
198  if (m->display) {
199  gCoord w, h;
200 
201  // We now need display information
202  w = gdispGGetWidth(m->display);
203  h = gdispGGetHeight(m->display);
204 
205  #if GDISP_NEED_CONTROL
206  // Do we need to rotate the reading to match the display
207  if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
208  gCoord t;
209 
210  switch(gdispGGetOrientation(m->display)) {
211  case gOrientation0:
212  break;
213  case gOrientation90:
214  t = r.x;
215  r.x = w - 1 - r.y;
216  r.y = t;
217  break;
218  case gOrientation180:
219  r.x = w - 1 - r.x;
220  r.y = h - 1 - r.y;
221  break;
222  case gOrientation270:
223  t = r.y;
224  r.y = h - 1 - r.x;
225  r.x = t;
226  break;
227  default:
228  break;
229  }
230  }
231  #endif
232 
233  // Do we need to clip the reading to the display
234  if ((m->flags & GMOUSE_FLG_CLIP)) {
235  if (r.x < 0) r.x = 0;
236  else if (r.x >= w) r.x = w-1;
237  if (r.y < 0) r.y = 0;
238  else if (r.y >= h) r.y = h-1;
239  }
240  }
241  }
242  }
243 
244  // Step 4 - Apply jitter detection
245  #if !GINPUT_TOUCH_NOTOUCH
246  {
247  const GMouseJitter *pj;
248  gU32 diff;
249 
250  // Are we in pen or finger mode
251  pj = (m->flags & GMOUSE_FLG_FINGERMODE) ? &gmvmt(m)->finger_jitter : &gmvmt(m)->pen_jitter;
252 
253  // Is this just movement jitter
254  if (pj->move > 0) {
255  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);
256  if (diff < (gU32)pj->move * (gU32)pj->move) {
257  r.x = m->r.x;
258  r.y = m->r.y;
259  }
260  }
261 
262  // Check if the click has moved outside the click area and if so cancel the click
263  if (pj->click > 0 && (m->flags & GMOUSE_FLG_CLICK_TIMER)) {
264  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);
265  if (diff > (gU32)pj->click * (gU32)pj->click)
266  m->flags &= ~GMOUSE_FLG_CLICK_TIMER;
267  }
268  }
269  #endif
270 
271  // Step 5 - Click, context-click and other meta event detection
272  {
273  gU16 upbtns, dnbtns;
274 
275  // Calculate button transitions
276  dnbtns = r.buttons & ~m->r.buttons;
277  upbtns = ~r.buttons & m->r.buttons;
278 
279  // Left mouse down generates the Mouse-down meta event
280  if ((dnbtns & GINPUT_MOUSE_BTN_LEFT))
281  r.buttons |= GMETA_MOUSE_DOWN;
282 
283  // Left mouse up generates the Mouse-up meta event
284  if ((upbtns & GINPUT_MOUSE_BTN_LEFT))
285  r.buttons |= GMETA_MOUSE_UP;
286 
287  // Left/Right mouse down starts the click timer
288  if ((dnbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT))) {
289  m->clickpos.x = r.x;
290  m->clickpos.y = r.y;
291  m->clicktime = gfxSystemTicks();
292  m->flags |= GMOUSE_FLG_CLICK_TIMER;
293  }
294 
295  // Left/Right mouse up with the click timer still running may generate a click or context click
296  if ((upbtns & (GINPUT_MOUSE_BTN_LEFT|GINPUT_MOUSE_BTN_RIGHT)) && (m->flags & GMOUSE_FLG_CLICK_TIMER)) {
297  m->flags &= ~GMOUSE_FLG_CLICK_TIMER;
298  m->clicktime = gfxSystemTicks() - m->clicktime;
299 
300  // Was this a short click?
301  if (m->clicktime <= gfxMillisecondsToTicks(GINPUT_MOUSE_CLICK_TIME)) {
302  if ((upbtns & GINPUT_MOUSE_BTN_RIGHT))
303  r.buttons |= GMETA_MOUSE_CXTCLICK;
304  if ((upbtns & GINPUT_MOUSE_BTN_LEFT))
305  r.buttons |= GMETA_MOUSE_CLICK;
306  }
307 
308  #if !GINPUT_TOUCH_NOTOUCH
309  // Was this a long click on a touch device?
310  if ((gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH) && m->clicktime >= gfxMillisecondsToTicks(GINPUT_TOUCH_CXTCLICK_TIME))
311  r.buttons |= GMETA_MOUSE_CXTCLICK;
312  #endif
313  }
314  }
315 
316  // Step 6 - Send the event to the listeners that are interested.
317  {
318  GSourceListener *psl;
319 
320  // Send to the "All Mice" source listeners
321  psl = 0;
322  while ((psl = geventGetSourceListener((GSourceHandle)&MouseTimer, psl)))
323  SendMouseEvent(psl, m, &r);
324 
325  // Send to the mouse specific source listeners
326  psl = 0;
327  while ((psl = geventGetSourceListener((GSourceHandle)m, psl)))
328  SendMouseEvent(psl, m, &r);
329  }
330 
331  // Step 7 - Finally save the results
332  m->r.x = r.x;
333  m->r.y = r.y;
334  m->r.z = r.z;
335  m->r.buttons = r.buttons;
336 }
337 
338 static void MousePoll(void *param) {
339  GMouse * m;
340  (void) param;
341 
342  for(m = (GMouse *)gdriverGetNext(GDRIVER_TYPE_MOUSE, 0); m; m = (GMouse *)gdriverGetNext(GDRIVER_TYPE_MOUSE, (GDriver *)m)) {
343  if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_NOPOLL) || (m->flags & GMOUSE_FLG_NEEDREAD))
344  GetMouseReading(m);
345  }
346 }
347 
348 // Calibration user interface
349 #if !GINPUT_TOUCH_NOCALIBRATE_GUI
350  #if !defined(GFX_USE_GDISP) || !GFX_USE_GDISP
351  #error "GINPUT: GFX_USE_GDISP must be defined when calibration is required"
352  #endif
353 
354  static GFXINLINE void CalibrationCrossDraw(GMouse *m, const gPoint *pp) {
355  gdispGDrawLine(m->display, pp->x-CALIBRATION_CROSS_RADIUS, pp->y, pp->x-CALIBRATION_CROSS_INNERGAP, pp->y, CALIBRATION_CROSS_COLOR1);
356  gdispGDrawLine(m->display, pp->x+CALIBRATION_CROSS_INNERGAP, pp->y, pp->x+CALIBRATION_CROSS_RADIUS, pp->y, CALIBRATION_CROSS_COLOR1);
357  gdispGDrawLine(m->display, pp->x, pp->y-CALIBRATION_CROSS_RADIUS, pp->x, pp->y-CALIBRATION_CROSS_INNERGAP, CALIBRATION_CROSS_COLOR1);
358  gdispGDrawLine(m->display, pp->x, pp->y+CALIBRATION_CROSS_INNERGAP, pp->x, pp->y+CALIBRATION_CROSS_RADIUS, CALIBRATION_CROSS_COLOR1);
359  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);
360  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);
361  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);
362  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);
363  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);
364  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);
365  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);
366  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);
367  }
368 
369  static GFXINLINE void CalibrationCrossClear(GMouse *m, const gPoint *pp) {
370  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);
371  }
372 
373  static GFXINLINE void CalibrationCalculate(GMouse *m, const gPoint *cross, const gPoint *points) {
374  float dx;
375  gCoord c0, c1, c2;
376  (void) m;
377 
378  // Work on x values
379  c0 = cross[0].x;
380  c1 = cross[1].x;
381  c2 = cross[2].x;
382 
383  #if GDISP_NEED_CONTROL
384  if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
385  /* Convert all cross points back to gOrientation0 convention
386  * before calculating the calibration matrix.
387  */
388  switch(gdispGGetOrientation(m->display)) {
389  case gOrientation90:
390  c0 = cross[0].y;
391  c1 = cross[1].y;
392  c2 = cross[2].y;
393  break;
394  case gOrientation180:
395  c0 = c1 = c2 = gdispGGetWidth(m->display) - 1;
396  c0 -= cross[0].x;
397  c1 -= cross[1].x;
398  c2 -= cross[2].x;
399  break;
400  case gOrientation270:
401  c0 = c1 = c2 = gdispGGetHeight(m->display) - 1;
402  c0 -= cross[0].y;
403  c1 -= cross[1].y;
404  c2 -= cross[2].y;
405  break;
406  default:
407  break;
408  }
409  }
410  #endif
411 
412  /* Compute all the required determinants */
413  dx = (float)(points[0].x - points[2].x) * (float)(points[1].y - points[2].y)
414  - (float)(points[1].x - points[2].x) * (float)(points[0].y - points[2].y);
415 
416  m->caldata.ax = ((float)(c0 - c2) * (float)(points[1].y - points[2].y)
417  - (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx;
418  m->caldata.bx = ((float)(c1 - c2) * (float)(points[0].x - points[2].x)
419  - (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx;
420  m->caldata.cx = (c0 * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y)
421  - c1 * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y)
422  + c2 * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y)) / dx;
423 
424  // Work on y values
425  c0 = cross[0].y;
426  c1 = cross[1].y;
427  c2 = cross[2].y;
428 
429  #if GDISP_NEED_CONTROL
430  if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
431  switch(gdispGGetOrientation(m->display)) {
432  case gOrientation90:
433  c0 = c1 = c2 = gdispGGetWidth(m->display) - 1;
434  c0 -= cross[0].x;
435  c1 -= cross[1].x;
436  c2 -= cross[2].x;
437  break;
438  case gOrientation180:
439  c0 = c1 = c2 = gdispGGetHeight(m->display) - 1;
440  c0 -= cross[0].y;
441  c1 -= cross[1].y;
442  c2 -= cross[2].y;
443  break;
444  case gOrientation270:
445  c0 = cross[0].x;
446  c1 = cross[1].x;
447  c2 = cross[2].x;
448  break;
449  default:
450  break;
451  }
452  }
453  #endif
454 
455  m->caldata.ay = ((float)(c0 - c2) * (float)(points[1].y - points[2].y)
456  - (float)(c1 - c2) * (float)(points[0].y - points[2].y)) / dx;
457  m->caldata.by = ((float)(c1 - c2) * (float)(points[0].x - points[2].x)
458  - (float)(c0 - c2) * (float)(points[1].x - points[2].x)) / dx;
459  m->caldata.cy = (c0 * ((float)points[1].x * (float)points[2].y - (float)points[2].x * (float)points[1].y)
460  - c1 * ((float)points[0].x * (float)points[2].y - (float)points[2].x * (float)points[0].y)
461  + c2 * ((float)points[0].x * (float)points[1].y - (float)points[1].x * (float)points[0].y)) / dx;
462  }
463 
464  static gU32 CalibrateMouse(GMouse *m) {
465  gCoord w, h;
466  gPoint cross[4]; // The locations of the test points on the display
467  gPoint points[4]; // The x, y readings obtained from the mouse for each test point
468  gU32 err;
469  #if GDISP_NEED_TEXT
470  gFont font1, font2;
471  #endif
472 
473  #if GDISP_NEED_TEXT
474  font1 = gdispOpenFont(CALIBRATION_FONT1);
475  if (!font1) font1 = gdispOpenFont("*");
476  font2 = gdispOpenFont(CALIBRATION_FONT2);
477  if (!font2) font2 = gdispOpenFont("*");
478  #endif
479  err = 0;
480  w = gdispGGetWidth(m->display);
481  h = gdispGGetHeight(m->display);
482  #if GDISP_NEED_CLIP
483  gdispGSetClip(m->display, 0, 0, w, h);
484  #endif
485 
486  // Ensure we get minimally processed readings for the calibration
487  m->flags |= GMOUSE_FLG_IN_CAL;
488 
489  // Set up our calibration locations
490  if ((gmvmt(m)->d.flags & GMOUSE_VFLG_CAL_EXTREMES)) {
491  cross[0].x = 0; cross[0].y = 0;
492  cross[1].x = w-1; cross[1].y = 0;
493  cross[2].x = w-1; cross[2].y = h-1;
494  cross[3].x = w/2; cross[3].y = h/2;
495  } else {
496  cross[0].x = w/4; cross[0].y = h/4;
497  cross[1].x = w-w/4; cross[1].y = h/4;
498  cross[2].x = w-w/4; cross[2].y = h-h/4;
499  cross[3].x = w/2; cross[3].y = h/2;
500  }
501 
502  // Set up the calibration display
503  gdispGClear(m->display, CALIBRATION_BACKGROUND);
504  #if GDISP_NEED_TEXT
505  gdispGFillStringBox(m->display,
506  0, CALIBRATION_TITLE_Y, w, CALIBRATION_TITLE_HEIGHT,
507  CALIBRATION_TITLE, font1, CALIBRATION_TITLE_COLOR, CALIBRATION_TITLE_BACKGROUND,
509  #endif
510 
511  // Calculate the calibration
512  {
513  unsigned i, maxpoints;
514 
515  maxpoints = (gmvmt(m)->d.flags & GMOUSE_VFLG_CAL_TEST) ? 4 : 3;
516 
517  // Loop through the calibration points
518  for(i = 0; i < maxpoints; i++) {
519  gI32 px, py;
520  unsigned j;
521 
522  // Draw the current calibration point
523  CalibrationCrossDraw(m, &cross[i]);
524 
525  // Get a valid "point pressed" average reading
526  do {
527  // Wait for the mouse to be pressed
528  while(!(m->r.buttons & GINPUT_MOUSE_BTN_LEFT))
529  gfxSleepMilliseconds(CALIBRATION_POLL_PERIOD);
530 
531  // Sum samples taken every CALIBRATION_POLL_PERIOD milliseconds while the mouse is down
532  px = py = j = 0;
533  while((m->r.buttons & GINPUT_MOUSE_BTN_LEFT)) {
534  // Limit sampling period to prevent overflow
535  if (j < CALIBRATION_MAXPRESS_PERIOD/CALIBRATION_POLL_PERIOD) {
536  px += m->r.x;
537  py += m->r.y;
538  j++;
539  }
540  gfxSleepMilliseconds(CALIBRATION_POLL_PERIOD);
541  }
542 
543  // Ignore presses less than CALIBRATION_MINPRESS_PERIOD milliseconds
544  } while(j < CALIBRATION_MINPRESS_PERIOD/CALIBRATION_POLL_PERIOD);
545  points[i].x = px / j;
546  points[i].y = py / j;
547 
548  // Clear the current calibration point
549  CalibrationCrossClear(m, &cross[i]);
550  }
551  }
552 
553  // Apply 3 point calibration algorithm
554  CalibrationCalculate(m, cross, points);
555 
556  /* Verification of correctness of calibration (optional) :
557  * See if the 4th point (Middle of the screen) coincides with the calibrated
558  * result. If point is within +/- Squareroot(ERROR) pixel margin, then successful calibration
559  * Else return the error.
560  */
561  if ((gmvmt(m)->d.flags & GMOUSE_VFLG_CAL_TEST)) {
562  const GMouseJitter *pj;
563 
564  // Are we in pen or finger mode
565  pj = (m->flags & GMOUSE_FLG_FINGERMODE) ? &gmvmt(m)->finger_jitter : &gmvmt(m)->pen_jitter;
566 
567  // Transform the co-ordinates
568  CalibrationTransform((GMouseReading *)&points[3], &m->caldata);
569 
570  // Do we need to rotate the reading to match the display
571  #if GDISP_NEED_CONTROL
572  if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_SELFROTATION)) {
573  gCoord t;
574 
575  switch(gdispGGetOrientation(m->display)) {
576  case gOrientation0:
577  break;
578  case gOrientation90:
579  t = points[3].x;
580  points[3].x = w - 1 - points[3].y;
581  points[3].y = t;
582  break;
583  case gOrientation180:
584  points[3].x = w - 1 - points[3].x;
585  points[3].y = h - 1 - points[3].y;
586  break;
587  case gOrientation270:
588  t = points[3].y;
589  points[3].y = h - 1 - points[3].x;
590  points[3].x = t;
591  break;
592  default:
593  break;
594  }
595  }
596  #endif
597 
598  // Is this accurate enough?
599  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);
600  if (err > (gU32)pj->calibrate * (gU32)pj->calibrate) {
601  #if GDISP_NEED_TEXT
602  // No - Display error and return
603  gdispGFillStringBox(m->display,
604  0, CALIBRATION_ERROR_Y, w, CALIBRATION_ERROR_HEIGHT,
605  CALIBRATION_ERROR_TEXT, font2, CALIBRATION_ERROR_COLOR, CALIBRATION_ERROR_BACKGROUND,
607  gfxSleepMilliseconds(CALIBRATION_ERROR_DELAY);
608  #endif
609  } else
610  err = 0;
611  }
612 
613  // We are done calibrating
614  #if GDISP_NEED_TEXT
615  gdispCloseFont(font1);
616  gdispCloseFont(font2);
617  #endif
618  m->flags &= ~GMOUSE_FLG_IN_CAL;
619  m->flags |= GMOUSE_FLG_CLIP;
620 
621  // Save the calibration data (if possible)
622  if (!err) {
623  m->flags |= GMOUSE_FLG_CALIBRATE;
624 
625  #if GINPUT_TOUCH_USER_CALIBRATION_SAVE
626  SaveMouseCalibration(gdriverGetDriverInstanceNumber((GDriver *)m), &m->caldata, sizeof(GMouseCalibration));
627  #endif
628  if (gmvmt(m)->calsave)
629  gmvmt(m)->calsave(m, &m->caldata, sizeof(GMouseCalibration));
630  }
631 
632  // Force an initial reading
633  m->r.buttons = 0;
634  GetMouseReading(m);
635 
636  // Clear the screen using the GWIN default background color
637  #if GFX_USE_GWIN
638  gdispGClear(m->display, gwinGetDefaultBgColor());
639  #else
640  gdispGClear(m->display, GDISP_STARTUP_COLOR);
641  #endif
642 
643  return err;
644  }
645 #endif
646 
647 void _gmouseInit(void) {
648  // GINPUT_MOUSE_DRIVER_LIST is defined - create each driver instance
649  #if defined(GINPUT_MOUSE_DRIVER_LIST)
650  {
651  int i;
652  typedef const GMouseVMT const GMOUSEVMTLIST[1];
653 
654  extern GMOUSEVMTLIST GINPUT_MOUSE_DRIVER_LIST;
655  static const GMouseVMT * const dclist[] = {GINPUT_MOUSE_DRIVER_LIST};
656 
657  for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) {
658  if (!(dclist[i]->d.flags & GMOUSE_VFLG_DYNAMICONLY))
659  gdriverRegister(&dclist[i]->d, GDISP);
660  }
661  }
662 
663  // One and only one mouse
664  #else
665  {
666  /*
667  * This should be: extern const GMouseVMT const GMOUSEVMT_OnlyOne[1];
668  * However, some major compilers complain about the duplicate const specifier even though this is perfectly valid standard C.
669  */
670  extern const GMouseVMT GMOUSEVMT_OnlyOne[1];
671 
672  if (!(GMOUSEVMT_OnlyOne->d.flags & GMOUSE_VFLG_DYNAMICONLY))
673  gdriverRegister(&GMOUSEVMT_OnlyOne->d, GDISP);
674  }
675  #endif
676 
677 }
678 
679 void _gmouseDeinit(void) {
680  gtimerDeinit(&MouseTimer);
681 }
682 
683 gBool _gmouseInitDriver(GDriver *g, void *display, unsigned driverinstance, unsigned systeminstance) {
684  #define m ((GMouse *)g)
685  (void) systeminstance;
686 
687  // The initial display is passed in the parameter for mice
688  m->display = display;
689 
690  #if !GINPUT_TOUCH_NOTOUCH
691  // Should this mouse start in finger mode? (according to the VMT)
692  if ((gmvmt(m)->d.flags & GMOUSE_VFLG_DEFAULTFINGER))
693  m->flags |= GMOUSE_FLG_FINGERMODE;
694  #endif
695 
696  // Init the mouse
697  if (!gmvmt(m)->init((GMouse *)g, driverinstance))
698  return gFalse;
699 
700  // Ensure the Poll timer is started
701  if (!gtimerIsActive(&MouseTimer))
702  gtimerStart(&MouseTimer, MousePoll, 0, gTrue, GINPUT_MOUSE_POLL_PERIOD);
703 
704  return gTrue;
705 
706  #undef m
707 }
708 
709 void _gmousePostInitDriver(GDriver *g) {
710  #define m ((GMouse *)g)
711 
712  #if !GINPUT_TOUCH_STARTRAW
713  m->flags |= GMOUSE_FLG_CLIP;
714  #endif
715 
716  #if !GINPUT_TOUCH_NOCALIBRATE && !GINPUT_TOUCH_STARTRAW
717  if ((gmvmt(m)->d.flags & GMOUSE_VFLG_CALIBRATE)) {
718  #if GINPUT_TOUCH_USER_CALIBRATION_LOAD
719  if (LoadMouseCalibration(gdriverGetDriverInstanceNumber((GDriver *)m), &m->caldata, sizeof(GMouseCalibration)))
720  m->flags |= GMOUSE_FLG_CALIBRATE;
721  else
722  #endif
723  if (gmvmt(m)->calload && gmvmt(m)->calload(m, &m->caldata, sizeof(GMouseCalibration)))
724  m->flags |= GMOUSE_FLG_CALIBRATE;
725  #if !GINPUT_TOUCH_NOCALIBRATE_GUI
726  else
727  while (CalibrateMouse(m));
728  #endif
729  }
730  #endif
731 
732  // Get the first reading
733  GetMouseReading(m);
734 
735  #undef m
736 }
737 
738 void _gmouseDeInitDriver(GDriver *g) {
739  (void) g;
740 }
741 
742 GSourceHandle ginputGetMouse(unsigned instance) {
743  if (instance == GMOUSE_ALL_INSTANCES)
744  return (GSourceHandle)&MouseTimer;
745  return (GSourceHandle)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance);
746 }
747 
748 void ginputSetMouseDisplay(unsigned instance, GDisplay *g) {
749  GMouse *m;
750 
751  if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
752  return;
753 
754  m->display = g ? g : GDISP;
755 }
756 
757 GDisplay *ginputGetMouseDisplay(unsigned instance) {
758  GMouse *m;
759 
760  if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
761  return 0;
762 
763  return m->display;
764 }
765 
766 gBool ginputGetMouseStatus(unsigned instance, GEventMouse *pe) {
767  GMouse *m;
768 
769  // Win32 threads don't seem to recognise priority and/or pre-emption
770  // so we add a sleep here to prevent 100% polled applications from locking up.
772 
773  if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
774  return gFalse;
775 
776  #if !GINPUT_TOUCH_NOCALIBRATE_GUI
777  if ((m->flags & GMOUSE_FLG_IN_CAL))
778  return gFalse;
779  #endif
780 
781  #if !GINPUT_TOUCH_NOTOUCH
782  pe->type = (gmvmt(m)->d.flags & GMOUSE_VFLG_TOUCH) ? GEVENT_TOUCH : GEVENT_MOUSE;
783  #else
784  pe->type = GEVENT_MOUSE;
785  #endif
786  pe->x = m->r.x;
787  pe->y = m->r.y;
788  pe->z = m->r.z;
789  pe->buttons = m->r.buttons;
790  pe->display = m->display;
791  return gTrue;
792 }
793 
794 #if !GINPUT_TOUCH_NOTOUCH
795  void ginputSetFingerMode(unsigned instance, gBool on) {
796  GMouse *m;
797 
798  if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
799  return;
800 
801  if (on)
802  m->flags |= GMOUSE_FLG_FINGERMODE;
803  else
804  m->flags &= ~GMOUSE_FLG_FINGERMODE;
805 
806  }
807 #endif
808 
809 #if !GINPUT_TOUCH_NOCALIBRATE_GUI
810  gU32 ginputCalibrateMouse(unsigned instance) {
811  GMouse *m;
812 
813  // Find the instance
814  if (!(m = (GMouse *)gdriverGetInstance(GDRIVER_TYPE_MOUSE, instance)))
815  return 0;
816 
817  // Check it needs calibration
818  if (!(gmvmt(m)->d.flags & GMOUSE_VFLG_CALIBRATE))
819  return 0;
820 
821  return CalibrateMouse(m);
822  }
823 #endif
824 
825 /* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */
826 void _gmouseWakeup(GMouse *m) {
827  if (m)
828  m->flags |= GMOUSE_FLG_NEEDREAD;
829  gtimerJab(&MouseTimer);
830 }
831 
832 /* Wake up the mouse driver from an interrupt service routine (there may be new readings available) */
833 void _gmouseWakeupI(GMouse *m) {
834  if (m)
835  m->flags |= GMOUSE_FLG_NEEDREAD;
836  gtimerJabI(&MouseTimer);
837 }
838 
839 #endif /* GFX_USE_GINPUT && GINPUT_NEED_MOUSE */
GINPUT LLD header file for mouse/touch drivers.
gFont gdispOpenFont(const char *name)
Find a font and return it.
void gdispGClear(GDisplay *g, gColor color)
Clear the display to the specified color.
void gdispGSetClip(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy)
Clip all drawing to the defined area.
GDisplay * GDISP
The default screen to use for the gdispXXXX calls.
void gdispCloseFont(gFont font)
Release a font after use.
const struct mf_font_s * gFont
The type of a font.
Definition: gdisp.h:93
gOrientation gdispGGetOrientation(GDisplay *g)
Get the current display orientation.
gCoord gdispGGetWidth(GDisplay *g)
Get the display width in pixels.
void gdispGFillArea(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor color)
Fill an area with a color.
void gdispGDrawLine(GDisplay *g, gCoord x0, gCoord y0, gCoord x1, gCoord y1, gColor color)
Draw a line.
void gdispGFillStringBox(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, const char *str, gFont font, gColor color, gColor bgColor, gJustify justify)
Draw a text string vertically centered within the specified box. The box background is filled with th...
#define GDISP_STARTUP_COLOR
Define the initial background color for all displays in the system.
gCoord gdispGGetHeight(GDisplay *g)
Get the display height in pixels.
gI16 gCoord
The type for a coordinate or length on the screen.
Definition: gdisp.h:39
@ gJustifyCenter
Definition: gdisp.h:62
@ gOrientation180
Definition: gdisp.h:104
@ gOrientation0
Definition: gdisp.h:102
@ gOrientation90
Definition: gdisp.h:103
@ gOrientation270
Definition: gdisp.h:105
GDriver * gdriverRegister(const GDriverVMT *vmt, void *param)
Register a new driver instance.
Definition: gdriver.c:31
GDriver * gdriverGetInstance(gU16 type, unsigned instance)
Get the driver for a particular instance of a type of device.
Definition: gdriver.c:98
GDriver * gdriverGetNext(gU16 type, GDriver *driver)
Get the next driver for a type of device.
Definition: gdriver.c:127
unsigned gdriverGetDriverInstanceNumber(GDriver *driver)
Get the instance number for a device.
Definition: gdriver.c:136
GEvent * geventGetEventBuffer(GSourceListener *psl)
Get the event buffer from the GSourceListener.
Definition: gevent.c:187
void geventSendEvent(GSourceListener *psl)
Called by a source to indicate the listener's event buffer has been filled.
Definition: gevent.c:200
GSourceListener * geventGetSourceListener(GSourceHandle gsh, GSourceListener *lastlr)
Called by a source with a possible event to get a listener record.
Definition: gevent.c:163
#define GFXINLINE
Mark a function as inline.
#define GINPUT_TOUCH_CXTCLICK_TIME
Milliseconds to generate a CXTCLICK on a touch device.
#define GINPUT_MOUSE_CLICK_TIME
Maximum length of CLICK in milliseconds.
#define GINPUT_MOUSE_POLL_PERIOD
Milliseconds between mouse polls.
gTicks gfxSystemTicks(void)
Get the current operating system tick time.
gTicks gfxMillisecondsToTicks(gDelay ms)
Convert a given number of millseconds to a number of operating system ticks.
void gfxSleepMilliseconds(gDelay ms)
Put the current thread to sleep for the specified period in milliseconds.
void gtimerJabI(GTimer *pt)
Jab a timer causing the current period to immediate expire.
Definition: gtimer.c:223
void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, gBool periodic, gDelay millisec)
Set a timer going or alter its properties if it is already going.
Definition: gtimer.c:139
void gtimerDeinit(GTimer *pt)
Deinitialise a timer.
Definition: gtimer.c:134
gBool gtimerIsActive(GTimer *pt)
Test if a timer is currently active.
Definition: gtimer.c:208
void gtimerJab(GTimer *pt)
Jab a timer causing the current period to immediate expire.
Definition: gtimer.c:212
GDisplay * ginputGetMouseDisplay(unsigned instance)
Get the display currently associated with the mouse.
gBool ginputGetMouseStatus(unsigned instance, GEventMouse *pmouse)
Get the current mouse position and button status.
gBool SaveMouseCalibration(unsigned instance, const void *data, gMemSize sz)
Save a set of mouse calibration data.
void ginputSetMouseDisplay(unsigned instance, GDisplay *g)
Assign the display associated with the mouse.
gBool LoadMouseCalibration(unsigned instance, void *data, gMemSize sz)
Load a set of mouse calibration data.
GSourceHandle ginputGetMouse(unsigned instance)
Get the Source handler for a mouse using the instance number.
void ginputSetFingerMode(unsigned instance, gBool on)
Should this device be in Pen mode or Finger mode.
gU32 ginputCalibrateMouse(unsigned instance)
Performs a calibration.
gColor gwinGetDefaultBgColor(void)
Get the default background color for all new GWIN windows.
All runtime driver structures start with this structure.
Definition: gdriver.h:58
Type for a 2D point on the screen.
Definition: gdisp.h:51
gCoord y
Definition: gdisp.h:53
gCoord x
Definition: gdisp.h:52