NXWidgets: ckeyboard.cxx Source File

NXWidgets 1.19

ckeyboard.cxx
Go to the documentation of this file.
1 /********************************************************************************************
2 * NxWidgets/nxwm/src/ckeyboard.cxx
3 *
4 * Copyright (C) 2012 Gregory Nutt. All rights reserved.
5 * Author: Gregory Nutt <gnutt@nuttx.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name NuttX, NxWidgets, nor the names of its contributors
18 * me be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ********************************************************************************************/
35
36 /********************************************************************************************
37 * Included Files
38 ********************************************************************************************/
39
40 #include <nuttx/config.h>
41
42 #include <cunistd>
43 #include <cerrno>
44 #include <cfcntl>
45
46 #include <sched.h>
47 #include <pthread.h>
48 #include <assert.h>
49 #include <debug.h>
50
51 #include "nxwmconfig.hxx"
52 #include "ckeyboard.hxx"
53
54 /********************************************************************************************
55 * Pre-Processor Definitions
56 ********************************************************************************************/
57
58 /********************************************************************************************
59 * CKeyboard Method Implementations
60 ********************************************************************************************/
61
62 using namespace NxWM;
63
64 /**
65 * CKeyboard Constructor
66 *
67 * @param server. An instance of the NX server. This will be needed for
68 * injecting mouse data.
69 */
70
71 CKeyboard::CKeyboard(NXWidgets::CNxServer *server)
72 {
73 m_server = server; // Save the NX server
74 m_kbdFd = -1; // Device driver is not opened
75 m_state = LISTENER_NOTRUNNING; // The listener thread is not running yet
76
77 // Initialize the semaphore used to synchronize with the listener thread
78
79 sem_init(&m_waitSem, 0, 0);
80 }
81
82 /**
83 * CKeyboard Destructor
84 */
85
86 CKeyboard::~CKeyboard(void)
87 {
88 // Stop the listener thread
89
90 m_state = LISTENER_STOPREQUESTED;
91
92 // Wake up the listener thread so that it will use our buffer
93 // to receive data
94 // REVISIT: Need wait here for the listener thread to terminate
95
96 (void)pthread_kill(m_thread, CONFIG_NXWM_KEYBOARD_SIGNO);
97
98 // Close the keyboard device (or should these be done when the thread exits?)
99
100 if (m_kbdFd >= 0)
101 {
102 std::close(m_kbdFd);
103 }
104 }
105
106 /**
107 * Start the keyboard listener thread.
108 *
109 * @return True if the keyboard listener thread was correctly started.
110 */
111
112 bool CKeyboard::start(void)
113 {
114 pthread_attr_t attr;
115
116 ginfo("Starting listener\n");
117
118 // Start a separate thread to listen for keyboard events
119
120 (void)pthread_attr_init(&attr);
121
122 struct sched_param param;
123 param.sched_priority = CONFIG_NXWM_KEYBOARD_LISTENERPRIO;
124 (void)pthread_attr_setschedparam(&attr, &param);
125
126 (void)pthread_attr_setstacksize(&attr, CONFIG_NXWM_KEYBOARD_LISTENERSTACK);
127
128 m_state = LISTENER_STARTED; // The listener thread has been started, but is not yet running
129
130 int ret = pthread_create(&m_thread, &attr, listener, (FAR void *)this);
131 if (ret != 0)
132 {
133 gerr("ERROR: CKeyboard::start: pthread_create failed: %d\n", ret);
134 return false;
135 }
136
137 // Detach from the thread
138
139 (void)pthread_detach(m_thread);
140
141 // Don't return until we are sure that the listener thread is running
142 // (or until it reports an error).
143
144 while (m_state == LISTENER_STARTED)
145 {
146 // Wait for the listener thread to wake us up when we really
147 // are connected.
148
149 (void)sem_wait(&m_waitSem);
150 }
151
152 // Then return true only if the listener thread reported successful
153 // initialization.
154
155 ginfo("Listener m_state=%d\n", (int)m_state);
156 return m_state == LISTENER_RUNNING;
157 }
158
159 /**
160 * Open the keyboard device. Not very interesting for the case of
161 * standard device but much more interesting for a USB keyboard device
162 * that may disappear when the keyboard is disconnect but later reappear
163 * when the keyboard is reconnected. In this case, this function will
164 * not return until the keyboard device was successfully opened (or
165 * until an irrecoverable error occurs.
166 *
167 * Opens the keyboard device specified by CONFIG_NXWM_KEYBOARD_DEVPATH.
168 *
169 * @return On success, then method returns a valid file descriptor that
170 * can be used to redirect stdin. A negated errno value is returned
171 * if an irrecoverable error occurs.
172 */
173
174 int CKeyboard::open(void)
175 {
176 int fd;
177
178 // Loop until we have successfully opened the USB keyboard (or until some
179 // irrecoverable error occurs).
180
181 do
182 {
183 // Try to open the keyboard device
184
185 fd = std::open(CONFIG_NXWM_KEYBOARD_DEVPATH, O_RDONLY);
186 if (fd < 0)
187 {
188 int errcode = errno;
189 DEBUGASSERT(errcode > 0);
190
191 // EINTR should be ignored because it is not really an error at
192 // all. We should retry immediately
193
194 if (errcode != EINTR)
195 {
196 #ifdef CONFIG_NXWM_KEYBOARD_USBHOST
197 // ENOENT means that the USB device is not yet connected and,
198 // hence, has no entry under /dev. If the USB driver still
199 // exists under /dev (because other threads still have the driver
200 // open), then we might also get ENODEV.
201
202 if (errcode == ENOENT || errcode == ENODEV)
203 {
204 // REVIST: Can we inject a constant string here to let the
205 // user know that we are waiting for a USB keyboard to be
206 // connected?
207
208 // Sleep a bit and try again
209
210 ginfo("WAITING for a USB device\n");
211 std::sleep(2);
212 }
213
214 // Anything else would be really bad.
215
216 else
217 #endif
218 {
219 // Let the top-level logic decide what it wants to do
220 // about all really bad things
221
222 gerr("ERROR: Failed to open %s for reading: %d\n",
223 CONFIG_NXWM_KEYBOARD_DEVPATH, errcode);
224 return -errcode;
225 }
226 }
227 }
228 }
229 while (fd < 0);
230
231 return fd;
232 }
233
234 /**
235 * This is the heart of the keyboard listener thread. It contains the
236 * actual logic that listeners for and dispatches keyboard events to the
237 * NX server.
238 *
239 * @return If the session terminates gracefully (i.e., because >m_state
240 * is no longer equal to LISTENER_RUNNING), then method returns OK. A
241 * negated errno value is returned if an error occurs while reading from
242 * the keyboard device. A read error, depending upon the type of the
243 * error, may simply indicate that a USB keyboard was removed and we
244 * should wait for the keyboard to be connected.
245 */
246
247 int CKeyboard::session(void)
248 {
249 ginfo("Session started\n");
250
251 // Loop, reading and dispatching keyboard data
252
253 while (m_state == LISTENER_RUNNING)
254 {
255 // Read one keyboard sample
256
257 ginfo("Listening for keyboard input\n");
258
259 uint8_t rxbuffer[CONFIG_NXWM_KEYBOARD_BUFSIZE];
260 ssize_t nbytes = read(m_kbdFd, rxbuffer,
261 CONFIG_NXWM_KEYBOARD_BUFSIZE);
262
263 // Check for errors
264
265 if (nbytes < 0)
266 {
267 int errcode = errno;
268 DEBUGASSERT(errcode > 0);
269
270 // EINTR is not really an error, it simply means that something is
271 // trying to get our attention. We need to check m_state to see
272 // if we were asked to terminate
273
274 if (errcode != EINTR)
275 {
276 // Let the top-level listener logic decide what to do about
277 // the read failure.
278
279 gerr("ERROR: read %s failed: %d\n",
280 CONFIG_NXWM_KEYBOARD_DEVPATH, errcode);
281 return -errcode;
282 }
283
284 fwarn("WARNING: Awakened with EINTR\n");
285 }
286
287 // Give the keyboard input to NX
288
289 else if (nbytes > 0)
290 {
291 // Looks like good keyboard input... process it.
292 // First, get the server handle
293
294 NXHANDLE handle = m_server->getServer();
295
296 // Then inject the keyboard input into NX
297
298 int ret = nx_kbdin(handle, (uint8_t)nbytes, rxbuffer);
299 if (ret < 0)
300 {
301 gerr("ERROR: nx_kbdin failed: %d\n", ret);
302 //break; ignore the error
303 }
304 }
305 }
306
307 return OK;
308 }
309
310 /**
311 * The keyboard listener thread. This is the entry point of a thread
312 * that listeners for and dispatches keyboard events to the NX server.
313 * It simply opens the keyboard device (using CKeyboard::open()) and
314 * executes the session (via CKeyboard::session()).
315 *
316 * If an errors while reading from the keyboard device AND we are
317 * configured to use a USB keyboard, then this function will wait for
318 * the USB keyboard to be re-connected.
319 *
320 * @param arg. The CKeyboard 'this' pointer cast to a void*.
321 * @return This function normally does not return but may return NULL on
322 * error conditions.
323 */
324
325 FAR void *CKeyboard::listener(FAR void *arg)
326 {
327 CKeyboard *This = (CKeyboard *)arg;
328
329 ginfo("Listener started\n");
330
331 #ifdef CONFIG_NXWM_KEYBOARD_USBHOST
332 // Indicate that we have successfully started. We might be stuck waiting
333 // for a USB keyboard to be connected, but we are technically running
334
335 This->m_state = LISTENER_RUNNING;
336 sem_post(&This->m_waitSem);
337
338 // Loop until we are told to quit
339
340 while (This->m_state == LISTENER_RUNNING)
341 #endif
342 {
343 // Open/Re-open the keyboard device
344
345 This->m_kbdFd = This->open();
346 if (This->m_kbdFd < 0)
347 {
348 gerr("ERROR: open failed: %d\n", This->m_kbdFd);
349 This->m_state = LISTENER_FAILED;
350 sem_post(&This->m_waitSem);
351 return (FAR void *)0;
352 }
353
354 #ifndef CONFIG_NXWM_KEYBOARD_USBHOST
355 // Indicate that we have successfully initialized
356
357 This->m_state = LISTENER_RUNNING;
358 sem_post(&This->m_waitSem);
359 #endif
360
361 // Now execute the session. The session will run until either (1) we
362 // were asked to terminate gracefully (with m_state !=LISTENER_RUNNING),
363 // of if an error occurred while reading from the keyboard device. If
364 // we are configured to use a USB keyboard, then this error, depending
365 // upon what the error is, may indicate that the USB keyboard has been
366 // removed. In that case, we need to continue looping and, hopefully,
367 // the USB keyboard will be reconnected.
368
369 int ret = This->session();
370 #ifdef CONFIG_NXWM_KEYBOARD_USBHOST
371 if (ret < 0)
372 {
373 ferr("ERROR: CKeyboard::session() returned %d\n", ret);
374 }
375 #else
376 // No errors from session() are expected
377
378 DEBUGASSERT(ret == OK);
379 UNUSED(ret);
380 #endif
381
382 // Close the keyboard device
383
384 (void)std::close(This->m_kbdFd);
385 This->m_kbdFd = -1;
386 }
387
388 // We should get here only if we were asked to terminate via
389 // m_state = LISTENER_STOPREQUESTED (or perhaps if some irrecoverable
390 // error has occurred).
391
392 ginfo("Listener exiting\n");
393 This->m_state = LISTENER_TERMINATED;
394 return (FAR void *)0;
395 }
NxWM::CKeyboard::LISTENER_NOTRUNNING
Definition: ckeyboard.hxx:77
NxWM::CKeyboard::m_state
enum EListenerState m_state
Definition: ckeyboard.hxx:92
NxWM::CKeyboard::CKeyboard
CKeyboard(NXWidgets::CNxServer *server)
Definition: ckeyboard.cxx:71
NxWM::CKeyboard::session
int session(void)
Definition: ckeyboard.cxx:247
NxWM::CKeyboard
Definition: ckeyboard.hxx:68
NXWidgets::CNxServer::getServer
NXHANDLE getServer(void)
Definition: cnxserver.hxx:133
ckeyboard.hxx
nxwmconfig.hxx
NxWM::CKeyboard::LISTENER_FAILED
Definition: ckeyboard.hxx:82
NxWM
Definition: capplicationwindow.hxx:66
NxWM::CKeyboard::LISTENER_STOPREQUESTED
Definition: ckeyboard.hxx:80
NxWM::CKeyboard::listener
static FAR void * listener(FAR void *arg)
Definition: ckeyboard.cxx:325
NxWM::CKeyboard::~CKeyboard
~CKeyboard(void)
Definition: ckeyboard.cxx:86
NxWM::CKeyboard::start
bool start(void)
Definition: ckeyboard.cxx:112
NxWM::CKeyboard::m_server
NXWidgets::CNxServer * m_server
Definition: ckeyboard.hxx:89
NxWM::CKeyboard::open
int open(void)
Definition: ckeyboard.cxx:174
NxWM::CKeyboard::m_waitSem
sem_t m_waitSem
Definition: ckeyboard.hxx:93
NxWM::CKeyboard::LISTENER_STARTED
Definition: ckeyboard.hxx:78
NxWM::CKeyboard::m_kbdFd
int m_kbdFd
Definition: ckeyboard.hxx:90
NxWM::CKeyboard::LISTENER_TERMINATED
Definition: ckeyboard.hxx:81
NxWM::CKeyboard::m_thread
pthread_t m_thread
Definition: ckeyboard.hxx:91
NxWM::CKeyboard::LISTENER_RUNNING
Definition: ckeyboard.hxx:79
NXWidgets::CNxServer
Definition: cnxserver.hxx:80