vdr 2.7.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 4.1 2019/05/13 13:01:44 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
26eKeys cRemote::keys[MaxKeys];
27int cRemote::in = 0;
28int cRemote::out = 0;
31char *cRemote::unknownCode = NULL;
34const char *cRemote::keyMacroPlugin = NULL;
35const char *cRemote::callPlugin = NULL;
36bool cRemote::enabled = true;
37time_t cRemote::lastActivity = 0;
38
40{
41 name = Name ? strdup(Name) : NULL;
42 Remotes.Add(this);
43}
44
46{
47 Remotes.Del(this, false);
48 free(name);
49}
50
51const char *cRemote::GetSetup(void)
52{
53 return Keys.GetSetup(Name());
54}
55
56void 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
73{
74 cMutexLock MutexLock(&mutex);
75 in = out = 0;
76 if (learning) {
77 free(unknownCode);
78 unknownCode = NULL;
79 }
80}
81
82bool 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 }
102 keyPressed.Broadcast();
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
124bool 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
131bool 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);
146 keyPressed.Broadcast();
147 }
148 return false;
149}
150
151bool 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
162const 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
181eKeys 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
215struct tKbdMap {
217 uint64_t code;
218 };
219
220static 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
246bool cKbdRemote::kbdAvailable = false;
247bool 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
274void cKbdRemote::SetRawMode(bool RawMode)
275{
276 rawMode = RawMode;
277}
278
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
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
299void 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(Setup.RcRepeatDelta * 3 / 2)) {
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 else if (FirstCommand && FirstTime.Elapsed() > (uint)Setup.RcRepeatDelay) {
435 // Don't wait too long for that second key press:
436 Delayed = false;
437 FirstCommand = 0;
438 }
439 LastCommand = Command;
440 }
441}
static const char * SystemCharacterTable(void)
Definition tools.h:174
static bool rawMode
Definition remote.h:108
uint64_t ReadKeySequence(void)
Definition remote.c:321
int ReadKey(void)
Definition remote.c:307
int MapCodeToFunc(uint64_t Code)
Definition remote.c:288
cKbdRemote(void)
Definition remote.c:249
void PutKey(uint64_t Code, bool Repeat=false, bool Release=false)
Definition remote.c:299
bool systemIsUtf8
Definition remote.h:109
virtual void Action(void) override
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition remote.c:382
static uint64_t MapFuncToCode(int Func)
Definition remote.c:279
struct termios savedTm
Definition remote.h:110
virtual ~cKbdRemote() override
Definition remote.c:267
static void SetRawMode(bool RawMode)
Definition remote.c:274
static bool kbdAvailable
Definition remote.h:107
const eKeys * Macro(void) const
Definition keys.h:135
int NumKeys(void) const
Returns the number of keys in this macro.
Definition keys.h:131
const char * Plugin(void) const
Definition keys.h:136
bool Poll(int TimeoutMs=0)
Definition tools.c:1585
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
static cRemote * learning
Definition remote.h:27
static const char * keyMacroPlugin
Definition remote.h:32
static time_t lastActivity
Definition remote.h:31
const char * Name(void)
Definition remote.h:46
static bool IsLearning()
Definition remote.h:48
char * name
Definition remote.h:35
static eKeys Get(int WaitMs=1000, char **UnknownCode=NULL)
Definition remote.c:181
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition remote.c:124
static void Clear(void)
Definition remote.c:72
static cCondVar keyPressed
Definition remote.h:30
static char * unknownCode
Definition remote.h:28
static bool PutMacro(eKeys Key)
Definition remote.c:110
static cTimeMs repeatTimeout
Definition remote.h:26
static bool HasKeys(void)
Definition remote.c:175
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
const char * GetSetup(void)
Definition remote.c:51
static bool CallPlugin(const char *Plugin)
Initiates calling the given plugin's main menu function.
Definition remote.c:151
cRemote(const char *Name)
Definition remote.c:39
static int in
Definition remote.h:24
static int out
Definition remote.h:25
static const char * callPlugin
Definition remote.h:33
virtual bool Ready(void)
Definition remote.h:44
static eKeys keys[MaxKeys]
Definition remote.h:23
void PutSetup(const char *Setup)
Definition remote.c:56
virtual bool Initialize(void)
Definition remote.c:61
virtual ~cRemote() override
Definition remote.c:45
static bool enabled
Definition remote.h:34
@ MaxKeys
Definition remote.h:22
static cMutex mutex
Definition remote.h:29
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:305
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition thread.h:101
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
Definition thread.c:239
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:355
uint64_t Elapsed(void) const
Definition tools.c:818
void Set(int Ms=0)
Sets the timer.
Definition tools.c:808
cSetup Setup
Definition config.c:372
cKeyMacros KeyMacros
Definition keys.c:267
cKeys Keys
Definition keys.c:156
#define KBDKEY(k)
Definition keys.h:84
eKeys
Definition keys.h:16
@ kNone
Definition keys.h:55
@ k_Release
Definition keys.h:62
@ k_Plugin
Definition keys.h:58
@ k_Repeat
Definition keys.h:61
cRemotes Remotes
Definition remote.c:211
static tKbdMap KbdMap[]
Definition remote.c:220
#define REPEATTIMEOUT
Definition remote.c:24
#define INITTIMEOUT
Definition remote.c:23
cRemotes Remotes
Definition remote.c:211
eKbdFunc
Definition remote.h:79
@ kfNone
Definition remote.h:80
@ kfF10
Definition remote.h:90
@ kfF11
Definition remote.h:91
@ kfF7
Definition remote.h:87
@ kfUp
Definition remote.h:93
@ kfF9
Definition remote.h:89
@ kfDown
Definition remote.h:94
@ kfF3
Definition remote.h:83
@ kfRight
Definition remote.h:96
@ kfF2
Definition remote.h:82
@ kfF4
Definition remote.h:84
@ kfPgUp
Definition remote.h:99
@ kfF8
Definition remote.h:88
@ kfLeft
Definition remote.h:95
@ kfF6
Definition remote.h:86
@ kfIns
Definition remote.h:101
@ kfDel
Definition remote.h:102
@ kfF12
Definition remote.h:92
@ kfEnd
Definition remote.h:98
@ kfF1
Definition remote.h:81
@ kfF5
Definition remote.h:85
@ kfHome
Definition remote.h:97
@ kfPgDown
Definition remote.h:100
uint64_t code
Definition remote.c:217
eKbdFunc func
Definition remote.c:216
ssize_t safe_read(int filedes, void *buffer, size_t size)
Definition tools.c:53
uint Utf8CharGet(const char *s, int Length)
Returns the UTF-8 symbol at the beginning of the given string.
Definition tools.c:841
#define LOG_ERROR_STR(s)
Definition tools.h:40
unsigned char uchar
Definition tools.h:31