From a280d993608d0c40ad0b4efc8adcbf72f67d53a4 Mon Sep 17 00:00:00 2001 From: patacongo Date: Fri, 11 May 2012 00:05:25 +0000 Subject: NxWM: Finishes touchscreen implementation; NuttX: Standardize touchscreen initialization interfaces for all boards git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4721 42af7a65-404d-4744-a932-0658087f49c3 --- NxWidgets/nxwm/include/ccalibration.hxx | 7 +- NxWidgets/nxwm/include/ctaskbar.hxx | 12 + NxWidgets/nxwm/include/ctouchscreen.hxx | 119 +++++++++- NxWidgets/nxwm/include/nxwmconfig.hxx | 34 +++ NxWidgets/nxwm/src/ccalibration.cxx | 5 +- NxWidgets/nxwm/src/ctouchscreen.cxx | 385 ++++++++++++++++++++++++++++++-- 6 files changed, 534 insertions(+), 28 deletions(-) (limited to 'NxWidgets/nxwm') diff --git a/NxWidgets/nxwm/include/ccalibration.hxx b/NxWidgets/nxwm/include/ccalibration.hxx index 0adcd894f..42eb56ad2 100644 --- a/NxWidgets/nxwm/include/ccalibration.hxx +++ b/NxWidgets/nxwm/include/ccalibration.hxx @@ -50,7 +50,6 @@ #include "iapplication.hxx" #include "cfullscreenwindow.hxx" -#include "ctouchscreen.hxx" /**************************************************************************** * Pre-processor Definitions @@ -72,6 +71,12 @@ namespace NxWM { + /** + * Forward references + */ + + struct CTouchscreen; + /** * Touchscreen calibration data */ diff --git a/NxWidgets/nxwm/include/ctaskbar.hxx b/NxWidgets/nxwm/include/ctaskbar.hxx index 69595163c..398087f84 100644 --- a/NxWidgets/nxwm/include/ctaskbar.hxx +++ b/NxWidgets/nxwm/include/ctaskbar.hxx @@ -385,6 +385,18 @@ namespace NxWM bool stopApplication(IApplication *app); + /** + * Get the size of the physical display device as it is known to the task + * bar. + * + * @return The size of the display + */ + + inline bool getWindowSize(FAR struct nxgl_size_s *size) + { + return m_taskbar->getSize(size); + } + /** * Simulate a mouse click on the icon at index. This inline method is only * used during automated testing of NxWM. diff --git a/NxWidgets/nxwm/include/ctouchscreen.hxx b/NxWidgets/nxwm/include/ctouchscreen.hxx index f8eaeaee8..c3aa198cb 100644 --- a/NxWidgets/nxwm/include/ctouchscreen.hxx +++ b/NxWidgets/nxwm/include/ctouchscreen.hxx @@ -43,8 +43,13 @@ #include #include +#include + #include +#include "cnxserver.hxx" +#include "ccalibration.hxx" + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -63,16 +68,68 @@ namespace NxWM class CTouchscreen { private: - int m_touchFd; /**< File descriptor of the opened touchscreen device */ - sem_t m_waitSem; /**< Semaphore the supports waits for touchscreen data */ + /** + * The state of the listener thread. + */ + + enum EListenerState + { + LISTENER_NOTRUNNING = 0, /**< The listener thread has not yet been started */ + LISTENER_STARTED, /**< The listener thread has been started, but is not yet running */ + LISTENER_RUNNING, /**< The listener thread is running normally */ + LISTENER_STOPREQUESTED, /**< The listener thread has been requested to stop */ + LISTENER_TERMINATED, /**< The listener thread terminated normally */ + LISTENER_FAILED /**< The listener thread terminated abnormally */ + }; + + /** + * CTouchscreen state data + */ + + NXWidgets::CNxServer *m_server; /**< The current NX server */ + int m_touchFd; /**< File descriptor of the opened touchscreen device */ + sem_t m_waitSem; /**< Semaphore the supports waits for touchscreen data */ + pthread_t m_thread; /**< The listener thread ID */ + volatile enum EListenerState m_state; /**< The state of the listener thread */ + volatile bool m_enabled; /**< True: Normal touchscreen processing */ + volatile bool m_capture; /**< True: There is a thread waiting for raw touch data */ + volatile bool m_calibrated; /**< True: If have calibration data */ + struct nxgl_size_s m_windowSize; /**< The size of the physical display */ + struct SCalibrationData m_calibData; /**< Calibration data */ + struct touch_sample_s m_sample; /**< In normal mode, touch data is collected here */ + struct touch_sample_s *m_touch; /**< Points to the current touch data buffer */ + + /** + * The touchscreen listener thread. This is the entry point of a thread that + * listeners for and dispatches touchscreens events to the NX server. + * + * @param arg. The CTouchscreen 'this' pointer cast to a void*. + * @return This function normally does not return but may return NULL on + * error conditions. + */ + + static FAR void *listener(FAR void *arg); + + /** + * Inject touchscreen data into NX as mouse intput + * + * @param sample. The buffer where data was collected. + */ + + void handleMouseInput(struct touch_sample_s *sample); public: /** * CTouchscreen Constructor + * + * @param server. An instance of the NX server. This will be needed for + * injecting mouse data. + * @param windowSize. The size of the physical window in pixels. This + * is needed for touchscreen scaling. */ - CTouchscreen(void); + CTouchscreen(NXWidgets::CNxServer *server, struct nxgl_size_s *windowSize); /** * CTouchscreen Destructor @@ -81,22 +138,66 @@ namespace NxWM ~CTouchscreen(void); /** - * Initialize the touchscreen device. Initialization is separate from - * object instantiation so that failures can be reported. + * Start the touchscreen listener thread. * - * @return True if the touchscreen device was correctly initialized + * @return True if the touchscreen listener thread was correctly started. */ - bool open(void); + bool start(void); + + /** + * Enable/disable touchscreen data processing. When enabled, touchscreen events + * are calibrated and forwarded to the NX layer which dispatches the touchscreen + * events in window-relative positions to the correct NX window. + * + * When disabled, touchscreen data is not forwarded to NX, but is instead captured + * and made available for touchscreen calibration. The touchscreen driver is + * initially disabled and must be specifically enabled be begin normal processing. + * Normal processing also requires calibration data (see method setCalibrationData) + * + * @param capture. True enables capture mode; false disables. + */ + + inline void setEnabled(bool enable) + { + // Set the capture flag. m_calibrated must also be set to get to normal + // mode where touchscreen data is forwarded to NX. + + m_enabled = enable; + } + + /** + * Provide touchscreen calibration data. If calibration data is received (and + * the touchscreen is enabled), then received touchscreen data will be scaled + * using the calibration data and forward to the NX layer which dispatches the + * touchscreen events in window-relative positions to the correct NX window. + * + * @param data. A reference to the touchscreen data. + */ + + inline void setCalibrationData(struct SCalibrationData &caldata) + { + // Save a copy of the calibration data + + m_calibData = caldata; + + // Note that we have calibration data. Data will now be scaled and forwarded + // to NX (unless we are still in cpature mode) + + m_calibrated = true; + } /** - * Capture raw driver data. + * Capture raw driver data. This method will capture mode one raw touchscreen + * input. The normal use of this method is for touchscreen calibration. * + * This function is not re-entrant: There may be only one thread waiting for + * raw touchscreen data. * * @return True if the raw touchscreen data was sucessfully obtained */ - bool waitRawTouchData(struct touch_sample_s &touch); + bool waitRawTouchData(struct touch_sample_s *touch); }; } diff --git a/NxWidgets/nxwm/include/nxwmconfig.hxx b/NxWidgets/nxwm/include/nxwmconfig.hxx index 4e08b0481..4e51cea1d 100644 --- a/NxWidgets/nxwm/include/nxwmconfig.hxx +++ b/NxWidgets/nxwm/include/nxwmconfig.hxx @@ -42,6 +42,8 @@ #include +#include + #include "nxconfig.hxx" #include "crlepalettebitmap.hxx" @@ -322,12 +324,43 @@ #endif /* Touchscreen device *******************************************************/ +/** + * Touchscreen device settings + * + * CONFIG_NXWM_TOUCHSCREEN_DEVNO - Touchscreen device minor number, i.e., the + * N in /dev/inputN. Default: 0 + * CONFIG_NXWM_TOUCHSCREEN_DEVNO - The full path to the touchscreen device. + * Default: "/dev/input0" + * CONFIG_NXWM_TOUCHSCREEN_SIGNO - The realtime signal used to wake up the + * touchscreen listener thread. Default: 5 + */ + +#ifndef CONFIG_NXWM_TOUCHSCREEN_DEVNO +# define CONFIG_NXWM_TOUCHSCREEN_DEVNO 0 +#endif #ifndef CONFIG_NXWM_TOUCHSCREEN_DEVPATH # define CONFIG_NXWM_TOUCHSCREEN_DEVPATH "/dev/input0" #endif +#ifndef CONFIG_NXWM_TOUCHSCREEN_SIGNO +# define CONFIG_NXWM_TOUCHSCREEN_SIGNO 5 +#endif + /* Calibration display ******************************************************/ +/** + * Calibration display settings: + * + * CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR - The background color of the + * touchscreen calibration display. Default: Same as + * CONFIG_NXWM_DEFAULT_BACKGROUNDCOLOR + * CONFIG_NXWM_CALIBRATION_LINECOLOR - The color of the lines used in the + * touchscreen calibration display. Default: MKRGB(0, 0, 128) (dark blue) + * CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR - The background color of the + * touchscreen calibration display. Default: MKRGB(255, 255, 255) (white) + * CONFIG_NXWM_CALIBRATION_ICON - The ICON to use for the touchscreen + * calibration application. Default: NxWM::g_calibrationBitmap + */ #ifndef CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR # define CONFIG_NXWM_CALIBRATION_BACKGROUNDCOLOR CONFIG_NXWM_DEFAULT_BACKGROUNDCOLOR @@ -344,6 +377,7 @@ #ifndef CONFIG_NXWM_CALIBRATION_ICON # define CONFIG_NXWM_CALIBRATION_ICON NxWM::g_calibrationBitmap #endif + /**************************************************************************** * Global Function Prototypes ****************************************************************************/ diff --git a/NxWidgets/nxwm/src/ccalibration.cxx b/NxWidgets/nxwm/src/ccalibration.cxx index 6e66f3028..82c12ebb3 100644 --- a/NxWidgets/nxwm/src/ccalibration.cxx +++ b/NxWidgets/nxwm/src/ccalibration.cxx @@ -43,6 +43,7 @@ #include "nxwmconfig.hxx" #include "nxwmglyphs.hxx" +#include "ctouchscreen.hxx" #include "ccalibration.hxx" /**************************************************************************** @@ -161,7 +162,7 @@ bool CCalibration::run(void) // Wait for the next raw touchscreen input struct touch_sample_s sample; - while (!m_touchscreen->waitRawTouchData(sample)); + while (!m_touchscreen->waitRawTouchData(&sample)); // Then process the raw touchscreen input @@ -190,7 +191,7 @@ void CCalibration::stop(void) void CCalibration::hide(void) { -#warning "Revisit" + // REVISIT } /** diff --git a/NxWidgets/nxwm/src/ctouchscreen.cxx b/NxWidgets/nxwm/src/ctouchscreen.cxx index 215e50064..737c7d9e3 100644 --- a/NxWidgets/nxwm/src/ctouchscreen.cxx +++ b/NxWidgets/nxwm/src/ctouchscreen.cxx @@ -43,6 +43,9 @@ #include #include +#include +#include +#include #include #include @@ -58,6 +61,21 @@ /******************************************************************************************** * Pre-Processor Definitions ********************************************************************************************/ +/* We want debug output from this file if either input/touchscreen or graphics debug is + * enabled. + */ + +#if !defined(CONFIG_DEBUG_INPUT) && !defined(CONFIG_DEBUG_GRAPHICS) +# undef dbg +# undef vdbg +# ifdef CONFIG_CPP_HAVE_VARARGS +# define dbg(x...) +# define vdbg(x...) +# else +# define dbg (void) +# define vdbg (void) +# endif +#endif /******************************************************************************************** * CTouchscreen Method Implementations @@ -67,11 +85,32 @@ using namespace NxWM; /** * CTouchscreen Constructor + * + * @param server. An instance of the NX server. This will be needed for + * injecting mouse data. + * @param windowSize. The size of the physical window in pixels. This + * is needed for touchscreen scaling. */ -CTouchscreen::CTouchscreen(void) +CTouchscreen::CTouchscreen(NXWidgets::CNxServer *server, struct nxgl_size_s *windowSize) { - m_touchFd = -1; + m_server = server; // Save the NX server + m_touchFd = -1; // Device driver is not opened + m_state = LISTENER_NOTRUNNING; // The listener thread is not running yet + m_enabled = false; // Normal forwarding is not enabled + m_capture = false; // There is no thread waiting for touchscreen data + m_calibrated = false; // We have no calibration data + + // Save the window size + + m_windowSize = *windowSize; + + // Use the default touch data buffer + + m_touch = &m_sample; + + // Initialize the m_waitSem semaphore so that any waits for data will block + sem_init(&m_waitSem, 0, 0); } @@ -81,45 +120,359 @@ CTouchscreen::CTouchscreen(void) CTouchscreen::~CTouchscreen(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 + + (void)pthread_kill(m_thread, CONFIG_NXWM_TOUCHSCREEN_SIGNO); + + // Close the touchscreen device (or should these be done when the thread exits?) + if (m_touchFd >= 0) { std::close(m_touchFd); } + // Destroy the semaphores that we created. + sem_destroy(&m_waitSem); } /** - * Initialize the touchscreen device. Initialization is separate from - * object instantiation so that failures can be reported. + * Start the touchscreen listener thread. * - * @return True if the touchscreen device was correctly initialized + * @return True if the touchscreen listener thread was correctly started. */ -bool CTouchscreen::open(void) +bool CTouchscreen::start(void) { - // Open the touchscreen device + pthread_attr_t attr; + + vdbg("Starting listener\n"); + + // Start a separate thread to listen for touchscreen events - m_touchFd = std::open(CONFIG_NXWM_TOUCHSCREEN_DEVPATH, O_RDONLY); - if (m_touchFd < 0) + (void)pthread_attr_init(&attr); + + struct sched_param param; + param.sched_priority = CONFIG_NXWIDGETS_LISTENERPRIO; + (void)pthread_attr_setschedparam(&attr, ¶m); + + (void)pthread_attr_setstacksize(&attr, CONFIG_NXWIDGETS_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) { - gdbg("ERROR Failed to open %s for reading: %d\n", - CONFIG_NXWM_TOUCHSCREEN_DEVPATH, errno); + dbg("NxServer::connect: pthread_create failed: %d\n", ret); return false; } - return true; + // Detach from the thread + + (void)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. + + (void)sem_wait(&m_waitSem); + } + + // Then return true only if the listener thread reported successful + // initialization. + + vdbg("Listener m_state=%d\n", (int)m_state); + return m_state == LISTENER_RUNNING; } /** - * Capture raw driver data. + * Capture raw driver data. This method will capture mode one raw touchscreen + * input. The normal use of this method is for touchscreen calibration. * + * This function is not re-entrant: There may be only one thread waiting for + * raw touchscreen data. * * @return True if the raw touchscreen data was sucessfully obtained */ -bool CTouchscreen::waitRawTouchData(struct touch_sample_s &touch) +bool CTouchscreen::waitRawTouchData(struct touch_sample_s *touch) { -#warning "Missing logic" - return true; + vdbg("Capturing touch input\n"); + + // Setup to cpature raw data into the user provided buffer + + sched_lock(); + m_touch = touch; + m_capture = true; + + // Wake up the listener thread so that it will use our buffer + // to receive data + + (void)pthread_kill(m_thread, CONFIG_NXWM_TOUCHSCREEN_SIGNO); + + // And wait for touch data + + int ret = OK; + while (m_capture) + { + ret = sem_wait(&m_waitSem); + DEBUGASSERT(ret == 0 || errno == EINTR); + } + sched_unlock(); + + // And return success. The listener thread will have (1) reset both + // m_touch and m_capture and (2) posted m_waitSem + + vdbg("Returning touch input: %d\n", ret); + return ret == OK; } + +/** + * The touchscreen listener thread. This is the entry point of a thread that + * listeners for and dispatches touchscreens events to the NX server. + * + * @param arg. The CTouchscreen 'this' pointer cast to a void*. + * @return This function normally does not return but may return NULL on + * error conditions. + */ + +FAR void *CTouchscreen::listener(FAR void *arg) +{ + CTouchscreen *This = (CTouchscreen *)arg; + + vdbg("Listener started\n"); + + // Initialize the touchscreen device + + int ret = arch_tcinitialize(CONFIG_NXWM_TOUCHSCREEN_DEVNO); + if (ret < 0) + { + dbg("ERROR Failed initialize the touchscreen device: %d\n", ret); + This->m_state = LISTENER_FAILED; + sem_post(&This->m_waitSem); + return (FAR void *)0; + } + + // Open the touchscreen device that we just created. + + This->m_touchFd = std::open(CONFIG_NXWM_TOUCHSCREEN_DEVPATH, O_RDONLY); + if (This->m_touchFd < 0) + { + dbg("ERROR Failed to open %s for reading: %d\n", + CONFIG_NXWM_TOUCHSCREEN_DEVPATH, errno); + This->m_state = LISTENER_FAILED; + sem_post(&This->m_waitSem); + return (FAR void *)0; + } + + // Indicate that we have successfully initialized + + This->m_state = LISTENER_RUNNING; + sem_post(&This->m_waitSem); + + // Now loop, reading and dispatching touchscreen data + + while (This->m_state == LISTENER_RUNNING) + { + // The sample pointer can change dynamically let's sample it once + // and stick with that pointer. + + struct touch_sample_s *sample = This->m_touch; + + // Read one touchscreen sample + + vdbg("Listening for sample %p\n", sample); + DEBUGASSERT(sample); + ssize_t nbytes = read(This->m_touchFd, sample, + sizeof(struct touch_sample_s)); + vdbg("Received nbytes=%d\n", nbytes); + + // Check for errors + + if (nbytes < 0) + { + // The only expect error is to be interrupt by a signal + + int errval = errno; + + dbg("read %s failed: %d\n", + CONFIG_NXWM_TOUCHSCREEN_DEVPATH, errval); + DEBUGASSERT(errval == EINTR); + } + + // On a truly success read, the size of the returned data will + // be exactly the size of one touchscreen sample + + else if (nbytes == sizeof(struct touch_sample_s)) + { + // Looks like good touchscreen input... process it + + This->handleMouseInput(sample); + } + else + { + dbg("ERROR Unexpected read size=%d, expected=%d\n", + nbytes, sizeof(struct touch_sample_s)); + } + } + + // We should get here only if we were asked to terminate via + // m_state = LISTENER_STOPREQUESTED + + vdbg("Listener exiting\n"); + This->m_state = LISTENER_TERMINATED; + return (FAR void *)0; +} + +/** + * Inject touchscreen data into NX as mouse intput + */ + +void CTouchscreen::handleMouseInput(struct touch_sample_s *sample) +{ + vdbg("Touch id: %d flags: %02x x: %d y: %d h: %d w: %d pressure: %d\n", + sample->point[0].id, sample->point[0].flags, sample->point[0].x, + sample->point[0].y, sample->point[0].h, sample->point[0].w, + sample->point[0].pressure); + + // Verify the touchscreen data + + if (sample->npoints < 1 || + ((sample->point[0].flags & TOUCH_POS_VALID) == 0 && + (sample->point[0].flags & TOUCH_UP) == 0)) + { + // The pen is (probably) down, but we have do not have valid + // X/Y position data to report. This should not happen. + + return; + } + + // Was this data captured by some external logic? (probably the + // touchscreen calibration logic) + + if (m_capture && sample != &m_sample) + { + // Yes.. let waitRawTouchData know that the data is available + // and restore normal buffering + + m_touch = &m_sample; + m_capture = false; + sem_post(&m_waitSem); + return; + } + + // Sanity checks. Re-directed touch data should never reach this point. + // After posting m_waitSem, m_touch might change asynchronously. + + DEBUGASSERT(sample == &m_sample); + + // Check if normal processing of touchscreen data is enaable. Check if + // we have been given calibration data. + + if (!m_enabled || !m_calibrated) + { + // No.. we are not yet ready to process touchscreen data + + return; + } + + // Now we will inject the touchscreen into NX as mouse input. First + // massage the data a litle so that it behaves a little more like a + // mouse with only a left button + // + // Was the button up or down? + + uint8_t buttons; + if ((sample->point[0].flags & (TOUCH_DOWN|TOUCH_MOVE)) != 0) + { + buttons = NX_MOUSE_LEFTBUTTON; + } + else if ((sample->point[0].flags & TOUCH_UP) != 0) + { + buttons = NX_MOUSE_NOBUTTONS; + } + else + { + // The pen is neither up nor down. This should not happen + + return; + } + + // Get the "raw" touch coordinates (if they are valid) + + nxgl_coord_t x; + nxgl_coord_t y; + + if ((sample->point[0].flags & TOUCH_POS_VALID) == 0) + { + x = 0; + y = 0; + } + else + { + // We have valid coordinates. Get the raw touch + // position from the sample + + uint32_t rawX = (uint32_t)sample->point[0].x; + uint32_t rawY = (uint32_t)sample->point[0].y; + + // Get the fixed precision, scaled X and Y values + + b16_t scaledX = rawX * m_calibData.xSlope + m_calibData.xOffset; + b16_t scaledY = rawY * m_calibData.ySlope + m_calibData.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 + + if (bigX < 0) + { + x = 0; + } + else if (bigX >= m_windowSize.w) + { + x = m_windowSize.w - 1; + } + else + { + x = (nxgl_coord_t)bigX; + } + + if (bigY < 0) + { + y = 0; + } + else if (bigY >= m_windowSize.h) + { + y = m_windowSize.h - 1; + } + else + { + y = (nxgl_coord_t)bigY; + } + + vdbg("raw: (%d, %d) scaled: (%d, %d)\n", rawX, rawY, x, y); + } + + // Get the server handle and "inject the mouse data + + NXHANDLE handle = m_server->getServer(); + (void)nx_mousein(handle, x, y, buttons); +} + + + -- cgit v1.2.3