978 lines
27 KiB
C++
978 lines
27 KiB
C++
/****************************************************************************
|
|
* apps/graphics/NxWidgets/nxwm/src/cinput.cxx
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <cerrno>
|
|
|
|
#include <fcntl.h>
|
|
#include <sched.h>
|
|
#include <poll.h>
|
|
#include <pthread.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
|
|
#include <nuttx/semaphore.h>
|
|
#include <nuttx/nx/nxglib.h>
|
|
#include <nuttx/input/mouse.h>
|
|
|
|
#ifdef CONFIG_TWM4NX_MOUSE
|
|
# include <nuttx/nx/nxcursor.h>
|
|
# include "graphics/twm4nx/twm4nx_cursor.hxx"
|
|
#else
|
|
# include <nuttx/input/touchscreen.h>
|
|
#endif
|
|
|
|
#include "graphics/twm4nx/twm4nx_config.hxx"
|
|
#include "graphics/twm4nx/ctwm4nx.hxx"
|
|
#include "graphics/twm4nx/cinput.hxx"
|
|
|
|
/****************************************************************************
|
|
* Pre-Processor Definitions
|
|
****************************************************************************/
|
|
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
# define KBD_INDEX 0
|
|
# ifndef CONFIG_TWM4NX_NOMOUSE
|
|
# define MOUSE_INDEX 1
|
|
# define NINPUT_DEVICES 2
|
|
# else
|
|
# define NINPUT_DEVICES 1
|
|
# endif
|
|
#else
|
|
# define MOUSE_INDEX 0
|
|
# define NINPUT_DEVICES 1
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
* CInput Method Implementations
|
|
****************************************************************************/
|
|
|
|
using namespace Twm4Nx;
|
|
|
|
/**
|
|
* CInput Constructor
|
|
*
|
|
* @param twm4nx. An instance of the NX server. This will be needed for
|
|
* injecting keyboard data.
|
|
*/
|
|
|
|
CInput::CInput(CTwm4Nx *twm4nx)
|
|
{
|
|
// Session
|
|
|
|
m_twm4nx = twm4nx; // Save the NX server
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
m_kbdFd = -1; // Keyboard driver is not opened
|
|
#endif
|
|
#ifndef CONFIG_TWM4NX_NOMOUSE
|
|
m_mouseFd = -1; // Mouse/touchscreen driver is not opened
|
|
#endif
|
|
|
|
// Listener
|
|
|
|
m_state = LISTENER_NOTRUNNING; // The listener thread is not running yet
|
|
|
|
// Initialize the semaphore used to synchronize with the listener thread
|
|
|
|
sem_init(&m_waitSem, 0, 0);
|
|
|
|
#ifdef CONFIG_TWM4NX_TOUCHSCREEN
|
|
// Calibration
|
|
|
|
m_calib = false; // Use raw touches until calibrated
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* CInput Destructor
|
|
*/
|
|
|
|
CInput::~CInput(void)
|
|
{
|
|
// Stop the listener thread
|
|
|
|
m_state = LISTENER_STOPREQUESTED;
|
|
|
|
// Wake up the listener thread so that it will use our buffer
|
|
// to receive data
|
|
// REVISIT: Need wait here for the listener thread to terminate
|
|
|
|
pthread_kill(m_thread, CONFIG_TWM4NX_INPUT_SIGNO);
|
|
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
// Close the keyboard device
|
|
|
|
if (m_kbdFd >= 0)
|
|
{
|
|
close(m_kbdFd);
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
// Close the mouse/touchscreen device
|
|
|
|
if (m_mouseFd >= 0)
|
|
{
|
|
close(m_mouseFd);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Start the keyboard listener thread.
|
|
*
|
|
* @return True if the keyboard listener thread was correctly started.
|
|
*/
|
|
|
|
bool CInput::start(void)
|
|
{
|
|
pthread_attr_t attr;
|
|
|
|
twminfo("Starting listener\n");
|
|
|
|
// Start a separate thread to listen for keyboard events
|
|
|
|
pthread_attr_init(&attr);
|
|
|
|
struct sched_param param;
|
|
param.sched_priority = CONFIG_TWM4NX_INPUT_LISTENERPRIO;
|
|
pthread_attr_setschedparam(&attr, ¶m);
|
|
|
|
pthread_attr_setstacksize(&attr, CONFIG_TWM4NX_INPUT_LISTENERSTACK);
|
|
|
|
m_state = LISTENER_STARTED; // The listener thread has been started, but is not yet running
|
|
|
|
int ret = pthread_create(&m_thread, &attr, listener, (FAR void *)this);
|
|
if (ret != 0)
|
|
{
|
|
twmerr("ERROR: CInput::start: pthread_create failed: %d\n", ret);
|
|
return false;
|
|
}
|
|
|
|
// Detach from the thread
|
|
|
|
pthread_detach(m_thread);
|
|
|
|
// Don't return until we are sure that the listener thread is running
|
|
// (or until it reports an error).
|
|
|
|
while (m_state == LISTENER_STARTED)
|
|
{
|
|
// Wait for the listener thread to wake us up when we really
|
|
// are connected.
|
|
|
|
sem_wait(&m_waitSem);
|
|
}
|
|
|
|
// Then return true only if the listener thread reported successful
|
|
// initialization.
|
|
|
|
twminfo("Listener m_state=%d\n", (int)m_state);
|
|
return m_state == LISTENER_RUNNING;
|
|
}
|
|
|
|
/**
|
|
* Open the keyboard device. Not very interesting for the case of
|
|
* standard device but much more interesting for a USB keyboard device
|
|
* that may disappear when the keyboard is disconnect but later reappear
|
|
* when the keyboard is reconnected. In this case, this function will
|
|
* not return until the keyboard device was successfully opened (or
|
|
* until an irrecoverable error occurs.
|
|
*
|
|
* Opens the keyboard device specified by CONFIG_TWM4NX_KEYBOARD_DEVPATH.
|
|
*
|
|
* @return On success, then method returns a valid file descriptor. A
|
|
* negated errno value is returned if an irrecoverable error occurs.
|
|
*/
|
|
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
int CInput::keyboardOpen(void)
|
|
{
|
|
int fd;
|
|
|
|
// Loop until we have successfully opened the USB keyboard (or until some
|
|
// irrecoverable error occurs).
|
|
|
|
do
|
|
{
|
|
// Try to open the keyboard device non-blocking.
|
|
|
|
fd = open(CONFIG_TWM4NX_KEYBOARD_DEVPATH, O_RDONLY | O_NONBLOCK);
|
|
if (fd < 0)
|
|
{
|
|
int errcode = errno;
|
|
DEBUGASSERT(errcode > 0);
|
|
|
|
// EINTR should be ignored because it is not really an error at
|
|
// all. We should retry immediately
|
|
|
|
if (errcode != EINTR)
|
|
{
|
|
#ifdef CONFIG_TWM4NX_KEYBOARD_USBHOST
|
|
// ENOENT means that the USB keyboard is not yet connected and,
|
|
// hence, has no entry under /dev. If the USB driver still
|
|
// exists under /dev (because other threads still have the driver
|
|
// open), then we might also get ENODEV.
|
|
|
|
if (errcode == ENOENT || errcode == ENODEV)
|
|
{
|
|
// REVIST: Can we inject a constant string here to let the
|
|
// user know that we are waiting for a USB keyboard to be
|
|
// connected?
|
|
|
|
// Sleep a bit and try again
|
|
|
|
twminfo("WAITING for a USB keyboard\n");
|
|
sleep(2);
|
|
}
|
|
|
|
// Anything else would be really bad.
|
|
|
|
else
|
|
#endif
|
|
{
|
|
// Let the top-level logic decide what it wants to do
|
|
// about all really bad things
|
|
|
|
twmerr("ERROR: Failed to open %s for reading: %d\n",
|
|
CONFIG_TWM4NX_KEYBOARD_DEVPATH, errcode);
|
|
return -errcode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (fd < 0);
|
|
|
|
return fd;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Open the mouse/touchscreen input devices. Not very interesting for the
|
|
* case of standard character device but much more interesting for
|
|
* USB mouse devices that may disappear when disconnected but later
|
|
* reappear when reconnected. In this case, this function will
|
|
* not return until the input device was successfully opened (or
|
|
* until an irrecoverable error occurs).
|
|
*
|
|
* Opens the mouse input device specified by CONFIG_TWM4NX_MOUSE_DEVPATH.
|
|
*
|
|
* @return On success, then method returns a valid file descriptor. A
|
|
* negated errno value is returned if an irrecoverable error occurs.
|
|
*/
|
|
|
|
#ifndef CONFIG_TWM4NX_NOMOUSE
|
|
inline int CInput::mouseOpen(void)
|
|
{
|
|
int fd;
|
|
|
|
// Loop until we have successfully opened the USB mouse (or until some
|
|
// irrecoverable error occurs).
|
|
|
|
do
|
|
{
|
|
// Try to open the mouse device non-blocking
|
|
|
|
fd = open(CONFIG_TWM4NX_MOUSE_DEVPATH, O_RDONLY | O_NONBLOCK);
|
|
if (fd < 0)
|
|
{
|
|
int errcode = errno;
|
|
DEBUGASSERT(errcode > 0);
|
|
|
|
// EINTR should be ignored because it is not really an error at
|
|
// all. We should retry immediately
|
|
|
|
if (errcode != EINTR)
|
|
{
|
|
#ifdef CONFIG_TWM4NX_MOUSE_USBHOST
|
|
// ENOENT means that the USB mouse is not yet connected and,
|
|
// hence, has no entry under /dev. If the USB driver still
|
|
// exists under /dev (because other threads still have the driver
|
|
// open), then we might also get ENODEV.
|
|
|
|
if (errcode == ENOENT || errcode == ENODEV)
|
|
{
|
|
// REVIST: Can we inject a constant string here to let the
|
|
// user know that we are waiting for a USB mouse to be
|
|
// connected?
|
|
|
|
// Sleep a bit and try again
|
|
|
|
twminfo("WAITING for a USB mouse\n");
|
|
sleep(2);
|
|
}
|
|
|
|
// Anything else would be really bad.
|
|
|
|
else
|
|
#endif
|
|
{
|
|
// Let the top-level logic decide what it wants to do
|
|
// about all really bad things
|
|
|
|
twmerr("ERROR: Failed to open %s for reading: %d\n",
|
|
CONFIG_TWM4NX_MOUSE_DEVPATH, errcode);
|
|
return -errcode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (fd < 0);
|
|
|
|
return fd;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Read data from the keyboard device and inject the keyboard data
|
|
* into NX for proper distribution.
|
|
*
|
|
* @return On success, then method returns a valid file descriptor. A
|
|
* negated errno value is returned if an irrecoverable error occurs.
|
|
*/
|
|
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
int CInput::keyboardInput(void)
|
|
{
|
|
// Read one keyboard sample
|
|
|
|
twminfo("Reading keyboard input\n");
|
|
|
|
uint8_t rxbuffer[CONFIG_TWM4NX_KEYBOARD_BUFSIZE];
|
|
ssize_t nbytes = read(m_kbdFd, rxbuffer,
|
|
CONFIG_TWM4NX_KEYBOARD_BUFSIZE);
|
|
|
|
// Check for errors
|
|
|
|
if (nbytes < 0)
|
|
{
|
|
int errcode = errno;
|
|
DEBUGASSERT(errcode > 0);
|
|
|
|
// EAGAIN and EINTR are not really error. EAGAIN just indicates that
|
|
// there is nothing available to read now.
|
|
|
|
if (errcode == EAGAIN)
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
// EINTR it simply means that something is trying to get our attention
|
|
// We need to check m_state to see if we were asked to terminate.
|
|
// Anything else is bad news
|
|
|
|
if (errcode != EINTR)
|
|
{
|
|
// Let the top-level listener logic decide what to do about
|
|
// the read failure.
|
|
|
|
twmerr("ERROR: read %s failed: %d\n",
|
|
CONFIG_TWM4NX_KEYBOARD_DEVPATH, errcode);
|
|
return -errcode;
|
|
}
|
|
|
|
fwarn("WARNING: Awakened with EINTR\n");
|
|
}
|
|
|
|
// Give the keyboard input to NX
|
|
|
|
else if (nbytes > 0)
|
|
{
|
|
// Looks like good keyboard input... process it.
|
|
// NOTE: m_twm4nx inherits from NXWidgets::CNXServer so we all ready
|
|
// have the server instance.
|
|
|
|
// Inject the keyboard input into NX
|
|
|
|
NXHANDLE server = m_twm4nx->getServer();
|
|
int ret = nx_kbdin(server, (uint8_t)nbytes, rxbuffer);
|
|
if (ret < 0)
|
|
{
|
|
twmerr("ERROR: nx_kbdin failed: %d\n", ret);
|
|
|
|
// Ignore the error
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Calibrate raw touchscreen input.
|
|
*
|
|
* @param raw The raw touch sample
|
|
* @param scaled The location to return the scaled touch position
|
|
* @return On success, this method returns zero (OK). A negated errno
|
|
* value is returned if an irrecoverable error occurs.
|
|
*/
|
|
|
|
#ifdef CONFIG_TWM4NX_TOUCHSCREEN
|
|
int CInput::scaleTouchData(FAR const struct touch_point_s &raw,
|
|
FAR struct nxgl_point_s &scaled)
|
|
{
|
|
#ifdef CONFIG_NXWM_CALIBRATION_ANISOTROPIC
|
|
// Create a line (varying in X) that have the same matching Y values
|
|
// X lines:
|
|
//
|
|
// x2 = slope*y1 + offset
|
|
//
|
|
// X value calculated on the left side for the given value of y
|
|
|
|
float leftX = raw.y * m_calData.left.slope + m_calData.left.offset;
|
|
|
|
// X value calculated on the right side for the given value of y
|
|
|
|
float rightX = raw.y * m_calData.right.slope + m_calData.right.offset;
|
|
|
|
// Line of X values between (m_calData.leftX,leftX) and
|
|
// (m_calData.rightX,rightX) the are possible solutions:
|
|
//
|
|
// x2 = slope * x1 - offset
|
|
|
|
struct SCalibrationLine xLine;
|
|
xLine.slope = (float)((int)m_calData.rightX -
|
|
(int)m_calData.leftX) / (rightX - leftX);
|
|
xLine.offset = (float)m_calData.leftX - leftX * xLine.slope;
|
|
|
|
// Create a line (varying in Y) that have the same matching X value
|
|
// X lines:
|
|
//
|
|
// y2 = slope*x1 + offset
|
|
//
|
|
// Y value calculated on the top side for a given value of X
|
|
|
|
float topY = raw.x * m_calData.top.slope + m_calData.top.offset;
|
|
|
|
// Y value calculated on the bottom side for a give value of X
|
|
|
|
float bottomY = raw.x * m_calData.bottom.slope +
|
|
m_calData.bottom.offset;
|
|
|
|
// Line of Y values between (topy,m_calData.topY) and
|
|
// (bottomy,m_calData.bottomY) that are possible solutions:
|
|
//
|
|
// y2 = slope * y1 - offset
|
|
|
|
struct SCalibrationLine yLine;
|
|
yLine.slope = (float)((int)m_calData.bottomY -
|
|
(int)m_calData.topY) / (bottomY - topY);
|
|
yLine.offset = (float)m_calData.topY - topY * yLine.slope;
|
|
|
|
// Then scale the raw x and y positions
|
|
|
|
float scaledX = raw.x * xLine.slope + xLine.offset;
|
|
float scaledY = raw.y * yLine.slope + yLine.offset;
|
|
|
|
scaled.x = (nxgl_coord_t)scaledX;
|
|
scaled.y = (nxgl_coord_t)scaledY;
|
|
|
|
twminfo("raw: (%6.2f, %6.2f) scaled: (%6.2f, %6.2f) (%d, %d)\n",
|
|
raw.x, raw.y, scaledX, scaledY, scaled.x, scaled.y);
|
|
return OK;
|
|
#else
|
|
// Get the fixed precision, scaled X and Y values
|
|
|
|
b16_t scaledX = raw.x * m_calData.xSlope + m_calData.xOffset;
|
|
b16_t scaledY = raw.y * m_calData.ySlope + m_calData.yOffset;
|
|
|
|
// Get integer scaled X and Y positions and clip
|
|
// to fix in the window
|
|
|
|
int32_t bigX = b16toi(scaledX + b16HALF);
|
|
int32_t bigY = b16toi(scaledY + b16HALF);
|
|
|
|
// Clip to the display
|
|
|
|
struct nxgl_size_s displaySize;
|
|
m_twm4nx->getDisplaySize(&displaySize);
|
|
|
|
|
|
if (bigX < 0)
|
|
{
|
|
scaled.x = 0;
|
|
}
|
|
else if (bigX >= displaySize.w)
|
|
{
|
|
scaled.x = displaySize.w - 1;
|
|
}
|
|
else
|
|
{
|
|
scaled.x = (nxgl_coord_t)bigX;
|
|
}
|
|
|
|
if (bigY < 0)
|
|
{
|
|
scaled.y = 0;
|
|
}
|
|
else if (bigY >= displaySize.h)
|
|
{
|
|
scaled.y = displaySize.h - 1;
|
|
}
|
|
else
|
|
{
|
|
scaled.y = (nxgl_coord_t)bigY;
|
|
}
|
|
|
|
twminfo("raw: (%d, %d) scaled: (%d, %d)\n",
|
|
raw.x, raw.y, scaled.x, scaled.y);
|
|
return OK;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Read data from the mouse/touchscreen device. If the input device is a
|
|
* mouse, then update the cursor position. And, in either case, inject
|
|
* the mouse data into NX for proper distribution.
|
|
*
|
|
* @return On success, then method returns zero (OK). A negated errno
|
|
* value is returned if an irrecoverable error occurs.
|
|
*/
|
|
|
|
#ifndef CONFIG_TWM4NX_NOMOUSE
|
|
int CInput::mouseInput(void)
|
|
{
|
|
// Read one mouse sample
|
|
|
|
twminfo("Reading XY input\n");
|
|
|
|
uint8_t rxbuffer[CONFIG_TWM4NX_MOUSE_BUFSIZE];
|
|
ssize_t nbytes = read(m_mouseFd, rxbuffer,
|
|
CONFIG_TWM4NX_MOUSE_BUFSIZE);
|
|
|
|
// Check for errors
|
|
|
|
if (nbytes < 0)
|
|
{
|
|
int errcode = errno;
|
|
DEBUGASSERT(errcode > 0);
|
|
|
|
// EAGAIN and EINTR are not really error. EAGAIN just indicates that
|
|
// there is nothing available to read now.
|
|
|
|
if (errcode == EAGAIN)
|
|
{
|
|
return OK;
|
|
}
|
|
|
|
// EINTR it simply means that something is trying to get our attention
|
|
// We need to check m_state to see if we were asked to terminate.
|
|
// Anything else is bad news
|
|
|
|
if (errcode != EINTR)
|
|
{
|
|
// Let the top-level listener logic decide what to do about
|
|
// the read failure.
|
|
|
|
twmerr("ERROR: read %s failed: %d\n",
|
|
CONFIG_TWM4NX_KEYBOARD_DEVPATH, errcode);
|
|
return -errcode;
|
|
}
|
|
|
|
fwarn("WARNING: Awakened with EINTR\n");
|
|
}
|
|
|
|
#ifdef CONFIG_TWM4NX_MOUSE
|
|
// On a truly successful read, the size of the returned data will
|
|
// be greater than or equal to size of one mouse report.
|
|
|
|
else if (nbytes < (ssize_t)sizeof(struct mouse_report_s))
|
|
{
|
|
twmerr("ERROR Unexpected read size=%d, expected=%d\n",
|
|
nbytes, sizeof(struct mouse_report_s));
|
|
return -EIO;
|
|
}
|
|
else
|
|
{
|
|
// Looks like good mouse input... process it.
|
|
// NOTE: m_twm4nx inherits from NXWidgets::CNXServer so we all ready
|
|
// have the server instance.
|
|
|
|
// Update the cursor position
|
|
|
|
FAR struct mouse_report_s *rpt =
|
|
(FAR struct mouse_report_s *)rxbuffer;
|
|
|
|
twminfo("Mouse pos: (%d,%d) Buttons: %02x\n",
|
|
rtp->x, rpt->y, rpt->buttons);
|
|
|
|
struct nxgl_point_s pos =
|
|
{
|
|
.x = rpt->x,
|
|
.y = rpt->y
|
|
};
|
|
|
|
int ret = nxcursor_setposition(m_twm4nx, &pos);
|
|
if (ret < 0)
|
|
{
|
|
twmerr("ERROR: nxcursor_setposition failed: %d\n", ret);
|
|
|
|
// Ignore the error
|
|
}
|
|
|
|
// Then inject the mouse input into NX
|
|
|
|
NXHANDLE server = m_twm4nx->getServer();
|
|
ret = nx_mousein(server, rpt->x, rpt->y, rpt->buttons);
|
|
if (ret < 0)
|
|
{
|
|
twmerr("ERROR: nx_mousein failed: %d\n", ret);
|
|
|
|
// Ignore the error
|
|
}
|
|
}
|
|
#else
|
|
// On a truly successful read, the size of the returned data will
|
|
// be greater than or equal to size of one touchscreen sample. It
|
|
// be greater only in the case of a multi-touch touchscreen device
|
|
// when multi-touches are reported.
|
|
|
|
else if (nbytes < (ssize_t)SIZEOF_TOUCH_SAMPLE_S(1))
|
|
{
|
|
twmerr("ERROR Unexpected read size=%d, expected=%d\n",
|
|
nbytes, SIZEOF_TOUCH_SAMPLE_S(1));
|
|
return -EIO;
|
|
}
|
|
else
|
|
{
|
|
// Looks like good touchscreen input... process it.
|
|
// NOTE: m_twm4nx inherits from NXWidgets::CNXServer so we all ready
|
|
// have the server instance.
|
|
|
|
// Inject the touchscreen as mouse input into NX (with the left
|
|
// button pressed)
|
|
|
|
FAR struct touch_sample_s *sample =
|
|
(FAR struct touch_sample_s *)rxbuffer;
|
|
|
|
// Have we received calibration data yet?
|
|
|
|
struct nxgl_point_s touchPos;
|
|
int ret;
|
|
|
|
if (m_calib)
|
|
{
|
|
// Yes.. scale the raw mouse input to fit the display
|
|
|
|
ret = scaleTouchData(sample->point[0], touchPos);
|
|
if (ret < 0)
|
|
{
|
|
twmerr("ERROR: scaleTouchData failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Hmm.. It is probably an error if no calibration data is
|
|
// provided. However, it is also possible that the touch data as
|
|
// received does not require calibration. We will just have to
|
|
// trust that the user knows what they are doing.
|
|
|
|
touchPos.x = sample->point[0].x;
|
|
touchPos.y = sample->point[0].y;
|
|
}
|
|
|
|
// Now we will inject the touchscreen into NX as mouse input. First
|
|
// massage the data a little so that it behaves a little more like a
|
|
// mouse with only a left button
|
|
//
|
|
// Was the touch up or down?
|
|
|
|
uint8_t buttons;
|
|
if ((sample->point[0].flags & (TOUCH_DOWN | TOUCH_MOVE)) != 0)
|
|
{
|
|
buttons = MOUSE_BUTTON_1;
|
|
}
|
|
else if ((sample->point[0].flags & TOUCH_UP) != 0)
|
|
{
|
|
buttons = 0;
|
|
}
|
|
else
|
|
{
|
|
// The touch is neither up nor down. This should not happen
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
twminfo("Touch pos: (%d,%d)->(%d,%d) Buttons: %02x\n",
|
|
sample->point[0].x, sample->point[0].y,
|
|
touchPos.x, touchPos.y, buttons);
|
|
|
|
NXHANDLE server = m_twm4nx->getServer();
|
|
ret = nx_mousein(server, touchPos.x, touchPos.y, buttons);
|
|
if (ret < 0)
|
|
{
|
|
twmerr("ERROR: nx_mousein failed: %d\n", ret);
|
|
|
|
// Ignore the error
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* This is the heart of the keyboard/mouse listener thread. It
|
|
* contains the actual logic that listeners for and dispatches input
|
|
* events to the NX server.
|
|
*
|
|
* @return If the session terminates gracefully (i.e., because >m_state
|
|
* is no longer equal to LISTENER_RUNNING, then method returns OK. A
|
|
* negated errno value is returned if an error occurs while reading from
|
|
* the input device. A read error, depending upon the type of the
|
|
* error, may simply indicate that a USB device was removed and we
|
|
* should wait for the device to be connected.
|
|
*/
|
|
|
|
int CInput::session(void)
|
|
{
|
|
twminfo("Session started\n");
|
|
|
|
#ifdef CONFIG_TWM4NX_MOUSE
|
|
// Center the cursor
|
|
|
|
struct nxgl_size_s size;
|
|
m_twm4nx->getDisplaySize(&size);
|
|
|
|
struct nxgl_point_s pos;
|
|
pos.x = size.w / 2,
|
|
pos.y = size.h / 2,
|
|
|
|
m_twm4nx->setCursorPosition(&pos);
|
|
|
|
// Set the default cursor image
|
|
|
|
m_twm4nx->setCursorImage(&CONFIG_TWM4NX_CURSOR_IMAGE);
|
|
|
|
// Enable the cursor
|
|
|
|
m_twm4nx->enableCursor(true);
|
|
#endif
|
|
|
|
// Loop, reading and dispatching keyboard data
|
|
|
|
int ret = OK;
|
|
while (m_state == LISTENER_RUNNING)
|
|
{
|
|
// Wait for data availability
|
|
|
|
struct pollfd pfd[NINPUT_DEVICES];
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
pfd[KBD_INDEX].fd = m_kbdFd;
|
|
pfd[KBD_INDEX].events = POLLIN;
|
|
pfd[KBD_INDEX].revents = 0;
|
|
#endif
|
|
#ifndef CONFIG_TWM4NX_NOMOUSE
|
|
pfd[MOUSE_INDEX].fd = m_mouseFd;
|
|
pfd[MOUSE_INDEX].events = POLLIN;
|
|
pfd[MOUSE_INDEX].revents = 0;
|
|
#endif
|
|
|
|
ret = poll(pfd, NINPUT_DEVICES, -1);
|
|
if (ret < 0)
|
|
{
|
|
int errcode = errno;
|
|
|
|
/* Ignore signal interruptions */
|
|
|
|
if (errcode == EINTR)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
twmerr("ERROR: poll() failed");
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
// Check for keyboard input
|
|
|
|
if ((pfd[KBD_INDEX].revents & (POLLERR | POLLHUP)) != 0)
|
|
{
|
|
twmerr("ERROR: keyboard poll() failed. revents=%08" PRIx32 "\n",
|
|
pfd[KBD_INDEX].revents);
|
|
ret = -EIO;
|
|
break;
|
|
}
|
|
|
|
if ((pfd[KBD_INDEX].revents & POLLIN) != 0)
|
|
{
|
|
ret = keyboardInput();
|
|
if (ret < 0)
|
|
{
|
|
twmerr("ERROR: keyboardInput() failed: %d\n", ret);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_TWM4NX_NOMOUSE
|
|
// Check for mouse input
|
|
|
|
if ((pfd[MOUSE_INDEX].revents & (POLLERR | POLLHUP)) != 0)
|
|
{
|
|
twmerr("ERROR: Mouse poll() failed. revents=%08" PRIx32 "\n",
|
|
pfd[MOUSE_INDEX].revents);
|
|
ret = -EIO;
|
|
break;
|
|
}
|
|
|
|
if ((pfd[MOUSE_INDEX].revents & POLLIN) != 0)
|
|
{
|
|
ret = mouseInput();
|
|
if (ret < 0)
|
|
{
|
|
twmerr("ERROR: mouseInput() failed: %d\n", ret);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_TWM4NX_MOUSE
|
|
// Disable the cursor
|
|
|
|
m_twm4nx->enableCursor(false);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* The keyboard/mouse listener thread. This is the entry point of a
|
|
* thread that listeners for and dispatches keyboard and mouse events
|
|
* to the NX server. It simply opens the input devices (using
|
|
* CInput::keyboardOpen() and CInput::mouseOpen()) and executes the
|
|
* session (via CInput::session()).
|
|
*
|
|
* If an errors while reading from the input device AND that device is
|
|
* configured to use a USB connection, then this function will wait for
|
|
* the USB device to be re-connected.
|
|
*
|
|
* @param arg. The CInput 'this' pointer cast to a void*.
|
|
* @return This function normally does not return but may return NULL
|
|
* on error conditions.
|
|
*/
|
|
|
|
FAR void *CInput::listener(FAR void *arg)
|
|
{
|
|
CInput *This = (CInput *)arg;
|
|
|
|
twminfo("Listener started\n");
|
|
|
|
#if defined(CONFIG_TWM4NX_KEYBOARD_USBHOST) || defined(CONFIG_TWM4NX_MOUSE_USBHOST)
|
|
// Indicate that we have successfully started. We might be stuck waiting
|
|
// for a USB keyboard to be connected, but we are technically running
|
|
|
|
This->m_state = LISTENER_RUNNING;
|
|
sem_post(&This->m_waitSem);
|
|
|
|
// Loop until we are told to quit
|
|
|
|
while (This->m_state == LISTENER_RUNNING)
|
|
#endif
|
|
{
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
// Open/Re-open the keyboard device
|
|
|
|
This->m_kbdFd = This->keyboardOpen();
|
|
if (This->m_kbdFd < 0)
|
|
{
|
|
twmerr("ERROR: open failed: %d\n", This->m_kbdFd);
|
|
This->m_state = LISTENER_FAILED;
|
|
sem_post(&This->m_waitSem);
|
|
return (FAR void *)0;
|
|
}
|
|
#endif
|
|
|
|
#ifndef CONFIG_TWM4NX_NOMOUSE
|
|
// Open/Re-open the mouse device
|
|
|
|
This->m_mouseFd = This->mouseOpen();
|
|
if (This->m_mouseFd < 0)
|
|
{
|
|
twmerr("ERROR: open failed: %d\n", This->m_mouseFd);
|
|
This->m_state = LISTENER_FAILED;
|
|
sem_post(&This->m_waitSem);
|
|
return (FAR void *)0;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(CONFIG_TWM4NX_KEYBOARD_USBHOST) && !defined(CONFIG_TWM4NX_MOUSE_USBHOST)
|
|
// Indicate that we have successfully initialized
|
|
|
|
This->m_state = LISTENER_RUNNING;
|
|
sem_post(&This->m_waitSem);
|
|
#endif
|
|
|
|
// Now execute the session. The session will run until either (1) we
|
|
// were asked to terminate gracefully (with m_state !=LISTENER_RUNNING),
|
|
// of if an error occurred while reading from the keyboard device. If
|
|
// we are configured to use a USB keyboard, then this error, depending
|
|
// upon what the error is, may indicate that the USB keyboard has been
|
|
// removed. In that case, we need to continue looping and, hopefully,
|
|
// the USB keyboard will be reconnected.
|
|
|
|
int ret = This->session();
|
|
#if defined(CONFIG_TWM4NX_KEYBOARD_USBHOST) || defined(CONFIG_TWM4NX_MOUSE_USBHOST)
|
|
if (ret < 0)
|
|
{
|
|
ferr("ERROR: CInput::session() returned %d\n", ret);
|
|
}
|
|
#else
|
|
// No errors from session() are expected
|
|
|
|
DEBUGASSERT(ret == OK);
|
|
UNUSED(ret);
|
|
#endif
|
|
|
|
#ifndef CONFIG_TWM4NX_NOKEYBOARD
|
|
// Close the keyboard device
|
|
|
|
close(This->m_kbdFd);
|
|
This->m_kbdFd = -1;
|
|
#endif
|
|
|
|
#ifndef CONFIG_TWM4NX_NOMOUSE
|
|
// Close the mouse device
|
|
|
|
close(This->m_mouseFd);
|
|
This->m_mouseFd = -1;
|
|
#endif
|
|
}
|
|
|
|
// We should get here only if we were asked to terminate via
|
|
// m_state = LISTENER_STOPREQUESTED (or perhaps if some irrecoverable
|
|
// error has occurred).
|
|
|
|
twminfo("Listener exiting\n");
|
|
This->m_state = LISTENER_TERMINATED;
|
|
return (FAR void *)0;
|
|
}
|