00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <cstdlib>
00012 #include <cmath>
00013 #include <fcntl.h>
00014 #include <errno.h>
00015
00016
00017 #include <QApplication>
00018 #include <QRegExp>
00019 #include <QTextStream>
00020 #include <QTextCodec>
00021 #include <QByteArray>
00022
00023
00024 #include "lcddevice.h"
00025 #include "mythlogging.h"
00026 #include "compat.h"
00027 #include "mythdb.h"
00028 #include "mythdirs.h"
00029 #include "mythevent.h"
00030 #include "mythsocket.h"
00031 #include "mythsystem.h"
00032 #include "exitcodes.h"
00033
00034
00035 #define LOC QString("LCDdevice: ")
00036
00037 LCD::LCD()
00038 : QObject(),
00039 socket(NULL), socketLock(QMutex::Recursive),
00040 hostname("localhost"), port(6545),
00041 bConnected(false),
00042
00043 retryTimer(new QTimer(this)), LEDTimer(new QTimer(this)),
00044
00045 lcd_width(0), lcd_height(0),
00046
00047 lcd_ready(false), lcd_showtime(false),
00048 lcd_showmenu(false), lcd_showgeneric(false),
00049 lcd_showmusic(false), lcd_showchannel(false),
00050 lcd_showvolume(false), lcd_showrecstatus(false),
00051 lcd_backlighton(false), lcd_heartbeaton(false),
00052
00053 lcd_popuptime(0),
00054
00055 lcd_showmusic_items(),
00056 lcd_keystring(),
00057
00058 lcd_ledmask(0),
00059 GetLEDMask(NULL)
00060 {
00061 send_buffer.clear(); last_command.clear();
00062 lcd_showmusic_items.clear(); lcd_keystring.clear();
00063
00064 setObjectName("LCD");
00065
00066
00067
00068
00069
00070
00071 LOG(VB_GENERAL, LOG_DEBUG, LOC +
00072 "An LCD object now exists (LCD() was called)");
00073
00074 connect(retryTimer, SIGNAL(timeout()), this, SLOT(restartConnection()));
00075 connect(LEDTimer, SIGNAL(timeout()), this, SLOT(outputLEDs()));
00076 }
00077
00078 bool LCD::m_enabled = false;
00079 bool LCD::m_server_unavailable = false;
00080 LCD *LCD::m_lcd = NULL;
00081
00082 LCD *LCD::Get(void)
00083 {
00084 if (m_enabled && m_lcd == NULL && m_server_unavailable == false)
00085 m_lcd = new LCD;
00086 return m_lcd;
00087 }
00088
00089 void LCD::SetupLCD (void)
00090 {
00091 QString lcd_host;
00092 int lcd_port;
00093
00094 if (m_lcd)
00095 {
00096 delete m_lcd;
00097 m_lcd = NULL;
00098 m_server_unavailable = false;
00099 }
00100
00101 lcd_host = GetMythDB()->GetSetting("LCDServerHost", "localhost");
00102 lcd_port = GetMythDB()->GetNumSetting("LCDServerPort", 6545);
00103 m_enabled = GetMythDB()->GetNumSetting("LCDEnable", 0);
00104
00105
00106 if (lcd_host == "localhost")
00107 lcd_host = "127.0.0.1";
00108
00109 if (m_enabled && lcd_host.length() > 0 && lcd_port > 1024)
00110 {
00111 LCD *lcd = LCD::Get();
00112 if (lcd->connectToHost(lcd_host, lcd_port) == false)
00113 {
00114 delete m_lcd;
00115 m_lcd = NULL;
00116 m_server_unavailable = false;
00117 }
00118 }
00119 }
00120
00121 bool LCD::connectToHost(const QString &lhostname, unsigned int lport)
00122 {
00123 QMutexLocker locker(&socketLock);
00124
00125 LOG(VB_NETWORK, LOG_DEBUG, LOC +
00126 QString("connecting to host: %1 - port: %2")
00127 .arg(lhostname).arg(lport));
00128
00129
00130
00131 hostname = lhostname;
00132 port = lport;
00133
00134
00135 if (!(m_enabled = GetMythDB()->GetNumSetting("LCDEnable", 0)))
00136 {
00137 bConnected = false;
00138 m_server_unavailable = true;
00139 return bConnected;
00140 }
00141
00142
00143 uint flags = kMSRunShell | kMSDontBlockInputDevs | kMSDontDisableDrawing;
00144 if (myth_system("ps ch -C mythlcdserver -o pid > /dev/null", flags) == 1)
00145 {
00146
00147 LOG(VB_GENERAL, LOG_NOTICE, "Starting mythlcdserver");
00148
00149 if (!startLCDServer())
00150 {
00151 LOG(VB_GENERAL, LOG_ERR, "Failed start MythTV LCD Server");
00152 return bConnected;
00153 }
00154
00155 usleep(500000);
00156 }
00157
00158 if (!bConnected)
00159 {
00160 int count = 0;
00161 do
00162 {
00163 ++count;
00164
00165 LOG(VB_GENERAL, LOG_INFO, QString("Connecting to lcd server: "
00166 "%1:%2 (try %3 of 10)").arg(hostname).arg(port)
00167 .arg(count));
00168
00169 if (socket)
00170 socket->DownRef();
00171
00172 socket = new MythSocket(-1, this);
00173 if (socket->connect(hostname, port))
00174 {
00175 lcd_ready = false;
00176 bConnected = true;
00177 QTextStream os(socket);
00178 os << "HELLO\n";
00179
00180
00181 usleep(1000);
00182 socket->Lock();
00183 socket->Unlock();
00184
00185 break;
00186 }
00187
00188 usleep(500000);
00189 }
00190 while (count < 10 && !bConnected);
00191 }
00192
00193 if (bConnected == false)
00194 m_server_unavailable = true;
00195
00196 return bConnected;
00197 }
00198
00199 void LCD::sendToServer(const QString &someText)
00200 {
00201 QMutexLocker locker(&socketLock);
00202
00203 if (!socket || !lcd_ready)
00204 return;
00205
00206
00207 if (socket->state() == MythSocket::Idle)
00208 {
00209 lcd_ready = false;
00210
00211
00212
00213 retryTimer->setSingleShot(false);
00214 retryTimer->start(10000);
00215 LOG(VB_GENERAL, LOG_ERR,
00216 "Connection to LCDServer died unexpectedly. "
00217 "Trying to reconnect every 10 seconds...");
00218
00219 bConnected = false;
00220 return;
00221 }
00222
00223 QTextStream os(socket);
00224 os.setCodec(QTextCodec::codecForName("ISO 8859-1"));
00225
00226 last_command = someText;
00227
00228 if (bConnected)
00229 {
00230 LOG(VB_NETWORK, LOG_DEBUG, LOC +
00231 QString(LOC + "Sending to Server: %1").arg(someText));
00232
00233
00234 os << someText << "\n";
00235 }
00236 else
00237 {
00238
00239
00240 send_buffer += someText;
00241 send_buffer += '\n';
00242 }
00243 }
00244
00245 void LCD::restartConnection()
00246 {
00247
00248 lcd_ready = false;
00249 bConnected = false;
00250 m_server_unavailable = false;
00251
00252
00253 connectToHost(hostname, port);
00254 }
00255
00256 void LCD::readyRead(MythSocket *sock)
00257 {
00258 (void) sock;
00259
00260 QMutexLocker locker(&socketLock);
00261
00262 QString lineFromServer, tempString;
00263 QStringList aList;
00264 QStringList::Iterator it;
00265
00266
00267
00268
00269
00270
00271
00272 int dataSize = socket->bytesAvailable() + 1;
00273 QByteArray data(dataSize + 1, 0);
00274
00275 socket->readBlock(data.data(), dataSize);
00276
00277 lineFromServer = data;
00278 lineFromServer = lineFromServer.replace( QRegExp("\n"), " " );
00279 lineFromServer = lineFromServer.replace( QRegExp("\r"), " " );
00280 lineFromServer = lineFromServer.simplified();
00281
00282
00283 if (lineFromServer != "OK")
00284 LOG(VB_NETWORK, LOG_DEBUG, LOC + QString("Received from server: %1")
00285 .arg(lineFromServer));
00286
00287 aList = lineFromServer.split(' ');
00288 if (aList[0] == "CONNECTED")
00289 {
00290
00291
00292 if (aList.count() != 3)
00293 {
00294 LOG(VB_GENERAL, LOG_ERR, LOC + "received bad no. of arguments in "
00295 "CONNECTED response from LCDServer");
00296 }
00297
00298 bool bOK;
00299 lcd_width = aList[1].toInt(&bOK);
00300 if (!bOK)
00301 {
00302 LOG(VB_GENERAL, LOG_ERR, LOC + "received bad int for width in "
00303 "CONNECTED response from LCDServer");
00304 }
00305
00306 lcd_height = aList[2].toInt(&bOK);
00307 if (!bOK)
00308 {
00309 LOG(VB_GENERAL, LOG_ERR, LOC + "received bad int for height in "
00310 "CONNECTED response from LCDServer");
00311 }
00312
00313 init();
00314 }
00315 else if (aList[0] == "HUH?")
00316 {
00317 LOG(VB_GENERAL, LOG_WARNING, LOC + "Something is getting passed to "
00318 "LCDServer that it does not understand");
00319 LOG(VB_GENERAL, LOG_WARNING, LOC +
00320 QString("last command: %1").arg(last_command));
00321 }
00322 else if (aList[0] == "KEY")
00323 handleKeyPress(aList.last().trimmed());
00324 }
00325
00326 void LCD::handleKeyPress(QString key_pressed)
00327 {
00328 int key = 0;
00329
00330 QChar mykey = key_pressed.at(0);
00331 if (mykey == lcd_keystring.at(0))
00332 key = Qt::Key_Up;
00333 else if (mykey == lcd_keystring.at(1))
00334 key = Qt::Key_Down;
00335 else if (mykey == lcd_keystring.at(2))
00336 key = Qt::Key_Left;
00337 else if (mykey == lcd_keystring.at(3))
00338 key = Qt::Key_Right;
00339 else if (mykey == lcd_keystring.at(4))
00340 key = Qt::Key_Space;
00341 else if (mykey == lcd_keystring.at(5))
00342 key = Qt::Key_Escape;
00343
00344 QCoreApplication::postEvent(
00345 (QObject *)(QApplication::activeWindow()),
00346 new ExternalKeycodeEvent(key));
00347 }
00348
00349 void LCD::init()
00350 {
00351
00352 retryTimer->stop();
00353
00354
00355 lcd_showmusic = (GetMythDB()->GetSetting("LCDShowMusic", "1") == "1");
00356 lcd_showtime = (GetMythDB()->GetSetting("LCDShowTime", "1") == "1");
00357 lcd_showchannel = (GetMythDB()->GetSetting("LCDShowChannel", "1") == "1");
00358 lcd_showgeneric = (GetMythDB()->GetSetting("LCDShowGeneric", "1") == "1");
00359 lcd_showvolume = (GetMythDB()->GetSetting("LCDShowVolume", "1") == "1");
00360 lcd_showmenu = (GetMythDB()->GetSetting("LCDShowMenu", "1") == "1");
00361 lcd_showrecstatus = (GetMythDB()->GetSetting("LCDShowRecStatus", "1") == "1");
00362 lcd_keystring = GetMythDB()->GetSetting("LCDKeyString", "ABCDEF");
00363
00364 bConnected = true;
00365 lcd_ready = true;
00366
00367
00368 if (send_buffer.length() > 0)
00369 {
00370 sendToServer(send_buffer);
00371 send_buffer = "";
00372 }
00373 }
00374
00375 void LCD::connectionClosed(MythSocket *sock)
00376 {
00377 (void) sock;
00378 bConnected = false;
00379 }
00380
00381 void LCD::connectionFailed(MythSocket *sock)
00382 {
00383 QMutexLocker locker(&socketLock);
00384 QString err = sock->errorToString();
00385 LOG(VB_GENERAL, LOG_ERR, QString("Could not connect to LCDServer: %1")
00386 .arg(err));
00387 }
00388
00389 void LCD::stopAll()
00390 {
00391 if (!lcd_ready)
00392 return;
00393
00394 LOG(VB_GENERAL, LOG_DEBUG, LOC + "stopAll");
00395
00396 sendToServer("STOP_ALL");
00397 }
00398
00399 void LCD::setSpeakerLEDs(enum LCDSpeakerSet speaker, bool on)
00400 {
00401 if (!lcd_ready)
00402 return;
00403 lcd_ledmask &= ~SPEAKER_MASK;
00404 if (on)
00405 lcd_ledmask |= speaker;
00406 sendToServer(QString("UPDATE_LEDS %1").arg(lcd_ledmask));
00407 }
00408
00409 void LCD::setAudioFormatLEDs(enum LCDAudioFormatSet acodec, bool on)
00410 {
00411 if (!lcd_ready)
00412 return;
00413
00414 lcd_ledmask &= ~AUDIO_MASK;
00415 if (on)
00416 lcd_ledmask |= (acodec & AUDIO_MASK);
00417
00418 sendToServer(QString("UPDATE_LEDS %1").arg(lcd_ledmask));
00419 }
00420
00421 void LCD::setVideoFormatLEDs(enum LCDVideoFormatSet vcodec, bool on)
00422 {
00423 if (!lcd_ready)
00424 return;
00425
00426 lcd_ledmask &= ~VIDEO_MASK;
00427 if (on)
00428 lcd_ledmask |= (vcodec & VIDEO_MASK);
00429
00430 sendToServer(QString("UPDATE_LEDS %1").arg(lcd_ledmask));
00431 }
00432
00433 void LCD::setVideoSrcLEDs(enum LCDVideoSourceSet vsrc, bool on)
00434 {
00435 if (!lcd_ready)
00436 return;
00437 lcd_ledmask &= ~VSRC_MASK;
00438 if (on)
00439 lcd_ledmask |= vsrc;
00440 sendToServer(QString("UPDATE_LEDS %1").arg(lcd_ledmask));
00441 }
00442
00443 void LCD::setFunctionLEDs(enum LCDFunctionSet func, bool on)
00444 {
00445 if (!lcd_ready)
00446 return;
00447 lcd_ledmask &= ~FUNC_MASK;
00448 if (on)
00449 lcd_ledmask |= func;
00450 sendToServer(QString("UPDATE_LEDS %1").arg(lcd_ledmask));
00451 }
00452
00453 void LCD::setVariousLEDs(enum LCDVariousFlags various, bool on)
00454 {
00455 if (!lcd_ready)
00456 return;
00457 if (on) {
00458 lcd_ledmask |= various;
00459 if (various == VARIOUS_SPDIF)
00460 lcd_ledmask |= SPDIF_MASK;
00461 } else {
00462 lcd_ledmask &= ~various;
00463 if (various == VARIOUS_SPDIF)
00464 lcd_ledmask &= ~SPDIF_MASK;
00465 }
00466 sendToServer(QString("UPDATE_LEDS %1").arg(lcd_ledmask));
00467 }
00468
00469 void LCD::setTunerLEDs(enum LCDTunerSet tuner, bool on)
00470 {
00471 if (!lcd_ready)
00472 return;
00473 lcd_ledmask &= ~TUNER_MASK;
00474 if (on)
00475 lcd_ledmask |= tuner;
00476 sendToServer(QString("UPDATE_LEDS %1").arg(lcd_ledmask));
00477 }
00478
00479 void LCD::setChannelProgress(const QString &time, float value)
00480 {
00481 if (!lcd_ready || !lcd_showchannel)
00482 return;
00483
00484 value = std::min(std::max(0.0f, value), 1.0f);
00485 sendToServer(QString("SET_CHANNEL_PROGRESS %1 %2").arg(quotedString(time))
00486 .arg(value));
00487 }
00488
00489 void LCD::setGenericProgress(float value)
00490 {
00491 if (!lcd_ready || !lcd_showgeneric)
00492 return;
00493
00494 value = std::min(std::max(0.0f, value), 1.0f);
00495 sendToServer(QString("SET_GENERIC_PROGRESS 0 %1").arg(value));
00496 }
00497
00498 void LCD::setGenericBusy()
00499 {
00500 if (!lcd_ready || !lcd_showgeneric)
00501 return;
00502
00503 sendToServer("SET_GENERIC_PROGRESS 1 0.0");
00504 }
00505
00506 void LCD::setMusicProgress(QString time, float value)
00507 {
00508 if (!lcd_ready || !lcd_showmusic)
00509 return;
00510
00511 value = std::min(std::max(0.0f, value), 1.0f);
00512 sendToServer("SET_MUSIC_PROGRESS " + quotedString(time) + ' ' +
00513 QString().setNum(value));
00514 }
00515
00516 void LCD::setMusicShuffle(int shuffle)
00517 {
00518 if (!lcd_ready || !lcd_showmusic)
00519 return;
00520
00521 sendToServer(QString("SET_MUSIC_PLAYER_PROP SHUFFLE %1").arg(shuffle));
00522 }
00523
00524 void LCD::setMusicRepeat(int repeat)
00525 {
00526 if (!lcd_ready || !lcd_showmusic)
00527 return;
00528
00529 sendToServer(QString("SET_MUSIC_PLAYER_PROP REPEAT %1").arg(repeat));
00530 }
00531
00532 void LCD::setVolumeLevel(float value)
00533 {
00534 if (!lcd_ready || !lcd_showvolume)
00535 return;
00536
00537 if (value < 0.0)
00538 value = 0.0;
00539 else if (value > 1.0)
00540 value = 1.0;
00541
00542 sendToServer("SET_VOLUME_LEVEL " + QString().setNum(value));
00543 }
00544
00545 void LCD::setupLEDs(int(*LedMaskFunc)(void))
00546 {
00547 GetLEDMask = LedMaskFunc;
00548
00549 LEDTimer->setSingleShot(false);
00550 LEDTimer->start(10000);
00551 }
00552
00553 void LCD::outputLEDs()
00554 {
00555
00556 return;
00557 #if 0
00558 if (!lcd_ready)
00559 return;
00560
00561 QString aString;
00562 int mask = 0;
00563 if (0 && GetLEDMask)
00564 mask = GetLEDMask();
00565 aString = "UPDATE_LEDS ";
00566 aString += QString::number(mask);
00567 sendToServer(aString);
00568 #endif
00569 }
00570
00571 void LCD::switchToTime()
00572 {
00573 if (!lcd_ready)
00574 return;
00575
00576 LOG(VB_GENERAL, LOG_DEBUG, LOC + "switchToTime");
00577
00578 sendToServer("SWITCH_TO_TIME");
00579 }
00580
00581 void LCD::switchToMusic(const QString &artist, const QString &album, const QString &track)
00582 {
00583 if (!lcd_ready || !lcd_showmusic)
00584 return;
00585
00586 LOG(VB_GENERAL, LOG_DEBUG, LOC + "switchToMusic");
00587
00588 sendToServer("SWITCH_TO_MUSIC " + quotedString(artist) + ' '
00589 + quotedString(album) + ' '
00590 + quotedString(track));
00591 }
00592
00593 void LCD::switchToChannel(QString channum, QString title, QString subtitle)
00594 {
00595 if (!lcd_ready || !lcd_showchannel)
00596 return;
00597
00598 LOG(VB_GENERAL, LOG_DEBUG, LOC + "switchToChannel");
00599
00600 sendToServer("SWITCH_TO_CHANNEL " + quotedString(channum) + ' '
00601 + quotedString(title) + ' '
00602 + quotedString(subtitle));
00603 }
00604
00605 void LCD::switchToMenu(QList<LCDMenuItem> &menuItems, QString app_name,
00606 bool popMenu)
00607 {
00608 if (!lcd_ready || !lcd_showmenu)
00609 return;
00610
00611 LOG(VB_GENERAL, LOG_DEBUG, LOC + "switchToMenu");
00612
00613 if (menuItems.isEmpty())
00614 return;
00615
00616 QString s = "SWITCH_TO_MENU ";
00617
00618 s += quotedString(app_name);
00619 s += ' ' + QString(popMenu ? "TRUE" : "FALSE");
00620
00621
00622 QListIterator<LCDMenuItem> it(menuItems);
00623 const LCDMenuItem *curItem;
00624
00625 while (it.hasNext())
00626 {
00627 curItem = &(it.next());
00628 s += ' ' + quotedString(curItem->ItemName());
00629
00630 if (curItem->isChecked() == CHECKED)
00631 s += " CHECKED";
00632 else if (curItem->isChecked() == UNCHECKED)
00633 s += " UNCHECKED";
00634 else if (curItem->isChecked() == NOTCHECKABLE)
00635 s += " NOTCHECKABLE";
00636
00637 s += ' ' + QString(curItem->isSelected() ? "TRUE" : "FALSE");
00638 s += ' ' + QString(curItem->Scroll() ? "TRUE" : "FALSE");
00639 QString sIndent;
00640 sIndent.setNum(curItem->getIndent());
00641 s += ' ' + sIndent;
00642 }
00643
00644 sendToServer(s);
00645 }
00646
00647 void LCD::switchToGeneric(QList<LCDTextItem> &textItems)
00648 {
00649 if (!lcd_ready || !lcd_showgeneric)
00650 return;
00651
00652 LOG(VB_GENERAL, LOG_DEBUG, LOC + "switchToGeneric");
00653
00654 if (textItems.isEmpty())
00655 return;
00656
00657 QString s = "SWITCH_TO_GENERIC";
00658
00659 QListIterator<LCDTextItem> it(textItems);
00660 const LCDTextItem *curItem;
00661
00662 while (it.hasNext())
00663 {
00664 curItem = &(it.next());
00665
00666 QString sRow;
00667 sRow.setNum(curItem->getRow());
00668 s += ' ' + sRow;
00669
00670 if (curItem->getAlignment() == ALIGN_LEFT)
00671 s += " ALIGN_LEFT";
00672 else if (curItem->getAlignment() == ALIGN_RIGHT)
00673 s += " ALIGN_RIGHT";
00674 else if (curItem->getAlignment() == ALIGN_CENTERED)
00675 s += " ALIGN_CENTERED";
00676
00677 s += ' ' + quotedString(curItem->getText());
00678 s += ' ' + quotedString(curItem->getScreen());
00679 s += ' ' + QString(curItem->getScroll() ? "TRUE" : "FALSE");
00680 }
00681
00682 sendToServer(s);
00683 }
00684
00685 void LCD::switchToVolume(QString app_name)
00686 {
00687 if (!lcd_ready || !lcd_showvolume)
00688 return;
00689
00690 LOG(VB_GENERAL, LOG_DEBUG, LOC + "switchToVolume");
00691
00692 sendToServer("SWITCH_TO_VOLUME " + quotedString(app_name));
00693 }
00694
00695 void LCD::switchToNothing()
00696 {
00697 if (!lcd_ready)
00698 return;
00699
00700 LOG(VB_GENERAL, LOG_DEBUG, LOC + "switchToNothing");
00701
00702 sendToServer("SWITCH_TO_NOTHING");
00703 }
00704
00705 void LCD::shutdown()
00706 {
00707 QMutexLocker locker(&socketLock);
00708
00709 LOG(VB_GENERAL, LOG_DEBUG, LOC + "shutdown");
00710
00711 if (socket)
00712 socket->close();
00713
00714 lcd_ready = false;
00715 bConnected = false;
00716 }
00717
00718 void LCD::resetServer()
00719 {
00720 QMutexLocker locker(&socketLock);
00721
00722 if (!lcd_ready)
00723 return;
00724
00725 LOG(VB_GENERAL, LOG_DEBUG, LOC + "RESET");
00726
00727 sendToServer("RESET");
00728 }
00729
00730 LCD::~LCD()
00731 {
00732 m_lcd = NULL;
00733
00734 LOG(VB_GENERAL, LOG_DEBUG, LOC + "An LCD device is being snuffed out of "
00735 "existence (~LCD() was called)");
00736
00737 if (socket)
00738 {
00739 socket->DownRef();
00740 lcd_ready = false;
00741 }
00742 }
00743
00744 QString LCD::quotedString(const QString &s)
00745 {
00746 QString sRes = s;
00747 sRes.replace(QRegExp("\""), QString("\"\""));
00748 sRes = "\"" + sRes + "\"";
00749
00750 return(sRes);
00751 }
00752
00753 bool LCD::startLCDServer(void)
00754 {
00755 QString command = GetInstallPrefix() + "/bin/mythlcdserver";
00756 command += logPropagateArgs;
00757 uint flags = kMSDontBlockInputDevs | kMSDontDisableDrawing |
00758 kMSRunBackground;
00759
00760 uint retval = myth_system(command, flags);
00761 return( retval == GENERIC_EXIT_RUNNING );
00762 }