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