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