* apps/graphics/nxwm/src/ckeyboard.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 <pthread.h>
#include <assert.h>
#include <debug.h>
#include <unistd.h>
#include "graphics/nxwm/nxwmconfig.hxx"
#include "graphics/nxwm/ckeyboard.hxx"
* Pre-Processor Definitions
********************************************************************************************/
* CKeyboard Method Implementations
********************************************************************************************/
using namespace NxWM;
* CKeyboard Constructor
*
* @param server. An instance of the NX server. This will be needed for
* injecting keyboard data.
*/
CKeyboard::CKeyboard(NXWidgets::CNxServer *server)
{
m_server = server;
m_kbdFd = -1;
m_state = LISTENER_NOTRUNNING;
sem_init(&m_waitSem, 0, 0);
}
* CKeyboard Destructor
*/
CKeyboard::~CKeyboard(void)
{
m_state = LISTENER_STOPREQUESTED;
pthread_kill(m_thread, CONFIG_NXWM_KEYBOARD_SIGNO);
if (m_kbdFd >= 0)
{
close(m_kbdFd);
}
}
* Start the keyboard listener thread.
*
* @return True if the keyboard listener thread was correctly started.
*/
bool CKeyboard::start(void)
{
pthread_attr_t attr;
ginfo("Starting listener\n");
pthread_attr_init(&attr);
struct sched_param param;
param.sched_priority = CONFIG_NXWM_KEYBOARD_LISTENERPRIO;
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setstacksize(&attr, CONFIG_NXWM_KEYBOARD_LISTENERSTACK);
m_state = LISTENER_STARTED;
int ret = pthread_create(&m_thread, &attr, listener, (FAR void *)this);
if (ret != 0)
{
gerr("ERROR: CKeyboard::start: pthread_create failed: %d\n", ret);
return false;
}
pthread_detach(m_thread);
while (m_state == LISTENER_STARTED)
{
sem_wait(&m_waitSem);
}
ginfo("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_NXWM_KEYBOARD_DEVPATH.
*
* @return On success, then method returns a valid file descriptor that
* can be used to redirect stdin. A negated errno value is returned
* if an irrecoverable error occurs.
*/
int CKeyboard::open(void)
{
int fd;
do
{
fd = ::open(CONFIG_NXWM_KEYBOARD_DEVPATH, O_RDONLY);
if (fd < 0)
{
int errcode = errno;
DEBUGASSERT(errcode > 0);
if (errcode != EINTR)
{
#ifdef CONFIG_NXWM_KEYBOARD_USBHOST
if (errcode == ENOENT || errcode == ENODEV)
{
ginfo("WAITING for a USB device\n");
sleep(2);
}
else
#endif
{
gerr("ERROR: Failed to open %s for reading: %d\n",
CONFIG_NXWM_KEYBOARD_DEVPATH, errcode);
return -errcode;
}
}
}
}
while (fd < 0);
return fd;
}
* This is the heart of the keyboard listener thread. It contains the
* actual logic that listeners for and dispatches keyboard 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 keyboard device. A read error, depending upon the type of the
* error, may simply indicate that a USB keyboard was removed and we
* should wait for the keyboard to be connected.
*/
int CKeyboard::session(void)
{
ginfo("Session started\n");
while (m_state == LISTENER_RUNNING)
{
ginfo("Listening for keyboard input\n");
uint8_t rxbuffer[CONFIG_NXWM_KEYBOARD_BUFSIZE];
ssize_t nbytes = read(m_kbdFd, rxbuffer,
CONFIG_NXWM_KEYBOARD_BUFSIZE);
if (nbytes < 0)
{
int errcode = errno;
DEBUGASSERT(errcode > 0);
if (errcode != EINTR)
{
gerr("ERROR: read %s failed: %d\n",
CONFIG_NXWM_KEYBOARD_DEVPATH, errcode);
return -errcode;
}
fwarn("WARNING: Awakened with EINTR\n");
}
else if (nbytes > 0)
{
NXHANDLE handle = m_server->getServer();
int ret = nx_kbdin(handle, (uint8_t)nbytes, rxbuffer);
if (ret < 0)
{
gerr("ERROR: nx_kbdin failed: %d\n", ret);
}
}
}
return OK;
}
* The keyboard listener thread. This is the entry point of a thread
* that listeners for and dispatches keyboard events to the NX server.
* It simply opens the keyboard device (using CKeyboard::open()) and
* executes the session (via CKeyboard::session()).
*
* If an errors while reading from the keyboard device AND we are
* configured to use a USB keyboard, then this function will wait for
* the USB keyboard to be re-connected.
*
* @param arg. The CKeyboard 'this' pointer cast to a void*.
* @return This function normally does not return but may return NULL on
* error conditions.
*/
FAR void *CKeyboard::listener(FAR void *arg)
{
CKeyboard *This = (CKeyboard *)arg;
ginfo("Listener started\n");
#ifdef CONFIG_NXWM_KEYBOARD_USBHOST
This->m_state = LISTENER_RUNNING;
sem_post(&This->m_waitSem);
while (This->m_state == LISTENER_RUNNING)
#endif
{
This->m_kbdFd = This->open();
if (This->m_kbdFd < 0)
{
gerr("ERROR: open failed: %d\n", This->m_kbdFd);
This->m_state = LISTENER_FAILED;
sem_post(&This->m_waitSem);
return (FAR void *)0;
}
#ifndef CONFIG_NXWM_KEYBOARD_USBHOST
This->m_state = LISTENER_RUNNING;
sem_post(&This->m_waitSem);
#endif
int ret = This->session();
#ifdef CONFIG_NXWM_KEYBOARD_USBHOST
if (ret < 0)
{
ferr("ERROR: CKeyboard::session() returned %d\n", ret);
}
#else
DEBUGASSERT(ret == OK);
UNUSED(ret);
#endif
close(This->m_kbdFd);
This->m_kbdFd = -1;
}
ginfo("Listener exiting\n");
This->m_state = LISTENER_TERMINATED;
return (FAR void *)0;
}