MythTV master
lcdprocclient.cpp
Go to the documentation of this file.
1/*
2 lcdprocclient.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// c/c++
11#include <algorithm>
12#include <chrono> // for milliseconds
13#include <cmath>
14#include <cstdlib>
15#include <thread> // for sleep_for
16#include <utility>
17
18//qt
19#include <QCoreApplication>
20#include <QEvent>
21#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
22#include <QStringConverter>
23#endif
24#include <QTimer>
25
26// mythtv
27#include "libmythbase/compat.h"
33#include "libmythtv/tv.h"
34
35// mythlcdserver
36#include "lcdprocclient.h"
37#include "lcdserver.h"
38
39static constexpr uint8_t LCD_START_COL { 3 };
40
41static constexpr uint8_t LCD_VERSION_4 { 1 };
42static constexpr uint8_t LCD_VERSION_5 { 2 };
43
44static constexpr std::chrono::milliseconds LCD_TIME_TIME { 3s };
45static constexpr std::chrono::milliseconds LCD_SCROLLLIST_TIME { 2s };
46
48
50 : QObject(nullptr),
51 m_socket(new QTcpSocket(this)),
52 m_timeTimer (new QTimer(this)),
53 m_scrollWTimer (new QTimer(this)),
54 m_preScrollWTimer (new QTimer(this)),
55 m_menuScrollTimer (new QTimer(this)),
56 m_menuPreScrollTimer (new QTimer(this)),
57 m_popMenuTimer (new QTimer(this)),
58 m_checkConnectionsTimer (new QTimer(this)),
59 m_recStatusTimer (new QTimer(this)),
60 m_scrollListTimer (new QTimer(this)),
61 m_showMessageTimer (new QTimer(this)),
62 m_updateRecInfoTimer (new QTimer(this)),
63 m_lcdTextItems (new QList<LCDTextItem>),
64 m_lcdMenuItems (new QList<LCDMenuItem>),
65 m_parentLCDServer (lparent)
66{
67 // Constructor for LCDProcClient
68 //
69 // Note that this does *not* include opening the socket and initiating
70 // communications with the LDCd daemon.
71
72 if (debug_level > 0)
73 LOG(VB_GENERAL, LOG_INFO,
74 "LCDProcClient: An LCDProcClient object now exists");
75
76 connect(m_socket, &QAbstractSocket::errorOccurred, this, &LCDProcClient::veryBadThings);
77 connect(m_socket, &QIODevice::readyRead, this, &LCDProcClient::serverSendingData);
78
80 if ( m_lcdWidth < 12)
81 {
82 if ( m_lcdHeight == 1)
83 lcdStartCol = 0;
84 else
85 lcdStartCol = 1;
86 }
87
89
91
92 m_preScrollWTimer->setSingleShot(true);
93 connect( m_preScrollWTimer, &QTimer::timeout, this,
95
96 m_popMenuTimer->setSingleShot(true);
98
100
101 connect( m_menuPreScrollTimer, &QTimer::timeout, this,
103
106 m_checkConnectionsTimer->start(10s);
107
109
111
112 m_showMessageTimer->setSingleShot(true);
113 connect( m_showMessageTimer, &QTimer::timeout, this,
115
116 m_updateRecInfoTimer->setSingleShot(true);
117 connect( m_updateRecInfoTimer, &QTimer::timeout, this,
119
121}
122
124{
125 QString lcd_host = gCoreContext->GetSetting("LCDHost", "localhost");
126 int lcd_port = gCoreContext->GetNumSetting("LCDPort", 13666);
127
128 if (lcd_host.length() > 0 && lcd_port > 1024)
129 connectToHost(lcd_host, lcd_port);
130
131 return m_connected;
132}
133
134bool LCDProcClient::connectToHost(const QString &lhostname, unsigned int lport)
135{
136 // Open communications
137 // Store the hostname and port in case we need to reconnect.
138
139 m_hostname = lhostname;
140 m_port = lport;
141
142 // Don't even try to connect if we're currently disabled.
143 if (!gCoreContext->GetBoolSetting("LCDEnable", false))
144 {
145 m_connected = false;
146 return m_connected;
147 }
148
149 if (!m_connected )
150 {
151 QTextStream os(m_socket);
152 m_socket->connectToHost(m_hostname, m_port);
153
154 int timeout = 1000;
155 while (--timeout && m_socket->state() != QAbstractSocket::ConnectedState)
156 {
157 qApp->processEvents();
158 std::this_thread::sleep_for(1ms);
159
160 if (m_socket->state() == QAbstractSocket::ConnectedState)
161 {
162 m_connected = true;
163 os << "hello\n";
164 break;
165 }
166 }
167 }
168
169 return m_connected;
170}
171
172void LCDProcClient::sendToServer(const QString &someText)
173{
174 // Check the socket, make sure the connection is still up
175 if (m_socket->state() != QAbstractSocket::ConnectedState)
176 {
177 if (!m_lcdReady )
178 return;
179
180 m_lcdReady = false;
181
182 //Stop everything
183 stopAll();
184
185 // Ack, connection to server has been severed try to re-establish the
186 // connection
187 LOG(VB_GENERAL, LOG_ERR,
188 "LCDProcClient: Connection to LCDd died unexpectedly.");
189 return;
190 }
191
192 QTextStream os(m_socket);
193#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
194 os.setCodec("ISO 8859-1");
195#else
196 os.setEncoding(QStringConverter::Latin1);
197#endif
198
199 m_lastCommand = someText;
200
201 if ( m_connected )
202 {
203 if (debug_level > 9)
204 LOG(VB_NETWORK, LOG_INFO,
205 "LCDProcClient: Sending to Server: " + someText);
206
207 // Just stream the text out the socket
208
209 os << someText << "\n";
210 }
211 else
212 {
213 // Buffer this up in the hope that the connection will open soon
214
215 m_sendBuffer += someText;
216 m_sendBuffer += "\n";
217 }
218}
219
220void LCDProcClient::setPriority(const QString &screen, PRIORITY priority)
221{
222 QString aString;
223 int err = 0;
224 aString = "screen_set ";
225 aString += screen;
226 aString += " priority ";
227
228 switch (priority) {
229 case TOP:
230 aString += m_prioTop;
231 break;
232 case URGENT:
233 aString += m_prioUrgent;
234 break;
235 case HIGH:
236 aString += m_prioHigh;
237 break;
238 case MEDIUM:
239 aString += m_prioMedium;
240 break;
241 case LOW:
242 aString += m_prioLow;
243 break;
244 case OFF:
245 aString += m_prioOff;
246 break;
247 default:
248 err = 1;
249 break;
250 }
251 if (err == 0)
252 sendToServer (aString);
253}
254
255void LCDProcClient::setHeartbeat (const QString &screen, bool onoff)
256{
257 QString msg;
258 if (onoff)
259 {
261 {
262 msg = "widget_add " + screen + " heartbeat";
263 }
265 {
266 msg = "screen_set " + screen + " heartbeat on";
267 }
268 }
269 else
270 {
272 {
273 msg = "widget_del " + screen + " heartbeat";
274 }
276 {
277 msg = "screen_set " + screen + " heartbeat off";
278 }
279 }
280 sendToServer (msg);
281}
282
284{
285 if (debug_level > 0)
286 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: checking connections");
287
288 // check connection to mythbackend
290 {
291 if (debug_level > 0)
292 LOG(VB_GENERAL, LOG_INFO,
293 "LCDProcClient: connecting to master server");
295 LOG(VB_GENERAL, LOG_ERR,
296 "LCDProcClient: connecting to master server failed");
297 }
298
299 //check connection to LCDProc server
300 if (m_socket->state() != QAbstractSocket::ConnectedState)
301 {
302 if (debug_level > 0)
303 LOG(VB_GENERAL, LOG_INFO,
304 "LCDProcClient: connecting to LCDProc server");
305
306 m_lcdReady = false;
307 m_connected = false;
308
309 // Retry to connect. . . Maybe the user restarted LCDd?
311 }
312}
313
315{
316 QString lineFromServer;
317 QString tempString;
318 QStringList aList;
319 QStringList::Iterator it;
320
321 // This gets activated automatically by the QSocket class whenever
322 // there's something to read.
323 //
324 // We currently spend most of our time (except for the first line sent
325 // back) ignoring it.
326 //
327 // Note that if anyone has an LCDproc type lcd with buttons on it, this is
328 // where we would want to catch button presses and make the rest of
329 // mythTV/mythMusic do something (change tracks, channels, etc.)
330
331 while(m_socket->canReadLine())
332 {
333 lineFromServer = m_socket->readLine();
334 lineFromServer = lineFromServer.remove("\n");
335 lineFromServer = lineFromServer.remove("\r");
336
337 if (debug_level > 0)
338 {
339 // Make debugging be less noisy
340 if (lineFromServer != "success")
341 LOG(VB_NETWORK, LOG_INFO,
342 "LCDProcClient: Received from server: " + lineFromServer);
343 }
344
345 aList = lineFromServer.split(" ");
346 if (aList.first() == "connect")
347 {
348 // We got a connect, which is a response to "hello"
349 //
350 // Need to parse out some data according the LCDproc client/server
351 // spec (which is poorly documented)
352 it = aList.begin();
353 it++;
354 if ((*it) != "LCDproc")
355 {
356 LOG(VB_GENERAL, LOG_WARNING,
357 "LCDProcClient: WARNING: Second parameter "
358 "returned from LCDd was not \"LCDproc\"");
359 }
360
361 // Skip through some stuff
362 it++; // server version
363 QString server_version = *it;
364 it++; // the string "protocol"
365 it++; // protocol version
366 QString protocol_version = *it;
367 setVersion (server_version, protocol_version);
368 it++; // the string "lcd"
369 it++; // the string "wid";
370 it++; // Ah, the LCD width
371
372 tempString = *it;
373 setWidth(tempString.toInt());
374
375 it++; // the string "hgt"
376 it++; // the LCD height
377
378 tempString = *it;
379 setHeight(tempString.toInt());
380 it++; // the string "cellwid"
381 it++; // Cell width in pixels;
382
383 tempString = *it;
384 setCellWidth(tempString.toInt());
385
386 it++; // the string "cellhgt"
387 it++; // Cell height in pixels;
388
389 tempString = *it;
390 setCellHeight(tempString.toInt());
391
392 init();
393
395 }
396
397 if (aList.first() == "huh?")
398 {
399 LOG(VB_GENERAL, LOG_WARNING,
400 "LCDProcClient: WARNING: Something is getting"
401 "passed to LCDd that it doesn't understand");
402 LOG(VB_GENERAL, LOG_WARNING, "last command: " + m_lastCommand );
403 }
404 else if (aList.first() == "key")
405 {
406 if ( m_parentLCDServer )
407 m_parentLCDServer->sendKeyPress(aList.last().trimmed());
408 }
409 }
410}
411
413{
414 QString aString;
415 m_lcdKeyString = "";
416
417 m_connected = true;
418
419 // This gets called when we receive the "connect" string from the server
420 // indicating that "hello" was succesful
421 sendToServer("client_set name Myth");
422
423 // Create all the screens and widgets (when we change activity in the myth
424 // program, we just swap the priorities of the screens to show only the
425 // "current one")
426 sendToServer("screen_add Time");
427 setPriority("Time", MEDIUM);
428
429 if (gCoreContext->GetSetting("LCDBigClock", "1") == "1")
430 {
431 // Big Clock - spans multiple lines
432 sendToServer("widget_add Time rec1 string");
433 sendToServer("widget_add Time rec2 string");
434 sendToServer("widget_add Time rec3 string");
435 sendToServer("widget_add Time recCnt string");
436 sendToServer("widget_add Time d0 num");
437 sendToServer("widget_add Time d1 num");
438 sendToServer("widget_add Time sep num");
439 sendToServer("widget_add Time d2 num");
440 sendToServer("widget_add Time d3 num");
441 sendToServer("widget_add Time ampm string");
442 sendToServer("widget_add Time dot string");
443 }
444 else
445 {
446 sendToServer("widget_add Time timeWidget string");
447 sendToServer("widget_add Time topWidget string");
448 }
449
450 // The Menu Screen
451 // I'm not sure how to do multi-line widgets with lcdproc so we're going
452 // the ghetto way
453 sendToServer("screen_add Menu");
454 setPriority("Menu", LOW);
455 sendToServer("widget_add Menu topWidget string");
456 for (unsigned int i = 1; i <= m_lcdHeight; i++)
457 {
458 aString = "widget_add Menu menuWidget";
459 aString += QString::number (i);
460 aString += " string";
461 sendToServer(aString);
462 }
463
464 // The Music Screen
465 sendToServer("screen_add Music");
466 setPriority("Music", LOW);
467 sendToServer("widget_add Music topWidget1 string");
468 sendToServer("widget_add Music topWidget2 string");
469 sendToServer("widget_add Music timeWidget string");
470 sendToServer("widget_add Music infoWidget string");
471 sendToServer("widget_add Music progressBar hbar");
472
473 // The Channel Screen
474 sendToServer("screen_add Channel");
475 setPriority("Channel", LOW);
476 sendToServer("widget_add Channel topWidget string");
477 sendToServer("widget_add Channel botWidget string");
478 sendToServer("widget_add Channel timeWidget string");
479 sendToServer("widget_add Channel progressBar hbar");
480
481 // The Generic Screen
482 sendToServer("screen_add Generic");
483 setPriority("Generic", LOW);
484 sendToServer("widget_add Generic textWidget1 string");
485 sendToServer("widget_add Generic textWidget2 string");
486 sendToServer("widget_add Generic textWidget3 string");
487 sendToServer("widget_add Generic textWidget4 string");
488 sendToServer("widget_add Generic progressBar hbar");
489
490 // The Volume Screen
491 sendToServer("screen_add Volume");
492 setPriority("Volume", LOW);
493 sendToServer("widget_add Volume topWidget string");
494 sendToServer("widget_add Volume botWidget string");
495 sendToServer("widget_add Volume progressBar hbar");
496
497 // The Recording Status Screen
498 sendToServer("screen_add RecStatus");
499 setPriority("RecStatus", LOW);
500 sendToServer("widget_add RecStatus textWidget1 string");
501 sendToServer("widget_add RecStatus textWidget2 string");
502 sendToServer("widget_add RecStatus textWidget3 string");
503 sendToServer("widget_add RecStatus textWidget4 string");
504 sendToServer("widget_add RecStatus progressBar hbar");
505
506 m_lcdReady = true;
507 loadSettings();
508
509 // default to showing time
510 switchToTime();
511
513
514 // do we need to show the startup message
515 if (!m_startupMessage.isEmpty())
517
518 // send buffer if there's anything in there
519 if ( m_sendBuffer.length() > 0)
520 {
522 m_sendBuffer = "";
523 }
524}
525
526QString LCDProcClient::expandString(const QString &aString) const
527{
529 return aString;
530
531 // if version 5 then white space seperate the list of characters
532 auto add_ws = [](const QString& str, auto x){ return str + x + QString(" "); };
533 return std::accumulate(aString.cbegin(), aString.cend(), QString(), add_ws);
534}
535
537{
538 if (!m_lcdReady )
539 return;
540
541 QString aString;
542 QString old_keystring = m_lcdKeyString;
543
544 m_timeFormat = gCoreContext->GetSetting("LCDTimeFormat", "");
545 if ( m_timeFormat.isEmpty())
546 m_timeFormat = gCoreContext->GetSetting("TimeFormat", "h:mm AP");
547
548 m_dateFormat = gCoreContext->GetSetting("DateFormat", "dd.MM.yyyy");
549
550 // Get LCD settings
551 m_lcdShowMusic=(gCoreContext->GetSetting("LCDShowMusic", "1")=="1");
552 m_lcdShowMusicItems=(gCoreContext->GetSetting("LCDShowMusicItems", "ArtistAlbumTitle"));
553 m_lcdShowTime=(gCoreContext->GetSetting("LCDShowTime", "1")=="1");
554 m_lcdShowChannel=(gCoreContext->GetSetting("LCDShowChannel", "1")=="1");
555 m_lcdShowGeneric=(gCoreContext->GetSetting("LCDShowGeneric", "1")=="1");
556 m_lcdShowVolume=(gCoreContext->GetSetting("LCDShowVolume", "1")=="1");
557 m_lcdShowMenu=(gCoreContext->GetSetting("LCDShowMenu", "1")=="1");
558 m_lcdShowRecstatus=(gCoreContext->GetSetting("LCDShowRecStatus", "1")=="1");
559 m_lcdBacklightOn=(gCoreContext->GetSetting("LCDBacklightOn", "1")=="1");
560 m_lcdHeartbeatOn=(gCoreContext->GetSetting("LCDHeartBeatOn", "1")=="1");
561 m_lcdPopupTime = gCoreContext->GetDurSetting<std::chrono::seconds>("LCDPopupTime", 5s);
562 m_lcdBigClock = (gCoreContext->GetSetting("LCDBigClock", "1")=="1");
563 m_lcdKeyString = gCoreContext->GetSetting("LCDKeyString", "ABCDEF");
564
565 if (!old_keystring.isEmpty())
566 {
567 aString = "client_del_key " + expandString(old_keystring);
568 sendToServer(aString);
569 }
570
571 aString = "client_add_key " + expandString( m_lcdKeyString );
572 sendToServer(aString);
573
575 if ( m_lcdBacklightOn )
576 sendToServer("screen_set Time backlight on");
577 else
578 sendToServer("screen_set Time backlight off");
579
581 sendToServer("screen_set Menu backlight on");
582
583 setHeartbeat ("Music", m_lcdHeartbeatOn );
584 sendToServer("screen_set Music backlight on");
585
586 setHeartbeat ("Channel", m_lcdHeartbeatOn );
587 sendToServer("screen_set Channel backlight on");
588
589 setHeartbeat ("Generic", m_lcdHeartbeatOn );
590 sendToServer("screen_set Generic backlight on");
591
592 setHeartbeat ("Volume", m_lcdHeartbeatOn );
593 sendToServer("screen_set Volume backlight on");
594
595 setHeartbeat ("RecStatus", m_lcdHeartbeatOn );
596 sendToServer("screen_set RecStatus backlight on");
597}
598
600{
601 QList<LCDTextItem> textItems;
602
603 QStringList list = formatScrollerText( m_startupMessage );
604
605 int startrow = 1;
606 if (list.count() < (int) m_lcdHeight )
607 startrow = (( m_lcdHeight - list.count()) / 2) + 1;
608
609 for (int x = 0; x < list.count(); x++)
610 {
611 if (x == (int) m_lcdHeight )
612 break;
613 textItems.append(LCDTextItem(x + startrow, ALIGN_LEFT, list[x],
614 "Generic", false));
615 }
616
617 switchToGeneric(&textItems);
618
620}
621
623{
624 switchToTime();
625}
626
627void LCDProcClient::setStartupMessage(QString msg, std::chrono::seconds messagetime)
628{
629 m_startupMessage = std::move(msg);
630 m_startupShowTime = messagetime;
631}
632
633void LCDProcClient::setWidth(unsigned int x)
634{
635 if (x < 1 || x > 80)
636 return;
637 m_lcdWidth = x;
638}
639
640void LCDProcClient::setHeight(unsigned int x)
641{
642 if (x < 1 || x > 80)
643 return;
644 m_lcdHeight = x;
645}
646
647void LCDProcClient::setCellWidth(unsigned int x)
648{
649 if (x < 1 || x > 16)
650 return;
651 m_cellWidth = x;
652}
653
655{
656 if (x < 1 || x > 16)
657 return;
658 m_cellHeight = x;
659}
660
661void LCDProcClient::setVersion(const QString &sversion, const QString &pversion)
662{
663 m_protocolVersion = pversion;
664 m_serverVersion = sversion;
665
666 // the pVersion number is used internally to designate which protocol
667 // version LCDd is using:
668
669 if ( m_serverVersion.startsWith ("CVS-current") ||
670 m_serverVersion.startsWith ("0.5"))
671 {
672 // Latest CVS versions of LCDd has priorities switched
674 m_prioTop = "input";
675 m_prioUrgent = "alert";
676 m_prioHigh = "foreground";
677 m_prioMedium = "info";
678 m_prioLow = "background";
679 m_prioOff = "hidden";
680 }
681 else
682 {
684 m_prioTop = "64";
685 m_prioUrgent = "128";
686 m_prioHigh = "240";
687 m_prioMedium = "248";
688 m_prioLow = "252";
689 m_prioOff = "255";
690 }
691}
692
694{
695 if (debug_level > 0)
696 {
697 LOG(VB_GENERAL, LOG_INFO,
698 QString("LCDProcClient: The server is %1x%2 with each cell "
699 "being %3x%4.")
700 .arg( m_lcdWidth ).arg( m_lcdHeight ).arg( m_cellWidth ).arg( m_cellHeight ));
701 LOG(VB_GENERAL, LOG_INFO,
702 QString("LCDProcClient: LCDd version %1, protocol version %2.")
704 }
705
706 if (debug_level > 1)
707 {
708 LOG(VB_GENERAL, LOG_INFO,
709 QString("LCDProcClient: MythTV LCD settings:"));
710 LOG(VB_GENERAL, LOG_INFO,
711 QString("LCDProcClient: - showmusic : %1")
712 .arg( m_lcdShowMusic ));
713 LOG(VB_GENERAL, LOG_INFO,
714 QString("LCDProcClient: - showmusicitems : %1")
715 .arg( m_lcdShowMusicItems ));
716 LOG(VB_GENERAL, LOG_INFO,
717 QString("LCDProcClient: - showtime : %1")
718 .arg( m_lcdShowTime ));
719 LOG(VB_GENERAL, LOG_INFO,
720 QString("LCDProcClient: - showchannel : %1")
721 .arg( m_lcdShowChannel ));
722 LOG(VB_GENERAL, LOG_INFO,
723 QString("LCDProcClient: - showrecstatus : %1")
724 .arg( m_lcdShowRecstatus ));
725 LOG(VB_GENERAL, LOG_INFO,
726 QString("LCDProcClient: - showgeneric : %1")
727 .arg( m_lcdShowGeneric ));
728 LOG(VB_GENERAL, LOG_INFO,
729 QString("LCDProcClient: - showvolume : %1")
730 .arg( m_lcdShowVolume ));
731 LOG(VB_GENERAL, LOG_INFO,
732 QString("LCDProcClient: - showmenu : %1")
733 .arg( m_lcdShowMenu ));
734 LOG(VB_GENERAL, LOG_INFO,
735 QString("LCDProcClient: - backlighton : %1")
736 .arg( m_lcdBacklightOn ));
737 LOG(VB_GENERAL, LOG_INFO,
738 QString("LCDProcClient: - heartbeaton : %1")
739 .arg( m_lcdHeartbeatOn ));
740 LOG(VB_GENERAL, LOG_INFO,
741 QString("LCDProcClient: - popuptime : %1")
742 .arg( m_lcdPopupTime.count() ));
743 }
744}
745
746void LCDProcClient::veryBadThings(QAbstractSocket::SocketError /*error*/)
747{
748 // Deal with failures to connect and inabilities to communicate
749 LOG(VB_GENERAL, LOG_ERR, QString("Could not connect to LCDd: %1")
750 .arg(m_socket->errorString()));
751 m_socket->close();
752}
753
755{
756 if ( m_scrollListItems.count() == 0)
757 return;
758
760 return;
761
764
766 if ((int) m_scrollListItem >= m_scrollListItems.count())
768}
769
771{
772 // The usual reason things would get this far and then lcd_ready being
773 // false is the connection died and we're trying to re-establish the
774 // connection
775 if (debug_level > 1)
776 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: stopAll");
777
778 if ( m_lcdReady )
779 {
780 setPriority("Time", OFF);
781 setPriority("Music", OFF);
782 setPriority("Channel", OFF);
783 setPriority("Generic", OFF);
784 setPriority("Volume", OFF);
785 setPriority("Menu", OFF);
786 setPriority("RecStatus", OFF);
787 }
788
789 m_timeTimer->stop();
790 m_preScrollWTimer->stop();
791 m_scrollWTimer->stop();
792 m_popMenuTimer->stop();
793 m_menuScrollTimer->stop();
794 m_menuPreScrollTimer->stop();
795 m_recStatusTimer->stop();
796 m_scrollListTimer->stop();
797
798 unPopMenu();
799}
800
802{
803 setPriority("Time", MEDIUM);
804 setPriority("RecStatus", LOW);
805
806 m_timeTimer->start(1s);
807 outputTime();
808 m_activeScreen = "Time";
809 m_isTimeVisible = true;
810
813}
814
815void LCDProcClient::outputText(QList<LCDTextItem> *textItems)
816{
817 if (!m_lcdReady )
818 return;
819
820 QList<LCDTextItem>::iterator it = textItems->begin();
821 QString num;
822 unsigned int counter = 1;
823
824 // Do the definable scrolling in here.
825 // Use asignScrollingWidgets(curItem->getText(), "textWidget" + num);
826 // When scrolling is set, alignment has no effect
827 while (it != textItems->end() && counter < m_lcdHeight )
828 {
829 LCDTextItem *curItem = &(*it);
830 ++it;
831 num.setNum(curItem->getRow());
832
833 if (curItem->getScroll())
834 {
835 assignScrollingWidgets(curItem->getText(), curItem->getScreen(),
836 "textWidget" + num, curItem->getRow());
837 }
838 else
839 {
840 switch (curItem->getAlignment())
841 {
842 case ALIGN_LEFT:
843 outputLeftText(curItem->getScreen(), curItem->getText(),
844 "textWidget" + num, curItem->getRow());
845 break;
846 case ALIGN_RIGHT:
847 outputRightText(curItem->getScreen(), curItem->getText(),
848 "textWidget" + num, curItem->getRow());
849 break;
850 case ALIGN_CENTERED:
851 outputCenteredText(curItem->getScreen(), curItem->getText(),
852 "textWidget" + num, curItem->getRow());
853 break;
854 default: break;
855 }
856 }
857
858 ++counter;
859 }
860}
861
862void LCDProcClient::outputCenteredText(const QString& theScreen, QString theText, const QString& widget,
863 int row)
864{
865 QString aString;
866 unsigned int x = 0;
867
868 x = (( m_lcdWidth - theText.length()) / 2) + 1;
869
870 if (x > m_lcdWidth )
871 x = 1;
872
873 aString = "widget_set ";
874 aString += theScreen;
875 aString += " " + widget + " ";
876 aString += QString::number(x);
877 aString += " ";
878 aString += QString::number(row);
879 aString += " \"";
880 aString += theText.replace ('"', "\"");
881 aString += "\"";
882 sendToServer(aString);
883}
884
885void LCDProcClient::outputLeftText(const QString& theScreen, QString theText, const QString& widget,
886 int row)
887{
888 QString aString;
889 aString = "widget_set ";
890 aString += theScreen;
891 aString += " " + widget + " 1 ";
892 aString += QString::number(row);
893 aString += " \"";
894 aString += theText.replace ('"', "\"");
895 aString += "\"";
896 sendToServer(aString);
897}
898
899void LCDProcClient::outputRightText(const QString& theScreen, QString theText, const QString& widget,
900 int row)
901{
902 QString aString;
903 unsigned int x = (int)( m_lcdWidth - theText.length()) + 1;
904
905 aString = "widget_set ";
906 aString += theScreen;
907 aString += " " + widget + " ";
908 aString += QString::number(x);
909 aString += " ";
910 aString += QString::number(row);
911 aString += " \"";
912 aString += theText.replace ('"', "\"");
913 aString += "\"";
914 sendToServer(aString);
915}
916
917void LCDProcClient::assignScrollingList(QStringList theList, QString theScreen,
918 QString theWidget, int theRow)
919{
920 m_scrollListScreen = std::move(theScreen);
921 m_scrollListWidget = std::move(theWidget);
922 m_scrollListRow = theRow;
923 m_scrollListItems = std::move(theList);
924
926 scrollList();
928}
929
930//
931// Prepare for scrolling one or more text widgets on a single screen.
932// Notes:
933// - Before assigning the first text, call: lcdTextItems->clear();
934// - After assigning the last text, call: formatScrollingWidgets()
935// That's it ;-)
936
937void LCDProcClient::assignScrollingWidgets(const QString& theText, const QString& theScreen,
938 const QString& theWidget, int theRow)
939{
940 m_scrollScreen = theScreen;
941
942 // Alignment is not used...
943 m_lcdTextItems->append(LCDTextItem(theRow, ALIGN_LEFT, theText,
944 theScreen, true, theWidget));
945}
946
948{
949 m_scrollWTimer->stop();
950 m_preScrollWTimer->stop();
951
952 if ( m_lcdTextItems->isEmpty())
953 return; // Weird...
954
955 // Get the length of the longest item to scroll
956 auto longest = [](int cur, const auto & item)
957 { return std::max(cur, static_cast<int>(item.getText().length())); };
958 int max_len = std::accumulate(m_lcdTextItems->cbegin(), m_lcdTextItems->cend(),
959 0, longest);
960
961 // Make all scrollable items the same lenght and do the initial output
962 auto it = m_lcdTextItems->begin();
963 while (it != m_lcdTextItems->end())
964 {
965 LCDTextItem *curItem = &(*it);
966 ++it;
967 if (curItem->getText().length() > (int) m_lcdWidth )
968 {
969 QString temp;
970 QString temp2;
971 temp = temp.fill(QChar(' '), max_len - curItem->getText().length());
972 temp2 = temp2.fill(QChar(' '), m_lcdWidth );
973 curItem->setText(temp2 + curItem->getText() + temp);
975 curItem->getText().mid( m_lcdWidth, max_len),
976 curItem->getWidget(), curItem->getRow());
977 }
978 else
979 {
980 curItem->setScrollable(false);
982 curItem->getWidget(), curItem->getRow());
983 }
984 }
985
986 if (max_len <= (int) m_lcdWidth )
987 // We're done, no scrolling
988 return;
989
990 m_preScrollWTimer->start(2s);
991}
992
994{
996 m_preScrollWTimer->stop();
997 m_scrollWTimer->start(400ms);
998}
999
1001{
1003 return;
1004
1005 if ( m_lcdTextItems->isEmpty())
1006 return; // Weird...
1007
1008 unsigned int len = 0;
1009 for (const auto & item : std::as_const(*m_lcdTextItems))
1010 {
1011 if (item.getScroll())
1012 {
1013 // Note that all scrollable items have the same length!
1014 len = item.getText().length();
1015
1017 item.getText().mid( m_scrollPosition, m_lcdWidth ),
1018 item.getWidget(), item.getRow());
1019 }
1020 }
1021
1022 if (len == 0)
1023 {
1024 // Shouldn't happen, but....
1025 LOG(VB_GENERAL, LOG_ERR,
1026 "LCDProcClient::scrollWidgets called without scrollable items");
1027 m_scrollWTimer->stop();
1028 return;
1029 }
1031 if ( m_scrollPosition >= len)
1033}
1034
1035void LCDProcClient::startMusic(QString artist, const QString& album, const QString& track)
1036{
1037 // Playing music displays:
1038 // For 1-line displays:
1039 // <ArtistAlbumTitle>
1040 // For 2-line displays:
1041 // <ArtistAlbumTitle>
1042 // <Elapse/Remaining Time>
1043 // For 3-line displays:
1044 // <ArtistAlbumTitle>
1045 // <Elapse/Remaining Time>
1046 // <Info+ProgressBar>
1047 // For displays with more than 3 lines:
1048 // <ArtistAlbum>
1049 // <Title>
1050 // <Elapse/Remaining Time>
1051 // <Info+ProgressBar>
1052
1053 // Clear the progressBar and timeWidget before activating the Music
1054 // screen. This prevents the display of outdated junk.
1055 sendToServer("widget_set Music progressBar 1 1 0");
1056 sendToServer("widget_set Music timeWidget 1 1 \"\"");
1057 m_lcdTextItems->clear();
1058
1059 m_musicProgress = 0.0F;
1060 QString aString = std::move(artist);
1061 if ( m_lcdShowMusicItems == "ArtistAlbumTitle")
1062 {
1063 aString += " [";
1064 aString += album;
1065 aString += "] ";
1066 }
1067 else if ( m_lcdHeight < 4)
1068 {
1069 aString += " - ";
1070 }
1071
1072 if ( m_lcdHeight < 4)
1073 {
1074 aString += track;
1075 }
1076 else
1077 {
1078 assignScrollingWidgets(track, "Music", "topWidget2", 2);
1079 }
1080 assignScrollingWidgets(aString, "Music", "topWidget1", 1);
1082
1083 // OK, everything is at least somewhat initialised. Activate
1084 // the music screen.
1085 m_activeScreen = "Music";
1086 if ( m_lcdShowMusic )
1087 setPriority("Music", HIGH);
1088}
1089
1090void LCDProcClient::startChannel(const QString& channum, const QString& title, const QString& subtitle)
1091{
1092 QString aString;
1093
1094 if ( m_lcdShowChannel )
1095 setPriority("Channel", HIGH);
1096
1097 m_activeScreen = "Channel";
1098
1099 if ( m_lcdHeight <= 2)
1100 {
1101 aString = channum + "|" + title;
1102 if (!subtitle.isEmpty())
1103 aString += "|" + subtitle;
1104 QStringList list = formatScrollerText(aString);
1105 assignScrollingList(list, "Channel", "topWidget", 1);
1106 }
1107 else
1108 {
1109 aString = channum;
1110 m_lcdTextItems->clear();
1111 assignScrollingWidgets(aString, "Channel", "topWidget", 1);
1112 aString = title;
1113 if (subtitle.length() > 0)
1114 {
1115 aString += " - '";
1116 aString += subtitle;
1117 aString += "'";
1118 }
1119 assignScrollingWidgets(aString, "Channel", "botWidget", 2);
1121 }
1122
1123 m_channelTime = "";
1124 m_progress = 0.0;
1125 outputChannel();
1126}
1127
1128void LCDProcClient::startGeneric(QList<LCDTextItem> *textItems)
1129{
1130 QList<LCDTextItem>::iterator it = textItems->begin();
1131 LCDTextItem *curItem = &(*it);
1132
1133 if ( m_lcdShowGeneric )
1134 setPriority("Generic", TOP);
1135
1136 // Clear out the LCD. Do this before checking if its empty incase the user
1137 // wants to just clear the lcd
1138 outputLeftText("Generic", "", "textWidget1", 1);
1139 outputLeftText("Generic", "", "textWidget2", 2);
1140 outputLeftText("Generic", "", "textWidget3", 3);
1141
1142 // If nothing, return without setting up the timer, etc
1143 if (textItems->isEmpty())
1144 return;
1145
1146 m_activeScreen = "Generic";
1147
1148 m_busyProgress = false;
1149 m_busyPos = 1;
1150 m_busyDirection = 1;
1151 m_busyIndicatorSize = 2.0F;
1152 m_genericProgress = 0.0;
1153
1154 // Todo, make scrolling definable in LCDTextItem
1155 ++it;
1156
1157
1158 // Weird observations:
1159 // - The first item is always assumed 'scrolling', for this
1160 // item, the scrollable property is ignored...
1161 // - Why output line 1, progressbar, rest of lines? Why not
1162 // all outputlines, progressbar? That way, outputText() can
1163 // just handle the whole thing and the 'pop off' stuff can go.
1164 //
1165 m_lcdTextItems->clear();
1166 assignScrollingWidgets(curItem->getText(), "Generic",
1167 "textWidget1", curItem->getRow());
1168
1169 outputGeneric();
1170
1171 // Pop off the first item so item one isn't written twice
1172 textItems->removeFirst();
1173 if (!textItems->isEmpty())
1174 outputText(textItems);
1176}
1177
1178void LCDProcClient::startMenu(QList<LCDMenuItem> *menuItems, QString app_name,
1179 bool popMenu)
1180{
1181 // Now do the menu items
1182 if (menuItems->isEmpty())
1183 return;
1184
1185 QString aString;
1186
1187 // Stop the scrolling if the menu has changed
1188 m_menuScrollTimer->stop();
1189
1190 // Menu is higher priority than volume
1191 if ( m_lcdShowMenu )
1192 setPriority("Menu", URGENT);
1193
1194 // Write out the app name
1195 if ( m_lcdHeight > 1)
1196 outputCenteredText("Menu", std::move(app_name), "topWidget", 1);
1197
1198 QList<LCDMenuItem>::iterator it = menuItems->begin();
1199
1200 // First loop through and figure out where the selected item is in the
1201 // list so we know how many above and below to display
1202 unsigned int selectedItem = 0;
1203 unsigned int counter = 0;
1204 bool oneSelected = false;
1205
1206 while (it != menuItems->end())
1207 {
1208 LCDMenuItem *curItem = &(*it);
1209 ++it;
1210 if (curItem->isSelected() && !oneSelected)
1211 {
1212 selectedItem = counter + 1;
1213 oneSelected = true;
1214 break;
1215 }
1216 ++counter;
1217 }
1218
1219 // If there isn't one selected item, then write it on the display and return
1220 if (!oneSelected)
1221 {
1222 sendToServer("widget_set Menu topWidget 1 1 \"No menu item selected\"");
1223 sendToServer("widget_set Menu menuWidget1 1 2 \" ABORTING \"");
1224 m_menuScrollTimer->stop();
1225 return;
1226 }
1227
1228 m_popMenuTimer->stop();
1229 // Start the unPop timer if this is a popup menu
1230 if (popMenu)
1232
1233 // QPtrListIterator doesn't contain a deep copy constructor. . .
1234 // This will contain a copy of the menuItems for scrolling purposes
1235 QList<LCDMenuItem>::iterator itTemp = menuItems->begin();
1236 m_lcdMenuItems->clear();
1237 counter = 1;
1238 while (itTemp != menuItems->end())
1239 {
1240 LCDMenuItem *curItem = &(*itTemp);
1241 ++itTemp;
1242 m_lcdMenuItems->append(LCDMenuItem(curItem->isSelected(),
1243 curItem->isChecked(), curItem->ItemName(),
1244 curItem->getIndent()));
1245 ++counter;
1246 }
1247
1248 // If there is only one or two lines on the display, then just write the selected
1249 // item and leave
1250 if ( m_lcdHeight <= 2)
1251 {
1252 it = menuItems->begin();
1253 while (it != menuItems->end())
1254 {
1255 LCDMenuItem *curItem = &(*it);
1256 ++it;
1257 if (curItem->isSelected())
1258 {
1259 // Set the scroll flag if necessary, otherwise set it to false
1260 if (curItem->ItemName().length() > (int)( m_lcdWidth -lcdStartCol))
1261 {
1262 m_menuPreScrollTimer->setSingleShot(true);
1263 m_menuPreScrollTimer->start(2s);
1264 curItem->setScroll(true);
1265 }
1266 else
1267 {
1268 m_menuPreScrollTimer->stop();
1269 curItem->setScroll(false);
1270 }
1271 if ( m_lcdHeight == 2)
1272 {
1273 aString = "widget_set Menu menuWidget1 1 2 \">";
1274 }
1275 else
1276 {
1277 aString = "widget_set Menu menuWidget1 1 1 \"";
1278 }
1279
1280 if (lcdStartCol != 0)
1281 {
1282 switch (curItem->isChecked())
1283 {
1284 case CHECKED: aString += "X "; break;
1285 case UNCHECKED: aString += "O "; break;
1286 case NOTCHECKABLE: aString += " "; break;
1287 default: break;
1288 }
1289 }
1290
1291 aString += curItem->ItemName().left( m_lcdWidth - lcdStartCol) +
1292 "\"";
1293 sendToServer(aString);
1294 return;
1295 }
1296 }
1297
1298 return;
1299 }
1300
1301 // Reset things
1302 counter = 1;
1303 it = menuItems->begin();
1304
1305 // Move the iterator to selectedItem lcdHeight/2, if > 1, -1.
1306 unsigned int midPoint = ( m_lcdHeight/2) - 1;
1307 if (selectedItem > midPoint && menuItems->size() >= (int) m_lcdHeight-1)
1308 {
1309 while (counter != selectedItem)
1310 {
1311 ++it;
1312 ++counter;
1313 }
1314 it -= midPoint;
1315 counter -= midPoint;
1316 }
1317
1318 // Back up one if we're at the end so the last item shows up at the bottom
1319 // of the display
1320 if (counter + midPoint > menuItems->size() - midPoint && counter > midPoint)
1321 {
1322 it -= (counter + ( m_lcdHeight / 2) - 1) - (menuItems->size() - midPoint);
1323 }
1324
1325 counter = 1;
1326 while (it != menuItems->end())
1327 {
1328 LCDMenuItem *curItem = &(*it);
1329 // Can't write more menu items then we have on the display
1330 if ((counter + 1) > m_lcdHeight )
1331 break;
1332
1333 ++it;
1334
1335 aString = "widget_set Menu menuWidget";
1336 aString += QString::number(counter) + " 1 ";
1337 aString += QString::number(counter + 1) + " \"";
1338
1339 if (curItem->isSelected())
1340 aString += ">";
1341 else
1342 aString += " ";
1343
1344 switch (curItem->isChecked())
1345 {
1346 case CHECKED: aString += "X "; break;
1347 case UNCHECKED: aString += "O "; break;
1348 case NOTCHECKABLE: aString += " "; break;
1349 default: break;
1350 }
1351
1352 aString += curItem->ItemName().left( m_lcdWidth - lcdStartCol) + "\"";
1353 sendToServer(aString);
1354
1355 ++counter;
1356 }
1357
1358 // Make sure to clear out the rest of the screen
1359 while (counter < m_lcdHeight )
1360 {
1361 aString = "widget_set Menu menuWidget";
1362 aString += QString::number(counter) + " 1 ";
1363 aString += QString::number(counter + 1) + " \"\"";
1364 sendToServer(aString);
1365
1366 ++counter;
1367 }
1368
1369 m_menuPreScrollTimer->setSingleShot(true);
1370 m_menuPreScrollTimer->start(2s);
1371}
1372
1374{
1375 // If there are items to scroll, wait 2 seconds for the user to read whats
1376 // already there
1377
1378 if (!m_lcdMenuItems )
1379 return;
1380
1382
1383 QList<LCDMenuItem>::iterator it = m_lcdMenuItems->begin();
1384
1385 QString temp;
1386 // Loop through and prepend everything with enough spaces
1387 // for smooth scrolling, and update the position
1388 while (it != m_lcdMenuItems->end())
1389 {
1390 LCDMenuItem *curItem = &(*it);
1391 ++it;
1392 // Don't setup for smooth scrolling if the item isn't long enough
1393 // (It causes problems with items being scrolled when they shouldn't)
1394 if (curItem->ItemName().length() > (int)( m_lcdWidth - lcdStartCol))
1395 {
1396 temp = temp.fill(QChar(' '), m_lcdWidth - curItem->getIndent() -
1397 lcdStartCol);
1398 curItem->setItemName(temp + curItem->ItemName());
1399 curItem->setScrollPos(curItem->getIndent() + temp.length());
1400 curItem->setScroll(true);
1401 }
1402 else
1403 {
1404 curItem->setScroll(false);
1405 }
1406 }
1407
1408 // Can get segfaults if we try to start a timer thats already running. . .
1409 m_menuScrollTimer->stop();
1410 m_menuScrollTimer->start(250ms);
1411}
1412
1414{
1415 if (!m_lcdMenuItems )
1416 return;
1417
1418 QString aString;
1419 QString bString;
1420 QList<LCDMenuItem>::iterator it = m_lcdMenuItems->begin();
1421
1423
1424 // First loop through and figure out where the selected item is in the
1425 // list so we know how many above and below to display
1426 unsigned int selectedItem = 0;
1427 unsigned int counter = 0;
1428
1429 while (it != m_lcdMenuItems->end())
1430 {
1431 LCDMenuItem *curItem = &(*it);
1432 ++it;
1433 if (curItem->isSelected())
1434 {
1435 selectedItem = counter + 1;
1436 break;
1437 }
1438 ++counter;
1439 }
1440
1441 // If there is only one or two lines on the display, then just write
1442 // the selected item and leave
1443 it = m_lcdMenuItems->begin();
1444 if ( m_lcdHeight <= 2)
1445 {
1446 while (it != m_lcdMenuItems->end())
1447 {
1448 LCDMenuItem *curItem = &(*it);
1449 ++it;
1450 if (curItem->isSelected())
1451 {
1452 curItem->incrementScrollPos();
1453 if ((int)curItem->getScrollPos() > curItem->ItemName().length())
1454 {
1455 // Scroll slower second and subsequent times through
1456 m_menuScrollTimer->stop();
1457 m_menuScrollTimer->start(500ms);
1458 curItem->setScrollPos(curItem->getIndent());
1459 }
1460
1461 // Stop the timer if this item really doesn't need to scroll.
1462 // This should never have to get invoked because in theory
1463 // the code in startMenu has done its job. . .
1464 if (curItem->ItemName().length() < (int)( m_lcdWidth - lcdStartCol))
1465 m_menuScrollTimer->stop();
1466
1467 if ( m_lcdHeight == 2)
1468 {
1469 aString = "widget_set Menu menuWidget1 1 2 \">";
1470 }
1471 else
1472 {
1473 aString = "widget_set Menu menuWidget1 1 1 \"";
1474 }
1475
1476 if ( m_lcdWidth < 12)
1477 {
1478 switch(curItem->isChecked())
1479 {
1480 case CHECKED: aString += "X"; break;
1481 case UNCHECKED: aString += "O"; break;
1482 case NOTCHECKABLE: aString += ""; break;
1483 default: break;
1484 }
1485 }
1486 else
1487 {
1488 switch(curItem->isChecked())
1489 {
1490 case CHECKED: aString += "X "; break;
1491 case UNCHECKED: aString += "O "; break;
1492 case NOTCHECKABLE: aString += " "; break;
1493 default: break;
1494 }
1495 }
1496
1497 // Indent this item if nessicary
1498 aString += bString.fill(' ', curItem->getIndent());
1499
1500#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1501 aString += curItem->ItemName().midRef(curItem->getScrollPos(),
1502 ( m_lcdWidth - lcdStartCol));
1503#else
1504 aString += QStringView(curItem->ItemName())
1505 .mid(curItem->getScrollPos(), (m_lcdWidth - lcdStartCol));
1506#endif
1507 aString += "\"";
1508 sendToServer(aString);
1509 return;
1510 }
1511 }
1512
1513 return;
1514 }
1515
1516 // Find the longest line, if menuScrollPosition is longer then this, then
1517 // reset them all
1518 it = m_lcdMenuItems->begin();
1519 int longest_line = 0;
1520 int max_scroll_pos = 0;
1521
1522 while (it != m_lcdMenuItems->end())
1523 {
1524 LCDMenuItem *curItem = &(*it);
1525 ++it;
1526 longest_line = std::max(
1527#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1528 curItem->ItemName().length(),
1529#else
1530 static_cast<int>(curItem->ItemName().length()),
1531#endif
1532 longest_line);
1533
1534 if ((int)curItem->getScrollPos() > max_scroll_pos)
1535 max_scroll_pos = curItem->getScrollPos();
1536 }
1537
1538 // If max_scroll_pos > longest_line then reset
1539 if (max_scroll_pos > longest_line)
1540 {
1541 // Scroll slower second and subsequent times through
1542 m_menuScrollTimer->stop();
1543 m_menuScrollTimer->start(500ms);
1545
1546 it = m_lcdMenuItems->begin();
1547 while (it != m_lcdMenuItems->end())
1548 {
1549 LCDMenuItem *curItem = &(*it);
1550 ++it;
1551 curItem->setScrollPos(curItem->getIndent());
1552 }
1553 }
1554
1555 // Reset things
1556 counter = 1;
1557 it = m_lcdMenuItems->begin();
1558
1559 // Move the iterator to selectedItem -1
1560 if (selectedItem != 1 && m_lcdMenuItems->size() >= (int) m_lcdHeight )
1561 {
1562 while (counter != selectedItem)
1563 {
1564 ++it;
1565 ++counter;
1566 }
1567 --it;
1568 }
1569
1570 // Back up one if were at the end so the last item shows up at the bottom
1571 // of the display
1572 if (counter > 1 && (int)counter == m_lcdMenuItems->size())
1573 --it;
1574
1575 bool stopTimer = true;
1576
1577 counter = 1;
1578 while (it != m_lcdMenuItems->end() && counter <= m_lcdHeight )
1579 {
1580 LCDMenuItem *curItem = &(*it);
1581 // Can't write more menu items then we have on the display
1582 if ((counter + 1) > m_lcdHeight )
1583 break;
1584
1585 ++it;
1586
1587 if (curItem->Scroll())
1588 {
1589 stopTimer = false;
1590 aString = "widget_set Menu menuWidget";
1591 aString += QString::number(counter) + " 1 ";
1592 aString += QString::number(counter + 1) + " \"";
1593
1594 if (curItem->isSelected())
1595 aString += ">";
1596 else
1597 aString += " ";
1598
1599 switch (curItem->isChecked())
1600 {
1601 case CHECKED: aString += "X "; break;
1602 case UNCHECKED: aString += "O "; break;
1603 case NOTCHECKABLE: aString += " "; break;
1604 default: break;
1605 }
1606
1607 // Indent this item if nessicary
1608 bString = "";
1609 bString.fill(' ', curItem->getIndent());
1610 aString += bString;
1611
1612 // Increment the scroll position counter for this item
1613 curItem->incrementScrollPos();
1614
1615 if ((int)curItem->getScrollPos() <= longest_line)
1616 {
1617#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1618 aString += curItem->ItemName().midRef(curItem->getScrollPos(),
1620#else
1621 aString += QStringView(curItem->ItemName())
1622 .mid(curItem->getScrollPos(), (m_lcdWidth-lcdStartCol));
1623#endif
1624 }
1625
1626 aString += "\"";
1627 sendToServer(aString);
1628 }
1629
1630 ++counter;
1631 }
1632
1633 // If there are no items to scroll, don't waste our time
1634 if (stopTimer)
1635 m_menuScrollTimer->stop();
1636}
1637
1638void LCDProcClient::startVolume(const QString& app_name)
1639{
1640 if ( m_lcdShowVolume )
1641 setPriority("Volume", TOP);
1642 if ( m_lcdHeight > 1)
1643 outputCenteredText("Volume", "MythTV " + app_name + " Volume");
1644 m_volumeLevel = 0.0;
1645
1646 outputVolume();
1647}
1648
1650{
1651 // Stop the scrolling timer
1652 m_menuScrollTimer->stop();
1653 setPriority("Menu", OFF);
1654}
1655
1656void LCDProcClient::setChannelProgress(const QString &time, float value)
1657{
1658 if (!m_lcdReady )
1659 return;
1660
1661 m_progress = value;
1662 m_channelTime = time;
1663
1664 if ( m_progress < 0.0F)
1665 m_progress = 0.0F;
1666 else if ( m_progress > 1.0F)
1667 m_progress = 1.0F;
1668
1669 outputChannel();
1670}
1671
1672void LCDProcClient::setGenericProgress(bool b, float value)
1673{
1674 if (!m_lcdReady )
1675 return;
1676
1677 m_genericProgress = value;
1678
1679 if ( m_genericProgress < 0.0F)
1680 m_genericProgress = 0.0F;
1681 else if ( m_genericProgress > 1.0F)
1682 m_genericProgress = 1.0F;
1683
1684 // Note, this will let us switch to/from busy indicator by
1685 // alternating between being passed true or false for b.
1686 m_busyProgress = b;
1687 if ( m_busyProgress )
1688 {
1689 // If we're at either end of the line, switch direction
1690 if (( m_busyPos + m_busyDirection >
1691 (signed int) m_lcdWidth - m_busyIndicatorSize ) ||
1692 ( m_busyPos + m_busyDirection < 1))
1693 {
1695 }
1698 }
1699 else
1700 {
1701 m_busyPos = 1;
1702 }
1703
1704 outputGeneric();
1705}
1706
1707void LCDProcClient::setMusicProgress(QString time, float value)
1708{
1709 if (!m_lcdReady )
1710 return;
1711
1712 m_musicProgress = value;
1713 m_musicTime = std::move(time);
1714
1715 if ( m_musicProgress < 0.0F)
1716 m_musicProgress = 0.0F;
1717 else if ( m_musicProgress > 1.0F)
1718 m_musicProgress = 1.0F;
1719
1720 outputMusic();
1721}
1722
1724{
1725 if (!m_lcdReady )
1726 return;
1727
1728 m_musicRepeat = repeat;
1729
1730 outputMusic ();
1731}
1732
1734{
1735 if (!m_lcdReady )
1736 return;
1737
1738 m_musicShuffle = shuffle;
1739
1740 outputMusic ();
1741}
1742
1744{
1745 if (!m_lcdReady )
1746 return;
1747
1748 m_volumeLevel = value;
1749
1750 m_volumeLevel = std::clamp(m_volumeLevel, 0.0F, 1.0F);
1751
1752 outputVolume();
1753}
1754
1756{
1757 QString aString;
1758 aString = "output ";
1759 aString += QString::number(mask);
1760 sendToServer(aString);
1761}
1762
1764{
1765 removeWidgets();
1766 loadSettings();
1767 init();
1768}
1769
1771{
1772 QString aString;
1773 QString time = QTime::currentTime().toString( m_timeFormat );
1774 int toffset = 0;
1775 int xoffset = 0;
1776
1777 // kludge ahead: use illegal number (11) to clear num display type
1778
1779 // kluge - Uses string length to determine time format for parsing
1780 // 1:00 = 4 characters = 24-hour format, 1 digit hour
1781 // 12:00 = 5 characters = 24-hour format, 2 digit hour
1782 // 1:00 am = 7 characters = 12-hour format, 1 digit hour
1783 // 12:00 am = 8 characters = 12-hour format, 2 digit hour
1784 if ((time.length() == 8) || (time.length() == 5))
1785 toffset = 1;
1786
1787 // if 12-hour clock, add AM/PM indicator to end of the 2nd line
1788 if (time.length() > 6)
1789 {
1790 aString = time.at(5 + toffset);
1791 aString += time.at(6 + toffset);
1792 xoffset = 1;
1793 }
1794 else
1795 {
1796 aString = " ";
1797 }
1798 outputRightText("Time", aString, "ampm", m_lcdHeight - 1);
1799
1800 if ( m_isRecording )
1801 {
1802 outputLeftText("Time","R","rec1",1);
1803 outputLeftText("Time","E","rec2",2);
1804 outputLeftText("Time","C","rec3",3);
1805 aString = QString::number((int) m_tunerList.size());
1806 outputLeftText("Time",aString,"recCnt",4);
1807
1808 }
1809 else
1810 {
1811 outputLeftText("Time"," ","rec1",1);
1812 outputLeftText("Time"," ","rec2",2);
1813 outputLeftText("Time"," ","rec3",3);
1814 outputLeftText("Time"," ","recCnt",4);
1815 }
1816
1817 // Add Hour 10's Digit
1818 aString = "widget_set Time d0 ";
1819 aString += QString::number( (m_lcdWidth/2) - 5 - xoffset) + " ";
1820 if (toffset == 0)
1821 aString += "11";
1822 else
1823 aString += time.at(0);
1824 sendToServer(aString);
1825
1826 // Add Hour 1's Digit
1827 aString = "widget_set Time d1 ";
1828 aString += QString::number( (m_lcdWidth/2) - 2 - xoffset) + " ";
1829 aString += time.at(0 + toffset);
1830 sendToServer(aString);
1831
1832 // Add the Colon
1833 aString = "widget_set Time sep ";
1834 aString += QString::number( (m_lcdWidth/2) + 1 - xoffset);
1835 aString += " 10"; // 10 means: colon
1836 sendToServer(aString);
1837
1838 // Add Minute 10's Digit
1839 aString = "widget_set Time d2 ";
1840 aString += QString::number( (m_lcdWidth/2) + 2 - xoffset) + " ";
1841 aString += time.at(2 + toffset);
1842 sendToServer(aString);
1843
1844 // Add Minute 1's Digit
1845 aString = "widget_set Time d3 ";
1846 aString += QString::number( (m_lcdWidth/2) + 5 - xoffset) + " ";
1847 aString += time.at(3 + toffset);
1848 sendToServer(aString);
1849
1850 // Added a flashing dot in the bottom-right corner
1851 if ( m_timeFlash )
1852 {
1853 outputRightText("Time", ".", "dot", m_lcdHeight );
1854 m_timeFlash = false;
1855 }
1856 else
1857 {
1858 outputRightText("Time", " ", "dot", m_lcdHeight );
1859 m_timeFlash = true;
1860 }
1861}
1862
1864{
1865 if ( m_lcdBigClock )
1866 dobigclock();
1867 else
1868 dostdclock();
1869}
1870
1872{
1873 if (!m_lcdShowTime )
1874 return;
1875
1877 {
1878 outputCenteredText("Time", tr("RECORDING"), "topWidget", 1);
1879 }
1880 else
1881 {
1883 "Time", MythDate::current().toLocalTime().toString( m_dateFormat ),
1884 "topWidget", 1);
1885 }
1886
1887 QString aString;
1888 int x = 0;
1889 int y = 0;
1890
1891 if ( m_lcdHeight < 3)
1892 y = m_lcdHeight;
1893 else
1894 y = (int) std::rint( m_lcdHeight / 2) + 1;
1895
1896 QString time = QTime::currentTime().toString( m_timeFormat );
1897 x = (( m_lcdWidth - time.length()) / 2) + 1;
1898 aString = "widget_set Time timeWidget ";
1899 aString += QString::number(x);
1900 aString += " ";
1901 aString += QString::number(y);
1902 aString += " \"";
1903 if ( m_lcdShowTime ) {
1904 aString += time + "\"";
1905 if ( m_timeFlash )
1906 {
1907 aString = aString.replace(':', ' ');
1908 m_timeFlash = false;
1909 }
1910 else
1911 {
1912 m_timeFlash = true;
1913 }
1914 }
1915 else
1916 {
1917 aString += " \"";
1918 }
1919 sendToServer(aString);
1920}
1921
1922// if one or more recordings are taking place we alternate between
1923// showing the time and the recording status screen
1925{
1927 return;
1928
1930 {
1931 // switch to the rec status screen
1932 setPriority("RecStatus", MEDIUM);
1933 setPriority("Time", LOW);
1934
1935 m_timeTimer->stop();
1936 m_scrollWTimer->stop();
1937 m_scrollListTimer->stop();
1938 m_isTimeVisible = false;
1939 m_activeScreen = "RecStatus";
1940
1941 if (m_lcdTunerNo > (int) m_tunerList.size() - 1)
1942 m_lcdTunerNo = 0;
1943 }
1944 else if ( m_lcdTunerNo > (int) m_tunerList.size() - 1)
1945 {
1946 m_lcdTunerNo = 0;
1947
1948 // switch to the time screen
1949 setPriority("Time", MEDIUM);
1950 setPriority("RecStatus", LOW);
1951
1952 m_timeTimer->start(1s);
1953 m_scrollWTimer->stop();
1954 m_scrollListTimer->stop();
1956
1957 outputTime();
1958 m_activeScreen = "Time";
1959 m_isTimeVisible = true;
1960
1961 return;
1962 }
1963
1964 QString aString;
1965 QString status;
1966 QStringList list;
1967 std::chrono::milliseconds listTime { 1 };
1968
1970
1971 m_scrollListItems.clear();
1972 if ( m_lcdHeight >= 4)
1973 {
1974 // LINE 1 - "R" + Channel
1975 status = tr("R ");
1976 status += tuner.channame;
1977 outputLeftText("RecStatus", status, "textWidget1", 1);
1978
1979 // LINE 2 - "E" + Program Title
1980 status = tr("E ");
1981 status += tuner.title;
1982 outputLeftText("RecStatus", status, "textWidget2", 2);
1983 //list = formatScrollerText(status);
1984 //assignScrollingList(list, "RecStatus", "textWidget2", 2);
1985
1986 // LINE 3 - "C" + Program Subtitle
1987 status = tr("C ");
1988 status += tuner.subtitle;
1989 outputLeftText("RecStatus", status, "textWidget3", 3);
1990 //list = formatScrollerText(status);
1991 //assignScrollingList(list, "RecStatus", "textWidget3", 3);
1992
1993 // LINE 4 - hh:mm-hh:mm + Progress Bar
1994 status = tuner.startTime.toLocalTime().toString("hh:mm") + "-" +
1995 tuner.endTime.toLocalTime().toString("hh:mm");
1996 outputLeftText("RecStatus", status, "textWidget4", 4);
1997
1998 int length = tuner.startTime.secsTo(tuner.endTime);
1999 int delta = tuner.startTime.secsTo(MythDate::current());
2000 double rec_progress = (double) delta / length;
2001
2002 aString = "widget_set RecStatus progressBar 13 ";
2003 aString += QString::number( m_lcdHeight );
2004 aString += " ";
2005 aString += QString::number((int)rint(rec_progress * ( m_lcdWidth - 13) *
2006 m_cellWidth ));
2007 sendToServer(aString);
2008
2009 listTime = list.count() * LCD_SCROLLLIST_TIME * 2;
2010 }
2011 else
2012 {
2013 status = tr("RECORDING|");
2014 status += tuner.title;
2015 if (!tuner.subtitle.isEmpty())
2016 status += "|(" + tuner.subtitle + ")";
2017
2018 status += "|" + tuner.startTime.toLocalTime().toString("hh:mm")
2019 + " to " +
2020 tuner.endTime.toLocalTime().toString("hh:mm");
2021
2022 list = formatScrollerText(status);
2023 assignScrollingList(list, "RecStatus", "textWidget1", 1);
2024
2025 if ( m_lcdHeight > 1)
2026 {
2027 int length = tuner.startTime.secsTo(tuner.endTime);
2028 int delta = tuner.startTime.secsTo(MythDate::current());
2029 double rec_progress = (double) delta / length;
2030
2031 aString = "widget_set RecStatus progressBar 1 ";
2032 aString += QString::number( m_lcdHeight );
2033 aString += " ";
2034 aString += QString::number((int)rint(rec_progress * m_lcdWidth *
2035 m_cellWidth ));
2036 sendToServer(aString);
2037 }
2038 else
2039 {
2040 sendToServer("widget_set RecStatus progressBar 1 1 0");
2041 }
2042
2043 listTime = list.count() * LCD_SCROLLLIST_TIME * 2;
2044 }
2045
2046 if (listTime < LCD_TIME_TIME)
2047 listTime = LCD_TIME_TIME;
2048
2049 m_recStatusTimer->start(listTime);
2050 m_lcdTunerNo++;
2051}
2052
2053void LCDProcClient::outputScrollerText(const QString& theScreen, const QString& theText,
2054 const QString& widget, int top, int bottom)
2055{
2056 QString aString;
2057 aString = "widget_set " + theScreen + " " + widget;
2058 aString += " 1 ";
2059 aString += QString::number(top) + " ";
2060 aString += QString::number( m_lcdWidth ) + " ";
2061 aString += QString::number(bottom);
2062 aString += " v 8 \"" + theText + "\"";
2063
2064 sendToServer(aString);
2065}
2066
2067QStringList LCDProcClient::formatScrollerText(const QString &text) const
2068{
2069 QString separators = " |-_/:('<~";
2070 QStringList lines;
2071
2072 int lastSplit = 0;
2073 QString line = "";
2074
2075 for (const auto& x : std::as_const(text))
2076 {
2077 if (separators.contains(x))
2078 lastSplit = line.length();
2079
2080 line += x;
2081 if (line.length() > (int) m_lcdWidth || x == '|')
2082 {
2083 QString formatedLine;
2084 formatedLine.fill(' ', m_lcdWidth );
2085 formatedLine = formatedLine.replace(( m_lcdWidth - lastSplit) / 2,
2086 lastSplit, line.left(lastSplit));
2087
2088 lines.append(formatedLine); // clazy:exclude=reserve-candidates
2089
2090 if (line[lastSplit] == ' ' || line[lastSplit] == '|')
2091 line = line.mid(lastSplit + 1);
2092 else
2093 line = line.mid(lastSplit);
2094
2095 lastSplit = m_lcdWidth;
2096 }
2097 }
2098
2099 // make sure we add the last line
2100 QString formatedLine;
2101 formatedLine.fill(' ', m_lcdWidth );
2102 formatedLine = formatedLine.replace(( m_lcdWidth - line.length()) / 2,
2103 line.length(), line);
2104
2105 lines.append(formatedLine);
2106
2107 return lines;
2108}
2109
2111{
2112 // See startMusic() for a discription of the Music screen contents
2113
2114 outputCenteredText("Music", m_musicTime, "timeWidget",
2115 m_lcdHeight < 4 ? 2 : 3);
2116
2117 if ( m_lcdHeight > 2)
2118 {
2119 QString aString;
2120 QString shuffle = "";
2121 QString repeat = "";
2122 int info_width = 0;
2123
2124 if ( m_musicShuffle == 1)
2125 {
2126 shuffle = "S:? ";
2127 }
2128 else if ( m_musicShuffle == 2)
2129 {
2130 shuffle = "S:i ";
2131 }
2132 else if ( m_musicShuffle == 3)
2133 {
2134 shuffle = "S:a ";
2135 }
2136
2137 if ( m_musicRepeat == 1)
2138 {
2139 repeat = "R:1 ";
2140 }
2141 else if ( m_musicRepeat == 2)
2142 {
2143 repeat = "R:* ";
2144 }
2145
2146 if (shuffle.length() != 0 || repeat.length() != 0)
2147 {
2148 aString = shuffle + repeat;
2149 info_width = aString.length();
2150 outputLeftText("Music", aString, "infoWidget", m_lcdHeight );
2151 }
2152 else
2153 {
2154 outputLeftText("Music", " ", "infoWidget", m_lcdHeight );
2155 }
2156
2157 aString = "widget_set Music progressBar ";
2158 aString += QString::number(info_width + 1);
2159 aString += " ";
2160 aString += QString::number( m_lcdHeight );
2161 aString += " ";
2162 aString += QString::number((int)std::rint( m_musicProgress *
2163 ( m_lcdWidth - info_width) * m_cellWidth ));
2164 sendToServer(aString);
2165 }
2166}
2167
2169{
2170 if ( m_lcdHeight > 1)
2171 {
2172 QString aString;
2173 aString = "widget_set Channel progressBar 1 ";
2174 aString += QString::number( m_lcdHeight );
2175 aString += " ";
2176 aString += QString::number((int)std::rint( m_progress * m_lcdWidth * m_cellWidth ));
2177 sendToServer(aString);
2178
2179 if ( m_lcdHeight >= 4)
2180 outputCenteredText("Channel", m_channelTime, "timeWidget", 3);
2181 }
2182 else
2183 {
2184 sendToServer("widget_set Channel progressBar 1 1 0");
2185 }
2186}
2187
2189{
2190 if ( m_lcdHeight > 1)
2191 {
2192 QString aString;
2193 aString = "widget_set Generic progressBar ";
2194 aString += QString::number ( m_busyPos );
2195 aString += " ";
2196 aString += QString::number( m_lcdHeight );
2197 aString += " ";
2198 aString += QString::number((int)std::rint( m_genericProgress * m_lcdWidth *
2199 m_cellWidth ));
2200 sendToServer(aString);
2201 }
2202 else
2203 {
2204 sendToServer("widget_set Generic progressBar 1 1 0");
2205 }
2206}
2207
2209{
2210 QString aString;
2211 int line = 3;
2212
2213 if ( m_lcdHeight > 1)
2214 {
2215 aString = "widget_set Volume progressBar 1 ";
2216 aString += QString::number( m_lcdHeight );
2217 aString += " ";
2218 aString += QString::number((int)std::rint( m_volumeLevel * m_lcdWidth * m_cellWidth ));
2219 sendToServer(aString);
2220 }
2221
2222 aString = QString::number((int)( m_volumeLevel * 100));
2223 aString += "%";
2224
2225 if ( m_lcdHeight > 3)
2226 line = 3;
2227 else
2228 line = m_lcdHeight;
2229 outputRightText("Volume", aString, "botWidget", line);
2230}
2231
2233{
2234 if (!m_lcdReady )
2235 return;
2236
2237 stopAll();
2238
2239 if (debug_level > 1)
2240 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: switchToTime");
2241
2242 startTime();
2243}
2244
2245void LCDProcClient::switchToMusic(const QString &artist, const QString &album, const QString &track)
2246{
2247 if (!m_lcdReady )
2248 return;
2249
2250 stopAll();
2251
2252 if (debug_level > 1)
2253 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: switchToMusic") ;
2254
2255 startMusic(artist, album, track);
2256}
2257
2258void LCDProcClient::switchToChannel(const QString& channum, const QString& title, const QString& subtitle)
2259{
2260 if (!m_lcdReady )
2261 return;
2262
2263 stopAll();
2264
2265 if (debug_level > 1)
2266 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: switchToChannel");
2267
2268 startChannel(channum, title, subtitle);
2269}
2270
2271void LCDProcClient::switchToMenu(QList<LCDMenuItem> *menuItems, const QString& app_name,
2272 bool popMenu)
2273{
2274 if (!m_lcdReady )
2275 return;
2276
2277 if (debug_level > 1)
2278 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: switchToMenu");
2279
2280 startMenu(menuItems, app_name, popMenu);
2281}
2282
2283void LCDProcClient::switchToGeneric(QList<LCDTextItem> *textItems)
2284{
2285 if (!m_lcdReady )
2286 return;
2287 stopAll();
2288
2289 if (debug_level > 1)
2290 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: switchToGeneric");
2291
2292 startGeneric(textItems);
2293}
2294
2295void LCDProcClient::switchToVolume(const QString& app_name)
2296{
2297 if (!m_lcdReady )
2298 return;
2299
2300 stopAll();
2301
2302 if (debug_level > 1)
2303 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: switchToVolume");
2304
2305 startVolume(app_name);
2306}
2307
2309{
2310 if (!m_lcdReady )
2311 return;
2312
2313 stopAll();
2314
2315 if (debug_level > 1)
2316 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: switchToNothing");
2317}
2318
2320{
2321 if (debug_level > 1)
2322 LOG(VB_GENERAL, LOG_INFO, "LCDProcClient: shutdown");
2323
2324 stopAll();
2325
2326 // Remove all the widgets and screens for a clean exit from the server
2327 removeWidgets();
2328
2329 m_socket->close();
2330
2331 m_lcdReady = false;
2332 m_connected = false;
2333}
2334
2336{
2337 sendToServer("widget_del Channel progressBar");
2338 sendToServer("widget_del Channel topWidget");
2339 sendToServer("widget_del Channel timeWidget");
2340 sendToServer("screen_del Channel");
2341
2342 sendToServer("widget_del Generic progressBar");
2343 sendToServer("widget_del Generic textWidget1");
2344 sendToServer("widget_del Generic textWidget2");
2345 sendToServer("widget_del Generic textWidget3");
2346 sendToServer("screen_del Generic");
2347
2348 sendToServer("widget_del Volume progressBar");
2349 sendToServer("widget_del Volume topWidget");
2350 sendToServer("screen_del Volume");
2351
2352 sendToServer("widget_del Menu topWidget");
2353 sendToServer("widget_del Menu menuWidget1");
2354 sendToServer("widget_del Menu menuWidget2");
2355 sendToServer("widget_del Menu menuWidget3");
2356 sendToServer("widget_del Menu menuWidget4");
2357 sendToServer("widget_del Menu menuWidget5");
2358 sendToServer("screen_del Menu");
2359
2360 sendToServer("widget_del Music progressBar");
2361 sendToServer("widget_del Music infoWidget");
2362 sendToServer("widget_del Music timeWidget");
2363 sendToServer("widget_del Music topWidget");
2364 sendToServer("screen_del Music");
2365
2366 if ( m_lcdBigClock )
2367 {
2368 sendToServer("widget_del Time rec1");
2369 sendToServer("widget_del Time rec2");
2370 sendToServer("widget_del Time rec3");
2371 sendToServer("widget_del Time recCnt");
2372 sendToServer("widget_del Time d0");
2373 sendToServer("widget_del Time d1");
2374 sendToServer("widget_del Time sep");
2375 sendToServer("widget_del Time d2");
2376 sendToServer("widget_del Time d3");
2377 sendToServer("widget_del Time ampm");
2378 sendToServer("widget_del Time dot");
2379 }
2380 else
2381 {
2382 sendToServer("widget_del Time timeWidget");
2383 sendToServer("widget_del Time topWidget");
2384 }
2385
2386 sendToServer("screen_del Time");
2387
2388 sendToServer("widget_del RecStatus textWidget1");
2389 sendToServer("widget_del RecStatus textWidget2");
2390 sendToServer("widget_del RecStatus textWidget3");
2391 sendToServer("widget_del RecStatus textWidget4");
2392 sendToServer("widget_del RecStatus progressBar");
2393}
2394
2396{
2397 if (debug_level > 1)
2398 {
2399 LOG(VB_GENERAL, LOG_INFO,
2400 "LCDProcClient: An LCD device is being snuffed out"
2401 "of existence (~LCDProcClient() was called)");
2402 }
2403
2404 if (m_socket)
2405 {
2406 delete m_socket;
2407 m_lcdReady = false;
2408 }
2409
2410 delete m_lcdMenuItems;
2411
2413}
2414
2416{
2417 if (e->type() == MythEvent::kMythEventMessage)
2418 {
2419 auto *me = dynamic_cast<MythEvent *>(e);
2420 if (me == nullptr)
2421 return;
2422
2423 if (me->Message().startsWith("RECORDING_LIST_CHANGE") ||
2424 me->Message() == "UPDATE_PROG_INFO")
2425 {
2426 if ( m_lcdShowRecstatus && !m_updateRecInfoTimer->isActive())
2427 {
2428 if (debug_level > 1)
2429 LOG(VB_GENERAL, LOG_INFO,
2430 "LCDProcClient: Received recording list change");
2431
2432 // we can't query the backend from inside the customEvent
2433 // so fire the recording list update from a timer
2434 m_updateRecInfoTimer->start(500ms);
2435 }
2436 }
2437 }
2438}
2439
2441{
2442 m_tunerList.clear();
2443 m_isRecording = false;
2444
2446 {
2448 {
2449 LOG(VB_GENERAL, LOG_ERR,
2450 "LCDProcClient: Cannot get recording status "
2451 "- is the master server running?\n\t\t\t"
2452 "Will retry in 30 seconds");
2453 QTimer::singleShot(30s, this, &LCDProcClient::updateRecordingList);
2454
2455 // If we can't get the recording status and we're showing
2456 // it, switch back to time. Maybe it would be even better
2457 // to show that the backend is unreachable ?
2458 if (m_activeScreen == "RecStatus")
2459 switchToTime();
2460 return;
2461 }
2462 }
2463
2465
2466 m_lcdTunerNo = 0;
2467
2468 if (m_activeScreen == "Time" || m_activeScreen == "RecStatus")
2469 startTime();
2470}
2471
2472#include "moc_lcdprocclient.cpp"
void setScroll(bool value)
Definition: lcddevice.h:43
unsigned int getIndent() const
Definition: lcddevice.h:37
void setItemName(const QString &value)
Definition: lcddevice.h:42
CHECKED_STATE isChecked() const
Definition: lcddevice.h:33
void incrementScrollPos()
Definition: lcddevice.h:46
unsigned int getScrollPos() const
Definition: lcddevice.h:38
bool isSelected() const
Definition: lcddevice.h:34
void setScrollPos(unsigned int value)
Definition: lcddevice.h:45
bool Scroll() const
Definition: lcddevice.h:36
QString ItemName() const
Definition: lcddevice.h:35
void startMenu(QList< LCDMenuItem > *menuItems, QString app_name, bool popMenu)
void switchToChannel(const QString &channum="", const QString &title="", const QString &subtitle="")
bool m_lcdShowRecstatus
void outputText(QList< LCDTextItem > *textItems)
std::chrono::seconds m_startupShowTime
void setWidth(unsigned int x)
void assignScrollingList(QStringList theList, QString theScreen, QString theWidget="topWidget", int theRow=1)
void startMusic(QString artist, const QString &album, const QString &track)
void startChannel(const QString &channum, const QString &title, const QString &subtitle)
uint8_t m_cellWidth
void reset(void)
void setMusicShuffle(int shuffle)
QString m_activeScreen
void showStartupMessage(void)
void setHeight(unsigned int x)
QString m_scrollScreen
void setCellHeight(unsigned int x)
QString m_serverVersion
QString m_startupMessage
void checkConnections()
void setGenericProgress(bool busy, float value)
QTimer * m_scrollListTimer
void startVolume(const QString &app_name)
QTimer * m_showMessageTimer
uint8_t m_pVersion
QList< LCDMenuItem > * m_lcdMenuItems
void switchToVolume(const QString &app_name)
QTimer * m_recStatusTimer
void stopAll(void)
QString m_lcdKeyString
int m_busyDirection
Direction of the busy indicator on the, -1 or 1, used if m_busyProgress is true.
void formatScrollingWidgets(void)
QTimer * m_scrollWTimer
QString m_prioTop
QTimer * m_preScrollWTimer
bool m_busyProgress
true if the generic progress indicator is a busy (ie.
uint8_t m_cellHeight
QString m_channelTime
QString m_prioMedium
void setMusicRepeat(int repeat)
void dobigclock(void)
bool connectToHost(const QString &hostname, unsigned int port)
QTcpSocket * m_socket
QTimer * m_timeTimer
std::vector< TunerStatus > m_tunerList
QStringList formatScrollerText(const QString &text) const
void beginScrollingMenuText()
QTimer * m_updateRecInfoTimer
void veryBadThings(QAbstractSocket::SocketError error)
float m_genericProgress
int m_busyPos
Current position of the busy indicator, used if m_busyProgress is true.
QString m_lcdShowMusicItems
void switchToMenu(QList< LCDMenuItem > *menuItems, const QString &app_name="", bool popMenu=true)
void startGeneric(QList< LCDTextItem > *textItems)
QTimer * m_popMenuTimer
QString m_protocolVersion
void serverSendingData()
QTimer * m_menuPreScrollTimer
void outputRightText(const QString &theScreen, QString theText, const QString &widget="topWidget", int row=1)
void switchToGeneric(QList< LCDTextItem > *textItems)
LCDServer * m_parentLCDServer
void setCellWidth(unsigned int x)
void outputCenteredText(const QString &theScreen, QString theText, const QString &widget="topWidget", int row=1)
QString m_scrollListWidget
QTimer * m_checkConnectionsTimer
void switchToMusic(const QString &artist, const QString &album, const QString &track)
QString m_scrollListScreen
~LCDProcClient() override
QString m_musicTime
void sendToServer(const QString &someText)
QString m_lastCommand
void setVolumeLevel(float value)
unsigned int m_menuScrollPosition
QString m_prioUrgent
void beginScrollingWidgets(void)
void outputLeftText(const QString &theScreen, QString theText, const QString &widget="topWidget", int row=1)
unsigned int m_port
QString m_hostname
void setMusicProgress(QString time, float value)
QString m_timeFormat
float m_musicProgress
std::chrono::milliseconds m_lcdPopupTime
void setVersion(const QString &sversion, const QString &pversion)
void setChannelProgress(const QString &time, float value)
QString expandString(const QString &aString) const
void setHeartbeat(const QString &screen, bool onoff)
unsigned int m_scrollPosition
uint8_t m_lcdWidth
bool SetupLCD(void)
uint8_t m_lcdHeight
QString m_dateFormat
QTimer * m_menuScrollTimer
void customEvent(QEvent *e) override
QString m_prioLow
LCDProcClient(LCDServer *lparent)
void assignScrollingWidgets(const QString &theText, const QString &theScreen, const QString &theWidget="topWidget", int theRow=1)
void setPriority(const QString &screen, PRIORITY priority)
QString m_prioHigh
QList< LCDTextItem > * m_lcdTextItems
void updateRecordingList(void)
float m_busyIndicatorSize
How many "blocks" the busy indicator must be, used if m_busyProgress is true.
unsigned int m_scrollListItem
void scrollWidgets(void)
QStringList m_scrollListItems
void setStartupMessage(QString msg, std::chrono::seconds messagetime)
void removeStartupMessage(void)
void outputScrollerText(const QString &theScreen, const QString &theText, const QString &widget="scroller", int top=1, int bottom=1)
QString m_prioOff
QString m_sendBuffer
void updateLEDs(int mask)
void sendKeyPress(const QString &key_pressed)
Definition: lcdserver.cpp:302
QString getWidget() const
Definition: lcddevice.h:75
QString getScreen() const
Definition: lcddevice.h:74
TEXT_ALIGNMENT getAlignment() const
Definition: lcddevice.h:72
void setText(const QString &value)
Definition: lcddevice.h:80
unsigned int getRow() const
Definition: lcddevice.h:71
QString getText() const
Definition: lcddevice.h:73
bool getScroll() const
Definition: lcddevice.h:76
void setScrollable(bool value)
Definition: lcddevice.h:83
bool IsConnectedToMaster(void)
QString GetSetting(const QString &key, const QString &defaultval="")
T GetDurSetting(const QString &key, T defaultval=T::zero())
bool ConnectToMasterServer(bool blockingClient=true, bool openEventSocket=true)
int GetNumSetting(const QString &key, int defaultval=0)
bool GetBoolSetting(const QString &key, bool defaultval=false)
This class is used as a container for messages.
Definition: mythevent.h:17
static const Type kMythEventMessage
Definition: mythevent.h:79
void addListener(QObject *listener)
Add a listener to the observable.
void removeListener(QObject *listener)
Remove a listener to the observable.
recording status stuff
Definition: tvremoteutil.h:17
QDateTime endTime
Definition: tvremoteutil.h:25
QString subtitle
Definition: tvremoteutil.h:23
QString channame
Definition: tvremoteutil.h:21
QString title
Definition: tvremoteutil.h:22
QDateTime startTime
Definition: tvremoteutil.h:24
@ ALIGN_CENTERED
Definition: lcddevice.h:57
@ ALIGN_LEFT
Definition: lcddevice.h:57
@ ALIGN_RIGHT
Definition: lcddevice.h:57
@ NOTCHECKABLE
Definition: lcddevice.h:17
@ CHECKED
Definition: lcddevice.h:17
@ UNCHECKED
Definition: lcddevice.h:17
static constexpr std::chrono::milliseconds LCD_TIME_TIME
uint8_t lcdStartCol
static constexpr uint8_t LCD_VERSION_4
static constexpr uint8_t LCD_START_COL
static constexpr uint8_t LCD_VERSION_5
static constexpr std::chrono::milliseconds LCD_SCROLLLIST_TIME
int debug_level
Definition: lcdserver.cpp:76
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
bool RemoteGetRecordingStatus(std::vector< TunerStatus > *tunerList, bool list_inactive)