vdr  2.0.7
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 2.8.1.1 2014/02/15 12:44:48 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;
263  Start();
264 }
265 
267 {
268  kbdAvailable = false;
269  Cancel(3);
270  tcsetattr(STDIN_FILENO, TCSANOW, &savedTm);
271 }
272 
273 void cKbdRemote::SetRawMode(bool RawMode)
274 {
275  rawMode = RawMode;
276 }
277 
278 uint64_t cKbdRemote::MapFuncToCode(int Func)
279 {
280  for (tKbdMap *p = KbdMap; p->func != kfNone; p++) {
281  if (p->func == Func)
282  return p->code;
283  }
284  return (Func <= 0xFF) ? Func : 0;
285 }
286 
287 int cKbdRemote::MapCodeToFunc(uint64_t Code)
288 {
289  for (tKbdMap *p = KbdMap; p->func != kfNone; p++) {
290  if (p->code == Code)
291  return p->func;
292  }
293  if (Code <= 0xFF)
294  return Code;
295  return kfNone;
296 }
297 
298 void cKbdRemote::PutKey(uint64_t Code, bool Repeat, bool Release)
299 {
300  if (rawMode || (!Put(Code, Repeat, Release) && !IsLearning())) {
301  if (int func = MapCodeToFunc(Code))
302  Put(KBDKEY(func), Repeat, Release);
303  }
304 }
305 
307 {
308  cPoller Poller(STDIN_FILENO);
309  if (Poller.Poll(50)) {
310  uchar ch = 0;
311  int r = safe_read(STDIN_FILENO, &ch, 1);
312  if (r == 1)
313  return ch;
314  if (r < 0)
315  LOG_ERROR_STR("cKbdRemote");
316  }
317  return -1;
318 }
319 
321 {
322  uint64_t k = 0;
323  int key1;
324 
325  if ((key1 = ReadKey()) >= 0) {
326  k = key1;
327  if (key1 == 0x1B) {
328  // Start of escape sequence
329  if ((key1 = ReadKey()) >= 0) {
330  k <<= 8;
331  k |= key1 & 0xFF;
332  switch (key1) {
333  case 0x4F: // 3-byte sequence
334  if ((key1 = ReadKey()) >= 0) {
335  k <<= 8;
336  k |= key1 & 0xFF;
337  }
338  break;
339  case 0x5B: // 3- or more-byte sequence
340  if ((key1 = ReadKey()) >= 0) {
341  k <<= 8;
342  k |= key1 & 0xFF;
343  switch (key1) {
344  case 0x31 ... 0x3F: // more-byte sequence
345  case 0x5B: // strange, may apparently occur
346  do {
347  if ((key1 = ReadKey()) < 0)
348  break; // Sequence ends here
349  k <<= 8;
350  k |= key1 & 0xFF;
351  } while (key1 != 0x7E);
352  break;
353  default: ;
354  }
355  }
356  break;
357  default: ;
358  }
359  }
360  }
361  }
362  return k;
363 }
364 
366 {
367  cTimeMs FirstTime;
368  cTimeMs LastTime;
369  uint64_t FirstCommand = 0;
370  uint64_t LastCommand = 0;
371  bool Delayed = false;
372  bool Repeat = false;
373 
374  while (Running()) {
375  uint64_t Command = ReadKeySequence();
376  if (Command) {
377  if (Command == LastCommand) {
378  // If two keyboard events with the same command come in without an intermediate
379  // timeout, this is a long key press that caused the repeat function to kick in:
380  Delayed = false;
381  FirstCommand = 0;
382  if (FirstTime.Elapsed() < (uint)Setup.RcRepeatDelay)
383  continue; // repeat function kicks in after a short delay
384  if (LastTime.Elapsed() < (uint)Setup.RcRepeatDelta)
385  continue; // skip same keys coming in too fast
386  PutKey(Command, true);
387  Repeat = true;
388  LastTime.Set();
389  }
390  else if (Command == FirstCommand) {
391  // If the same command comes in twice with an intermediate timeout, we
392  // need to delay the second command to see whether it is going to be
393  // a repeat function or a separate key press:
394  Delayed = true;
395  }
396  else {
397  // This is a totally new key press, so we accept it immediately:
398  PutKey(Command);
399  Delayed = false;
400  FirstCommand = Command;
401  FirstTime.Set();
402  }
403  }
404  else if (Repeat) {
405  // Timeout after a repeat function, so we generate a 'release':
406  PutKey(LastCommand, false, true);
407  Repeat = false;
408  }
409  else if (Delayed && FirstCommand) {
410  // Timeout after two normal key presses of the same key, so accept the
411  // delayed key:
412  PutKey(FirstCommand);
413  Delayed = false;
414  FirstCommand = 0;
415  FirstTime.Set();
416  }
417  LastCommand = Command;
418  }
419 }
char * name
Definition: remote.h:35
Definition: remote.h:92
Definition: remote.h:90
int ReadKey(void)
Definition: remote.c:306
unsigned char uchar
Definition: tools.h:30
Definition: remote.h:80
Definition: remote.h:98
void Set(int Ms=0)
Definition: tools.c:689
const char * GetSetup(void)
Definition: remote.c:51
Definition: remote.h:85
void Add(cListObject *Object, cListObject *After=NULL)
Definition: tools.c:1945
static void SetRawMode(bool RawMode)
Definition: remote.c:273
uint64_t ReadKeySequence(void)
Definition: remote.c:320
static const char * keyMacroPlugin
Definition: remote.h:32
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:365
virtual ~cKbdRemote()
Definition: remote.c:266
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
bool TimedOut(void)
Definition: tools.c:694
#define LOG_ERROR_STR(s)
Definition: tools.h:39
void PutSetup(const char *Remote, const char *Setup)
Definition: keys.c:191
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:1374
uint64_t Elapsed(void)
Definition: tools.c:699
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:298
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
const eKeys * Macro(void) const
Definition: keys.h:133
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)
Actually starts the thread.
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:294
#define KBDKEY(k)
Definition: keys.h:82
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:134
static int out
Definition: remote.h:25
Definition: remote.h:93
void Del(cListObject *Object, bool DeleteObject=true)
Definition: tools.c:1977
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:129
struct termios savedTm
Definition: remote.h:109
#define INITTIMEOUT
Definition: remote.c:23
static uint64_t MapFuncToCode(int Func)
Definition: remote.c:278
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:323
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:347
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition: thread.c:323
int MapCodeToFunc(uint64_t Code)
Definition: remote.c:287
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:295
static bool CallPlugin(const char *Plugin)
Initiates calling the given plugin's main menu function.
Definition: remote.c:151