vdr  2.2.0
remote.c
Go to the documentation of this file.
1 /*
2  * remote.c: General Remote Control handling
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: remote.c 3.3 2015/01/20 14:53:57 kls Exp $
8  */
9 
10 #include "remote.h"
11 #include <fcntl.h>
12 #define __STDC_FORMAT_MACROS // Required for format specifiers
13 #include <inttypes.h>
14 #include <netinet/in.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <sys/time.h>
18 #include <unistd.h>
19 #include "tools.h"
20 
21 // --- cRemote ---------------------------------------------------------------
22 
23 #define INITTIMEOUT 10000 // ms
24 #define REPEATTIMEOUT 1000 // ms
25 
26 eKeys cRemote::keys[MaxKeys];
27 int cRemote::in = 0;
28 int cRemote::out = 0;
31 char *cRemote::unknownCode = NULL;
34 const char *cRemote::keyMacroPlugin = NULL;
35 const char *cRemote::callPlugin = NULL;
36 bool cRemote::enabled = true;
37 time_t cRemote::lastActivity = 0;
38 
39 cRemote::cRemote(const char *Name)
40 {
41  name = Name ? strdup(Name) : NULL;
42  Remotes.Add(this);
43 }
44 
46 {
47  Remotes.Del(this, false);
48  free(name);
49 }
50 
51 const char *cRemote::GetSetup(void)
52 {
53  return Keys.GetSetup(Name());
54 }
55 
56 void cRemote::PutSetup(const char *Setup)
57 {
58  Keys.PutSetup(Name(), Setup);
59 }
60 
62 {
63  if (Ready()) {
64  char *NewCode = NULL;
65  eKeys Key = Get(INITTIMEOUT, &NewCode);
66  if (Key != kNone || NewCode)
67  return true;
68  }
69  return false;
70 }
71 
72 void cRemote::Clear(void)
73 {
74  cMutexLock MutexLock(&mutex);
75  in = out = 0;
76  if (learning) {
77  free(unknownCode);
78  unknownCode = NULL;
79  }
80 }
81 
82 bool cRemote::Put(eKeys Key, bool AtFront)
83 {
84  if (Key != kNone) {
85  cMutexLock MutexLock(&mutex);
86  if (in != out && (keys[out] & k_Repeat) && (Key & k_Release))
87  Clear();
88  int d = out - in;
89  if (d <= 0)
90  d = MaxKeys + d;
91  if (d - 1 > 0) {
92  if (AtFront) {
93  if (--out < 0)
94  out = MaxKeys - 1;
95  keys[out] = Key;
96  }
97  else {
98  keys[in] = Key;
99  if (++in >= MaxKeys)
100  in = 0;
101  }
103  return true;
104  }
105  return false;
106  }
107  return true; // only a real key shall report an overflow!
108 }
109 
111 {
112  const cKeyMacro *km = KeyMacros.Get(Key);
113  if (km) {
114  keyMacroPlugin = km->Plugin();
115  cMutexLock MutexLock(&mutex);
116  for (int i = km->NumKeys(); --i > 0; ) {
117  if (!Put(km->Macro()[i], true))
118  return false;
119  }
120  }
121  return true;
122 }
123 
124 bool cRemote::Put(uint64_t Code, bool Repeat, bool Release)
125 {
126  char buffer[32];
127  snprintf(buffer, sizeof(buffer), "%016" PRIX64, Code);
128  return Put(buffer, Repeat, Release);
129 }
130 
131 bool cRemote::Put(const char *Code, bool Repeat, bool Release)
132 {
133  if (learning && this != learning)
134  return false;
135  eKeys Key = Keys.Get(Name(), Code);
136  if (Key != kNone) {
137  if (Repeat)
138  Key = eKeys(Key | k_Repeat);
139  if (Release)
140  Key = eKeys(Key | k_Release);
141  return Put(Key);
142  }
143  if (learning) {
144  free(unknownCode);
145  unknownCode = strdup(Code);
147  }
148  return false;
149 }
150 
151 bool cRemote::CallPlugin(const char *Plugin)
152 {
153  cMutexLock MutexLock(&mutex);
154  if (!callPlugin) {
155  callPlugin = Plugin;
156  Put(k_Plugin);
157  return true;
158  }
159  return false;
160 }
161 
162 const char *cRemote::GetPlugin(void)
163 {
164  cMutexLock MutexLock(&mutex);
165  const char *p = keyMacroPlugin;
166  if (p)
167  keyMacroPlugin = NULL;
168  else {
169  p = callPlugin;
170  callPlugin = NULL;
171  }
172  return p;
173 }
174 
176 {
177  cMutexLock MutexLock(&mutex);
178  return in != out && !(keys[out] & k_Repeat);
179 }
180 
181 eKeys cRemote::Get(int WaitMs, char **UnknownCode)
182 {
183  for (;;) {
184  cMutexLock MutexLock(&mutex);
185  if (in != out) {
186  eKeys k = keys[out];
187  if (++out >= MaxKeys)
188  out = 0;
189  if ((k & k_Repeat) != 0)
192  return enabled ? k : kNone;
193  }
194  else if (!WaitMs || !keyPressed.TimedWait(mutex, WaitMs) && repeatTimeout.TimedOut())
195  return kNone;
196  else if (learning && UnknownCode && unknownCode) {
197  *UnknownCode = unknownCode;
198  unknownCode = NULL;
199  return kNone;
200  }
201  }
202 }
203 
205 {
206  lastActivity = time(NULL);
207 }
208 
209 // --- cRemotes --------------------------------------------------------------
210 
212 
213 // --- cKbdRemote ------------------------------------------------------------
214 
215 struct tKbdMap {
217  uint64_t code;
218  };
219 
220 static tKbdMap KbdMap[] = {
221  { kfF1, 0x0000001B5B31317EULL },
222  { kfF2, 0x0000001B5B31327EULL },
223  { kfF3, 0x0000001B5B31337EULL },
224  { kfF4, 0x0000001B5B31347EULL },
225  { kfF5, 0x0000001B5B31357EULL },
226  { kfF6, 0x0000001B5B31377EULL },
227  { kfF7, 0x0000001B5B31387EULL },
228  { kfF8, 0x0000001B5B31397EULL },
229  { kfF9, 0x0000001B5B32307EULL },
230  { kfF10, 0x0000001B5B32317EULL },
231  { kfF11, 0x0000001B5B32327EULL },
232  { kfF12, 0x0000001B5B32337EULL },
233  { kfUp, 0x00000000001B5B41ULL },
234  { kfDown, 0x00000000001B5B42ULL },
235  { kfLeft, 0x00000000001B5B44ULL },
236  { kfRight, 0x00000000001B5B43ULL },
237  { kfHome, 0x00000000001B5B48ULL },
238  { kfEnd, 0x00000000001B5B46ULL },
239  { kfPgUp, 0x000000001B5B357EULL },
240  { kfPgDown, 0x000000001B5B367EULL },
241  { kfIns, 0x000000001B5B327EULL },
242  { kfDel, 0x000000001B5B337EULL },
243  { kfNone, 0x0000000000000000ULL }
244  };
245 
246 bool cKbdRemote::kbdAvailable = false;
247 bool cKbdRemote::rawMode = false;
248 
250 :cRemote("KBD")
251 ,cThread("KBD remote control")
252 {
253  tcgetattr(STDIN_FILENO, &savedTm);
254  struct termios tm;
255  if (tcgetattr(STDIN_FILENO, &tm) == 0) {
256  tm.c_iflag = 0;
257  tm.c_lflag &= ~(ICANON | ECHO);
258  tm.c_cc[VMIN] = 0;
259  tm.c_cc[VTIME] = 0;
260  tcsetattr(STDIN_FILENO, TCSANOW, &tm);
261  }
262  kbdAvailable = true;
264  Start();
265 }
266 
268 {
269  kbdAvailable = false;
270  Cancel(3);
271  tcsetattr(STDIN_FILENO, TCSANOW, &savedTm);
272 }
273 
274 void cKbdRemote::SetRawMode(bool RawMode)
275 {
276  rawMode = RawMode;
277 }
278 
279 uint64_t cKbdRemote::MapFuncToCode(int Func)
280 {
281  for (tKbdMap *p = KbdMap; p->func != kfNone; p++) {
282  if (p->func == Func)
283  return p->code;
284  }
285  return (Func <= 0xFF) ? Func : 0;
286 }
287 
288 int cKbdRemote::MapCodeToFunc(uint64_t Code)
289 {
290  for (tKbdMap *p = KbdMap; p->func != kfNone; p++) {
291  if (p->code == Code)
292  return p->func;
293  }
294  if (Code <= 0xFF)
295  return Code;
296  return kfNone;
297 }
298 
299 void cKbdRemote::PutKey(uint64_t Code, bool Repeat, bool Release)
300 {
301  if (rawMode || (!Put(Code, Repeat, Release) && !IsLearning())) {
302  if (int func = MapCodeToFunc(Code))
303  Put(KBDKEY(func), Repeat, Release);
304  }
305 }
306 
308 {
309  cPoller Poller(STDIN_FILENO);
310  if (Poller.Poll(50)) {
311  uchar ch = 0;
312  int r = safe_read(STDIN_FILENO, &ch, 1);
313  if (r == 1)
314  return ch;
315  if (r < 0)
316  LOG_ERROR_STR("cKbdRemote");
317  }
318  return -1;
319 }
320 
322 {
323  uint64_t k = 0;
324  int key1;
325 
326  if ((key1 = ReadKey()) >= 0) {
327  k = key1;
328  if (systemIsUtf8 && (key1 & 0xC0) == 0xC0) {
329  char bytes[4] = { 0 };
330  bytes[0] = key1;
331  int bytescount = 1;
332  if ((key1 & 0xF0) == 0xF0)
333  bytescount = 3;
334  else if ((key1 & 0xE0) == 0xE0)
335  bytescount = 2;
336  for (int i = 0; i < bytescount; i++) {
337  if ((key1 = ReadKey()) >= 0)
338  bytes[i + 1] = key1;
339  }
340  k = Utf8CharGet(bytes);
341  if (k > 0xFF)
342  k = 0;
343  }
344  else if (key1 == 0x1B) {
345  // Start of escape sequence
346  if ((key1 = ReadKey()) >= 0) {
347  k <<= 8;
348  k |= key1 & 0xFF;
349  switch (key1) {
350  case 0x4F: // 3-byte sequence
351  if ((key1 = ReadKey()) >= 0) {
352  k <<= 8;
353  k |= key1 & 0xFF;
354  }
355  break;
356  case 0x5B: // 3- or more-byte sequence
357  if ((key1 = ReadKey()) >= 0) {
358  k <<= 8;
359  k |= key1 & 0xFF;
360  switch (key1) {
361  case 0x31 ... 0x3F: // more-byte sequence
362  case 0x5B: // strange, may apparently occur
363  do {
364  if ((key1 = ReadKey()) < 0)
365  break; // Sequence ends here
366  k <<= 8;
367  k |= key1 & 0xFF;
368  } while (key1 != 0x7E);
369  break;
370  default: ;
371  }
372  }
373  break;
374  default: ;
375  }
376  }
377  }
378  }
379  return k;
380 }
381 
383 {
384  cTimeMs FirstTime;
385  cTimeMs LastTime;
386  uint64_t FirstCommand = 0;
387  uint64_t LastCommand = 0;
388  bool Delayed = false;
389  bool Repeat = false;
390 
391  while (Running()) {
392  uint64_t Command = ReadKeySequence();
393  if (Command) {
394  if (Command == LastCommand) {
395  // If two keyboard events with the same command come in without an intermediate
396  // timeout, this is a long key press that caused the repeat function to kick in:
397  Delayed = false;
398  FirstCommand = 0;
399  if (FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
400  continue; // repeat function kicks in after a short delay
401  if (LastTime.Elapsed() < (uint)Setup.RcRepeatDelta)
402  continue; // skip same keys coming in too fast
403  PutKey(Command, true);
404  Repeat = true;
405  LastTime.Set();
406  }
407  else if (Command == FirstCommand) {
408  // If the same command comes in twice with an intermediate timeout, we
409  // need to delay the second command to see whether it is going to be
410  // a repeat function or a separate key press:
411  Delayed = true;
412  }
413  else {
414  // This is a totally new key press, so we accept it immediately:
415  PutKey(Command);
416  Delayed = false;
417  FirstCommand = Command;
418  FirstTime.Set();
419  }
420  }
421  else if (Repeat) {
422  // Timeout after a repeat function, so we generate a 'release':
423  PutKey(LastCommand, false, true);
424  Repeat = false;
425  }
426  else if (Delayed && FirstCommand) {
427  // Timeout after two normal key presses of the same key, so accept the
428  // delayed key:
429  PutKey(FirstCommand);
430  Delayed = false;
431  FirstCommand = 0;
432  FirstTime.Set();
433  }
434  LastCommand = Command;
435  }
436 }
char * name
Definition: remote.h:35
Definition: remote.h:92
Definition: remote.h:90
int ReadKey(void)
Definition: remote.c:307
unsigned char uchar
Definition: tools.h:30
Definition: remote.h:80
Definition: remote.h:98
void Set(int Ms=0)
Definition: tools.c:738
const char * GetSetup(void)
Definition: remote.c:51
Definition: remote.h:85
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:2014
static void SetRawMode(bool RawMode)
Definition: remote.c:274
uint64_t ReadKeySequence(void)
Definition: remote.c:321
bool systemIsUtf8
Definition: remote.h:109
static const char * keyMacroPlugin
Definition: remote.h:32
static const char * SystemCharacterTable(void)
Definition: tools.h:164
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: remote.c:382
virtual ~cKbdRemote()
Definition: remote.c:267
Definition: remote.h:94
Definition: remote.h:86
static tKbdMap KbdMap[]
Definition: remote.c:220
static eKeys Get(int WaitMs=1000, char **UnknownCode=NULL)
Definition: remote.c:181
Definition: keys.h:61
static bool rawMode
Definition: remote.h:108
Definition: remote.h:83
#define LOG_ERROR_STR(s)
Definition: tools.h:39
void PutSetup(const char *Remote, const char *Setup)
Definition: keys.c:191
#define KBDKEY(k)
Definition: keys.h:84
static void Clear(void)
Definition: remote.c:72
Definition: remote.h:84
cRemotes Remotes
Definition: remote.c:211
static bool enabled
Definition: remote.h:34
Definition: remote.h:88
bool Poll(int TimeoutMs=0)
Definition: tools.c:1443
static time_t lastActivity
Definition: remote.h:31
Definition: remote.h:81
Definition: keys.h:55
Definition: keys.h:58
Definition: remote.h:87
static bool IsLearning()
Definition: remote.h:48
void PutSetup(const char *Setup)
Definition: remote.c:56
cKbdRemote(void)
Definition: remote.c:249
static bool PutMacro(eKeys Key)
Definition: remote.c:110
Definition: remote.h:96
void PutKey(uint64_t Code, bool Repeat=false, bool Release=false)
Definition: remote.c:299
Definition: remote.h:91
#define REPEATTIMEOUT
Definition: remote.c:24
eKeys Get(const char *Remote, const char *Code)
Definition: keys.c:169
void Broadcast(void)
Definition: thread.c:135
bool TimedOut(void) const
Definition: tools.c:743
const eKeys * Macro(void) const
Definition: keys.h:135
static cCondVar keyPressed
Definition: remote.h:30
const cKeyMacro * Get(eKeys Key)
Definition: keys.c:269
cKeys Keys
Definition: keys.c:156
virtual ~cRemote()
Definition: remote.c:45
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition: thread.c:273
cSetup Setup
Definition: config.c:373
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition: remote.c:124
static bool HasKeys(void)
Definition: remote.c:175
virtual bool Ready(void)
Definition: remote.h:44
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:99
static const char * GetPlugin(void)
Returns the name of the plugin that was set with a previous call to PutMacro() or CallPlugin()...
Definition: remote.c:162
Definition: thread.h:63
bool TimedWait(cMutex &Mutex, int TimeoutMs)
Definition: thread.c:117
static char * unknownCode
Definition: remote.h:28
eKbdFunc
Definition: remote.h:79
cRemote(const char *Name)
Definition: remote.c:39
int RcRepeatDelay
Definition: config.h:299
static cRemote * learning
Definition: remote.h:27
uint64_t code
Definition: remote.c:217
static void TriggerLastActivity(void)
Simulates user activity, for instance to keep the current menu open even if no remote control key has...
Definition: remote.c:204
Definition: remote.h:20
virtual bool Initialize(void)
Definition: remote.c:61
const char * Plugin(void) const
Definition: keys.h:136
static int out
Definition: remote.h:25
Definition: remote.h:93
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:2046
static bool kbdAvailable
Definition: remote.h:107
Definition: remote.h:99
Definition: remote.h:82
Definition: remote.h:89
cKeyMacros KeyMacros
Definition: keys.c:267
int NumKeys(void) const
Returns the number of keys in this macro.
Definition: keys.h:131
struct termios savedTm
Definition: remote.h:110
#define INITTIMEOUT
Definition: remote.c:23
static uint64_t MapFuncToCode(int Func)
Definition: remote.c:279
uint Utf8CharGet(const char *s, int Length)
Returns the UTF-8 symbol at the beginning of the given string.
Definition: tools.c:771
const char * Name(void)
Definition: remote.h:46
Definition: thread.h:77
static int in
Definition: remote.h:24
Definition: keys.h:62
ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition: tools.c:53
Definition: tools.h:333
static cMutex mutex
Definition: remote.h:29
Definition: remote.h:102
static const char * callPlugin
Definition: remote.h:33
static eKeys keys[MaxKeys]
Definition: remote.h:23
Definition: remote.h:101
Definition: remote.h:95
static cTimeMs repeatTimeout
Definition: remote.h:26
Definition: tools.h:357
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting &#39;running&#39; to false, so that the Action() loop can finish in an or...
Definition: thread.c:323
int MapCodeToFunc(uint64_t Code)
Definition: remote.c:288
eKbdFunc func
Definition: remote.c:216
Definition: remote.h:97
const char * GetSetup(const char *Remote)
Definition: keys.c:180
eKeys
Definition: keys.h:16
int RcRepeatDelta
Definition: config.h:300
uint64_t Elapsed(void) const
Definition: tools.c:748
static bool CallPlugin(const char *Plugin)
Initiates calling the given plugin&#39;s main menu function.
Definition: remote.c:151