Ticket #324: lcddevice.cpp

File lcddevice.cpp, 15.3 KB (added by Paul, 14 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}