NXWidgets  1.19
ccalibration.cxx
Go to the documentation of this file.
1 /****************************************************************************
2  * NxWidgets/nxwm/src/ccalibration.cxx
3  *
4  * Copyright (C) 2012-2013 Gregory Nutt. All rights reserved.
5  * Author: Gregory Nutt <gnutt@nuttx.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  * 3. Neither the name NuttX, NxWidgets, nor the names of its contributors
18  * me be used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  ****************************************************************************/
35 
36 /****************************************************************************
37  * Included Files
38  ****************************************************************************/
39 
40 #include <cunistd>
41 #include <cerrno>
42 
43 #include <sched.h>
44 #include <limits.h>
45 #include <assert.h>
46 #include <debug.h>
47 
48 #ifdef CONFIG_NXWM_TOUCHSCREEN_CONFIGDATA
49 # include "platform/configdata.h"
50 #endif
51 
52 #include "nxwmconfig.hxx"
53 #include "nxwmglyphs.hxx"
54 #include "ctouchscreen.hxx"
55 #include "ccalibration.hxx"
56 
57 /****************************************************************************
58  * Pre-processor Definitions
59  ****************************************************************************/
60 /****************************************************************************
61  * Configuration
62  */
63 
64 #ifndef CONFIG_NX
65 # error "NX is not enabled (CONFIG_NX)"
66 #endif
67 
68 /**
69  * Positional/size data for the calibration lines and circles
70  */
71 
72 #define CALIBRATION_LEFTX CONFIG_NXWM_CALIBRATION_MARGIN
73 #define CALIBRATION_RIGHTX (windowSize.w - CONFIG_NXWM_CALIBRATION_MARGIN + 1)
74 #define CALIBRATION_TOPY CONFIG_NXWM_CALIBRATION_MARGIN
75 #define CALIBRATION_BOTTOMY (windowSize.h - CONFIG_NXWM_CALIBRATION_MARGIN + 1)
76 
77 #define CALIBRATION_CIRCLE_RADIUS 16
78 #define CALIBRATION_LINE_THICKNESS 2
79 
80 /* We want debug output from some logic in this file if either input/touchscreen
81  * or graphics debug is enabled.
82  */
83 
84 #ifndef CONFIG_DEBUG_INPUT
85 # undef ierr
86 # define ierr gerr
87 # undef iinfo
88 # define iinfo ginfo
89 #endif
90 
91 /****************************************************************************
92  * Private Data
93  ****************************************************************************/
94 
95 using namespace NxWM;
96 
97 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
98 static const char g_touchMsg[] = "Touch";
99 static const char g_againMsg[] = "Again";
100 static const char g_okMsg[] = "OK";
101 #endif
102 
103 /****************************************************************************
104  * CCalibration Implementation Classes
105  ****************************************************************************/
106 
107 /**
108  * CCalibration Constructor
109  *
110  * @param taskbar. The taskbar instance used to terminate calibration
111  * @param window. The window to use for the calibration display
112  * @param touchscreen. An instance of the class that wraps the touchscreen
113  * device.
114  */
115 
117  CTouchscreen *touchscreen)
118 {
119  // Initialize state data
120 
121  m_taskbar = taskbar;
122  m_window = window;
123  m_touchscreen = touchscreen;
124  m_thread = 0;
127  m_touched = false;
128 
129  // Nullify widgets that will be instantiated when the calibration thread
130  // is started
131 
132 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
133  m_text = (NXWidgets::CLabel *)0;
134  m_font = (NXWidgets::CNxFont *)0;
135 #endif
136 }
137 
138 /**
139  * CCalibration Destructor
140  */
141 
143 {
144  // Make sure that the application is not running (it should already
145  // have been stopped)
146 
147  stop();
148 
149  // Although we did not create the window, the rule is that I have to dispose
150  // of it
151 
152  delete m_window;
153 }
154 
155 /**
156  * Each implementation of IApplication must provide a method to recover
157  * the contained IApplicationWindow instance.
158  */
159 
161 {
162  return static_cast<IApplicationWindow*>(m_window);
163 }
164 
165 /**
166  * Get the icon associated with the application
167  *
168  * @return An instance if IBitmap that may be used to rend the
169  * application's icon. This is an new IBitmap instance that must
170  * be deleted by the caller when it is no long needed.
171  */
172 
174 {
176  new NXWidgets::CRlePaletteBitmap(&CONFIG_NXWM_CALIBRATION_ICON);
177 
178  return bitmap;
179 }
180 
181 /**
182  * Get the name string associated with the application
183  *
184  * @return A copy if CNxString that contains the name of the application.
185  */
186 
188 {
189  return NXWidgets::CNxString("Touchscreen Calibration");
190 }
191 
192 /**
193  * Start the application (perhaps in the minimized state).
194  *
195  * @return True if the application was successfully started.
196  */
197 
199 {
200  ginfo("Starting calibration: m_calthread=%d\n", (int)m_calthread);
201 
203 }
204 
205 /**
206  * Stop the application.
207  */
208 
210 {
211  ginfo("Stopping calibration: m_calthread=%d\n", (int)m_calthread);
212 
213  // Was the calibration thread created?
214 
215  if (m_thread != 0)
216  {
217  // Is the calibration thread running?
218 
219  if (isRunning())
220  {
221  // The main thread is stuck waiting for the next touchscreen input...
222  // We can signal that we would like the thread to stop, but we will be
223  // stuck here until the next touch
224 
225  m_calthread = CALTHREAD_STOPREQUESTED;
226 
227  // Try to wake up the calibration thread so that it will see our
228  // termination request
229 
230  ginfo("Stopping calibration: m_calthread=%d\n", (int)m_calthread);
231  (void)pthread_kill(m_thread, CONFIG_NXWM_CALIBRATION_SIGNO);
232 
233  // Wait for the calibration thread to exit
234 
235  FAR pthread_addr_t value;
236  (void)pthread_join(m_thread, &value);
237  }
238  }
239 }
240 
241 /**
242  * Destroy the application and free all of its resources. This method
243  * will initiate blocking of messages from the NX server. The server
244  * will flush the window message queue and reply with the blocked
245  * message. When the block message is received by CWindowMessenger,
246  * it will send the destroy message to the start window task which
247  * will, finally, safely delete the application.
248  */
249 
251 {
252  // Make sure that the application is stopped (should already be stopped)
253 
254  stop();
255 
256  // Block any further window messages
257 
258  m_window->block(this);
259 }
260 
261 /**
262  * The application window is hidden (either it is minimized or it is
263  * maximized, but it is not at the top of the hierarchy)
264  */
265 
267 {
268  ginfo("Entry\n");
269 
270  // Is the calibration thread running?
271 
273  {
274  // Ask the calibration thread to hide the display
275 
277  (void)pthread_kill(m_thread, CONFIG_NXWM_CALIBRATION_SIGNO);
278  }
279 }
280 
281 /**
282  * Redraw the entire window. The application has been maximized or
283  * otherwise moved to the top of the hierarchy. This method is called from
284  * CTaskbar when the application window must be displayed
285  */
286 
288 {
289  uint8_t waitcount = 0;
290 
291  ginfo("Entry\n");
292 
293  // Is the calibration thread still running? We might have to restart
294  // it if we have completed the calibration early but are being brought
295  // to top of the display again
296 
297  if (!isStarted())
298  {
299  ginfo("Starting calibration: m_calthread=%d\n", (int)m_calthread);
301  }
302 
303  // Is the calibration thread running? If not, then wait until it is.
304 
305  while (!isRunning() && (++waitcount < 10))
306  {
307  usleep(500);
308  }
309 
310  // The calibration thread is running. Make sure that is is not
311  // already processing a redraw
312 
314  {
315  // Ask the calibration thread to restart the calibration and redraw
316  // the display
317 
319  (void)pthread_kill(m_thread, CONFIG_NXWM_CALIBRATION_SIGNO);
320  }
321 }
322 
323 /**
324  * Report of this is a "normal" window or a full screen window. The
325  * primary purpose of this method is so that window manager will know
326  * whether or not it show draw the task bar.
327  *
328  * @return True if this is a full screen window.
329  */
330 
332 {
333  return m_window->isFullScreen();
334 }
335 
336 /**
337  * Accept raw touchscreen input.
338  *
339  * @param sample Touchscreen input sample
340  */
341 
342 void CCalibration::touchscreenInput(struct touch_sample_s &sample)
343 {
344  // Is this a new touch event? Or is it a drag event?
345 
346  if ((sample.point[0].flags & (TOUCH_DOWN|TOUCH_MOVE)) != 0)
347  {
348  // Yes.. but ignore drag events if we did not see the matching
349  // touch down event
350 
351  if ((sample.point[0].flags & TOUCH_DOWN) != 0 ||
352  (m_touched && sample.point[0].id == m_touchId))
353  {
354  // Yes.. save the touch position and wait for the TOUCH_UP report
355 
356  m_touchPos.x = sample.point[0].x;
357  m_touchPos.y = sample.point[0].y;
358 
359  iinfo("Touch id: %d flags: %02x x: %d y: %d h: %d w: %d pressure: %d\n",
360  sample.point[0].id, sample.point[0].flags, sample.point[0].x,
361  sample.point[0].y, sample.point[0].h, sample.point[0].w,
362  sample.point[0].pressure);
363 
364  // Show calibration screen again, changing the color of the circle to
365  // make it clear that the touch has been noticed.
366 
367  if (!m_touched)
368  {
369  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_TOUCHEDCOLOR;
370 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
372 #endif
373  showCalibration();
374  m_touched = true;
375  }
376 
377  // Remember the ID of the touch down event
378 
379  m_touchId = sample.point[0].id;
380  }
381  }
382 
383  // Was the touch released?
384 
385  else if ((sample.point[0].flags & TOUCH_UP) != 0)
386  {
387  // Yes.. did we see the pen down event?
388 
389  if (m_touched)
390  {
391  // Yes.. For the matching touch ID?
392 
393  if (sample.point[0].id == m_touchId)
394  {
395  // Yes.. invoke the state machine.
396 
397  iinfo("State: %d Screen x: %d y: %d Touch x: %d y: %d\n",
399  m_touchPos.x, m_touchPos.y);
400 
401  stateMachine();
402  }
403  else
404  {
405  // No... restore the un-highlighted circle
406 
407  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
408 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
409  m_text->setText("");
410 #endif
411  showCalibration();
412  }
413  }
414 
415  // In any event, the screen is not touched
416 
417  m_touched = false;
418  }
419 }
420 
421 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
422 /**
423  * Create widgets need by the calibration thread.
424  *
425  * @return True if the widgets were successfully created.
426  */
427 
429 {
430  // Select a font for the calculator
431 
432  m_font = new NXWidgets::
433  CNxFont((nx_fontid_e)CONFIG_NXWM_CALIBRATION_FONTID,
434  CONFIG_NXWM_DEFAULT_FONTCOLOR, CONFIG_NXWM_TRANSPARENT_COLOR);
435  if (!m_font)
436  {
437  gerr("ERROR failed to create font\n");
438  return false;
439  }
440 
441  // Recover the window instance contained in the application window
442 
444 
445  // Get the size of the window
446 
447  struct nxgl_size_s windowSize;
448  if (!window->getSize(&windowSize))
449  {
450  gerr("ERROR: Failed to get window size\n");
451  delete m_font;
452  m_font = (NXWidgets::CNxFont *)0;
453  return false;
454  }
455 
456  // How big is the biggest string we might display?
457 
458  nxgl_coord_t maxStringWidth = m_font->getStringWidth(g_touchMsg);
459  nxgl_coord_t altStringWidth = m_font->getStringWidth(g_againMsg);
460 
461  if (altStringWidth > maxStringWidth)
462  {
463  maxStringWidth = altStringWidth;
464  }
465 
466  // How big can the label be?
467 
468  struct nxgl_size_s labelSize;
469  labelSize.w = maxStringWidth + 2*4;
470  labelSize.h = m_font->getHeight() + 2*4;
471 
472  // Where should the label be?
473 
474  struct nxgl_point_s labelPos;
475  labelPos.x = ((windowSize.w - labelSize.w) / 2);
476  labelPos.y = ((windowSize.h - labelSize.h) / 2);
477 
478  // Get the widget control associated with the application window
479 
481 
482  // Create a label to show the calibration message.
483 
484  m_text = new NXWidgets::
485  CLabel(control, labelPos.x, labelPos.y, labelSize.w, labelSize.h, "");
486 
487  if (!m_text)
488  {
489  gerr("ERROR: Failed to create CLabel\n");
490  delete m_font;
491  m_font = (NXWidgets::CNxFont *)0;
492  return false;
493  }
494 
495  // No border
496 
497  m_text->setBorderless(true);
498 
499  // Center text
500 
502 
503  // Disable drawing and events until we are asked to redraw the window
504 
506  m_text->setRaisesEvents(false);
507 
508  // Select the font
509 
511  return true;
512 }
513 
514 /**
515  * Destroy widgets created for the calibration thread.
516  */
517 
519 {
520  delete m_text;
521  m_text = (NXWidgets::CLabel *)0;
522 
523  delete m_font;
524  m_font = (NXWidgets::CNxFont *)0;
525 }
526 #endif
527 
528 /**
529  * Start the calibration thread.
530  *
531  * @param initialState. The initial state of the calibration thread
532  * @return True if the thread was successfully started.
533  */
534 
536 {
537 
538  // Verify that the thread is not already running
539 
540  if (isRunning())
541  {
542  gwarn("WARNING: The calibration thread is already running\n");
543  return false;
544  }
545 
546  // Configure the calibration thread
547 
548  pthread_attr_t attr;
549  (void)pthread_attr_init(&attr);
550 
551  struct sched_param param;
552  param.sched_priority = CONFIG_NXWM_CALIBRATION_LISTENERPRIO;
553  (void)pthread_attr_setschedparam(&attr, &param);
554 
555  (void)pthread_attr_setstacksize(&attr, CONFIG_NXWM_CALIBRATION_LISTENERSTACK);
556 
557  // Set the initial state of the thread
558 
559  m_calthread = initialState;
560 
561  // Start the thread that will perform the calibration process
562 
563  int ret = pthread_create(&m_thread, &attr, calibration, (FAR void *)this);
564  if (ret != 0)
565  {
566  gerr("ERROR: pthread_create failed: %d\n", ret);
567  return false;
568  }
569 
570  ginfo("Calibration thread m_calthread=%d\n", (int)m_calthread);
571  return true;
572 }
573 
574 /**
575  * The calibration thread. This is the entry point of a thread that provides the
576  * calibration displays, waits for input, and collects calibration data.
577  *
578  * @param arg. The CCalibration 'this' pointer cast to a void*.
579  * @return This function always returns NULL when the thread exits
580  */
581 
582 FAR void *CCalibration::calibration(FAR void *arg)
583 {
584  CCalibration *This = (CCalibration *)arg;
585  bool stalled = true;
586 
587 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
588  // Create widgets that will be used in the calibration display
589 
590  if (!This->createWidgets())
591  {
592  gerr("ERROR: failed to create widgets\n");
593  return false;
594  }
595 
596  // No samples have yet been collected
597 
598  This->m_nsamples = 0;
599 #endif
600 
601  // The calibration thread is now running
602 
605  ginfo("Started: m_calthread=%d\n", (int)This->m_calthread);
606 
607  // Loop until calibration completes or we have been requested to terminate
608 
609  while (This->m_calthread != CALTHREAD_STOPREQUESTED &&
610  This->m_calphase != CALPHASE_COMPLETE)
611  {
612  // Check for state changes due to display order changes
613 
614  if (This->m_calthread == CALTHREAD_HIDE)
615  {
616  // This state is set by hide() when our display is no longer visible
617 
620  stalled = true;
621  }
622  else if (This->m_calthread == CALTHREAD_SHOW)
623  {
624  // This state is set by redraw() when our display has become visible
625 
628  stalled = false;
629  This->stateMachine();
630  }
631 
632  // The calibration thread will stall if has been asked to hide the
633  // display. While stalled, we will just sleep for a bit and test
634  // the state again. If we are re-awakened by a redraw(), then we
635  // will be given a signal which will wake us up immediately.
636  //
637  // Note that stalled is also initially true so we have to receive
638  // redraw() before we attempt to draw anything
639 
640  if (stalled)
641  {
642  // Sleep for a while (or until we receive a signal)
643 
644  std::usleep(500*1000);
645  }
646  else
647  {
648  // Wait for the next raw touchscreen input (or possibly a signal)
649 
650  struct touch_sample_s sample;
651  while (!This->m_touchscreen->waitRawTouchData(&sample) &&
652  This->m_calthread == CALTHREAD_RUNNING);
653 
654  // Then process the raw touchscreen input
655 
656  if (This->m_calthread == CALTHREAD_RUNNING)
657  {
658  This->touchscreenInput(sample);
659  }
660  }
661  }
662 
663  // Hide the message
664 
665 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
666  This->m_text->setText("");
667  This->m_text->enableDrawing();
668  This->m_text->redraw();
669  This->m_text->disableDrawing();
670 #endif
671 
672  // Perform the final steps of calibration
673 
674  This->finishCalibration();
675 
676 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
677  // Destroy widgets
678 
679  This->destroyWidgets();
680 #endif
681 
682  ginfo("Terminated: m_calthread=%d\n", (int)This->m_calthread);
683  return (FAR void *)0;
684 }
685 
686 /**
687  * Accumulate and average touch sample data
688  *
689  * @param average. When the averaged data is available, return it here
690  * @return True: Average data is available; False: Need to collect more samples
691  */
692 
693 #ifdef CONFIG_NXWM_CALIBRATION_AVERAGE
694 bool CCalibration::averageSamples(struct nxgl_point_s &average)
695 {
696  // Have we started collecting sample data? */
697 
698  // Save the sample data
699 
700  iinfo("Sample %d: Touch x: %d y: %d\n", m_nsamples+1, m_touchPos.x, m_touchPos.y);
701 
704  m_nsamples++;
705 
706  // Have all of the samples been collected?
707 
708  if (m_nsamples < CONFIG_NXWM_CALIBRATION_NSAMPLES)
709  {
710  // Not yet... return false
711 
712  return false;
713  }
714 
715  // Yes.. we have all of the samples
716 #ifdef CONFIG_NXWM_CALIBRATION_NSAMPLES
717  // Discard the smallest X and Y values
718 
719  int xValue = INT_MAX;
720  int xIndex = -1;
721 
722  int yValue = INT_MAX;
723  int yIndex = -1;
724 
725  for (int i = 0; i < CONFIG_NXWM_CALIBRATION_NSAMPLES; i++)
726  {
727  if ((int)m_sampleData[i].x < xValue)
728  {
729  xValue = (int)m_sampleData[i].x;
730  xIndex = i;
731  }
732 
733  if ((int)m_sampleData[i].y < yValue)
734  {
735  yValue = (int)m_sampleData[i].y;
736  yIndex = i;
737  }
738  }
739 
740  m_sampleData[xIndex].x = m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES-1].x;
741  m_sampleData[yIndex].y = m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES-1].y;
742 
743  // Discard the largest X and Y values
744 
745  xValue = -1;
746  xIndex = -1;
747 
748  yValue = -1;
749  yIndex = -1;
750 
751  for (int i = 0; i < CONFIG_NXWM_CALIBRATION_NSAMPLES-1; i++)
752  {
753  if ((int)m_sampleData[i].x > xValue)
754  {
755  xValue = (int)m_sampleData[i].x;
756  xIndex = i;
757  }
758 
759  if ((int)m_sampleData[i].y > yValue)
760  {
761  yValue = (int)m_sampleData[i].y;
762  yIndex = i;
763  }
764  }
765 
766  m_sampleData[xIndex].x = m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES-2].x;
767  m_sampleData[yIndex].y = m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES-2].y;
768 #endif
769 
770 #if NXWM_CALIBRATION_NAVERAGE > 1
771  // Calculate the average of the remaining values
772 
773  long xaccum = 0;
774  long yaccum = 0;
775 
776  for (int i = 0; i < NXWM_CALIBRATION_NAVERAGE; i++)
777  {
778  xaccum += m_sampleData[i].x;
779  yaccum += m_sampleData[i].y;
780  }
781 
782  average.x = xaccum / NXWM_CALIBRATION_NAVERAGE;
783  average.y = yaccum / NXWM_CALIBRATION_NAVERAGE;
784 #else
785  average.x = m_sampleData[0].x;
786  average.y = m_sampleData[0].y;
787 #endif
788 
789  iinfo("Average: Touch x: %d y: %d\n", average.x, average.y);
790  m_nsamples = 0;
791  return true;
792 }
793 #endif
794 
795 /**
796  * This is the calibration state machine. It is called initially and then
797  * as new touchscreen data is received.
798  */
799 
801 {
802  ginfo("Old m_calphase=%d\n", m_calphase);
803 
804 #ifdef CONFIG_NXWM_CALIBRATION_AVERAGE
805  // Are we collecting samples?
806 
807  struct nxgl_point_s average;
808 
809  switch (m_calphase)
810  {
811  case CALPHASE_UPPER_LEFT:
814  case CALPHASE_LOWER_LEFT:
815  {
816  // Yes... Have all of the samples been collected?
817 
818  if (!averageSamples(average))
819  {
820  // Not yet... Show the calibration screen again with the circle
821  // in the normal color and an instruction to touch the circle again.
822 
823  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
825  showCalibration();
826 
827  // And wait for the next touch
828 
829  return;
830  }
831  }
832  break;
833 
834  // No... we are not collecting data now
835 
836  default:
837  break;
838  }
839 #endif
840 
841  // Recover the window instance contained in the full screen window
842 
844 
845  // Get the size of the fullscreen window
846 
847  struct nxgl_size_s windowSize;
848  if (!window->getSize(&windowSize))
849  {
850  return;
851  }
852 
853  switch (m_calphase)
854  {
855  default:
857  {
858  // Clear the entire screen
859  // Get the widget control associated with the full screen window
860 
861  NXWidgets::CWidgetControl *control = window->getWidgetControl();
862 
863  // Get the CCGraphicsPort instance for this window
864 
865  NXWidgets::CGraphicsPort *port = control->getGraphicsPort();
866 
867  // Fill the entire window with the background color
868 
869  port->drawFilledRect(0, 0, windowSize.w, windowSize.h,
870  CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR);
871 
872  // Then draw the first calibration screen
873 
874  m_screenInfo.pos.x = CALIBRATION_LEFTX;
875  m_screenInfo.pos.y = CALIBRATION_TOPY;
876  m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_LINECOLOR;
877  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
878 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
880 #endif
881  showCalibration();
882 
883  // Then set up the current state
884 
886  }
887  break;
888 
889  case CALPHASE_UPPER_LEFT:
890  {
891  // A touch has been received while in the CALPHASE_UPPER_LEFT state.
892  // Save the touch data and set up the next calibration display
893 
894 #ifdef CONFIG_NXWM_CALIBRATION_AVERAGE
895  m_calibData[CALIB_UPPER_LEFT_INDEX].x = average.x;
896  m_calibData[CALIB_UPPER_LEFT_INDEX].y = average.y;
897 #else
898  m_calibData[CALIB_UPPER_LEFT_INDEX].x = m_touchPos.x;
899  m_calibData[CALIB_UPPER_LEFT_INDEX].y = m_touchPos.y;
900 #endif
901 
902  // Clear the previous screen by re-drawing it using the background
903  // color. That is much faster than clearing the whole display
904 
905  m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
906  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
907  showCalibration();
908 
909  // Then draw the next calibration screen
910 
911  m_screenInfo.pos.x = CALIBRATION_RIGHTX;
912  m_screenInfo.pos.y = CALIBRATION_TOPY;
913  m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_LINECOLOR;
914  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
915 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
917 #endif
918  showCalibration();
919 
920  // Then set up the current state
921 
923  }
924  break;
925 
927  {
928  // A touch has been received while in the CALPHASE_UPPER_RIGHT state.
929  // Save the touch data and set up the next calibration display
930 
931 #ifdef CONFIG_NXWM_CALIBRATION_AVERAGE
932  m_calibData[CALIB_UPPER_RIGHT_INDEX].x = average.x;
933  m_calibData[CALIB_UPPER_RIGHT_INDEX].y = average.y;
934 #else
935  m_calibData[CALIB_UPPER_RIGHT_INDEX].x = m_touchPos.x;
936  m_calibData[CALIB_UPPER_RIGHT_INDEX].y = m_touchPos.y;
937 #endif
938 
939  // Clear the previous screen by re-drawing it using the backgro9und
940  // color. That is much faster than clearing the whole display
941 
942  m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
943  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
944  showCalibration();
945 
946  // Then draw the next calibration screen
947 
948  m_screenInfo.pos.x = CALIBRATION_RIGHTX;
949  m_screenInfo.pos.y = CALIBRATION_BOTTOMY;
950  m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_LINECOLOR;
951  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
952 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
954 #endif
955  showCalibration();
956 
957  // Then set up the current state
958 
960  }
961  break;
962 
964  {
965  // A touch has been received while in the CALPHASE_LOWER_RIGHT state.
966  // Save the touch data and set up the next calibration display
967 
968 #ifdef CONFIG_NXWM_CALIBRATION_AVERAGE
969  m_calibData[CALIB_LOWER_RIGHT_INDEX].x = average.x;
970  m_calibData[CALIB_LOWER_RIGHT_INDEX].y = average.y;
971 #else
972  m_calibData[CALIB_LOWER_RIGHT_INDEX].x = m_touchPos.x;
973  m_calibData[CALIB_LOWER_RIGHT_INDEX].y = m_touchPos.y;
974 #endif
975 
976  // Clear the previous screen by re-drawing it using the backgro9und
977  // color. That is much faster than clearing the whole display
978 
979  m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
980  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
981  showCalibration();
982 
983  // Then draw the next calibration screen
984 
985  m_screenInfo.pos.x = CALIBRATION_LEFTX;
986  m_screenInfo.pos.y = CALIBRATION_BOTTOMY;
987  m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_LINECOLOR;
988  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_CIRCLECOLOR;
989 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
991 #endif
992  showCalibration();
993 
994  // Then set up the current state
995 
997  }
998  break;
999 
1000  case CALPHASE_LOWER_LEFT:
1001  {
1002  // A touch has been received while in the CALPHASE_LOWER_LEFT state.
1003  // Save the touch data and set up the next calibration display
1004 
1005 #ifdef CONFIG_NXWM_CALIBRATION_AVERAGE
1006  m_calibData[CALIB_LOWER_LEFT_INDEX].x = average.x;
1007  m_calibData[CALIB_LOWER_LEFT_INDEX].y = average.y;
1008 #else
1009  m_calibData[CALIB_LOWER_LEFT_INDEX].x = m_touchPos.x;
1010  m_calibData[CALIB_LOWER_LEFT_INDEX].y = m_touchPos.y;
1011 #endif
1012 
1013  // Clear the previous screen by re-drawing it using the background
1014  // color. That is much faster than clearing the whole display
1015 
1016  m_screenInfo.lineColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
1017  m_screenInfo.circleFillColor = CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR;
1018 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
1020 #endif
1021  showCalibration();
1022 
1023  // Inform any waiter that calibration is complete
1024 
1026  }
1027  break;
1028 
1029  case CALPHASE_COMPLETE:
1030  // Might happen... do nothing if it does
1031  break;
1032  }
1033 
1034  iinfo("New m_calphase=%d Screen x: %d y: %d\n",
1036 }
1037 
1038 /**
1039  * Presents the next calibration screen
1040  *
1041  * @param screenInfo Describes the next calibration screen
1042  */
1043 
1045 {
1046  // Recover the window instance contained in the full screen window
1047 
1049 
1050  // Get the widget control associated with the full screen window
1051 
1052  NXWidgets::CWidgetControl *control = window->getWidgetControl();
1053 
1054  // Get the CCGraphicsPort instance for this window
1055 
1056  NXWidgets::CGraphicsPort *port = control->getGraphicsPort();
1057 
1058  // Get the size of the fullscreen window
1059 
1060  struct nxgl_size_s windowSize;
1061  if (!window->getSize(&windowSize))
1062  {
1063  return;
1064  }
1065 
1066  // Draw the circle at the center of the touch position
1067 
1068  port->drawFilledCircle(&m_screenInfo.pos, CALIBRATION_CIRCLE_RADIUS,
1070 
1071  // Draw horizontal line
1072 
1073  port->drawFilledRect(0, m_screenInfo.pos.y, windowSize.w, CALIBRATION_LINE_THICKNESS,
1075 
1076  // Draw vertical line
1077 
1078  port->drawFilledRect(m_screenInfo.pos.x, 0, CALIBRATION_LINE_THICKNESS, windowSize.h,
1080 
1081  // Show the touchscreen message
1082 
1083 #ifdef CONFIG_NXWM_CALIBRATION_MESSAGES
1084  m_text->enableDrawing();
1085  m_text->redraw();
1087 #endif
1088 }
1089 
1090 /**
1091  * Finish calibration steps and provide the calibration data to the
1092  * touchscreen driver.
1093  */
1094 
1096 {
1097  // Did we finish calibration successfully?
1098 
1100  {
1101  // Yes... Get the final Calibration data
1102 
1103  struct SCalibrationData caldata;
1104  if (createCalibrationData(caldata))
1105  {
1106 #ifdef CONFIG_NXWM_TOUCHSCREEN_CONFIGDATA
1107  // Save the new calibration data. The saved calibration
1108  // data may be used to avoided recalibrating in the future.
1109 
1110  int ret = platform_setconfig(CONFIGDATA_TSCALIBRATION, 0,
1111  (FAR const uint8_t *)&caldata,
1112  sizeof(struct SCalibrationData));
1113  if (ret != 0)
1114  {
1115  gerr("ERROR: Failed to save calibration data\n");
1116  }
1117 #endif
1118  // And provide the calibration data to the touchscreen, enabling
1119  // touchscreen processing
1120 
1121  m_touchscreen->setEnabled(false);
1123  m_touchscreen->setEnabled(true);
1124  }
1125  }
1126 
1127  // Remove the touchscreen application from the taskbar
1128 
1129  m_taskbar->stopApplication(this);
1130 
1131  // And set the terminated stated
1132 
1134 }
1135 
1136 /**
1137  * Given the raw touch data collected by the calibration thread, create the
1138  * massaged calibration data needed by CTouchscreen.
1139  *
1140  * @param data. A reference to the location to save the calibration data
1141  * @return True if the calibration data was successfully created.
1142  */
1143 
1145 {
1146  // Recover the window instance contained in the full screen window
1147 
1149 
1150  // Get the size of the fullscreen window
1151 
1152  struct nxgl_size_s windowSize;
1153  if (!window->getSize(&windowSize))
1154  {
1155  gerr("ERROR: NXWidgets::INxWindow::getSize failed\n");
1156  return false;
1157  }
1158 
1159 #ifdef CONFIG_NXWM_CALIBRATION_ANISOTROPIC
1160  // X lines:
1161  //
1162  // x2 = slope*y1 + offset
1163  //
1164  // slope = (bottomY - topY) / (bottomX - topX)
1165  // offset = (topY - topX * slope)
1166 
1167  float topX = (float)m_calibData[CALIB_UPPER_LEFT_INDEX].x;
1168  float bottomX = (float)m_calibData[CALIB_LOWER_LEFT_INDEX].x;
1169 
1170  float topY = (float)m_calibData[CALIB_UPPER_LEFT_INDEX].y;
1171  float bottomY = (float)m_calibData[CALIB_LOWER_LEFT_INDEX].y;
1172 
1173  data.left.slope = (bottomX - topX) / (bottomY - topY);
1174  data.left.offset = topX - topY * data.left.slope;
1175 
1176  iinfo("Left slope: %6.2f offset: %6.2f\n", data.left.slope, data.left.offset);
1177 
1178  topX = (float)m_calibData[CALIB_UPPER_RIGHT_INDEX].x;
1179  bottomX = (float)m_calibData[CALIB_LOWER_RIGHT_INDEX].x;
1180 
1181  topY = (float)m_calibData[CALIB_UPPER_RIGHT_INDEX].y;
1182  bottomY = (float)m_calibData[CALIB_LOWER_RIGHT_INDEX].y;
1183 
1184  data.right.slope = (bottomX - topX) / (bottomY - topY);
1185  data.right.offset = topX - topY * data.right.slope;
1186 
1187  iinfo("Right slope: %6.2f offset: %6.2f\n", data.right.slope, data.right.offset);
1188 
1189  // Y lines:
1190  //
1191  // y2 = slope*x1 + offset
1192  //
1193  // slope = (rightX - topX) / (rightY - leftY)
1194  // offset = (topX - leftY * slope)
1195 
1196  float leftX = (float)m_calibData[CALIB_UPPER_LEFT_INDEX].x;
1197  float rightX = (float)m_calibData[CALIB_UPPER_RIGHT_INDEX].x;
1198 
1199  float leftY = (float)m_calibData[CALIB_UPPER_LEFT_INDEX].y;
1200  float rightY = (float)m_calibData[CALIB_UPPER_RIGHT_INDEX].y;
1201 
1202  data.top.slope = (rightY - leftY) / (rightX - leftX);
1203  data.top.offset = leftY - leftX * data.top.slope;
1204 
1205  iinfo("Top slope: %6.2f offset: %6.2f\n", data.top.slope, data.top.offset);
1206 
1207  leftX = (float)m_calibData[CALIB_LOWER_LEFT_INDEX].x;
1208  rightX = (float)m_calibData[CALIB_LOWER_RIGHT_INDEX].x;
1209 
1210  leftY = (float)m_calibData[CALIB_LOWER_LEFT_INDEX].y;
1211  rightY = (float)m_calibData[CALIB_LOWER_RIGHT_INDEX].y;
1212 
1213  data.bottom.slope = (rightY - leftY) / (rightX - leftX);
1214  data.bottom.offset = leftY - leftX * data.bottom.slope;
1215 
1216  iinfo("Bottom slope: %6.2f offset: %6.2f\n", data.bottom.slope, data.bottom.offset);
1217 
1218  // Save also the calibration screen positions
1219 
1220  data.leftX = CALIBRATION_LEFTX;
1221  data.rightX = CALIBRATION_RIGHTX;
1222  data.topY = CALIBRATION_TOPY;
1223  data.bottomY = CALIBRATION_BOTTOMY;
1224 #else
1225  // Calculate the calibration parameters
1226  //
1227  // (scaledX - LEFTX) / (rawX - leftX) = (RIGHTX - LEFTX) / (rightX - leftX)
1228  // scaledX = (rawX - leftX) * (RIGHTX - LEFTX) / (rightX - leftX) + LEFTX
1229  // = rawX * xSlope + (LEFTX - leftX * xSlope)
1230  // = rawX * xSlope + xOffset
1231  //
1232  // where:
1233  // xSlope = (RIGHTX - LEFTX) / (rightX - leftX)
1234  // xOffset = (LEFTX - leftX * xSlope)
1235 
1236  b16_t leftX = (m_calibData[CALIB_UPPER_LEFT_INDEX].x +
1237  m_calibData[CALIB_LOWER_LEFT_INDEX].x) << 15;
1238  b16_t rightX = (m_calibData[CALIB_UPPER_RIGHT_INDEX].x +
1239  m_calibData[CALIB_LOWER_RIGHT_INDEX].x) << 15;
1240 
1241  data.xSlope = b16divb16(itob16(CALIBRATION_RIGHTX - CALIBRATION_LEFTX), (rightX - leftX));
1242  data.xOffset = itob16(CALIBRATION_LEFTX) - b16mulb16(leftX, data.xSlope);
1243 
1244  iinfo("New xSlope: %08x xOffset: %08x\n", data.xSlope, data.xOffset);
1245 
1246  // Similarly for Y
1247  //
1248  // (scaledY - TOPY) / (rawY - topY) = (BOTTOMY - TOPY) / (bottomY - topY)
1249  // scaledY = (rawY - topY) * (BOTTOMY - TOPY) / (bottomY - topY) + TOPY
1250  // = rawY * ySlope + (TOPY - topY * ySlope)
1251  // = rawY * ySlope + yOffset
1252  //
1253  // where:
1254  // ySlope = (BOTTOMY - TOPY) / (bottomY - topY)
1255  // yOffset = (TOPY - topY * ySlope)
1256 
1257  b16_t topY = (m_calibData[CALIB_UPPER_LEFT_INDEX].y +
1258  m_calibData[CALIB_UPPER_RIGHT_INDEX].y) << 15;
1259  b16_t bottomY = (m_calibData[CALIB_LOWER_LEFT_INDEX].y +
1260  m_calibData[CALIB_LOWER_RIGHT_INDEX].y) << 15;
1261 
1262  data.ySlope = b16divb16(itob16(CALIBRATION_BOTTOMY - CALIBRATION_TOPY), (bottomY - topY));
1263  data.yOffset = itob16(CALIBRATION_TOPY) - b16mulb16(topY, data.ySlope);
1264 
1265  iinfo("New ySlope: %08x yOffset: %08x\n", data.ySlope, data.yOffset);
1266 #endif
1267 
1268  return true;
1269 }
1270 
1271 /**
1272  * CCalibrationFactory Constructor
1273  *
1274  * @param taskbar. The taskbar instance used to terminate calibration
1275  * @param touchscreen. An instance of the class that wraps the
1276  * touchscreen device.
1277  */
1278 
1280 {
1281  m_taskbar = taskbar;
1282  m_touchscreen = touchscreen;
1283 }
1284 
1285 /**
1286  * Create a new instance of an CCalibration (as IApplication).
1287  */
1288 
1290 {
1291  // Call CTaskBar::openFullScreenWindow to create a full screen window for
1292  // the calibration application
1293 
1295  if (!window)
1296  {
1297  gerr("ERROR: Failed to create CFullScreenWindow\n");
1298  return (IApplication *)0;
1299  }
1300 
1301  // Open the window (it is hot in here)
1302 
1303  if (!window->open())
1304  {
1305  gerr("ERROR: Failed to open CFullScreenWindow \n");
1306  delete window;
1307  return (IApplication *)0;
1308  }
1309 
1310  // Instantiate the application, providing the window to the application's
1311  // constructor
1312 
1314  if (!calibration)
1315  {
1316  gerr("ERROR: Failed to instantiate CCalibration\n");
1317  delete window;
1318  return (IApplication *)0;
1319  }
1320 
1321  return static_cast<IApplication*>(calibration);
1322 }
1323 
1324 /**
1325  * Get the icon associated with the application
1326  *
1327  * @return An instance if IBitmap that may be used to rend the
1328  * application's icon. This is an new IBitmap instance that must
1329  * be deleted by the caller when it is no long needed.
1330  */
1331 
1333 {
1335  new NXWidgets::CRlePaletteBitmap(&CONFIG_NXWM_CALIBRATION_ICON);
1336 
1337  return bitmap;
1338 }
struct SCalibrationLine left
CGraphicsPort * getGraphicsPort(void)
void destroyWidgets(void)
struct SCalibrationLine right
bool isFullScreen(void) const
IApplicationWindow * getWindow(void) const
void disableDrawing(void)
Definition: cnxwidget.hxx:908
void setRaisesEvents(bool raises)
Definition: cnxwidget.hxx:887
CTouchscreen * m_touchscreen
bool stopApplication(IApplication *app)
Definition: ctaskbar.cxx:667
CCalibrationFactory(CTaskbar *taskbar, CTouchscreen *touchscreen)
void block(IApplication *app)
NXWidgets::CWidgetControl * getWidgetControl(void) const
struct SCalibScreenInfo m_screenInfo
nxgl_coord_t getStringWidth(const CNxString &text) const
Definition: cnxfont.cxx:142
void drawFilledCircle(struct nxgl_point_s *center, nxgl_coord_t radius, nxgl_mxpixel_t color)
bool isRunning(void) const
virtual void setTextAlignmentHoriz(TextAlignmentHoriz alignment)
Definition: clabel.cxx:145
NXWidgets::IBitmap * getIcon(void)
virtual bool getSize(FAR struct nxgl_size_s *pSize)=0
bool waitRawTouchData(struct touch_sample_s *touch)
void setEnabled(bool enable)
NXWidgets::INxWindow * getWindow(void) const
struct nxgl_point_s m_calibData[CALIB_DATA_POINTS]
CFullScreenWindow * openFullScreenWindow(void)
Definition: ctaskbar.cxx:406
CFullScreenWindow * m_window
struct nxgl_point_s m_touchPos
bool createWidgets(void)
void touchscreenInput(struct touch_sample_s &sample)
NXWidgets::CLabel * m_text
void drawFilledRect(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, nxgl_coord_t height, nxgl_mxpixel_t color)
bool createCalibrationData(struct SCalibrationData &data)
bool averageSamples(struct nxgl_point_s &average)
struct SCalibrationLine top
bool startCalibration(enum ECalThreadState initialState)
void enableDrawing(void)
Definition: cnxwidget.hxx:917
const uint8_t getHeight(void) const
Definition: cnxfont.hxx:229
static const char g_againMsg[]
NXWidgets::IBitmap * getIcon(void)
struct SCalibrationLine bottom
IApplication * create(void)
void stateMachine(void)
virtual CWidgetControl * getWidgetControl(void) const =0
void setCalibrationData(const struct SCalibrationData &caldata)
static const NXWidgets::SRlePaletteBitmapEntry bitmap[]
static const char g_okMsg[]
static FAR void * calibration(FAR void *arg)
bool isStarted(void) const
static const char g_touchMsg[]
NXWidgets::CNxFont * m_font
void showCalibration(void)
virtual void setFont(CNxFont *font)
Definition: clabel.cxx:259
void finishCalibration(void)
virtual void setText(const CNxString &text)
Definition: clabel.cxx:171
void setBorderless(bool isBorderless)
Definition: cnxwidget.cxx:461
CCalibration(CTaskbar *taskbar, CFullScreenWindow *window, CTouchscreen *touchscreen)
NXWidgets::CNxString getName(void)
volatile uint8_t m_calthread
struct nxgl_point_s m_sampleData[CONFIG_NXWM_CALIBRATION_NSAMPLES]