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/src/ccalibration.cxx | 5 +- NxWidgets/nxwm/src/ctouchscreen.cxx | 385 ++++++++++++++++++++++++++++++++++-- 2 files changed, 372 insertions(+), 18 deletions(-) (limited to 'NxWidgets/nxwm/src') 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