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