]> git.vomp.tv Git - vompclient.git/blob - src/inputlinux.cc
Type change: UCHAR -> u1
[vompclient.git] / src / inputlinux.cc
1 /*
2     Copyright 2004-2020 Chris Tallon; 2012 Marten Richter
3
4     This file is part of VOMP.
5
6     VOMP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     VOMP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <linux/input.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/ioctl.h>
26 #include <fcntl.h>
27 #include <errno.h>
28
29 #include <cstdio>
30 #include <iostream>
31 #include <ostream>
32 #include <algorithm>
33
34 #include "i18n.h"
35 #include "vdr.h"
36 #include "woptionpane.h"
37
38 #include "inputlinux.h"
39
40 static const char* TAG = "InputLinux";
41
42 #define test_bit(input,b)  ((1 << ((b) % 8))&(input)[b / 8] )
43
44 bool InputLinux::init()
45 {
46   if (initted) return false;
47   initted = 1;
48
49   InitHWCListwithDefaults();
50   InitKeymap();
51
52   for (int eventid = 0; eventid < 100; eventid++)
53   {
54     char buffer[1024];
55     sprintf(buffer,"/dev/input/event%d", eventid);
56
57     struct stat test_buf;
58     if (stat(buffer, &test_buf) == 0)
59     {
60       LogNT::getInstance()->info(TAG, "Probe /dev/input/event{}", eventid);
61       // file exists
62       unsigned long ev_type = 0;
63       int new_fd = open(buffer, O_RDONLY);
64       if (new_fd < 0)
65       {
66         LogNT::getInstance()->info(TAG, "Can not open /dev/input/event{}", eventid);
67         continue;
68       }
69
70       if (ioctl(new_fd, EVIOCGBIT(0, EV_MAX), &ev_type) < 0)
71       {
72         LogNT::getInstance()->info(TAG, "Ioctl failed /dev/input/event{} {}", eventid, errno);
73         close(new_fd);
74       }
75
76       //Now test if it generates keyboard presses
77       if (test_bit(reinterpret_cast<char*>(&ev_type), EV_KEY))
78       {
79         LogNT::getInstance()->info(TAG, "Add /dev/input/event{} to List", eventid);
80         devices.push_back(new_fd);
81
82         // Grab the device - make it exclusive to vomp. Fixes rubbish input going to console in background
83         ioctl(new_fd, EVIOCGRAB, 1);
84       }
85       else
86       {
87         close(new_fd);
88       }
89     }
90   }
91   return true;
92 }
93
94 void InputLinux::shutdown()
95 {
96   if (!initted) return;
97
98   while (devices.size())
99   {
100     int cur_fd = devices.back();
101     devices.pop_back();
102     ioctl(cur_fd, EVIOCGRAB, 0);
103     close(cur_fd);
104   }
105
106   initted = 0;
107 }
108
109 u1 InputLinux::TranslateHWCFixed(int code)
110 {
111   // Translate /dev/input codes to VOMP codes for the hard coded buttons
112   switch (code)
113   {
114     case KEY_DOWN:      return DOWN;
115     case KEY_UP:        return UP;
116     case KEY_LEFT:      return LEFT;
117     case KEY_RIGHT:     return RIGHT;
118     case KEY_M:
119     case KEY_MEDIA:     return MENU;
120     case KEY_BACKSPACE:
121     case KEY_EXIT:      return BACK;
122     case KEY_ENTER:
123     case KEY_SPACE:
124     case KEY_OK:        return OK;
125     case KEY_SLEEP:
126     case KEY_POWER:
127     case KEY_ESC:       return POWER;
128     case POWER:         return POWER; // Where does this come from?
129     default:
130       return NA_UNKNOWN;
131   }
132 }
133
134 void InputLinux::InitHWCListwithDefaults()
135 {
136   LogNT::getInstance()->info(TAG, "InitHWCListwithDefaults");
137
138   // Processing VK_Messages
139   translist[KEY_9] = NINE;
140   translist[KEY_8] = EIGHT;
141   translist[KEY_7] = SEVEN;
142   translist[KEY_6] = SIX;
143   translist[KEY_5] = FIVE;
144   translist[KEY_4] = FOUR;
145   translist[KEY_3] = THREE;
146   translist[KEY_2] = TWO;
147   translist[KEY_1] = ONE;
148   translist[KEY_0] = ZERO;
149   translist[KEY_KPDOT] = STAR;
150   // translist[KEY_#] = HASH;
151
152   translist[KEY_KP9] = NINE;
153   translist[KEY_KP8] = EIGHT;
154   translist[KEY_KP7] = SEVEN;
155   translist[KEY_KP6] = SIX;
156   translist[KEY_KP5] = FIVE;
157   translist[KEY_KP4] = FOUR;
158   translist[KEY_KP3] = THREE;
159   translist[KEY_KP2] = TWO;
160   translist[KEY_KP1] = ONE;
161   translist[KEY_KP0] = ZERO;
162
163   translist[KEY_NUMERIC_9] = NINE;
164   translist[KEY_NUMERIC_8] = EIGHT;
165   translist[KEY_NUMERIC_7] = SEVEN;
166   translist[KEY_NUMERIC_6] = SIX;
167   translist[KEY_NUMERIC_5] = FIVE;
168   translist[KEY_NUMERIC_4] = FOUR;
169   translist[KEY_NUMERIC_3] = THREE;
170   translist[KEY_NUMERIC_2] = TWO;
171   translist[KEY_NUMERIC_1] = ONE;
172   translist[KEY_NUMERIC_0] = ZERO;
173   translist[KEY_NUMERIC_STAR] = STAR;
174   translist[KEY_NUMERIC_POUND] = HASH;
175
176
177   translist[KEY_J] = GO; //j for JUMP TO instead of go to
178   translist[KEY_R] = RED;
179   translist[KEY_G] = GREEN;
180   translist[KEY_Y] = YELLOW;
181   translist[KEY_B] = BLUE;
182   //Processing Remote Style Messages
183   translist[KEY_GREEN] = GREEN;
184   translist[KEY_RED] = RED;
185   translist[KEY_YELLOW] = YELLOW;
186   translist[KEY_BLUE] = BLUE;
187   translist[KEY_MENU] = MENU;
188
189   translist[KEY_RECORD] = RECORD;
190   translist[KEY_PLAY] = PLAY; //Playback Televison
191   translist[KEY_PAUSE] = PAUSE;
192   translist[KEY_STOP] = STOP;
193   translist[KEY_PLAYPAUSE] = PLAYPAUSE;
194   translist[KEY_P] = PLAYPAUSE;
195   translist[KEY_NEXT] = SKIPFORWARD;
196   translist[KEY_F2] = SKIPFORWARD;
197   translist[KEY_PREVIOUS] = SKIPBACK;
198   translist[KEY_F1] = SKIPBACK;
199   translist[KEY_FORWARD] = FORWARD;
200   translist[KEY_FASTFORWARD] = FORWARD;
201   translist[KEY_F] = FORWARD;
202   translist[KEY_BACK] = REVERSE;
203   translist[KEY_REWIND] = REVERSE;
204   translist[KEY_T] = REVERSE;
205   translist[KEY_MUTE] = MUTE;
206   translist[KEY_F8] = MUTE;
207   translist[KEY_F10] = VOLUMEUP;
208   translist[KEY_F9] = VOLUMEDOWN;
209   translist[KEY_VOLUMEUP] = VOLUMEUP;
210   translist[KEY_VOLUMEDOWN] = VOLUMEDOWN;
211   translist[KEY_CHANNELUP] = CHANNELUP;
212   translist[KEY_CHANNELDOWN] = CHANNELDOWN;
213   translist[KEY_PAGEUP] = CHANNELUP;
214   translist[KEY_PAGEDOWN] = CHANNELDOWN;
215 }
216
217 #define NAMETRICK(pre, code) linux_keymap[pre ## code]=  #code
218 //extracte from linux/input.h
219
220 static const char * linux_keymap[KEY_MAX+1];
221
222 void InputLinux::InitKeymap()
223 {
224   for (int i=0;i<KEY_MAX+1;i++)
225   {
226     linux_keymap[i] = NULL;
227   }
228
229   NAMETRICK(KEY_,RESERVED);
230   NAMETRICK(KEY_,ESC);
231   NAMETRICK(KEY_,1);
232   NAMETRICK(KEY_,2);
233   NAMETRICK(KEY_,3);
234   NAMETRICK(KEY_,4);
235   NAMETRICK(KEY_,5);
236   NAMETRICK(KEY_,6);
237   NAMETRICK(KEY_,7);
238   NAMETRICK(KEY_,8);
239   NAMETRICK(KEY_,9);
240   NAMETRICK(KEY_,0);
241   NAMETRICK(KEY_,MINUS);
242   NAMETRICK(KEY_,EQUAL);
243   NAMETRICK(KEY_,BACKSPACE);
244   NAMETRICK(KEY_,TAB);
245   NAMETRICK(KEY_,Q);
246   NAMETRICK(KEY_,W);
247   NAMETRICK(KEY_,E);
248   NAMETRICK(KEY_,R);
249   NAMETRICK(KEY_,T);
250   NAMETRICK(KEY_,Y);
251   NAMETRICK(KEY_,U);
252   NAMETRICK(KEY_,I);
253   NAMETRICK(KEY_,O);
254   NAMETRICK(KEY_,P);
255   NAMETRICK(KEY_,LEFTBRACE);
256   NAMETRICK(KEY_,RIGHTBRACE);
257   NAMETRICK(KEY_,ENTER);
258   NAMETRICK(KEY_,LEFTCTRL);
259   NAMETRICK(KEY_,A);
260   NAMETRICK(KEY_,S);
261   NAMETRICK(KEY_,D);
262   NAMETRICK(KEY_,F);
263   NAMETRICK(KEY_,G);
264   NAMETRICK(KEY_,H);
265   NAMETRICK(KEY_,J);
266   NAMETRICK(KEY_,K);
267   NAMETRICK(KEY_,L);
268   NAMETRICK(KEY_,SEMICOLON);
269   NAMETRICK(KEY_,APOSTROPHE);
270   NAMETRICK(KEY_,GRAVE);
271   NAMETRICK(KEY_,LEFTSHIFT);
272   NAMETRICK(KEY_,BACKSLASH);
273   NAMETRICK(KEY_,Z);
274   NAMETRICK(KEY_,X);
275   NAMETRICK(KEY_,C);
276   NAMETRICK(KEY_,V);
277   NAMETRICK(KEY_,B);
278   NAMETRICK(KEY_,N);
279   NAMETRICK(KEY_,M);
280   NAMETRICK(KEY_,COMMA);
281   NAMETRICK(KEY_,DOT);
282   NAMETRICK(KEY_,SLASH);
283   NAMETRICK(KEY_,RIGHTSHIFT);
284   NAMETRICK(KEY_,KPASTERISK);
285   NAMETRICK(KEY_,LEFTALT);
286   NAMETRICK(KEY_,SPACE);
287   NAMETRICK(KEY_,CAPSLOCK);
288   NAMETRICK(KEY_,F1);
289   NAMETRICK(KEY_,F2);
290   NAMETRICK(KEY_,F3);
291   NAMETRICK(KEY_,F4);
292   NAMETRICK(KEY_,F5);
293   NAMETRICK(KEY_,F6);
294   NAMETRICK(KEY_,F7);
295   NAMETRICK(KEY_,F8);
296   NAMETRICK(KEY_,F9);
297   NAMETRICK(KEY_,F10);
298   NAMETRICK(KEY_,NUMLOCK);
299   NAMETRICK(KEY_,SCROLLLOCK);
300   NAMETRICK(KEY_,KP7);
301   NAMETRICK(KEY_,KP8);
302   NAMETRICK(KEY_,KP9);
303   NAMETRICK(KEY_,KPMINUS);
304   NAMETRICK(KEY_,KP4);
305   NAMETRICK(KEY_,KP5);
306   NAMETRICK(KEY_,KP6);
307   NAMETRICK(KEY_,KPPLUS);
308   NAMETRICK(KEY_,KP1);
309   NAMETRICK(KEY_,KP2);
310   NAMETRICK(KEY_,KP3);
311   NAMETRICK(KEY_,KP0);
312   NAMETRICK(KEY_,KPDOT);
313   NAMETRICK(KEY_,F11);
314   NAMETRICK(KEY_,F12);
315   NAMETRICK(KEY_,KPENTER);
316   NAMETRICK(KEY_,RIGHTCTRL);
317   NAMETRICK(KEY_,KPSLASH);
318   NAMETRICK(KEY_,SYSRQ);
319   NAMETRICK(KEY_,RIGHTALT);
320   NAMETRICK(KEY_,LINEFEED);
321   NAMETRICK(KEY_,HOME);
322   NAMETRICK(KEY_,UP);
323   NAMETRICK(KEY_,PAGEUP);
324   NAMETRICK(KEY_,LEFT);
325   NAMETRICK(KEY_,RIGHT);
326   NAMETRICK(KEY_,END);
327   NAMETRICK(KEY_,DOWN);
328   NAMETRICK(KEY_,PAGEDOWN);
329   NAMETRICK(KEY_,INSERT);
330   NAMETRICK(KEY_,DELETE);
331   NAMETRICK(KEY_,MACRO);
332   NAMETRICK(KEY_,MUTE);
333   NAMETRICK(KEY_,VOLUMEDOWN);
334   NAMETRICK(KEY_,VOLUMEUP);
335   NAMETRICK(KEY_,POWER);
336   NAMETRICK(KEY_,KPEQUAL);
337   NAMETRICK(KEY_,KPPLUSMINUS);
338   NAMETRICK(KEY_,PLAY);
339   NAMETRICK(KEY_,PAUSE);
340   NAMETRICK(KEY_,SCALE);
341   NAMETRICK(KEY_,KPCOMMA);
342   NAMETRICK(KEY_,YEN);
343   NAMETRICK(KEY_,LEFTMETA);
344   NAMETRICK(KEY_,RIGHTMETA);
345   NAMETRICK(KEY_,COMPOSE);
346   NAMETRICK(KEY_,STOP);
347   NAMETRICK(KEY_,AGAIN);
348   NAMETRICK(KEY_,PROPS);
349   NAMETRICK(KEY_,UNDO);
350   NAMETRICK(KEY_,FRONT);
351   NAMETRICK(KEY_,COPY);
352   NAMETRICK(KEY_,OPEN);
353   NAMETRICK(KEY_,PASTE);
354   NAMETRICK(KEY_,FIND);
355   NAMETRICK(KEY_,CUT);
356   NAMETRICK(KEY_,HELP);
357   NAMETRICK(KEY_,MENU);
358   NAMETRICK(KEY_,CALC);
359   NAMETRICK(KEY_,SETUP);
360   NAMETRICK(KEY_,SLEEP);
361   NAMETRICK(KEY_,WAKEUP);
362   NAMETRICK(KEY_,FILE);
363   NAMETRICK(KEY_,SENDFILE);
364   NAMETRICK(KEY_,DELETEFILE);
365   NAMETRICK(KEY_,XFER);
366   NAMETRICK(KEY_,PROG1);
367   NAMETRICK(KEY_,PROG2);
368   NAMETRICK(KEY_,WWW);
369   NAMETRICK(KEY_,MSDOS);
370   NAMETRICK(KEY_,COFFEE);
371   NAMETRICK(KEY_,DIRECTION);
372   NAMETRICK(KEY_,CYCLEWINDOWS);
373   NAMETRICK(KEY_,MAIL);
374   NAMETRICK(KEY_,BOOKMARKS);
375   NAMETRICK(KEY_,COMPUTER);
376   NAMETRICK(KEY_,BACK);
377   NAMETRICK(KEY_,FORWARD);
378   NAMETRICK(KEY_,FASTFORWARD);
379   NAMETRICK(KEY_,CLOSECD);
380   NAMETRICK(KEY_,EJECTCD);
381   NAMETRICK(KEY_,EJECTCLOSECD);
382   NAMETRICK(KEY_,NEXTSONG);
383   NAMETRICK(KEY_,PLAYPAUSE);
384   NAMETRICK(KEY_,PREVIOUSSONG);
385   NAMETRICK(KEY_,STOPCD);
386   NAMETRICK(KEY_,RECORD);
387   NAMETRICK(KEY_,REWIND);
388   NAMETRICK(KEY_,PHONE);
389   NAMETRICK(KEY_,ISO);
390   NAMETRICK(KEY_,CONFIG);
391   NAMETRICK(KEY_,HOMEPAGE);
392   NAMETRICK(KEY_,REFRESH);
393   NAMETRICK(KEY_,EXIT);
394   NAMETRICK(KEY_,MOVE);
395   NAMETRICK(KEY_,EDIT);
396   NAMETRICK(KEY_,SCROLLUP);
397   NAMETRICK(KEY_,SCROLLDOWN);
398   NAMETRICK(KEY_,KPLEFTPAREN);
399   NAMETRICK(KEY_,KPRIGHTPAREN);
400   NAMETRICK(KEY_,NEW);
401   NAMETRICK(KEY_,REDO);
402   NAMETRICK(KEY_,OK);
403   NAMETRICK(KEY_,SELECT);
404   NAMETRICK(KEY_,GOTO);
405   NAMETRICK(KEY_,CLEAR);
406   NAMETRICK(KEY_,POWER2);
407   NAMETRICK(KEY_,OPTION);
408   NAMETRICK(KEY_,INFO);
409   NAMETRICK(KEY_,TIME);
410   NAMETRICK(KEY_,VENDOR);
411   NAMETRICK(KEY_,ARCHIVE);
412   NAMETRICK(KEY_,PROGRAM);
413   NAMETRICK(KEY_,CHANNEL);
414   NAMETRICK(KEY_,FAVORITES);
415   NAMETRICK(KEY_,EPG);
416   NAMETRICK(KEY_,PVR);
417   NAMETRICK(KEY_,MHP);
418   NAMETRICK(KEY_,LANGUAGE);
419   NAMETRICK(KEY_,TITLE);
420   NAMETRICK(KEY_,SUBTITLE);
421   NAMETRICK(KEY_,ANGLE);
422   NAMETRICK(KEY_,ZOOM);
423   NAMETRICK(KEY_,MODE);
424   NAMETRICK(KEY_,KEYBOARD);
425   NAMETRICK(KEY_,SCREEN);
426   NAMETRICK(KEY_,RED);
427   NAMETRICK(KEY_,GREEN);
428   NAMETRICK(KEY_,YELLOW);
429   NAMETRICK(KEY_,BLUE);
430   NAMETRICK(KEY_,CHANNELUP);
431   NAMETRICK(KEY_,CHANNELDOWN);
432   NAMETRICK(KEY_,FIRST);
433   NAMETRICK(KEY_,LAST);
434   NAMETRICK(KEY_,AB);
435   NAMETRICK(KEY_,NEXT);
436   NAMETRICK(KEY_,RESTART);
437   NAMETRICK(KEY_,SLOW);
438   NAMETRICK(KEY_,SHUFFLE);
439   NAMETRICK(KEY_,BREAK);
440   NAMETRICK(KEY_,PREVIOUS);
441   NAMETRICK(KEY_,DIGITS);
442   NAMETRICK(KEY_,TEEN);
443   NAMETRICK(KEY_,TWEN);
444   NAMETRICK(KEY_,VIDEOPHONE);
445   NAMETRICK(KEY_,GAMES);
446   NAMETRICK(KEY_,ZOOMIN);
447   NAMETRICK(KEY_,ZOOMOUT);
448   NAMETRICK(KEY_,ZOOMRESET);
449   NAMETRICK(KEY_,DOLLAR);
450   NAMETRICK(KEY_,EURO);
451   NAMETRICK(KEY_,MEDIA);
452   NAMETRICK(KEY_,FRAMEBACK);
453   NAMETRICK(KEY_,FRAMEFORWARD);
454   NAMETRICK(KEY_,CONTEXT_MENU);
455   NAMETRICK(KEY_,MEDIA_REPEAT);
456   NAMETRICK(KEY_,NUMERIC_0);
457   NAMETRICK(KEY_,NUMERIC_1);
458   NAMETRICK(KEY_,NUMERIC_2);
459   NAMETRICK(KEY_,NUMERIC_3);
460   NAMETRICK(KEY_,NUMERIC_4);
461   NAMETRICK(KEY_,NUMERIC_5);
462   NAMETRICK(KEY_,NUMERIC_6);
463   NAMETRICK(KEY_,NUMERIC_7);
464   NAMETRICK(KEY_,NUMERIC_8);
465   NAMETRICK(KEY_,NUMERIC_9);
466   NAMETRICK(KEY_,NUMERIC_STAR);
467   NAMETRICK(KEY_,NUMERIC_POUND);
468 }
469
470 const char* InputLinux::getHardCodedHardwareKeyNamesForVompKey(u1 vompKey)
471 {
472   switch (vompKey)
473   {
474   case DOWN:
475     return tr("Down");
476   case UP:
477     return tr("Up");
478   case LEFT:
479     return tr("Left");
480   case RIGHT:
481     return tr("Right");
482   case MENU:
483     return tr("M");
484   case BACK:
485     return tr("Backspace, Back");
486   case OK:
487     return tr("Return, Space");
488   default:
489     return "";
490   }
491 }
492
493 std::string InputLinux::getHardwareKeyName(int hardwareKey)
494 {
495   const char* desc = linux_keymap[hardwareKey];
496
497   std::string retval;
498
499   if (desc)
500   {
501     retval = desc;
502   }
503   else
504   {
505     char* rt = new char[10];
506     sprintf(rt, "0x%x", hardwareKey);
507     retval = rt;
508   }
509
510   return retval;
511 }
512
513 void InputLinux::EnterLearningMode(u1 vompKey)
514 {
515   learnMode = vompKey; // Armed
516 }
517
518 bool InputLinux::start()
519 {
520   LogNT::getInstance()->info(TAG, "start called");
521
522   threadStartProtect.lock(); // Make sure listenThread is fully initted before start returns
523   listenThread = std::thread( [this]
524   {
525     threadStartProtect.lock();
526     threadStartProtect.unlock();
527     listenLoop();
528   });
529   threadStartProtect.unlock();
530   return true;
531 }
532
533 void InputLinux::stop()
534 {
535   threadStartProtect.lock(); // Also use it to protect against starting while stopping
536
537   if (listenThread.joinable())
538   {
539     listenLoopStop = true;
540     write(pfds[1], "1", 1); // break the select in listenLoop
541     listenThread.join();
542   }
543
544   threadStartProtect.unlock();
545 }
546
547 void InputLinux::listenLoop()
548 {
549   fd_set readfds;
550   int maxfd;
551
552   if (pipe2(pfds, O_NONBLOCK) == -1)
553   {
554     LogNT::getInstance()->error(TAG, "pipe2() fail");
555     return;
556   }
557
558   LogNT::getInstance()->info(TAG, "Listen loop");
559
560   while(1)
561   {
562     if (listenLoopStop) break;
563
564     FD_ZERO(&readfds);
565
566     maxfd = 0;
567     for (unsigned int i = 0; i < devices.size(); i++)
568     {
569       int cur_fd = devices[i];
570       maxfd = std::max(cur_fd, maxfd);
571       FD_SET(cur_fd, &readfds);
572     }
573
574     FD_SET(pfds[0], &readfds);
575     maxfd = std::max(pfds[0], maxfd);
576
577     // 0 = nothing happened and timeout expired
578     // >0 = num of descriptors that changed
579     // -1 = error
580     if (select(maxfd + 1, &readfds, NULL, NULL, NULL) < 1)
581     {
582       LogNT::getInstance()->error(TAG, "Select fail");
583       break;
584     }
585
586     if (FD_ISSET(pfds[0], &readfds))
587     {
588       // assume quit signal
589       LogNT::getInstance()->info(TAG, "pfds quit");
590       break;
591
592       // FUTURE: read the byte and do different things? Read listenLoopStop and maybe other bools?
593     }
594
595     for (unsigned int i = 0; i < devices.size(); i++)
596     {
597       int cur_fd = devices[i];
598       if (FD_ISSET(cur_fd, &readfds))
599       {
600         struct input_event ev;
601         int count = read(cur_fd, &ev, sizeof(ev));
602         if (count == sizeof(ev))
603         {
604           if (ev.type == EV_KEY && ev.value == 1)
605           {
606             sendInputKey(TranslateHWC(ev.code));
607           }
608         }
609       }
610     }
611   }
612
613   close(pfds[1]);
614   close(pfds[0]);
615 }