2019-05-20 19:01:46 +02:00
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// apps/graphics/twm4nx/src/ccalibration.cxx
|
|
|
|
// Perform Touchscreen Calibration
|
|
|
|
//
|
|
|
|
// Copyright (C) 2019 Gregory Nutt. All rights reserved.
|
|
|
|
// Author: Gregory Nutt <gnutt@nuttx.org>
|
|
|
|
//
|
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions
|
|
|
|
// are met:
|
|
|
|
//
|
|
|
|
// 1. Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
// 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer in
|
|
|
|
// the documentation and/or other materials provided with the
|
|
|
|
// distribution.
|
|
|
|
// 3. Neither the name NuttX nor the names of its contributors may be
|
|
|
|
// used to endorse or promote products derived from this software
|
|
|
|
// without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
|
|
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
|
|
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
//
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Included Files
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
2020-11-28 01:47:19 +01:00
|
|
|
#include <cinttypes>
|
2019-05-20 19:01:46 +02:00
|
|
|
#include <cunistd>
|
|
|
|
#include <csched>
|
|
|
|
#include <cassert>
|
|
|
|
#include <cerrno>
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
#include <semaphore.h>
|
|
|
|
#include <debug.h>
|
|
|
|
|
|
|
|
#include <nuttx/semaphore.h>
|
|
|
|
#include <nuttx/nx/nxbe.h>
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_TOUCHSCREEN_CONFIGDATA
|
|
|
|
# include "platform/configdata.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "graphics/twm4nx/twm4nx_config.hxx"
|
|
|
|
#include "graphics/twm4nx/ctwm4nx.hxx"
|
|
|
|
#include "graphics/twm4nx/cinput.hxx"
|
|
|
|
#include "graphics/twm4nx/cmainmenu.hxx"
|
|
|
|
|
|
|
|
#include "graphics/twm4nx/apps/calib_config.hxx"
|
|
|
|
#include "graphics/twm4nx/apps/ccalibration.hxx"
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Pre-processor Definitions
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Configuration
|
|
|
|
|
|
|
|
#ifndef CONFIG_NX
|
|
|
|
# error "NX is not enabled (CONFIG_NX)"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Positional/size data for the calibration lines and circles
|
|
|
|
|
|
|
|
#define CALIBRATION_LEFTX CONFIG_TWM4NX_CALIBRATION_MARGIN
|
|
|
|
#define CALIBRATION_RIGHTX (windowSize.w - CONFIG_TWM4NX_CALIBRATION_MARGIN + 1)
|
|
|
|
#define CALIBRATION_TOPY CONFIG_TWM4NX_CALIBRATION_MARGIN
|
|
|
|
#define CALIBRATION_BOTTOMY (windowSize.h - CONFIG_TWM4NX_CALIBRATION_MARGIN + 1)
|
|
|
|
|
|
|
|
#define CALIBRATION_CIRCLE_RADIUS 16
|
|
|
|
#define CALIBRATION_LINE_THICKNESS 2
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Private Data
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
using namespace Twm4Nx;
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
static const char GTouchMsg[] = "Touch";
|
|
|
|
static const char GAgainMsg[] = "Again";
|
|
|
|
static const char GOkMsg[] = "OK";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CCalibration Method Implementations
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CCalibration Constructor
|
|
|
|
*
|
|
|
|
* @param twm4nx. The Twm4Nx session instance.
|
|
|
|
*/
|
|
|
|
|
|
|
|
CCalibration::CCalibration(FAR CTwm4Nx *twm4nx)
|
|
|
|
{
|
|
|
|
// Initialize state data
|
|
|
|
|
|
|
|
m_twm4nx = twm4nx;
|
|
|
|
m_nxWin = (FAR NXWidgets::CNxWindow *)0;
|
|
|
|
m_thread = (pthread_t)0;
|
|
|
|
m_calthread = CALTHREAD_NOTRUNNING;
|
|
|
|
m_calphase = CALPHASE_NOT_STARTED;
|
|
|
|
m_touched = false;
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_AVERAGE
|
|
|
|
m_nsamples = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Initialize touch sample.
|
|
|
|
|
|
|
|
m_sample.valid = false;
|
|
|
|
m_sample.pos.x = 0;
|
|
|
|
m_sample.pos.y = 0;
|
|
|
|
m_sample.buttons = 0;
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
// Nullify widgets that will be instantiated when the calibration thread
|
|
|
|
// is started
|
|
|
|
|
|
|
|
m_text = (NXWidgets::CLabel *)0;
|
|
|
|
m_font = (NXWidgets::CNxFont *)0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Set up the semaphores that are used to synchronize the calibration
|
|
|
|
// thread with Twm4Nx events
|
|
|
|
|
2020-01-02 13:09:50 +01:00
|
|
|
sem_init(&m_exclSem, 0, 1);
|
|
|
|
sem_init(&m_synchSem, 0, 0);
|
|
|
|
sem_setprotocol(&m_synchSem, SEM_PRIO_NONE);
|
2019-05-20 19:01:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CCalibration Destructor
|
|
|
|
*/
|
|
|
|
|
|
|
|
CCalibration::~CCalibration(void)
|
|
|
|
{
|
|
|
|
// Make sure that the application is not running (it should already
|
|
|
|
// have been stopped)
|
|
|
|
|
|
|
|
stop();
|
|
|
|
|
|
|
|
if (m_nxWin != (FAR NXWidgets::CNxWindow *)0)
|
|
|
|
{
|
|
|
|
delete m_nxWin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CCalibration Initializer. Performs parts of the instance
|
|
|
|
* construction that may fail. This function creates the
|
|
|
|
* initial calibration display.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibration::initialize(void)
|
|
|
|
{
|
|
|
|
// Create the calibration window
|
|
|
|
// 1. Get the server instance. m_twm4nx inherits from NXWidgets::CNXServer
|
|
|
|
// so we all ready have the server instance.
|
|
|
|
// 2. Create the style, using the selected colors (REVISIT)
|
|
|
|
|
|
|
|
// 3. Create a Widget control instance for the window using the default
|
|
|
|
// style for now. CWindowEvent derives from CWidgetControl.
|
|
|
|
|
|
|
|
struct SAppEvents events;
|
|
|
|
events.eventObj = (FAR void *)this;
|
|
|
|
events.redrawEvent = EVENT_CALIB_REDRAW;
|
|
|
|
events.mouseEvent = EVENT_CALIB_XYINPUT;
|
|
|
|
events.kbdEvent = EVENT_CALIB_KBDINPUT;
|
|
|
|
events.closeEvent = EVENT_CALIB_CLOSE;
|
|
|
|
events.deleteEvent = EVENT_CALIB_DELETE;
|
|
|
|
|
|
|
|
FAR CWindowEvent *control =
|
|
|
|
new CWindowEvent(m_twm4nx, (FAR CWindow *)0, events);
|
|
|
|
|
|
|
|
// 4. Create the calibration window
|
|
|
|
|
|
|
|
uint8_t wflags = NXBE_WINDOW_HIDDEN;
|
|
|
|
|
|
|
|
m_nxWin = m_twm4nx->createRawWindow(control, wflags);
|
|
|
|
if (m_nxWin == (FAR NXWidgets::CNxWindow *)0)
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmerr("ERROR: Failed open raw window\n");
|
2019-05-20 19:01:46 +02:00
|
|
|
delete control;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 5. Open and initialize the main window
|
|
|
|
|
|
|
|
bool success = m_nxWin->open();
|
|
|
|
if (!success)
|
|
|
|
{
|
|
|
|
delete m_nxWin;
|
|
|
|
m_nxWin = (FAR NXWidgets::CNxWindow *)0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 6. Set the window position to the display origin
|
|
|
|
|
|
|
|
struct nxgl_point_s pos =
|
|
|
|
{
|
|
|
|
.x = 0,
|
|
|
|
.y = 0
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!m_nxWin->setPosition(&pos))
|
|
|
|
{
|
|
|
|
delete m_nxWin;
|
|
|
|
m_nxWin = (FAR NXWidgets::CNxWindow *)0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 7. Set the window size to fill the whole display
|
|
|
|
|
|
|
|
struct nxgl_size_s displaySize;
|
|
|
|
m_twm4nx->getDisplaySize(&displaySize);
|
|
|
|
|
|
|
|
if (!m_nxWin->setSize(&displaySize))
|
|
|
|
{
|
|
|
|
delete m_nxWin;
|
|
|
|
m_nxWin = (FAR NXWidgets::CNxWindow *)0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
// Create widgets that will be used in the calibration display
|
|
|
|
|
|
|
|
if (!createWidgets())
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmerr("ERROR: failed to create widgets\n");
|
2019-05-20 19:01:46 +02:00
|
|
|
return (FAR void *)0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Disable touchscreen processing so that we receive raw, un-calibrated
|
|
|
|
// touchscreen input
|
|
|
|
|
|
|
|
FAR CInput *cinput = m_twm4nx->getInput();
|
|
|
|
|
|
|
|
cinput->enableCalibration(false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start the application (perhaps in the minimized state).
|
|
|
|
*
|
|
|
|
* @return True if the application was successfully started.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibration::run(void)
|
|
|
|
{
|
|
|
|
// Verify that the thread is not already running
|
|
|
|
|
|
|
|
if (m_calthread == CALTHREAD_RUNNING)
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmwarn("WARNING: The calibration thread is already running\n");
|
2019-05-20 19:01:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Configure the calibration thread
|
|
|
|
|
|
|
|
pthread_attr_t attr;
|
2020-01-02 13:09:50 +01:00
|
|
|
pthread_attr_init(&attr);
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
struct sched_param param;
|
|
|
|
param.sched_priority = CONFIG_TWM4NX_CALIBRATION_LISTENERPRIO;
|
2020-01-02 13:09:50 +01:00
|
|
|
pthread_attr_setschedparam(&attr, ¶m);
|
2019-05-20 19:01:46 +02:00
|
|
|
|
2020-01-02 13:09:50 +01:00
|
|
|
pthread_attr_setstacksize(&attr, CONFIG_TWM4NX_CALIBRATION_LISTENERSTACK);
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
// Set the initial state of the thread
|
|
|
|
|
|
|
|
m_calthread = CALTHREAD_STARTED;
|
|
|
|
|
|
|
|
// Start the thread that will perform the calibration process
|
|
|
|
|
|
|
|
int ret = pthread_create(&m_thread, &attr, calibration, (FAR void *)this);
|
|
|
|
if (ret != 0)
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmerr("ERROR: pthread_create failed: %d\n", ret);
|
2019-05-20 19:01:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-06-01 21:00:02 +02:00
|
|
|
twminfo("Calibration thread m_calthread=%d\n", (int)m_calthread);
|
2019-05-20 19:01:46 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle CCalibration events. This overrides a method from
|
|
|
|
* CTwm4NXEvent.
|
|
|
|
*
|
|
|
|
* @param eventmsg. The received NxWidget WINDOW event message.
|
|
|
|
* @return True if the message was properly handled. false is
|
|
|
|
* return on any failure.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibration::event(FAR struct SEventMsg *eventmsg)
|
|
|
|
{
|
|
|
|
bool success = true;
|
|
|
|
|
|
|
|
switch (eventmsg->eventID)
|
|
|
|
{
|
|
|
|
case EVENT_CALIB_XYINPUT: // New touchscreen input
|
|
|
|
{
|
|
|
|
// Make sure that the thread is still running
|
|
|
|
|
|
|
|
if (m_calthread == CALTHREAD_RUNNING)
|
|
|
|
{
|
|
|
|
// Get exclusive access to the data shared between threads
|
|
|
|
|
|
|
|
success = exclusiveAccess();
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
FAR struct SXyInputEventMsg *xymsg =
|
|
|
|
(FAR struct SXyInputEventMsg *)eventmsg;
|
|
|
|
|
|
|
|
// Save the new sample data
|
|
|
|
|
|
|
|
m_sample.valid = true;
|
|
|
|
m_sample.pos = xymsg->pos;
|
|
|
|
m_sample.buttons = xymsg->buttons;
|
|
|
|
|
|
|
|
// And let the let the calibration thread know that new
|
|
|
|
// data is available
|
|
|
|
|
|
|
|
sem_post(&m_synchSem);
|
|
|
|
sem_post(&m_exclSem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EVENT_CALIB_DELETE: // Today is a good day to die.
|
|
|
|
DEBUGASSERT(m_calthread == CALTHREAD_TERMINATED);
|
|
|
|
delete this; // This is suicide
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
success = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Accept raw touchscreen input.
|
|
|
|
*
|
|
|
|
* @param sample Touchscreen input sample
|
|
|
|
*/
|
|
|
|
|
|
|
|
void CCalibration::touchscreenInput(struct STouchSample &sample)
|
|
|
|
{
|
|
|
|
// Is this a new touch event?
|
|
|
|
|
|
|
|
if ((sample.buttons & MOUSE_BUTTON_1) != 0)
|
|
|
|
{
|
|
|
|
// Yes.. save the touch position and wait for the un-touch report
|
|
|
|
|
|
|
|
m_touchPos.x = sample.pos.x;
|
|
|
|
m_touchPos.y = sample.pos.y;
|
|
|
|
|
|
|
|
twminfo("Touch buttons: %02x x: %d y: %d\n",
|
|
|
|
sample.buttons, sample.pos.x, sample.pos.y);
|
|
|
|
|
|
|
|
// Show calibration screen again, changing the color of the circle to
|
|
|
|
// make it clear that the touch has been noticed.
|
|
|
|
|
|
|
|
if (!m_touched)
|
|
|
|
{
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_TOUCHEDCOLOR;
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
m_text->setText(GOkMsg);
|
|
|
|
#endif
|
|
|
|
showCalibration();
|
|
|
|
m_touched = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// There is no touch. Did we see the pen down event?
|
|
|
|
|
|
|
|
else if (m_touched)
|
|
|
|
{
|
|
|
|
// Yes.. invoke the state machine.
|
|
|
|
|
|
|
|
twminfo("State: %d Screen x: %d y: %d Touch x: %d y: %d\n",
|
|
|
|
m_calphase, m_screenInfo.pos.x, m_screenInfo.pos.y,
|
|
|
|
m_touchPos.x, m_touchPos.y);
|
|
|
|
|
|
|
|
stateMachine();
|
|
|
|
|
|
|
|
// The screen is no longer touched
|
|
|
|
|
|
|
|
m_touched = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
/**
|
|
|
|
* Create widgets need by the calibration thread.
|
|
|
|
*
|
|
|
|
* @return True if the widgets were successfully created.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibration::createWidgets(void)
|
|
|
|
{
|
|
|
|
// Select a font for the calculator
|
|
|
|
|
|
|
|
m_font = new NXWidgets::
|
|
|
|
CNxFont((nx_fontid_e)CONFIG_TWM4NX_CALIBRATION_FONTID,
|
|
|
|
CONFIG_TWM4NX_DEFAULT_FONTCOLOR, CONFIG_TWM4NX_TRANSPARENT_COLOR);
|
|
|
|
if (!m_font)
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmerr("ERROR failed to create font\n");
|
2019-05-20 19:01:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the size of the window
|
|
|
|
|
|
|
|
struct nxgl_size_s windowSize;
|
|
|
|
if (!m_nxWin->getSize(&windowSize))
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmerr("ERROR: Failed to get window size\n");
|
2019-05-20 19:01:46 +02:00
|
|
|
delete m_font;
|
|
|
|
m_font = (NXWidgets::CNxFont *)0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// How big is the biggest string we might display?
|
|
|
|
|
|
|
|
nxgl_coord_t maxStringWidth = m_font->getStringWidth(GTouchMsg);
|
|
|
|
nxgl_coord_t altStringWidth = m_font->getStringWidth(GAgainMsg);
|
|
|
|
|
|
|
|
if (altStringWidth > maxStringWidth)
|
|
|
|
{
|
|
|
|
maxStringWidth = altStringWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
// How big can the label be?
|
|
|
|
|
|
|
|
struct nxgl_size_s labelSize;
|
|
|
|
labelSize.w = maxStringWidth + 2*4;
|
|
|
|
labelSize.h = m_font->getHeight() + 2*4;
|
|
|
|
|
|
|
|
// Where should the label be?
|
|
|
|
|
|
|
|
struct nxgl_point_s labelPos;
|
|
|
|
labelPos.x = ((windowSize.w - labelSize.w) / 2);
|
|
|
|
labelPos.y = ((windowSize.h - labelSize.h) / 2);
|
|
|
|
|
|
|
|
// Get the widget control associated with the application window
|
|
|
|
|
|
|
|
NXWidgets::CWidgetControl *control = m_nxWin->getWidgetControl();
|
|
|
|
|
|
|
|
// Create a label to show the calibration message.
|
|
|
|
|
|
|
|
m_text = new NXWidgets::
|
|
|
|
CLabel(control, labelPos.x, labelPos.y, labelSize.w, labelSize.h, "");
|
|
|
|
|
|
|
|
if (!m_text)
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmerr("ERROR: Failed to create CLabel\n");
|
2019-05-20 19:01:46 +02:00
|
|
|
delete m_font;
|
|
|
|
m_font = (NXWidgets::CNxFont *)0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No border
|
|
|
|
|
|
|
|
m_text->setBorderless(true);
|
|
|
|
|
|
|
|
// Center text
|
|
|
|
|
|
|
|
m_text->setTextAlignmentHoriz(NXWidgets::CLabel::TEXT_ALIGNMENT_HORIZ_CENTER);
|
|
|
|
|
|
|
|
// Disable drawing and events until we are asked to redraw the window
|
|
|
|
|
|
|
|
m_text->disableDrawing();
|
|
|
|
m_text->setRaisesEvents(false);
|
|
|
|
|
|
|
|
// Select the font
|
|
|
|
|
|
|
|
m_text->setFont(m_font);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy widgets created for the calibration thread.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void CCalibration::destroyWidgets(void)
|
|
|
|
{
|
|
|
|
delete m_text;
|
|
|
|
m_text = (NXWidgets::CLabel *)0;
|
|
|
|
|
|
|
|
delete m_font;
|
|
|
|
m_font = (NXWidgets::CNxFont *)0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The calibration thread. This is the entry point of a thread that provides the
|
|
|
|
* calibration displays, waits for input, and collects calibration data.
|
|
|
|
*
|
|
|
|
* @param arg. The CCalibration 'this' pointer cast to a void*.
|
|
|
|
* @return This function always returns NULL when the thread exits
|
|
|
|
*/
|
|
|
|
|
|
|
|
FAR void *CCalibration::calibration(FAR void *arg)
|
|
|
|
{
|
|
|
|
CCalibration *This = (CCalibration *)arg;
|
|
|
|
|
|
|
|
// The calibration thread is now running
|
|
|
|
|
|
|
|
This->m_calthread = CALTHREAD_RUNNING;
|
|
|
|
This->m_calphase = CALPHASE_NOT_STARTED;
|
|
|
|
|
2019-06-01 21:00:02 +02:00
|
|
|
twminfo("Started: m_calthread=%d\n", (int)This->m_calthread);
|
2019-05-20 19:01:46 +02:00
|
|
|
|
2019-05-20 20:06:06 +02:00
|
|
|
// Make the calibration display visible and show the initial calibration
|
|
|
|
// display
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
This->m_nxWin->show();
|
2019-05-20 20:06:06 +02:00
|
|
|
This->stateMachine();
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
// Loop until calibration completes or we have been requested to terminate
|
|
|
|
|
|
|
|
while (This->m_calthread != CALTHREAD_STOPREQUESTED &&
|
|
|
|
This->m_calphase != CALPHASE_COMPLETE)
|
|
|
|
{
|
|
|
|
// Wait for the next raw touchscreen input (or possibly a signal)
|
|
|
|
|
|
|
|
struct STouchSample sample;
|
|
|
|
while (!This->waitTouchSample(sample) &&
|
|
|
|
This->m_calthread == CALTHREAD_RUNNING);
|
|
|
|
|
|
|
|
// Then process the raw touchscreen input
|
|
|
|
|
|
|
|
if (This->m_calthread == CALTHREAD_RUNNING)
|
|
|
|
{
|
|
|
|
This->touchscreenInput(sample);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
// Hide the message
|
|
|
|
|
|
|
|
This->m_text->setText("");
|
|
|
|
This->m_text->enableDrawing();
|
|
|
|
This->m_text->redraw();
|
|
|
|
This->m_text->disableDrawing();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Perform the final steps of calibration
|
|
|
|
|
|
|
|
This->finishCalibration();
|
|
|
|
|
|
|
|
// Then destroy the window and free resources
|
|
|
|
|
|
|
|
This->destroy();
|
|
|
|
|
2019-06-01 21:00:02 +02:00
|
|
|
twminfo("Terminated: m_calthread=%d\n", (int)This->m_calthread);
|
2019-05-20 19:01:46 +02:00
|
|
|
return (FAR void *)0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get exclusive access data shared across threads.
|
|
|
|
*
|
|
|
|
* @return True is returned if the sample data was obtained without error.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibration::exclusiveAccess(void)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ret = sem_wait(&m_exclSem);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
int errcode = errno;
|
|
|
|
DEBUGASSERT(errcode == EINTR || errcode == ECANCELED);
|
|
|
|
if (errcode != EINTR)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (ret < 0);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for touchscreen next sample data to become available.
|
|
|
|
*
|
|
|
|
* @param sample Snapshot of last touchscreen sample
|
|
|
|
* @return True is returned if the sample data was obtained without error.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibration::waitTouchSample(struct STouchSample &sample)
|
|
|
|
{
|
|
|
|
// Loop until we get the sample (or something really bad happens)
|
|
|
|
|
|
|
|
for (; ; )
|
|
|
|
{
|
|
|
|
// Is the sample valid?
|
|
|
|
|
|
|
|
if (!m_sample.valid)
|
|
|
|
{
|
|
|
|
// No, wait for new data to be posted
|
|
|
|
|
|
|
|
int ret = sem_wait(&m_synchSem);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
// EINTR is not an error, it just means that we were
|
|
|
|
// interrupted by a signal. Everything else is bad news.
|
|
|
|
|
|
|
|
int errcode = errno;
|
|
|
|
DEBUGASSERT(errcode == EINTR || errcode == ECANCELED);
|
|
|
|
if (errcode != EINTR)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The samples is valid! Get exclusive access and check again
|
|
|
|
|
|
|
|
else if (exclusiveAccess())
|
|
|
|
{
|
|
|
|
if (m_sample.valid)
|
|
|
|
{
|
|
|
|
// We have good data. Return it to the caller
|
|
|
|
|
|
|
|
sample = m_sample;
|
|
|
|
m_sample.valid = false;
|
|
|
|
|
|
|
|
sem_post(&m_exclSem);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// No.. What happened?
|
|
|
|
|
|
|
|
sem_post(&m_exclSem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Accumulate and average touch sample data
|
|
|
|
*
|
|
|
|
* @param average. When the averaged data is available, return it here
|
|
|
|
* @return True: Average data is available; False: Need to collect more samples
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_AVERAGE
|
|
|
|
bool CCalibration::averageSamples(struct nxgl_point_s &average)
|
|
|
|
{
|
|
|
|
// Have we started collecting sample data? */
|
|
|
|
|
|
|
|
// Save the sample data
|
|
|
|
|
|
|
|
twminfo("Sample %d: Touch x: %d y: %d\n",
|
|
|
|
m_nsamples + 1, m_touchPos.x, m_touchPos.y);
|
|
|
|
|
|
|
|
m_sampleData[m_nsamples].x = m_touchPos.x;
|
|
|
|
m_sampleData[m_nsamples].y = m_touchPos.y;
|
|
|
|
m_nsamples++;
|
|
|
|
|
|
|
|
// Have all of the samples been collected?
|
|
|
|
|
|
|
|
if (m_nsamples < CONFIG_TWM4NX_CALIBRATION_NSAMPLES)
|
|
|
|
{
|
|
|
|
// Not yet... return false
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Yes.. we have all of the samples
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_NSAMPLES
|
|
|
|
// Discard the smallest X and Y values
|
|
|
|
|
|
|
|
int xValue = INT_MAX;
|
|
|
|
int xIndex = -1;
|
|
|
|
|
|
|
|
int yValue = INT_MAX;
|
|
|
|
int yIndex = -1;
|
|
|
|
|
|
|
|
for (int i = 0; i < CONFIG_TWM4NX_CALIBRATION_NSAMPLES; i++)
|
|
|
|
{
|
|
|
|
if ((int)m_sampleData[i].x < xValue)
|
|
|
|
{
|
|
|
|
xValue = (int)m_sampleData[i].x;
|
|
|
|
xIndex = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((int)m_sampleData[i].y < yValue)
|
|
|
|
{
|
|
|
|
yValue = (int)m_sampleData[i].y;
|
|
|
|
yIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_sampleData[xIndex].x = m_sampleData[CONFIG_TWM4NX_CALIBRATION_NSAMPLES-1].x;
|
|
|
|
m_sampleData[yIndex].y = m_sampleData[CONFIG_TWM4NX_CALIBRATION_NSAMPLES-1].y;
|
|
|
|
|
|
|
|
// Discard the largest X and Y values
|
|
|
|
|
|
|
|
xValue = -1;
|
|
|
|
xIndex = -1;
|
|
|
|
|
|
|
|
yValue = -1;
|
|
|
|
yIndex = -1;
|
|
|
|
|
|
|
|
for (int i = 0; i < CONFIG_TWM4NX_CALIBRATION_NSAMPLES-1; i++)
|
|
|
|
{
|
|
|
|
if ((int)m_sampleData[i].x > xValue)
|
|
|
|
{
|
|
|
|
xValue = (int)m_sampleData[i].x;
|
|
|
|
xIndex = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((int)m_sampleData[i].y > yValue)
|
|
|
|
{
|
|
|
|
yValue = (int)m_sampleData[i].y;
|
|
|
|
yIndex = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_sampleData[xIndex].x = m_sampleData[CONFIG_TWM4NX_CALIBRATION_NSAMPLES-2].x;
|
|
|
|
m_sampleData[yIndex].y = m_sampleData[CONFIG_TWM4NX_CALIBRATION_NSAMPLES-2].y;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if TWM4NX_CALIBRATION_NAVERAGE > 1
|
|
|
|
// Calculate the average of the remaining values
|
|
|
|
|
|
|
|
long xaccum = 0;
|
|
|
|
long yaccum = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < TWM4NX_CALIBRATION_NAVERAGE; i++)
|
|
|
|
{
|
|
|
|
xaccum += m_sampleData[i].x;
|
|
|
|
yaccum += m_sampleData[i].y;
|
|
|
|
}
|
|
|
|
|
|
|
|
average.x = xaccum / TWM4NX_CALIBRATION_NAVERAGE;
|
|
|
|
average.y = yaccum / TWM4NX_CALIBRATION_NAVERAGE;
|
|
|
|
#else
|
|
|
|
average.x = m_sampleData[0].x;
|
|
|
|
average.y = m_sampleData[0].y;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
twminfo("Average: Touch x: %d y: %d\n", average.x, average.y);
|
|
|
|
m_nsamples = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is the calibration state machine. It is called initially and then
|
|
|
|
* as new touchscreen data is received.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void CCalibration::stateMachine(void)
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twminfo("Old m_calphase=%d\n", m_calphase);
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_AVERAGE
|
|
|
|
// Are we collecting samples?
|
|
|
|
|
|
|
|
struct nxgl_point_s average;
|
|
|
|
|
|
|
|
switch (m_calphase)
|
|
|
|
{
|
|
|
|
case CALPHASE_UPPER_LEFT:
|
|
|
|
case CALPHASE_UPPER_RIGHT:
|
|
|
|
case CALPHASE_LOWER_RIGHT:
|
|
|
|
case CALPHASE_LOWER_LEFT:
|
|
|
|
{
|
|
|
|
// Yes... Have all of the samples been collected?
|
|
|
|
|
|
|
|
if (!averageSamples(average))
|
|
|
|
{
|
|
|
|
// Not yet... Show the calibration screen again with the circle
|
|
|
|
// in the normal color and an instruction to touch the circle again.
|
|
|
|
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_CIRCLECOLOR;
|
|
|
|
m_text->setText(GAgainMsg);
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// And wait for the next touch
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// No... we are not collecting data now
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Get the size of the fullscreen window
|
|
|
|
|
|
|
|
struct nxgl_size_s windowSize;
|
|
|
|
if (!m_nxWin->getSize(&windowSize))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (m_calphase)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case CALPHASE_NOT_STARTED:
|
|
|
|
{
|
|
|
|
// Clear the entire screen
|
|
|
|
// Get the widget control associated with the full screen window
|
|
|
|
|
|
|
|
NXWidgets::CWidgetControl *control = m_nxWin->getWidgetControl();
|
|
|
|
|
|
|
|
// Get the CCGraphicsPort instance for this window
|
|
|
|
|
|
|
|
NXWidgets::CGraphicsPort *port = control->getGraphicsPort();
|
|
|
|
|
|
|
|
// Fill the entire window with the background color
|
|
|
|
|
|
|
|
port->drawFilledRect(0, 0, windowSize.w, windowSize.h,
|
|
|
|
CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR);
|
|
|
|
|
|
|
|
// Then draw the first calibration screen
|
|
|
|
|
|
|
|
m_screenInfo.pos.x = CALIBRATION_LEFTX;
|
|
|
|
m_screenInfo.pos.y = CALIBRATION_TOPY;
|
|
|
|
m_screenInfo.lineColor = CONFIG_TWM4NX_CALIBRATION_LINECOLOR;
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_CIRCLECOLOR;
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
m_text->setText(GTouchMsg);
|
|
|
|
#endif
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// Then set up the current state
|
|
|
|
|
|
|
|
m_calphase = CALPHASE_UPPER_LEFT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALPHASE_UPPER_LEFT:
|
|
|
|
{
|
|
|
|
// A touch has been received while in the CALPHASE_UPPER_LEFT state.
|
|
|
|
// Save the touch data and set up the next calibration display
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_AVERAGE
|
|
|
|
m_calibData[CALIB_UPPER_LEFT_INDEX].x = average.x;
|
|
|
|
m_calibData[CALIB_UPPER_LEFT_INDEX].y = average.y;
|
|
|
|
#else
|
|
|
|
m_calibData[CALIB_UPPER_LEFT_INDEX].x = m_touchPos.x;
|
|
|
|
m_calibData[CALIB_UPPER_LEFT_INDEX].y = m_touchPos.y;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Clear the previous screen by re-drawing it using the background
|
|
|
|
// color. That is much faster than clearing the whole display
|
|
|
|
|
|
|
|
m_screenInfo.lineColor = CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR;
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR;
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// Then draw the next calibration screen
|
|
|
|
|
|
|
|
m_screenInfo.pos.x = CALIBRATION_RIGHTX;
|
|
|
|
m_screenInfo.pos.y = CALIBRATION_TOPY;
|
|
|
|
m_screenInfo.lineColor = CONFIG_TWM4NX_CALIBRATION_LINECOLOR;
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_CIRCLECOLOR;
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
m_text->setText(GTouchMsg);
|
|
|
|
#endif
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// Then set up the current state
|
|
|
|
|
|
|
|
m_calphase = CALPHASE_UPPER_RIGHT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALPHASE_UPPER_RIGHT:
|
|
|
|
{
|
|
|
|
// A touch has been received while in the CALPHASE_UPPER_RIGHT state.
|
|
|
|
// Save the touch data and set up the next calibration display
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_AVERAGE
|
|
|
|
m_calibData[CALIB_UPPER_RIGHT_INDEX].x = average.x;
|
|
|
|
m_calibData[CALIB_UPPER_RIGHT_INDEX].y = average.y;
|
|
|
|
#else
|
|
|
|
m_calibData[CALIB_UPPER_RIGHT_INDEX].x = m_touchPos.x;
|
|
|
|
m_calibData[CALIB_UPPER_RIGHT_INDEX].y = m_touchPos.y;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Clear the previous screen by re-drawing it using the backgro9und
|
|
|
|
// color. That is much faster than clearing the whole display
|
|
|
|
|
|
|
|
m_screenInfo.lineColor = CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR;
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR;
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// Then draw the next calibration screen
|
|
|
|
|
|
|
|
m_screenInfo.pos.x = CALIBRATION_RIGHTX;
|
|
|
|
m_screenInfo.pos.y = CALIBRATION_BOTTOMY;
|
|
|
|
m_screenInfo.lineColor = CONFIG_TWM4NX_CALIBRATION_LINECOLOR;
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_CIRCLECOLOR;
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
m_text->setText(GTouchMsg);
|
|
|
|
#endif
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// Then set up the current state
|
|
|
|
|
|
|
|
m_calphase = CALPHASE_LOWER_RIGHT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALPHASE_LOWER_RIGHT:
|
|
|
|
{
|
|
|
|
// A touch has been received while in the CALPHASE_LOWER_RIGHT state.
|
|
|
|
// Save the touch data and set up the next calibration display
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_AVERAGE
|
|
|
|
m_calibData[CALIB_LOWER_RIGHT_INDEX].x = average.x;
|
|
|
|
m_calibData[CALIB_LOWER_RIGHT_INDEX].y = average.y;
|
|
|
|
#else
|
|
|
|
m_calibData[CALIB_LOWER_RIGHT_INDEX].x = m_touchPos.x;
|
|
|
|
m_calibData[CALIB_LOWER_RIGHT_INDEX].y = m_touchPos.y;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Clear the previous screen by re-drawing it using the backgro9und
|
|
|
|
// color. That is much faster than clearing the whole display
|
|
|
|
|
|
|
|
m_screenInfo.lineColor = CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR;
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR;
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// Then draw the next calibration screen
|
|
|
|
|
|
|
|
m_screenInfo.pos.x = CALIBRATION_LEFTX;
|
|
|
|
m_screenInfo.pos.y = CALIBRATION_BOTTOMY;
|
|
|
|
m_screenInfo.lineColor = CONFIG_TWM4NX_CALIBRATION_LINECOLOR;
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_CIRCLECOLOR;
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
m_text->setText(GTouchMsg);
|
|
|
|
#endif
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// Then set up the current state
|
|
|
|
|
|
|
|
m_calphase = CALPHASE_LOWER_LEFT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALPHASE_LOWER_LEFT:
|
|
|
|
{
|
|
|
|
// A touch has been received while in the CALPHASE_LOWER_LEFT state.
|
|
|
|
// Save the touch data and set up the next calibration display
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_AVERAGE
|
|
|
|
m_calibData[CALIB_LOWER_LEFT_INDEX].x = average.x;
|
|
|
|
m_calibData[CALIB_LOWER_LEFT_INDEX].y = average.y;
|
|
|
|
#else
|
|
|
|
m_calibData[CALIB_LOWER_LEFT_INDEX].x = m_touchPos.x;
|
|
|
|
m_calibData[CALIB_LOWER_LEFT_INDEX].y = m_touchPos.y;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Clear the previous screen by re-drawing it using the background
|
|
|
|
// color. That is much faster than clearing the whole display
|
|
|
|
|
|
|
|
m_screenInfo.lineColor = CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR;
|
|
|
|
m_screenInfo.circleFillColor = CONFIG_TWM4NX_CALIBRATION_BACKGROUNDCOLOR;
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
m_text->setText(GTouchMsg);
|
|
|
|
#endif
|
|
|
|
showCalibration();
|
|
|
|
|
|
|
|
// Inform any waiter that calibration is complete
|
|
|
|
|
|
|
|
m_calphase = CALPHASE_COMPLETE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALPHASE_COMPLETE:
|
|
|
|
// Might happen... do nothing if it does
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
twminfo("New m_calphase=%d Screen x: %d y: %d\n",
|
|
|
|
m_calphase, m_screenInfo.pos.x, m_screenInfo.pos.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Presents the next calibration screen
|
|
|
|
*
|
|
|
|
* @param screenInfo Describes the next calibration screen
|
|
|
|
*/
|
|
|
|
|
|
|
|
void CCalibration::showCalibration(void)
|
|
|
|
{
|
|
|
|
// Get the widget control associated with the full screen window
|
|
|
|
|
|
|
|
NXWidgets::CWidgetControl *control = m_nxWin->getWidgetControl();
|
|
|
|
|
|
|
|
// Get the CCGraphicsPort instance for this window
|
|
|
|
|
|
|
|
NXWidgets::CGraphicsPort *port = control->getGraphicsPort();
|
|
|
|
|
2019-05-20 20:06:06 +02:00
|
|
|
// Get the size of the full screen window
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
struct nxgl_size_s windowSize;
|
|
|
|
if (!m_nxWin->getSize(&windowSize))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the circle at the center of the touch position
|
|
|
|
|
|
|
|
port->drawFilledCircle(&m_screenInfo.pos, CALIBRATION_CIRCLE_RADIUS,
|
|
|
|
m_screenInfo.circleFillColor);
|
|
|
|
|
|
|
|
// Draw horizontal line
|
|
|
|
|
|
|
|
port->drawFilledRect(0, m_screenInfo.pos.y, windowSize.w, CALIBRATION_LINE_THICKNESS,
|
|
|
|
m_screenInfo.lineColor);
|
|
|
|
|
|
|
|
// Draw vertical line
|
|
|
|
|
|
|
|
port->drawFilledRect(m_screenInfo.pos.x, 0, CALIBRATION_LINE_THICKNESS, windowSize.h,
|
|
|
|
m_screenInfo.lineColor);
|
|
|
|
|
|
|
|
// Show the touchscreen message
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
m_text->enableDrawing();
|
|
|
|
m_text->redraw();
|
|
|
|
m_text->disableDrawing();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finish calibration steps and provide the calibration data to the
|
|
|
|
* touchscreen driver.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void CCalibration::finishCalibration(void)
|
|
|
|
{
|
|
|
|
// Hide the window
|
|
|
|
|
|
|
|
m_nxWin->hide();
|
|
|
|
|
|
|
|
// Did we finish calibration successfully?
|
|
|
|
|
|
|
|
if (m_calphase == CALPHASE_COMPLETE)
|
|
|
|
{
|
|
|
|
// Yes... Get the final Calibration data
|
|
|
|
|
|
|
|
struct SCalibrationData caldata;
|
|
|
|
if (createCalibrationData(caldata))
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_TWM4NX_TOUCHSCREEN_CONFIGDATA
|
|
|
|
// Save the new calibration data. The saved calibration
|
|
|
|
// data may be used to avoided recalibrating in the future.
|
|
|
|
|
|
|
|
int ret = platform_setconfig(CONFIGDATA_TSCALIBRATION, 0,
|
|
|
|
(FAR const uint8_t *)&caldata,
|
|
|
|
sizeof(struct SCalibrationData));
|
|
|
|
if (ret != 0)
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmerr("ERROR: Failed to save calibration data\n");
|
2019-05-20 19:01:46 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// And provide the calibration data to CInput, enabling
|
|
|
|
// touchscreen processing
|
|
|
|
|
|
|
|
FAR CInput *cinput = m_twm4nx->getInput();
|
|
|
|
|
|
|
|
cinput->setCalibrationData(caldata);
|
|
|
|
cinput->enableCalibration(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// And set the terminated stated
|
|
|
|
|
|
|
|
m_calthread = CALTHREAD_TERMINATED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop the application.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void CCalibration::stop(void)
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twminfo("Stopping calibration: m_calthread=%d\n", (int)m_calthread);
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
// Was the calibration thread created?
|
|
|
|
|
|
|
|
if (m_thread != 0)
|
|
|
|
{
|
|
|
|
// Is the calibration thread running?
|
|
|
|
|
|
|
|
if (m_calthread == CALTHREAD_RUNNING)
|
|
|
|
{
|
|
|
|
// The main thread is stuck waiting for the next touchscreen input...
|
|
|
|
// We can signal that we would like the thread to stop, but we will be
|
|
|
|
// stuck here until the next touch
|
|
|
|
|
|
|
|
m_calthread = CALTHREAD_STOPREQUESTED;
|
|
|
|
|
|
|
|
// Try to wake up the calibration thread so that it will see our
|
|
|
|
// termination request
|
|
|
|
|
2019-06-01 21:00:02 +02:00
|
|
|
twminfo("Stopping calibration: m_calthread=%d\n", (int)m_calthread);
|
2020-01-02 13:09:50 +01:00
|
|
|
pthread_kill(m_thread, CONFIG_TWM4NX_CALIBRATION_SIGNO);
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
// Wait for the calibration thread to exit
|
|
|
|
|
|
|
|
FAR pthread_addr_t value;
|
2020-01-02 13:09:50 +01:00
|
|
|
pthread_join(m_thread, &value);
|
2019-05-20 19:01:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy the application and free all of its resources. This method
|
|
|
|
* will initiate blocking of messages from the NX server. The server
|
|
|
|
* will flush the window message queue and reply with the blocked
|
|
|
|
* message. When the block message is received by CWindowEvent,
|
|
|
|
* it will send the destroy message to the calibration window task which
|
|
|
|
* will, finally, safely delete the application.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void CCalibration::destroy(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_MESSAGES
|
|
|
|
// Destroy widgets
|
|
|
|
|
|
|
|
destroyWidgets();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Close the calibration window... but not yet. Send the blocked message.
|
|
|
|
// The actual termination will no occur until the NX server drains all of
|
|
|
|
// the message events. We will get the EVENT_WINDOW_DELETE event at that
|
|
|
|
// point
|
|
|
|
|
|
|
|
NXWidgets::CWidgetControl *control = m_nxWin->getWidgetControl();
|
|
|
|
nx_block(control->getWindowHandle(), (FAR void *)m_nxWin);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given the raw touch data collected by the calibration thread, create the
|
|
|
|
* massaged calibration data needed by CTouchscreen.
|
|
|
|
*
|
|
|
|
* @param data. A reference to the location to save the calibration data
|
|
|
|
* @return True if the calibration data was successfully created.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibration::createCalibrationData(struct SCalibrationData &data)
|
|
|
|
{
|
|
|
|
// Get the size of the fullscreen window
|
|
|
|
|
|
|
|
struct nxgl_size_s windowSize;
|
|
|
|
if (!m_nxWin->getSize(&windowSize))
|
|
|
|
{
|
2019-06-01 21:00:02 +02:00
|
|
|
twmerr("ERROR: NXWidgets::INxWindow::getSize failed\n");
|
2019-05-20 19:01:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_TWM4NX_CALIBRATION_ANISOTROPIC
|
|
|
|
// X lines:
|
|
|
|
//
|
|
|
|
// x2 = slope*y1 + offset
|
|
|
|
//
|
|
|
|
// slope = (bottomY - topY) / (bottomX - topX)
|
|
|
|
// offset = (topY - topX * slope)
|
|
|
|
|
|
|
|
float topX = (float)m_calibData[CALIB_UPPER_LEFT_INDEX].x;
|
|
|
|
float bottomX = (float)m_calibData[CALIB_LOWER_LEFT_INDEX].x;
|
|
|
|
|
|
|
|
float topY = (float)m_calibData[CALIB_UPPER_LEFT_INDEX].y;
|
|
|
|
float bottomY = (float)m_calibData[CALIB_LOWER_LEFT_INDEX].y;
|
|
|
|
|
|
|
|
data.left.slope = (bottomX - topX) / (bottomY - topY);
|
|
|
|
data.left.offset = topX - topY * data.left.slope;
|
|
|
|
|
|
|
|
twminfo("Left slope: %6.2f offset: %6.2f\n", data.left.slope, data.left.offset);
|
|
|
|
|
|
|
|
topX = (float)m_calibData[CALIB_UPPER_RIGHT_INDEX].x;
|
|
|
|
bottomX = (float)m_calibData[CALIB_LOWER_RIGHT_INDEX].x;
|
|
|
|
|
|
|
|
topY = (float)m_calibData[CALIB_UPPER_RIGHT_INDEX].y;
|
|
|
|
bottomY = (float)m_calibData[CALIB_LOWER_RIGHT_INDEX].y;
|
|
|
|
|
|
|
|
data.right.slope = (bottomX - topX) / (bottomY - topY);
|
|
|
|
data.right.offset = topX - topY * data.right.slope;
|
|
|
|
|
|
|
|
twminfo("Right slope: %6.2f offset: %6.2f\n", data.right.slope, data.right.offset);
|
|
|
|
|
|
|
|
// Y lines:
|
|
|
|
//
|
|
|
|
// y2 = slope*x1 + offset
|
|
|
|
//
|
|
|
|
// slope = (rightX - topX) / (rightY - leftY)
|
|
|
|
// offset = (topX - leftY * slope)
|
|
|
|
|
|
|
|
float leftX = (float)m_calibData[CALIB_UPPER_LEFT_INDEX].x;
|
|
|
|
float rightX = (float)m_calibData[CALIB_UPPER_RIGHT_INDEX].x;
|
|
|
|
|
|
|
|
float leftY = (float)m_calibData[CALIB_UPPER_LEFT_INDEX].y;
|
|
|
|
float rightY = (float)m_calibData[CALIB_UPPER_RIGHT_INDEX].y;
|
|
|
|
|
|
|
|
data.top.slope = (rightY - leftY) / (rightX - leftX);
|
|
|
|
data.top.offset = leftY - leftX * data.top.slope;
|
|
|
|
|
|
|
|
twminfo("Top slope: %6.2f offset: %6.2f\n", data.top.slope, data.top.offset);
|
|
|
|
|
|
|
|
leftX = (float)m_calibData[CALIB_LOWER_LEFT_INDEX].x;
|
|
|
|
rightX = (float)m_calibData[CALIB_LOWER_RIGHT_INDEX].x;
|
|
|
|
|
|
|
|
leftY = (float)m_calibData[CALIB_LOWER_LEFT_INDEX].y;
|
|
|
|
rightY = (float)m_calibData[CALIB_LOWER_RIGHT_INDEX].y;
|
|
|
|
|
|
|
|
data.bottom.slope = (rightY - leftY) / (rightX - leftX);
|
|
|
|
data.bottom.offset = leftY - leftX * data.bottom.slope;
|
|
|
|
|
|
|
|
twminfo("Bottom slope: %6.2f offset: %6.2f\n", data.bottom.slope, data.bottom.offset);
|
|
|
|
|
|
|
|
// Save also the calibration screen positions
|
|
|
|
|
|
|
|
data.leftX = CALIBRATION_LEFTX;
|
|
|
|
data.rightX = CALIBRATION_RIGHTX;
|
|
|
|
data.topY = CALIBRATION_TOPY;
|
|
|
|
data.bottomY = CALIBRATION_BOTTOMY;
|
|
|
|
#else
|
|
|
|
// Calculate the calibration parameters
|
|
|
|
//
|
|
|
|
// (scaledX - LEFTX) / (rawX - leftX) = (RIGHTX - LEFTX) / (rightX - leftX)
|
|
|
|
// scaledX = (rawX - leftX) * (RIGHTX - LEFTX) / (rightX - leftX) + LEFTX
|
|
|
|
// = rawX * xSlope + (LEFTX - leftX * xSlope)
|
|
|
|
// = rawX * xSlope + xOffset
|
|
|
|
//
|
|
|
|
// where:
|
|
|
|
// xSlope = (RIGHTX - LEFTX) / (rightX - leftX)
|
|
|
|
// xOffset = (LEFTX - leftX * xSlope)
|
|
|
|
|
|
|
|
b16_t leftX = (m_calibData[CALIB_UPPER_LEFT_INDEX].x +
|
|
|
|
m_calibData[CALIB_LOWER_LEFT_INDEX].x) << 15;
|
|
|
|
b16_t rightX = (m_calibData[CALIB_UPPER_RIGHT_INDEX].x +
|
|
|
|
m_calibData[CALIB_LOWER_RIGHT_INDEX].x) << 15;
|
|
|
|
|
|
|
|
data.xSlope = b16divb16(itob16(CALIBRATION_RIGHTX - CALIBRATION_LEFTX), (rightX - leftX));
|
|
|
|
data.xOffset = itob16(CALIBRATION_LEFTX) - b16mulb16(leftX, data.xSlope);
|
|
|
|
|
2020-11-28 01:47:19 +01:00
|
|
|
twminfo("New xSlope: %08" PRIx32 " xOffset: %08" PRIx32 "\n",
|
|
|
|
data.xSlope, data.xOffset);
|
2019-05-20 19:01:46 +02:00
|
|
|
|
|
|
|
// Similarly for Y
|
|
|
|
//
|
|
|
|
// (scaledY - TOPY) / (rawY - topY) = (BOTTOMY - TOPY) / (bottomY - topY)
|
|
|
|
// scaledY = (rawY - topY) * (BOTTOMY - TOPY) / (bottomY - topY) + TOPY
|
|
|
|
// = rawY * ySlope + (TOPY - topY * ySlope)
|
|
|
|
// = rawY * ySlope + yOffset
|
|
|
|
//
|
|
|
|
// where:
|
|
|
|
// ySlope = (BOTTOMY - TOPY) / (bottomY - topY)
|
|
|
|
// yOffset = (TOPY - topY * ySlope)
|
|
|
|
|
|
|
|
b16_t topY = (m_calibData[CALIB_UPPER_LEFT_INDEX].y +
|
|
|
|
m_calibData[CALIB_UPPER_RIGHT_INDEX].y) << 15;
|
|
|
|
b16_t bottomY = (m_calibData[CALIB_LOWER_LEFT_INDEX].y +
|
|
|
|
m_calibData[CALIB_LOWER_RIGHT_INDEX].y) << 15;
|
|
|
|
|
|
|
|
data.ySlope = b16divb16(itob16(CALIBRATION_BOTTOMY - CALIBRATION_TOPY), (bottomY - topY));
|
|
|
|
data.yOffset = itob16(CALIBRATION_TOPY) - b16mulb16(topY, data.ySlope);
|
|
|
|
|
2020-11-28 01:47:19 +01:00
|
|
|
twminfo("New ySlope: %08" PRIx32 " yOffset: %08" PRIx32 "\n",
|
|
|
|
data.ySlope, data.yOffset);
|
2019-05-20 19:01:46 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// CCalibrationFactory Method Implementations
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* CCalibrationFactory Initializer. Performs parts of the instance
|
|
|
|
* construction that may fail. In this implementation, it will
|
|
|
|
* initialize the NSH library and register an menu item in the
|
|
|
|
* Main Menu.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibrationFactory::initialize(FAR CTwm4Nx *twm4nx)
|
|
|
|
{
|
|
|
|
// Save the session instance
|
|
|
|
|
|
|
|
m_twm4nx = twm4nx;
|
|
|
|
|
|
|
|
// Register an entry with the Main menu. When selected, this will
|
|
|
|
// create a new instance of the touchscreen calibration.
|
|
|
|
|
|
|
|
FAR CMainMenu *cmain = twm4nx->getMainMenu();
|
2019-05-20 20:06:06 +02:00
|
|
|
bool success = cmain->addApplication(this);
|
|
|
|
|
|
|
|
#ifdef CONFIG_TW4NX_STARTUP_CALIB
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
// Show the calibration thread at start-up (and later if selected
|
|
|
|
// from the Main Menu)
|
|
|
|
|
|
|
|
startFunction();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return success;
|
2019-05-20 19:01:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle CCalibrationFactory events. This overrides a method from
|
|
|
|
* CTwm4NXEvent
|
|
|
|
*
|
|
|
|
* @param eventmsg. The received NxWidget WINDOW event message.
|
|
|
|
* @return True if the message was properly handled. false is
|
|
|
|
* return on any failure.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibrationFactory::event(FAR struct SEventMsg *eventmsg)
|
|
|
|
{
|
|
|
|
bool success = true;
|
|
|
|
|
|
|
|
switch (eventmsg->eventID)
|
|
|
|
{
|
|
|
|
case EVENT_CALIB_START: // Main menu selection
|
|
|
|
startFunction(); // Create a new CCalibration instance
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
success = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create and start a new instance of CCalibration.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool CCalibrationFactory::startFunction(void)
|
|
|
|
{
|
|
|
|
// Instantiate the Nxterm application, providing only the session session
|
|
|
|
// instance to the constructor
|
|
|
|
|
|
|
|
CCalibration *calib = new CCalibration(m_twm4nx);
|
|
|
|
if (!calib)
|
|
|
|
{
|
|
|
|
twmerr("ERROR: Failed to instantiate CCalibration\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the Calibration application
|
|
|
|
|
|
|
|
if (!calib->initialize())
|
|
|
|
{
|
|
|
|
twmerr("ERROR: Failed to initialize CCalibration instance\n");
|
|
|
|
delete calib;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start the Calibration application instance
|
|
|
|
|
|
|
|
if (!calib->run())
|
|
|
|
{
|
|
|
|
twmerr("ERROR: Failed to start the Calibration application\n");
|
|
|
|
delete calib;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|