Ticket #324: lcddevice.cpp

File lcddevice.cpp, 15.3 KB (added by Paul, 20 years ago)

since there are a lot of changes to lcddevice.cpp it might be easier just to replace the entire file?

Line 
1/*
2 lcddevice.cpp
3
4 a MythTV project object to control an
5 LCDproc server
6
7 (c) 2002, 2003 Thor Sigvaldason, Dan Morphis and Isaac Richards
8*/
9
10#include "lcddevice.h"
11#include "mythcontext.h"
12#include "mythdialogs.h"
13
14#include <unistd.h>
15#include <cmath>
16
17#include <qapplication.h>
18#include <qregexp.h>
19
20
21/*
22 LCD_DEVICE_DEBUG control how much debug info we get
23 0 = none
24 1 = LCDServer info
25 2 = screen switch info
26 5 = every command received
27 10 = every command sent and error received
28 */
29
30#define LCD_DEVICE_DEBUG 0
31
32LCD::LCD()
33 :QObject(NULL, "LCD")
34{
35 // Constructor for LCD
36 //
37 // Note that this does *not* include opening the socket and initiating
38 // communications with the LDCd daemon.
39
40#if LCD_DEVICE_DEBUG > 0
41 VERBOSE(VB_ALL, "lcddevice: An LCD object now exists (LCD() was called)");
42#endif
43
44 GetLEDMask = NULL;
45
46 socket = new QSocket(this);
47 connect(socket, SIGNAL(error(int)), this, SLOT(veryBadThings(int)));
48 connect(socket, SIGNAL(readyRead()), this, SLOT(serverSendingData()));
49
50 lcd_ready = false;
51
52 hostname = "localhost";
53 port = 6545;
54
55 connected = false;
56 send_buffer = "";
57
58 retryTimer = new QTimer(this);
59 connect(retryTimer, SIGNAL(timeout()), this, SLOT(restartConnection()));
60
61 LEDTimer = new QTimer(this);
62 connect(LEDTimer, SIGNAL(timeout()), this, SLOT(outputLEDs()));
63}
64
65bool LCD::m_server_unavailable = false;
66class LCD * LCD::m_lcd = NULL;
67
68class LCD * LCD::Get(void)
69{
70 if (m_lcd == NULL && m_server_unavailable == false)
71 m_lcd = new LCD;
72 return m_lcd;
73}
74
75void LCD::SetupLCD (void)
76{
77 QString lcd_host;
78 int lcd_port;
79
80 if (m_lcd)
81 {
82 delete m_lcd;
83 m_lcd = NULL;
84 m_server_unavailable = false;
85 }
86
87 lcd_host = gContext->GetSetting("LCDServerHost", "localhost");
88 lcd_port = gContext->GetNumSetting("LCDServerPort", 6545);
89
90 if (lcd_host.length() > 0 && lcd_port > 1024)
91 {
92 class LCD * lcd = LCD::Get();
93 if (lcd->connectToHost(lcd_host, lcd_port) == false)
94 {
95 delete m_lcd;
96 m_lcd = NULL;
97 m_server_unavailable = false;
98 }
99 }
100}
101
102bool LCD::connectToHost(const QString &lhostname, unsigned int lport)
103{
104#if LCD_DEVICE_DEBUG > 0
105 VERBOSE(VB_ALL, "lcddevice: connecting to host: "
106 << lhostname << " - port: " << lport);
107#endif
108
109 // Open communications
110 // Store the hostname and port in case we need to reconnect.
111
112 int timeout = 1000;
113 hostname = lhostname;
114 port = lport;
115
116 // Don't even try to connect if we're currently disabled.
117 if (!gContext->GetNumSetting("LCDEnable", 0))
118 {
119 connected = false;
120 m_server_unavailable = true;
121 return connected;
122 }
123
124 // check if the 'mythlcdserver' is running
125 int res = system("ret=`ps cax | grep -c mythlcdserver`; exit $ret");
126 if (WIFEXITED(res))
127 res = WEXITSTATUS(res);
128
129 if (res == 0)
130 {
131 // we need to start the mythlcdserver
132 system(gContext->GetInstallPrefix() + "/bin/mythlcdserver&");
133 }
134
135 if (!connected)
136 {
137 QTextStream os(socket);
138 socket->connectToHost(hostname, port);
139
140 while (--timeout && socket->state() != QSocket::Idle)
141 {
142 qApp->lock();
143 qApp->processEvents();
144 qApp->unlock();
145 usleep(1000);
146
147 if (socket->state() == QSocket::Connected)
148 {
149 lcd_ready = true;
150 connected = true;
151 os << "HELLO\n";
152 break;
153 }
154 }
155 }
156
157 if (connected == false)
158 m_server_unavailable = true;
159
160 return connected;
161}
162
163void LCD::sendToServer(const QString &someText)
164{
165 // Check the socket, make sure the connection is still up
166 if (socket->state() == QSocket::Idle)
167 {
168 if (!lcd_ready)
169 return;
170
171 lcd_ready = false;
172
173 // Ack, connection to server has been severed try to re-establish the
174 // connection
175 retryTimer->start(10000, false);
176 VERBOSE(VB_ALL, "lcddevice: Connection to LCDServer died unexpectedly.\n\t\t\t"
177 "Trying to reconnect every 10 seconds. . .");
178 return;
179 }
180
181 QTextStream os(socket);
182
183 last_command = someText;
184
185 if (connected)
186 {
187#if LCD_DEVICE_DEBUG > 9
188 VERBOSE(VB_ALL, "lcddevice: Sending to Server: " << someText);
189#endif
190 // Just stream the text out the socket
191
192 os << someText << "\n";
193 }
194 else
195 {
196 // Buffer this up in the hope that the connection will open soon
197
198 send_buffer += someText;
199 send_buffer += "\n";
200 }
201}
202
203void LCD::restartConnection()
204{
205 // Reset the flag
206 lcd_ready = false;
207 connected = false;
208 m_server_unavailable = false;
209
210 // Retry to connect. . . Maybe the user restarted LCDd?
211 connectToHost(hostname, port);
212}
213
214void LCD::serverSendingData()
215{
216 QString lineFromServer, tempString;
217 QStringList aList;
218 QStringList::Iterator it;
219
220 // This gets activated automatically by the QSocket class whenever
221 // there's something to read.
222 //
223 // We currently spend most of our time (except for the first line sent
224 // back) ignoring it.
225 //
226 // Note that if anyone has an LCDproc type lcd with buttons on it, this is
227 // where we would want to catch button presses and make the rest of
228 // mythTV/mythMusic do something (change tracks, channels, etc.)
229
230 while(socket->canReadLine())
231 {
232 lineFromServer = socket->readLine();
233 lineFromServer = lineFromServer.replace( QRegExp("\n"), "" );
234 lineFromServer = lineFromServer.replace( QRegExp("\r"), "" );
235 lineFromServer.simplifyWhiteSpace();
236
237#if LCD_DEVICE_DEBUG > 4
238 // Make debugging be less noisy
239 if (lineFromServer != "OK\n")
240 VERBOSE(VB_ALL, "lcddevice: Received from server: " << lineFromServer);
241#endif
242
243 aList = QStringList::split(" ", lineFromServer);
244 if (aList[0] == "CONNECTED")
245 {
246 // We got "CONNECTED", which is a response to "HELLO"
247 lcd_ready = true;
248
249 // get lcd width & height
250 if (aList.count() != 3)
251 {
252 VERBOSE(VB_ALL, "lcddevice: received bad no. of arguments "
253 "in CONNECTED response from LCDServer");
254 }
255
256 bool bOK;
257 lcd_width = aList[1].toInt(&bOK);
258 if (!bOK)
259 {
260 VERBOSE(VB_ALL, "lcddevice: received bad int for width"
261 "in CONNECTED response from LCDServer");
262 }
263
264 lcd_height = aList[2].toInt(&bOK);
265 if (!bOK)
266 {
267 VERBOSE(VB_ALL, "lcddevice: received bad int for height"
268 "in CONNECTED response from LCDServer");
269 }
270
271 init();
272 }
273 else if (aList[0] == "HUH?")
274 {
275 VERBOSE(VB_ALL, "lcddevice: WARNING: Something is getting passed"
276 "to LCDServer that it doesn't understand");
277 VERBOSE(VB_ALL, "lcddevice: last command: " << last_command);
278 }
279 else if (aList[0] == "KEY")
280 handleKeyPress(aList.last().stripWhiteSpace());
281 }
282}
283
284void LCD::handleKeyPress(QString key_pressed)
285{
286 int key = 0;
287
288 QChar mykey = key_pressed.at(0);
289 if (mykey == lcd_keystring.at(0))
290 key = Qt::Key_Up;
291 else if (mykey == lcd_keystring.at(1))
292 key = Qt::Key_Down;
293 else if (mykey == lcd_keystring.at(2))
294 key = Qt::Key_Left;
295 else if (mykey == lcd_keystring.at(3))
296 key = Qt::Key_Right;
297 else if (mykey == lcd_keystring.at(4))
298 key = Qt::Key_Space;
299 else if (mykey == lcd_keystring.at(5))
300 key = Qt::Key_Escape;
301
302 QApplication::postEvent(gContext->GetMainWindow(),
303 new ExternalKeycodeEvent(key));
304}
305
306void LCD::init()
307{
308 // Stop the timer
309 retryTimer->stop();
310
311 // Get LCD settings
312 lcd_showmusic = (gContext->GetSetting("LCDShowMusic", "1") == "1");
313 lcd_showtime = (gContext->GetSetting("LCDShowTime", "1") == "1");
314 lcd_showchannel = (gContext->GetSetting("LCDShowChannel", "1") == "1");
315 lcd_showgeneric = (gContext->GetSetting("LCDShowGeneric", "1") == "1");
316 lcd_showvolume = (gContext->GetSetting("LCDShowVolume", "1") == "1");
317 lcd_showmenu = (gContext->GetSetting("LCDShowMenu", "1") == "1");
318 lcd_showrecstatus = (gContext->GetSetting("LCDShowRecStatus", "1") == "1");
319 lcd_keystring = gContext->GetSetting("LCDKeyString", "ABCDEF");
320
321 connected = TRUE;
322 lcd_ready = true;
323
324 // send buffer if there's anything in there
325 if (send_buffer.length() > 0)
326 {
327 sendToServer(send_buffer);
328 send_buffer = "";
329 }
330}
331
332void LCD::veryBadThings(int anError)
333{
334 // Deal with failures to connect and inabilities to communicate
335
336 QString err;
337
338 if (anError == QSocket::ErrConnectionRefused)
339 err = "connection refused.";
340 else if (anError == QSocket::ErrHostNotFound)
341 err = "host not found.";
342 else if (anError == QSocket::ErrSocketRead)
343 err = "socket read failed.";
344 else
345 err = "unknown error.";
346
347 VERBOSE(VB_IMPORTANT, QString("Could not connect to LCDServer: %1").arg(err));
348 socket->clearPendingData();
349 socket->close();
350}
351
352void LCD::stopAll()
353{
354 if (!lcd_ready)
355 return;
356
357#if LCD_DEVICE_DEBUG > 1
358 VERBOSE(VB_ALL, "lcddevice: stopAll");
359#endif
360
361 sendToServer("STOP_ALL");
362}
363
364void LCD::setChannelProgress(float value)
365{
366 if (!lcd_ready || !lcd_showchannel)
367 return;
368
369 if (value < 0.0)
370 value = 0.0;
371 else if (value > 1.0)
372 value = 1.0;
373
374 sendToServer("SET_CHANNEL_PROGRESS " + QString().setNum(value));
375}
376
377void LCD::setGenericProgress(float value)
378{
379 if (!lcd_ready || !lcd_showgeneric)
380 return;
381
382 if (value < 0.0)
383 value = 0.0;
384 else if (value > 1.0)
385 value = 1.0;
386
387 sendToServer("SET_GENERIC_PROGRESS " + QString().setNum(value));
388}
389
390void LCD::setMusicProgress(QString time, float value)
391{
392 if (!lcd_ready || !lcd_showmusic)
393 return;
394
395 if (value < 0.0)
396 value = 0.0;
397 else if (value > 1.0)
398 value = 1.0;
399
400 sendToServer("SET_MUSIC_PROGRESS " + quotedString(time) + " " +
401 QString().setNum(value));
402}
403
404void LCD::setVolumeLevel(float value)
405{
406 if (!lcd_ready || !lcd_showvolume)
407 return;
408
409 if (value < 0.0)
410 value = 0.0;
411 else if (value > 1.0)
412 value = 1.0;
413
414 sendToServer("SET_VOLUME_LEVEL " + QString().setNum(value));
415}
416
417void LCD::setupLEDs(int(*LedMaskFunc)(void))
418{
419 GetLEDMask = LedMaskFunc;
420 // update LED status every 10 seconds
421 LEDTimer->start(10000, FALSE);
422}
423
424void LCD::outputLEDs()
425{
426 QString aString;
427 int mask = 0;
428 if (0 && GetLEDMask)
429 mask = GetLEDMask();
430 aString = "UPDATE_LEDS ";
431 aString += QString::number(mask);
432 sendToServer(aString);
433}
434
435void LCD::switchToTime()
436{
437 if (!lcd_ready || !lcd_showtime)
438 return;
439
440#if LCD_DEVICE_DEBUG > 1
441 VERBOSE(VB_ALL, "lcddevice: switchToTime");
442#endif
443
444 sendToServer("SWITCH_TO_TIME");
445}
446
447void LCD::switchToMusic(const QString &artist, const QString &album, const QString &track)
448{
449 if (!lcd_ready || !lcd_showmusic)
450 return;
451
452#if LCD_DEVICE_DEBUG > 1
453 VERBOSE(VB_ALL, "lcddevice: switchToMusic");
454#endif
455
456 sendToServer("SWITCH_TO_MUSIC " + quotedString(artist) + " "
457 + quotedString(album) + " "
458 + quotedString(track));
459}
460
461void LCD::switchToChannel(QString channum, QString title, QString subtitle)
462{
463 if (!lcd_ready || !lcd_showchannel)
464 return;
465
466#if LCD_DEVICE_DEBUG > 1
467 VERBOSE(VB_ALL, "lcddevice: switchToChannel");
468#endif
469
470 sendToServer("SWITCH_TO_CHANNEL " + quotedString(channum) + " "
471 + quotedString(title) + " "
472 + quotedString(subtitle));
473}
474
475void LCD::switchToMenu(QPtrList<LCDMenuItem> *menuItems, QString app_name,
476 bool popMenu)
477{
478 if (!lcd_ready || !lcd_showmenu)
479 return;
480
481#if LCD_DEVICE_DEBUG > 1
482 VERBOSE(VB_ALL, "lcddevice: switchToMenu");
483#endif
484
485 if (menuItems->isEmpty())
486 return;
487
488 QString s = "SWITCH_TO_MENU ";
489
490 s += quotedString(app_name);
491 s += " " + QString(popMenu ? "TRUE" : "FALSE");
492
493
494 QPtrListIterator<LCDMenuItem> it(*menuItems);
495 LCDMenuItem *curItem;
496
497 while ((curItem = it.current()) != 0)
498 {
499 ++it;
500 s += " " + quotedString(curItem->ItemName());
501
502 if (curItem->isChecked() == CHECKED)
503 s += " CHECKED";
504 else if (curItem->isChecked() == CHECKED)
505 s += " UNCHECKED";
506 else if (curItem->isChecked() == NOTCHECKABLE)
507 s += " NOTCHECKABLE";
508
509 s += " " + QString(curItem->isSelected() ? "TRUE" : "FALSE");
510 s += " " + QString(curItem->Scroll() ? "TRUE" : "FALSE");
511 QString sIndent;
512 sIndent.setNum(curItem->getIndent());
513 s += " " + sIndent;
514 }
515
516 sendToServer(s);
517}
518
519void LCD::switchToGeneric(QPtrList<LCDTextItem> *textItems)
520{
521 if (!lcd_ready || !lcd_showgeneric)
522 return;
523
524#if LCD_DEVICE_DEBUG > 1
525 VERBOSE(VB_ALL, "lcddevice: switchToGeneric ");
526#endif
527
528 if (textItems->isEmpty())
529 return;
530
531 QString s = "SWITCH_TO_GENERIC";
532
533 QPtrListIterator<LCDTextItem> it(*textItems);
534 LCDTextItem *curItem;
535
536 while ((curItem = it.current()) != 0)
537 {
538 ++it;
539 QString sRow;
540 sRow.setNum(curItem->getRow());
541 s += " " + sRow;
542
543 if (curItem->getAlignment() == ALIGN_LEFT)
544 s += " ALIGN_LEFT";
545 else if (curItem->getAlignment() == ALIGN_RIGHT)
546 s += " ALIGN_RIGHT";
547 else if (curItem->getAlignment() == ALIGN_CENTERED)
548 s += " ALIGN_CENTERED";
549
550 s += " " + quotedString(curItem->getText());
551 s += " " + quotedString(curItem->getScreen());
552 s += " " + QString(curItem->getScroll() ? "TRUE" : "FALSE");
553 }
554
555 sendToServer(s);
556}
557
558void LCD::switchToVolume(QString app_name)
559{
560 if (!lcd_ready || !lcd_showvolume)
561 return;
562
563#if LCD_DEVICE_DEBUG > 1
564 VERBOSE(VB_ALL, "lcddevice: switchToVolume ");
565#endif
566
567 sendToServer("SWITCH_TO_VOLUME " + quotedString(app_name));
568}
569
570void LCD::switchToNothing()
571{
572 if (!lcd_ready)
573 return;
574
575#if LCD_DEVICE_DEBUG > 1
576 VERBOSE(VB_ALL, "lcddevice: switchToNothing");
577#endif
578
579 sendToServer("SWITCH_TO_NOTHING");
580}
581
582void LCD::shutdown()
583{
584#if LCD_DEVICE_DEBUG > 1
585 VERBOSE(VB_ALL, "lcddevice: shutdown");
586#endif
587
588 socket->close();
589
590 lcd_ready = false;
591 connected = false;
592}
593
594LCD::~LCD()
595{
596 m_lcd = NULL;
597
598#if LCD_DEVICE_DEBUG > 0
599 VERBOSE(VB_ALL, "lcddevice: An LCD device is being snuffed out of "
600 "existence (~LCD() was called)");
601#endif
602
603 if (socket)
604 {
605 delete socket;
606 lcd_ready = false;
607 }
608}
609
610// this does nothing
611void LCD::setLevels(int numbLevels, float *values)
612{
613 numbLevels = numbLevels;
614 values = values;
615
616#if LCD_DEVICE_DEBUG > 0
617 VERBOSE(VB_ALL, "lcddevice: setLevels");
618#endif
619}
620
621QString LCD::quotedString(const QString &s)
622{
623 QString sRes = s;
624 sRes.replace(QRegExp("\""), QString("\"\""));
625 sRes = "\"" + sRes + "\"";
626
627 return(sRes);
628}