MythTV  master
networkcontrol.cpp
Go to the documentation of this file.
1 // C++
2 #include <chrono> // for milliseconds
3 #include <thread> // for sleep_for
4 
5 // Qt
6 #include <QCoreApplication>
7 #include <QDir>
8 #include <QEvent>
9 #include <QKeyEvent>
10 #include <QMap>
11 #include <QRegularExpression>
12 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
13 #include <QStringConverter>
14 #endif
15 #include <QStringList>
16 #include <QTextStream>
17 
18 // MythTV
19 #include "libmyth/programinfo.h"
20 #include "libmyth/remoteutil.h"
21 #include "libmythbase/compat.h"
23 #include "libmythbase/mythdirs.h"
30 #include "libmythui/mythuibutton.h"
33 #include "libmythui/mythuiclock.h"
35 #include "libmythui/mythuigroup.h"
37 #include "libmythui/mythuihelper.h"
38 #include "libmythui/mythuiimage.h"
41 #include "libmythui/mythuishape.h"
44 #include "libmythui/mythuivideo.h"
45 #if CONFIG_QTWEBKIT
47 #endif
48 
49 // MythFrontend
50 #include "networkcontrol.h"
51 
52 
53 #define LOC QString("NetworkControl: ")
54 #define LOC_ERR QString("NetworkControl Error: ")
55 
56 static constexpr qint64 FE_SHORT_TO { 2000 }; // 2 seconds
57 static constexpr qint64 FE_LONG_TO { 10000 }; // 10 seconds
58 
59 static QEvent::Type kNetworkControlDataReadyEvent =
60  (QEvent::Type) QEvent::registerEventType();
62  (QEvent::Type) QEvent::registerEventType();
63 
64 static const QRegularExpression kChanID1RE { "^\\d+$" };
65 static const QRegularExpression kStartTimeRE
66  { R"(^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ?$)" };
67 
75 static bool is_abbrev(QString const& command,
76  QString const& test, int minchars = 1)
77 {
78  if (test.length() < minchars)
79  return command.toLower() == test.toLower();
80  return test.toLower() == command.left(test.length()).toLower();
81 }
82 
84  m_commandThread(new MThread("NetworkControl", this))
85 {
86  // Eventually this map should be in the jumppoints table
87  m_jumpMap["channelpriorities"] = "Channel Recording Priorities";
88  m_jumpMap["livetv"] = "Live TV";
89  m_jumpMap["mainmenu"] = "Main Menu";
90  m_jumpMap["managerecordings"] = "Manage Recordings / Fix Conflicts";
91  m_jumpMap["mythgallery"] = "MythGallery";
92  m_jumpMap["mythvideo"] = "Video Default";
93  m_jumpMap["mythweather"] = "MythWeather";
94  m_jumpMap["mythgame"] = "MythGame";
95  m_jumpMap["mythnews"] = "MythNews";
96  m_jumpMap["playdvd"] = "Play Disc";
97  m_jumpMap["playmusic"] = "Play music";
98  m_jumpMap["playlistview"] = "Play music";
99  m_jumpMap["programfinder"] = "Program Finder";
100  m_jumpMap["programguide"] = "Program Guide";
101  m_jumpMap["ripcd"] = "Rip CD";
102  m_jumpMap["musicplaylists"] = "Select music playlists";
103  m_jumpMap["playbackrecordings"] = "TV Recording Playback";
104  m_jumpMap["videobrowser"] = "Video Browser";
105  m_jumpMap["videogallery"] = "Video Gallery";
106  m_jumpMap["videolistings"] = "Video Listings";
107  m_jumpMap["videomanager"] = "Video Manager";
108  m_jumpMap["zoneminderconsole"] = "ZoneMinder Console";
109  m_jumpMap["zoneminderliveview"] = "ZoneMinder Live View";
110  m_jumpMap["zoneminderevents"] = "ZoneMinder Events";
111 
112  m_jumpMap["channelrecpriority"] = "Channel Recording Priorities";
113  m_jumpMap["viewscheduled"] = "Manage Recordings / Fix Conflicts";
114  m_jumpMap["previousbox"] = "Previously Recorded";
115  m_jumpMap["progfinder"] = "Program Finder";
116  m_jumpMap["guidegrid"] = "Program Guide";
117  m_jumpMap["managerecrules"] = "Manage Recording Rules";
118  m_jumpMap["statusbox"] = "Status Screen";
119  m_jumpMap["playbackbox"] = "TV Recording Playback";
120  m_jumpMap["pbb"] = "TV Recording Playback";
121 
122  m_jumpMap["reloadtheme"] = "Reload Theme";
123  m_jumpMap["showborders"] = "Toggle Show Widget Borders";
124  m_jumpMap["shownames"] = "Toggle Show Widget Names";
125 
126  m_keyMap["up"] = Qt::Key_Up;
127  m_keyMap["down"] = Qt::Key_Down;
128  m_keyMap["left"] = Qt::Key_Left;
129  m_keyMap["right"] = Qt::Key_Right;
130  m_keyMap["home"] = Qt::Key_Home;
131  m_keyMap["end"] = Qt::Key_End;
132  m_keyMap["enter"] = Qt::Key_Enter;
133  m_keyMap["return"] = Qt::Key_Return;
134  m_keyMap["pageup"] = Qt::Key_PageUp;
135  m_keyMap["pagedown"] = Qt::Key_PageDown;
136  m_keyMap["escape"] = Qt::Key_Escape;
137  m_keyMap["tab"] = Qt::Key_Tab;
138  m_keyMap["backtab"] = Qt::Key_Backtab;
139  m_keyMap["space"] = Qt::Key_Space;
140  m_keyMap["backspace"] = Qt::Key_Backspace;
141  m_keyMap["insert"] = Qt::Key_Insert;
142  m_keyMap["delete"] = Qt::Key_Delete;
143  m_keyMap["plus"] = Qt::Key_Plus;
144  m_keyMap["+"] = Qt::Key_Plus;
145  m_keyMap["comma"] = Qt::Key_Comma;
146  m_keyMap[","] = Qt::Key_Comma;
147  m_keyMap["minus"] = Qt::Key_Minus;
148  m_keyMap["-"] = Qt::Key_Minus;
149  m_keyMap["underscore"] = Qt::Key_Underscore;
150  m_keyMap["_"] = Qt::Key_Underscore;
151  m_keyMap["period"] = Qt::Key_Period;
152  m_keyMap["."] = Qt::Key_Period;
153  m_keyMap["numbersign"] = Qt::Key_NumberSign;
154  m_keyMap["poundsign"] = Qt::Key_NumberSign;
155  m_keyMap["hash"] = Qt::Key_NumberSign;
156  m_keyMap["#"] = Qt::Key_NumberSign;
157  m_keyMap["bracketleft"] = Qt::Key_BracketLeft;
158  m_keyMap["["] = Qt::Key_BracketLeft;
159  m_keyMap["bracketright"] = Qt::Key_BracketRight;
160  m_keyMap["]"] = Qt::Key_BracketRight;
161  m_keyMap["backslash"] = Qt::Key_Backslash;
162  m_keyMap["\\"] = Qt::Key_Backslash;
163  m_keyMap["dollar"] = Qt::Key_Dollar;
164  m_keyMap["$"] = Qt::Key_Dollar;
165  m_keyMap["percent"] = Qt::Key_Percent;
166  m_keyMap["%"] = Qt::Key_Percent;
167  m_keyMap["ampersand"] = Qt::Key_Ampersand;
168  m_keyMap["&"] = Qt::Key_Ampersand;
169  m_keyMap["parenleft"] = Qt::Key_ParenLeft;
170  m_keyMap["("] = Qt::Key_ParenLeft;
171  m_keyMap["parenright"] = Qt::Key_ParenRight;
172  m_keyMap[")"] = Qt::Key_ParenRight;
173  m_keyMap["asterisk"] = Qt::Key_Asterisk;
174  m_keyMap["*"] = Qt::Key_Asterisk;
175  m_keyMap["question"] = Qt::Key_Question;
176  m_keyMap["?"] = Qt::Key_Question;
177  m_keyMap["slash"] = Qt::Key_Slash;
178  m_keyMap["/"] = Qt::Key_Slash;
179  m_keyMap["colon"] = Qt::Key_Colon;
180  m_keyMap[":"] = Qt::Key_Colon;
181  m_keyMap["semicolon"] = Qt::Key_Semicolon;
182  m_keyMap[";"] = Qt::Key_Semicolon;
183  m_keyMap["less"] = Qt::Key_Less;
184  m_keyMap["<"] = Qt::Key_Less;
185  m_keyMap["equal"] = Qt::Key_Equal;
186  m_keyMap["="] = Qt::Key_Equal;
187  m_keyMap["greater"] = Qt::Key_Greater;
188  m_keyMap[">"] = Qt::Key_Greater;
189  m_keyMap["bar"] = Qt::Key_Bar;
190  m_keyMap["pipe"] = Qt::Key_Bar;
191  m_keyMap["|"] = Qt::Key_Bar;
192  m_keyMap["f1"] = Qt::Key_F1;
193  m_keyMap["f2"] = Qt::Key_F2;
194  m_keyMap["f3"] = Qt::Key_F3;
195  m_keyMap["f4"] = Qt::Key_F4;
196  m_keyMap["f5"] = Qt::Key_F5;
197  m_keyMap["f6"] = Qt::Key_F6;
198  m_keyMap["f7"] = Qt::Key_F7;
199  m_keyMap["f8"] = Qt::Key_F8;
200  m_keyMap["f9"] = Qt::Key_F9;
201  m_keyMap["f10"] = Qt::Key_F10;
202  m_keyMap["f11"] = Qt::Key_F11;
203  m_keyMap["f12"] = Qt::Key_F12;
204  m_keyMap["f13"] = Qt::Key_F13;
205  m_keyMap["f14"] = Qt::Key_F14;
206  m_keyMap["f15"] = Qt::Key_F15;
207  m_keyMap["f16"] = Qt::Key_F16;
208  m_keyMap["f17"] = Qt::Key_F17;
209  m_keyMap["f18"] = Qt::Key_F18;
210  m_keyMap["f19"] = Qt::Key_F19;
211  m_keyMap["f20"] = Qt::Key_F20;
212  m_keyMap["f21"] = Qt::Key_F21;
213  m_keyMap["f22"] = Qt::Key_F22;
214  m_keyMap["f23"] = Qt::Key_F23;
215  m_keyMap["f24"] = Qt::Key_F24;
216 
217  m_keyTextMap[Qt::Key_Space] = " ";
218  m_keyTextMap[Qt::Key_Plus] = "+";
219  m_keyTextMap[Qt::Key_Comma] = ",";
220  m_keyTextMap[Qt::Key_Minus] = "-";
221  m_keyTextMap[Qt::Key_Underscore] = "_";
222  m_keyTextMap[Qt::Key_Period] = ".";
223  m_keyTextMap[Qt::Key_NumberSign] = "#";
224  m_keyTextMap[Qt::Key_BracketLeft] = "[";
225  m_keyTextMap[Qt::Key_BracketRight] = "]";
226  m_keyTextMap[Qt::Key_Backslash] = "\\";
227  m_keyTextMap[Qt::Key_Dollar] = "$";
228  m_keyTextMap[Qt::Key_Percent] = "%";
229  m_keyTextMap[Qt::Key_Ampersand] = "&";
230  m_keyTextMap[Qt::Key_ParenLeft] = "(";
231  m_keyTextMap[Qt::Key_ParenRight] = ")";
232  m_keyTextMap[Qt::Key_Asterisk] = "*";
233  m_keyTextMap[Qt::Key_Question] = "?";
234  m_keyTextMap[Qt::Key_Slash] = "/";
235  m_keyTextMap[Qt::Key_Colon] = ":";
236  m_keyTextMap[Qt::Key_Semicolon] = ";";
237  m_keyTextMap[Qt::Key_Less] = "<";
238  m_keyTextMap[Qt::Key_Equal] = "=";
239  m_keyTextMap[Qt::Key_Greater] = ">";
240  m_keyTextMap[Qt::Key_Bar] = "|";
241 
243 
244  gCoreContext->addListener(this);
245 
246  connect(this, &ServerPool::newConnection,
248 }
249 
251 {
253 
254  m_clientLock.lock();
255  while (!m_clients.isEmpty())
256  {
257  NetworkControlClient *ncc = m_clients.takeFirst();
258  delete ncc;
259  }
260  m_clientLock.unlock();
261 
262  auto * cmd = new (std::nothrow) NetworkCommand(nullptr,
263  "mythfrontend shutting down, connection closing...");
264  if (cmd != nullptr)
265  {
266  m_nrLock.lock();
267  m_networkControlReplies.push_back(cmd);
268  m_nrLock.unlock();
270  }
271 
272  m_ncLock.lock();
273  m_stopCommandThread = true;
274  m_ncCond.wakeOne();
275  m_ncLock.unlock();
277  delete m_commandThread;
278  m_commandThread = nullptr;
279 }
280 
282 {
283  QMutexLocker locker(&m_ncLock);
284  while (!m_stopCommandThread)
285  {
286  // cppcheck-suppress knownConditionTrueFalse
288  m_ncCond.wait(&m_ncLock);
289  // cppcheck-suppress knownConditionTrueFalse
290  if (!m_stopCommandThread)
291  {
293  m_networkControlCommands.pop_front();
294  locker.unlock();
296  locker.relock();
297  }
298  }
299 }
300 
302 {
303  QMutexLocker locker(&m_clientLock);
304  QString result;
305 
306  int clientID = m_clients.indexOf(nc->getClient());
307 
308  if (is_abbrev("jump", nc->getArg(0)))
309  result = processJump(nc);
310  else if (is_abbrev("key", nc->getArg(0)))
311  result = processKey(nc);
312  else if (is_abbrev("play", nc->getArg(0)))
313  result = processPlay(nc, clientID);
314  else if (is_abbrev("query", nc->getArg(0)))
315  result = processQuery(nc);
316  else if (is_abbrev("set", nc->getArg(0)))
317  result = processSet(nc);
318  else if (is_abbrev("screenshot", nc->getArg(0)))
319  result = saveScreenshot(nc);
320  else if (is_abbrev("help", nc->getArg(0)))
321  result = processHelp(nc);
322  else if (is_abbrev("message", nc->getArg(0)))
323  result = processMessage(nc);
324  else if (is_abbrev("notification", nc->getArg(0)))
325  result = processNotification(nc);
326  else if (is_abbrev("theme", nc->getArg(0)))
327  result = processTheme(nc);
328  else if ((nc->getArg(0).toLower() == "exit") || (nc->getArg(0).toLower() == "quit"))
329  {
330  QCoreApplication::postEvent(this,
332  }
333  else if (! nc->getArg(0).isEmpty())
334  {
335  result = QString("INVALID command '%1', try 'help' for more info")
336  .arg(nc->getArg(0));
337  }
338 
339  m_nrLock.lock();
340  m_networkControlReplies.push_back(new NetworkCommand(nc->getClient(),result));
341  m_nrLock.unlock();
342 
344 }
345 
347 {
348  LOG(VB_GENERAL, LOG_INFO, LOC + "Client Socket disconnected");
349  QMutexLocker locker(&m_clientLock);
350 
351  gCoreContext->SendSystemEvent("NET_CTRL_DISCONNECTED");
352 
353  for (auto * ncc : qAsConst(m_clients))
354  {
355  if (ncc->getSocket()->state() == QTcpSocket::UnconnectedState)
356  {
357  deleteClient(ncc);
358  return;
359  }
360  }
361 }
362 
364 {
365  int index = m_clients.indexOf(ncc);
366  if (index >= 0)
367  {
368  m_clients.removeAt(index);
369 
370  delete ncc;
371  }
372  else
373  LOG(VB_GENERAL, LOG_ERR, LOC + QString("deleteClient(%1), unable to "
374  "locate specified NetworkControlClient").arg((long long)ncc));
375 }
376 
377 void NetworkControl::newControlConnection(QTcpSocket *client)
378 {
379  QString welcomeStr;
380 
381  LOG(VB_GENERAL, LOG_INFO, LOC + QString("New connection established."));
382 
383  gCoreContext->SendSystemEvent("NET_CTRL_CONNECTED");
384 
385  auto *ncc = new NetworkControlClient(client);
386 
387  QMutexLocker locker(&m_clientLock);
388  m_clients.push_back(ncc);
389 
392  connect(client, &QAbstractSocket::disconnected,
393  this, qOverload<>(&NetworkControl::deleteClient));
394 
395  welcomeStr = "MythFrontend Network Control\r\n";
396  welcomeStr += "Type 'help' for usage information\r\n"
397  "---------------------------------";
398  m_nrLock.lock();
399  m_networkControlReplies.push_back(new NetworkCommand(ncc,welcomeStr));
400  m_nrLock.unlock();
401 
403 }
404 
406 {
407  m_socket = s;
408  m_textStream = new QTextStream(s);
409 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
410  m_textStream->setCodec("UTF-8");
411 #else
412  m_textStream->setEncoding(QStringConverter::Utf8);
413 #endif
414  connect(m_socket, &QIODevice::readyRead, this, &NetworkControlClient::readClient);
415 }
416 
418 {
419  m_socket->close();
420  m_socket->deleteLater();
421 
422  delete m_textStream;
423 }
424 
426 {
427  auto *socket = (QTcpSocket *)sender();
428  if (!socket)
429  return;
430 
431  while (socket->canReadLine())
432  {
433  QString lineIn = socket->readLine();
434 #if 0
435  static const QRegularExpression badChars
436  { "[^-a-zA-Z0-9\\s\\.:_#/$%&()*+,;<=>?\\[\\]\\|]" };
437  lineIn.remove(badChars);
438 #endif
439 
440  lineIn = lineIn.simplified();
441  if (lineIn.isEmpty())
442  continue;
443 
444  LOG(VB_NETWORK, LOG_INFO, LOC +
445  QString("emit commandReceived(%1)").arg(lineIn));
446  emit commandReceived(lineIn);
447  }
448 }
449 
450 void NetworkControl::receiveCommand(QString &command)
451 {
452  LOG(VB_NETWORK, LOG_INFO, LOC +
453  QString("NetworkControl::receiveCommand(%1)").arg(command));
454  auto *ncc = qobject_cast<NetworkControlClient *>(sender());
455  if (!ncc)
456  return;
457 
458  m_ncLock.lock();
459  m_networkControlCommands.push_back(new NetworkCommand(ncc,command));
460  m_ncCond.wakeOne();
461  m_ncLock.unlock();
462 }
463 
465 {
466  QString result = "OK";
467 
468  if ((nc->getArgCount() < 2) || (!m_jumpMap.contains(nc->getArg(1))))
469  return QString("ERROR: See 'help %1' for usage information")
470  .arg(nc->getArg(0));
471 
473 
474  // Fixme, should do some better checking here, but that would
475  // depend on all Locations matching their jumppoints
476  QElapsedTimer timer;
477  timer.start();
478  while (!timer.hasExpired(FE_SHORT_TO) &&
479  (GetMythUI()->GetCurrentLocation().toLower() != nc->getArg(1)))
480  std::this_thread::sleep_for(10ms);
481 
482  return result;
483 }
484 
486 {
487  QString result = "OK";
488  QKeyEvent *event = nullptr;
489 
490  if (nc->getArgCount() < 2)
491  return QString("ERROR: See 'help %1' for usage information")
492  .arg(nc->getArg(0));
493 
494  QObject *keyDest = nullptr;
495 
496  if (GetMythMainWindow())
497  keyDest = GetMythMainWindow();
498  else
499  return {"ERROR: Application has no main window!\n"};
500 
501  int curToken = 1;
502  while (curToken < nc->getArgCount())
503  {
504  int tokenLen = nc->getArg(curToken).length();
505 
506  if (nc->getArg(curToken) == "sleep")
507  {
508  std::this_thread::sleep_for(1s);
509  }
510  else if (m_keyMap.contains(nc->getArg(curToken)))
511  {
512  int keyCode = m_keyMap[nc->getArg(curToken)];
513  QString keyText;
514 
515  if (m_keyTextMap.contains(keyCode))
516  keyText = m_keyTextMap[keyCode];
517 
519 
520  event = new QKeyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier,
521  keyText);
522  QCoreApplication::postEvent(keyDest, event);
523 
524  event = new QKeyEvent(QEvent::KeyRelease, keyCode, Qt::NoModifier,
525  keyText);
526  QCoreApplication::postEvent(keyDest, event);
527  }
528  else if (((tokenLen == 1) &&
529  (nc->getArg(curToken).at(0).isLetterOrNumber())) ||
530  ((tokenLen >= 1) &&
531  (nc->getArg(curToken).contains("+"))))
532  {
533  QKeySequence a(nc->getArg(curToken));
534 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
535  int keyCode = a[0];
536  Qt::KeyboardModifiers modifiers = Qt::NoModifier;
537 
538  if (tokenLen > 1)
539  {
540  QStringList tokenParts = nc->getArg(curToken).split('+');
541 
542  int partNum = 0;
543  while (partNum < (tokenParts.size() - 1))
544  {
545  if (tokenParts[partNum].toUpper() == "CTRL")
546  modifiers |= Qt::ControlModifier;
547  if (tokenParts[partNum].toUpper() == "SHIFT")
548  modifiers |= Qt::ShiftModifier;
549  if (tokenParts[partNum].toUpper() == "ALT")
550  modifiers |= Qt::AltModifier;
551  if (tokenParts[partNum].toUpper() == "META")
552  modifiers |= Qt::MetaModifier;
553 
554  partNum++;
555  }
556  }
557 #else
558  int keyCode = a[0].key();
559  Qt::KeyboardModifiers modifiers = a[0].keyboardModifiers();
560 #endif
561  if (tokenLen == 1)
562  {
563  if (nc->getArg(curToken) == nc->getArg(curToken).toUpper())
564  modifiers |= Qt::ShiftModifier;
565  }
566 
568 
569  event = new QKeyEvent(QEvent::KeyPress, keyCode, modifiers,
570  nc->getArg(curToken));
571  QCoreApplication::postEvent(keyDest, event);
572 
573  event = new QKeyEvent(QEvent::KeyRelease, keyCode, modifiers,
574  nc->getArg(curToken));
575  QCoreApplication::postEvent(keyDest, event);
576  }
577  else
578  {
579  return QString("ERROR: Invalid syntax at '%1', see 'help %2' for "
580  "usage information")
581  .arg(nc->getArg(curToken), nc->getArg(0));
582  }
583 
584  curToken++;
585  }
586 
587  return result;
588 }
589 
590 QString NetworkControl::processPlay(NetworkCommand *nc, int clientID)
591 {
592  QString result = "OK";
593  QString message;
594 
595  if (nc->getArgCount() < 2)
596  return QString("ERROR: See 'help %1' for usage information")
597  .arg(nc->getArg(0));
598 
599  if ((nc->getArgCount() >= 3) &&
600  (is_abbrev("file", nc->getArg(1))))
601  {
602  if (GetMythUI()->GetCurrentLocation().toLower() != "mainmenu")
603  {
604  GetMythMainWindow()->JumpTo(m_jumpMap["mainmenu"]);
605 
606  QElapsedTimer timer;
607  timer.start();
608  while (!timer.hasExpired(FE_LONG_TO) &&
609  (GetMythUI()->GetCurrentLocation().toLower() != "mainmenu"))
610  std::this_thread::sleep_for(10ms);
611  }
612 
613  if (GetMythUI()->GetCurrentLocation().toLower() == "mainmenu")
614  {
615  QStringList args;
616  args << nc->getFrom(2);
617  auto *me = new MythEvent(ACTION_HANDLEMEDIA, args);
618  qApp->postEvent(GetMythMainWindow(), me);
619  }
620  else
621  return {"Unable to change to main menu to start playback!"};
622  }
623  else if ((nc->getArgCount() >= 4) &&
624  (is_abbrev("program", nc->getArg(1))) &&
625  (nc->getArg(2).contains(kChanID1RE)) &&
626  (nc->getArg(3).contains(kStartTimeRE)))
627  {
628  if (GetMythUI()->GetCurrentLocation().toLower() == "playback")
629  {
630  QString msg = QString("NETWORK_CONTROL STOP");
631  MythEvent me(msg);
632  gCoreContext->dispatch(me);
633 
634  QElapsedTimer timer;
635  timer.start();
636  while (!timer.hasExpired(FE_LONG_TO) &&
637  (GetMythUI()->GetCurrentLocation().toLower() == "playback"))
638  std::this_thread::sleep_for(10ms);
639  }
640 
641  if (GetMythUI()->GetCurrentLocation().toLower() != "playbackbox")
642  {
643  GetMythMainWindow()->JumpTo(m_jumpMap["playbackbox"]);
644 
645  QElapsedTimer timer;
646  timer.start();
647  while (!timer.hasExpired(10000) &&
648  (GetMythUI()->GetCurrentLocation().toLower() != "playbackbox"))
649  std::this_thread::sleep_for(10ms);
650 
651  timer.start();
652  while (!timer.hasExpired(10000) && (!MythMainWindow::IsTopScreenInitialized()))
653  std::this_thread::sleep_for(10ms);
654  }
655 
656  if (GetMythUI()->GetCurrentLocation().toLower() == "playbackbox")
657  {
658  QString action = "PLAY";
659  if (nc->getArgCount() == 5 && nc->getArg(4) == "resume")
660  action = "RESUME";
661 
662  QString msg = QString("NETWORK_CONTROL %1 PROGRAM %2 %3 %4")
663  .arg(action, nc->getArg(2),
664  nc->getArg(3).toUpper(),
665  QString::number(clientID));
666 
667  result.clear();
668  m_gotAnswer = false;
669  QElapsedTimer timer;
670  timer.start();
671 
672  MythEvent me(msg);
673  gCoreContext->dispatch(me);
674 
675  while (!timer.hasExpired(FE_LONG_TO) && !m_gotAnswer)
676  std::this_thread::sleep_for(10ms);
677 
678  if (m_gotAnswer)
679  result += m_answer;
680  else
681  result = "ERROR: Timed out waiting for reply from player";
682 
683  }
684  else
685  {
686  result = QString("ERROR: Unable to change to PlaybackBox from "
687  "%1, cannot play requested file.")
688  .arg(GetMythUI()->GetCurrentLocation());
689  }
690  }
691  else if (is_abbrev("music", nc->getArg(1)))
692  {
693 #if 0
694  if (GetMythUI()->GetCurrentLocation().toLower() != "playmusic")
695  {
696  return QString("ERROR: You are in %1 mode and this command is "
697  "only for MythMusic")
698  .arg(GetMythUI()->GetCurrentLocation());
699  }
700 #endif
701 
702  QString hostname = gCoreContext->GetHostName();
703 
704  if (nc->getArgCount() == 3)
705  {
706  if (is_abbrev("play", nc->getArg(2)))
707  message = QString("MUSIC_COMMAND %1 PLAY").arg(hostname);
708  else if (is_abbrev("pause", nc->getArg(2)))
709  message = QString("MUSIC_COMMAND %1 PAUSE").arg(hostname);
710  else if (is_abbrev("stop", nc->getArg(2)))
711  message = QString("MUSIC_COMMAND %1 STOP").arg(hostname);
712  else if (is_abbrev("getvolume", nc->getArg(2)))
713  {
714  m_gotAnswer = false;
715 
716  MythEvent me(QString("MUSIC_COMMAND %1 GET_VOLUME").arg(hostname));
717  gCoreContext->dispatch(me);
718 
719  QElapsedTimer timer;
720  timer.start();
721  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
722  {
723  qApp->processEvents();
724  std::this_thread::sleep_for(10ms);
725  }
726 
727  if (m_gotAnswer)
728  return m_answer;
729 
730  return "unknown";
731  }
732  else if (is_abbrev("getmeta", nc->getArg(2)))
733  {
734  m_gotAnswer = false;
735 
736  MythEvent me(QString("MUSIC_COMMAND %1 GET_METADATA").arg(hostname));
737  gCoreContext->dispatch(me);
738 
739  QElapsedTimer timer;
740  timer.start();
741  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
742  {
743  qApp->processEvents();
744  std::this_thread::sleep_for(10ms);
745  }
746 
747  if (m_gotAnswer)
748  return m_answer;
749 
750  return "unknown";
751  }
752  else if (is_abbrev("getstatus", nc->getArg(2)))
753  {
754  m_gotAnswer = false;
755 
756  MythEvent me(QString("MUSIC_COMMAND %1 GET_STATUS").arg(hostname));
757  gCoreContext->dispatch(me);
758 
759  QElapsedTimer timer;
760  timer.start();
761  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
762  {
763  qApp->processEvents();
764  std::this_thread::sleep_for(10ms);
765  }
766 
767  if (m_gotAnswer)
768  return m_answer;
769 
770  return "unknown";
771  }
772  else
773  return {"ERROR: Invalid 'play music' command"};
774  }
775  else if (nc->getArgCount() > 3)
776  {
777  if (is_abbrev("setvolume", nc->getArg(2)))
778  {
779  message = QString("MUSIC_COMMAND %1 SET_VOLUME %2")
780  .arg(hostname, nc->getArg(3));
781  }
782  else if (is_abbrev("track", nc->getArg(2)))
783  {
784  message = QString("MUSIC_COMMAND %1 PLAY_TRACK %2")
785  .arg(hostname, nc->getArg(3));
786  }
787  else if (is_abbrev("url", nc->getArg(2)))
788  {
789  message = QString("MUSIC_COMMAND %1 PLAY_URL %2")
790  .arg(hostname, nc->getArg(3));
791  }
792  else if (is_abbrev("file", nc->getArg(2)))
793  {
794  message = QString("MUSIC_COMMAND %1 PLAY_FILE '%2'")
795  .arg(hostname, nc->getFrom(3));
796  }
797  else
798  {
799  return {"ERROR: Invalid 'play music' command"};
800  }
801  }
802  else
803  return {"ERROR: Invalid 'play music' command"};
804  }
805  // Everything below here requires us to be in playback mode so check to
806  // see if we are
807  else if (GetMythUI()->GetCurrentLocation().toLower() != "playback")
808  {
809  return QString("ERROR: You are in %1 mode and this command is only "
810  "for playback mode")
811  .arg(GetMythUI()->GetCurrentLocation());
812  }
813  else if (is_abbrev("chanid", nc->getArg(1), 5))
814  {
815  if (nc->getArg(2).contains(kChanID1RE))
816  message = QString("NETWORK_CONTROL CHANID %1").arg(nc->getArg(2));
817  else
818  return QString("ERROR: See 'help %1' for usage information")
819  .arg(nc->getArg(0));
820  }
821  else if (is_abbrev("channel", nc->getArg(1), 5))
822  {
823  static const QRegularExpression kChanID2RE { "^[-\\.\\d_#]+$" };
824 
825  if (nc->getArgCount() < 3)
826  return "ERROR: See 'help play' for usage information";
827 
828  if (is_abbrev("up", nc->getArg(2)))
829  message = "NETWORK_CONTROL CHANNEL UP";
830  else if (is_abbrev("down", nc->getArg(2)))
831  message = "NETWORK_CONTROL CHANNEL DOWN";
832  else if (nc->getArg(2).contains(kChanID2RE))
833  message = QString("NETWORK_CONTROL CHANNEL %1").arg(nc->getArg(2));
834  else
835  return QString("ERROR: See 'help %1' for usage information")
836  .arg(nc->getArg(0));
837  }
838  else if (is_abbrev("seek", nc->getArg(1), 2))
839  {
840  static const QRegularExpression kSeekTimeRE { R"(^\d\d:\d\d:\d\d$)" };
841 
842  if (nc->getArgCount() < 3)
843  return QString("ERROR: See 'help %1' for usage information")
844  .arg(nc->getArg(0));
845 
846  if (is_abbrev("beginning", nc->getArg(2)))
847  message = "NETWORK_CONTROL SEEK BEGINNING";
848  else if (is_abbrev("forward", nc->getArg(2)))
849  message = "NETWORK_CONTROL SEEK FORWARD";
850  else if (is_abbrev("rewind", nc->getArg(2)) ||
851  is_abbrev("backward", nc->getArg(2)))
852  message = "NETWORK_CONTROL SEEK BACKWARD";
853  else if (nc->getArg(2).contains(kSeekTimeRE))
854  {
855  int hours = nc->getArg(2).mid(0, 2).toInt();
856  int minutes = nc->getArg(2).mid(3, 2).toInt();
857  int seconds = nc->getArg(2).mid(6, 2).toInt();
858  message = QString("NETWORK_CONTROL SEEK POSITION %1")
859  .arg((hours * 3600) + (minutes * 60) + seconds);
860  }
861  else
862  return QString("ERROR: See 'help %1' for usage information")
863  .arg(nc->getArg(0));
864  }
865  else if (is_abbrev("speed", nc->getArg(1), 2))
866  {
867  static const QRegularExpression kSpeed1RE { R"(^\-*\d+x$)" };
868  static const QRegularExpression kSpeed2RE { R"(^\-*\d+\/\d+x$)" };
869  static const QRegularExpression kSpeed3RE { R"(^\-*\d*\.\d+x$)" };
870 
871  if (nc->getArgCount() < 3)
872  return QString("ERROR: See 'help %1' for usage information")
873  .arg(nc->getArg(0));
874 
875  QString token2 = nc->getArg(2).toLower();
876  if ((token2.contains(kSpeed1RE)) ||
877  (token2.contains(kSpeed2RE)) ||
878  (token2.contains(kSpeed3RE)))
879  message = QString("NETWORK_CONTROL SPEED %1").arg(token2);
880  else if (is_abbrev("normal", token2))
881  message = QString("NETWORK_CONTROL SPEED normal");
882  else if (is_abbrev("pause", token2))
883  message = QString("NETWORK_CONTROL SPEED 0x");
884  else
885  return QString("ERROR: See 'help %1' for usage information")
886  .arg(nc->getArg(0));
887  }
888  else if (is_abbrev("save", nc->getArg(1), 2))
889  {
890  if (is_abbrev("screenshot", nc->getArg(2), 2))
891  return saveScreenshot(nc);
892  }
893  else if (is_abbrev("stop", nc->getArg(1), 2))
894  message = QString("NETWORK_CONTROL STOP");
895  else if (is_abbrev("volume", nc->getArg(1), 2))
896  {
897  static const QRegularExpression kVolumeRE { "^\\d+%?$" };
898 
899  if ((nc->getArgCount() < 3) ||
900  (!nc->getArg(2).toLower().contains(kVolumeRE)))
901  {
902  return QString("ERROR: See 'help %1' for usage information")
903  .arg(nc->getArg(0));
904  }
905 
906  message = QString("NETWORK_CONTROL VOLUME %1")
907  .arg(nc->getArg(2).toLower());
908  }
909  else if (is_abbrev("subtitles", nc->getArg(1), 2))
910  {
911  static const QRegularExpression kNumberRE { "^\\d+$" };
912  if (nc->getArgCount() < 3)
913  message = QString("NETWORK_CONTROL SUBTITLES 0");
914  else if (!nc->getArg(2).toLower().contains(kNumberRE))
915  {
916  return QString("ERROR: See 'help %1' for usage information")
917  .arg(nc->getArg(0));
918  }
919  else
920  {
921  message = QString("NETWORK_CONTROL SUBTITLES %1")
922  .arg(nc->getArg(2));
923  }
924  }
925  else
926  return QString("ERROR: See 'help %1' for usage information")
927  .arg(nc->getArg(0));
928 
929  if (!message.isEmpty())
930  {
931  // Don't broadcast this event as the TV object will see it
932  // twice: once directly from the Qt event system, and once
933  // because its filtering all events before they are sent to
934  // the main window. Its much easier to get a pointer to the
935  // MainWindow object than is is to a pointer to the TV object,
936  // so send the event directly there. The TV object will get
937  // it anyway because because of the filter hook. (The last
938  // third of this function requires you to be in playback mode
939  // so the TV object is guaranteed to exist.)
940  auto *me = new MythEvent(message);
941  qApp->postEvent(GetMythMainWindow(), me);
942  }
943 
944  return result;
945 }
946 
948 {
949  QString result = "OK";
950 
951  if (nc->getArgCount() < 2)
952  return QString("ERROR: See 'help %1' for usage information")
953  .arg(nc->getArg(0));
954 
955  if (is_abbrev("location", nc->getArg(1)))
956  {
957  bool fullPath = false;
958  bool mainStackOnly = true;
959 
960  if (nc->getArgCount() > 2)
961  fullPath = (nc->getArg(2).toLower() == "true" || nc->getArg(2) == "1");
962  if (nc->getArgCount() > 3)
963  mainStackOnly = (nc->getArg(3).toLower() == "true" || nc->getArg(3) == "1");
964 
965  QString location = GetMythUI()->GetCurrentLocation(fullPath, mainStackOnly);
966  result = location;
967 
968  // if we're playing something, then find out what
969  if (location == "Playback")
970  {
971  result += " ";
972  m_gotAnswer = false;
973  QString message = QString("NETWORK_CONTROL QUERY POSITION");
974  MythEvent me(message);
975  gCoreContext->dispatch(me);
976 
977  QElapsedTimer timer;
978  timer.start();
979  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
980  std::this_thread::sleep_for(10ms);
981 
982  if (m_gotAnswer)
983  result += m_answer;
984  else
985  result = "ERROR: Timed out waiting for reply from player";
986  }
987  }
988  else if (is_abbrev("verbose", nc->getArg(1)))
989  {
990  return verboseString;
991  }
992  else if (is_abbrev("liveTV", nc->getArg(1)))
993  {
994  if(nc->getArgCount() == 3) // has a channel ID
995  return listSchedule(nc->getArg(2));
996  return listSchedule();
997  }
998  else if (is_abbrev("version", nc->getArg(1)))
999  {
1000  int dbSchema = gCoreContext->GetNumSetting("DBSchemaVer");
1001 
1002  return QString("VERSION: %1/%2 %3 %4 QT/%5 DBSchema/%6")
1003  .arg(GetMythSourceVersion(),
1007  QT_VERSION_STR,
1008  QString::number(dbSchema));
1009 
1010  }
1011  else if(is_abbrev("time", nc->getArg(1)))
1013  else if (is_abbrev("uptime", nc->getArg(1)))
1014  {
1015  QString str;
1016  std::chrono::seconds uptime = 0s;
1017 
1018  if (getUptime(uptime))
1019  str = QString::number(uptime.count());
1020  else
1021  str = QString("Could not determine uptime.");
1022  return str;
1023  }
1024  else if (is_abbrev("load", nc->getArg(1)))
1025  {
1026  QString str;
1027  loadArray loads = getLoadAvgs();
1028  if (loads[0] == -1)
1029  str = QString("getloadavg() failed");
1030  else
1031  str = QString("%1 %2 %3").arg(loads[0]).arg(loads[1]).arg(loads[2]);
1032  return str;
1033  }
1034  else if (is_abbrev("memstats", nc->getArg(1)))
1035  {
1036  QString str;
1037  int totalMB = 0;
1038  int freeMB = 0;
1039  int totalVM = 0;
1040  int freeVM = 0;
1041 
1042  if (getMemStats(totalMB, freeMB, totalVM, freeVM))
1043  {
1044  str = QString("%1 %2 %3 %4")
1045  .arg(totalMB).arg(freeMB).arg(totalVM).arg(freeVM);
1046  }
1047  else
1048  {
1049  str = QString("Could not determine memory stats.");
1050  }
1051  return str;
1052  }
1053  else if (is_abbrev("volume", nc->getArg(1)))
1054  {
1055  QString str = "0%";
1056 
1057  QString location = GetMythUI()->GetCurrentLocation(false, false);
1058 
1059  if (location != "Playback")
1060  return str;
1061 
1062  m_gotAnswer = false;
1063  QString message = QString("NETWORK_CONTROL QUERY VOLUME");
1064  MythEvent me(message);
1065  gCoreContext->dispatch(me);
1066 
1067  QElapsedTimer timer;
1068  timer.start();
1069  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
1070  std::this_thread::sleep_for(10ms);
1071 
1072  if (m_gotAnswer)
1073  str = m_answer;
1074  else
1075  str = "ERROR: Timed out waiting for reply from player";
1076 
1077  return str;
1078  }
1079  else if ((nc->getArgCount() == 4) &&
1080  is_abbrev("recording", nc->getArg(1)) &&
1081  (nc->getArg(2).contains(kChanID1RE)) &&
1082  (nc->getArg(3).contains(kStartTimeRE)))
1083  return listRecordings(nc->getArg(2), nc->getArg(3).toUpper());
1084  else if (is_abbrev("recordings", nc->getArg(1)))
1085  return listRecordings();
1086  else if (is_abbrev("channels", nc->getArg(1)))
1087  {
1088  if (nc->getArgCount() == 2)
1089  return listChannels(0, 0); // give us all you can
1090  if (nc->getArgCount() == 4)
1091  return listChannels(nc->getArg(2).toLower().toUInt(),
1092  nc->getArg(3).toLower().toUInt());
1093  return QString("ERROR: See 'help %1' for usage information "
1094  "(parameters mismatch)").arg(nc->getArg(0));
1095  }
1096  else
1097  return QString("ERROR: See 'help %1' for usage information")
1098  .arg(nc->getArg(0));
1099 
1100  return result;
1101 }
1102 
1104 {
1105  if (nc->getArgCount() == 1)
1106  return QString("ERROR: See 'help %1' for usage information")
1107  .arg(nc->getArg(0));
1108 
1109  if (nc->getArg(1) == "verbose")
1110  {
1111  if (nc->getArgCount() < 3)
1112  return {"ERROR: Missing filter name."};
1113 
1114  if (nc->getArgCount() > 3)
1115  {
1116  return QString("ERROR: Separate filters with commas with no "
1117  "space: playback,audio\r\n See 'help %1' for usage "
1118  "information").arg(nc->getArg(0));
1119  }
1120 
1121  QString oldVerboseString = verboseString;
1122  QString result = "OK";
1123 
1124  int pva_result = verboseArgParse(nc->getArg(2));
1125 
1126  if (pva_result != 0 /*GENERIC_EXIT_OK */)
1127  result = "Failed";
1128 
1129  result += "\r\n";
1130  result += " Previous filter: " + oldVerboseString + "\r\n";
1131  result += " New Filter: " + verboseString + "\r\n";
1132 
1133  LOG(VB_GENERAL, LOG_NOTICE,
1134  QString("Verbose mask changed, new level is: %1")
1135  .arg(verboseString));
1136 
1137  return result;
1138  }
1139 
1140  return QString("ERROR: See 'help %1' for usage information")
1141  .arg(nc->getArg(0));
1142 }
1143 
1145 {
1146  if (dynamic_cast<MythUIText *>(type))
1147  return "MythUIText";
1148  if (dynamic_cast<MythUITextEdit *>(type))
1149  return "MythUITextEdit";
1150  if (dynamic_cast<MythUIGroup *>(type))
1151  return "MythUIGroup";
1152  if (dynamic_cast<MythUIButton *>(type))
1153  return "MythUIButton";
1154  if (dynamic_cast<MythUICheckBox *>(type))
1155  return "MythUICheckBox";
1156  if (dynamic_cast<MythUIShape *>(type))
1157  return "MythUIShape";
1158  if (dynamic_cast<MythUIButtonList *>(type))
1159  return "MythUIButtonList";
1160  if (dynamic_cast<MythUIImage *>(type))
1161  return "MythUIImage";
1162  if (dynamic_cast<MythUISpinBox *>(type))
1163  return "MythUISpinBox";
1164 #if CONFIG_QTWEBKIT
1165  if (dynamic_cast<MythUIWebBrowser *>(type))
1166  return "MythUIWebBrowser";
1167 #endif
1168  if (dynamic_cast<MythUIClock *>(type))
1169  return "MythUIClock";
1170  if (dynamic_cast<MythUIStateType *>(type))
1171  return "MythUIStateType";
1172  if (dynamic_cast<MythUIProgressBar *>(type))
1173  return "MythUIProgressBar";
1174  if (dynamic_cast<MythUIButtonTree *>(type))
1175  return "MythUIButtonTree";
1176  if (dynamic_cast<MythUIScrollBar *>(type))
1177  return "MythUIScrollBar";
1178  if (dynamic_cast<MythUIVideo *>(type))
1179  return "MythUIVideo";
1180  if (dynamic_cast<MythUIGuideGrid *>(type))
1181  return "MythUIGuideGrid";
1182  if (dynamic_cast<MythUIEditBar *>(type))
1183  return "MythUIEditBar";
1184 
1185  return "Unknown";
1186 }
1187 
1189 {
1190  if (nc->getArgCount() == 1)
1191  return QString("ERROR: See 'help %1' for usage information")
1192  .arg(nc->getArg(0));
1193 
1194  if (nc->getArg(1) == "getthemeinfo")
1195  {
1196  QString themeName = GetMythUI()->GetThemeName();
1197  QString themeDir = GetMythUI()->GetThemeDir();
1198  return QString("%1 - %2").arg(themeName, themeDir);
1199  }
1200  if (nc->getArg(1) == "reload")
1201  {
1202  GetMythMainWindow()->JumpTo(m_jumpMap["reloadtheme"]);
1203 
1204  return "OK";
1205  }
1206  if (nc->getArg(1) == "showborders")
1207  {
1208  GetMythMainWindow()->JumpTo(m_jumpMap["showborders"]);
1209 
1210  return "OK";
1211  }
1212  if (nc->getArg(1) == "shownames")
1213  {
1214  GetMythMainWindow()->JumpTo(m_jumpMap["shownames"]);
1215 
1216  return "OK";
1217  }
1218  if (nc->getArg(1) == "getwidgetnames")
1219  {
1220  QStringList path;
1221 
1222  if (nc->getArgCount() >= 3)
1223  path = nc->getArg(2).split('/');
1224 
1225  MythScreenStack *stack = GetMythMainWindow()->GetStack("popup stack");
1226  MythScreenType *topScreen = stack->GetTopScreen();
1227 
1228  if (!topScreen)
1229  {
1230  stack = GetMythMainWindow()->GetMainStack();
1231  topScreen = stack->GetTopScreen();
1232  }
1233 
1234  if (!topScreen)
1235  return {"ERROR: no top screen found!"};
1236 
1237  MythUIType *currType = topScreen;
1238 
1239  while (!path.isEmpty())
1240  {
1241  QString childName = path.takeFirst();
1242  currType = currType->GetChild(childName);
1243  if (!currType)
1244  return QString("ERROR: Failed to find child '%1'").arg(childName);
1245  }
1246 
1247  QList<MythUIType*> *children = currType->GetAllChildren();
1248  QString result;
1249 
1250  for (int i = 0; i < children->count(); i++)
1251  {
1252  MythUIType *type = children->at(i);
1253  QString widgetName = type->objectName();
1254  QString widgetType = getWidgetType(type);
1255  result += QString("%1 - %2\n\r").arg(widgetName, -20).arg(widgetType);
1256  }
1257 
1258  return result;
1259  }
1260  if (nc->getArg(1) == "getarea")
1261  {
1262  if (nc->getArgCount() < 3)
1263  return {"ERROR: Missing widget name."};
1264 
1265  QString widgetName = nc->getArg(2);
1266  QStringList path = widgetName.split('/');
1267 
1268  MythScreenStack *stack = GetMythMainWindow()->GetStack("popup stack");
1269  MythScreenType *topScreen = stack->GetTopScreen();
1270 
1271  if (!topScreen)
1272  {
1273  stack = GetMythMainWindow()->GetMainStack();
1274  topScreen = stack->GetTopScreen();
1275  }
1276 
1277  if (!topScreen)
1278  return {"ERROR: no top screen found!"};
1279 
1280  MythUIType *currType = topScreen;
1281 
1282  while (path.count() > 1)
1283  {
1284  QString childName = path.takeFirst();
1285  currType = currType->GetChild(childName);
1286  if (!currType)
1287  return QString("ERROR: Failed to find child '%1'").arg(childName);
1288  }
1289 
1290  MythUIType* type = currType->GetChild(path.first());
1291  if (!type)
1292  return QString("ERROR: widget '%1' not found!").arg(widgetName);
1293 
1294  int x = type->GetFullArea().x();
1295  int y = type->GetFullArea().y();
1296  int w = type->GetFullArea().width();
1297  int h = type->GetFullArea().height();
1298  return QString("The area of '%1' is x:%2, y:%3, w:%4, h:%5")
1299  .arg(widgetName).arg(x).arg(y).arg(w).arg(h);
1300  }
1301  if (nc->getArg(1) == "setarea")
1302  {
1303  if (nc->getArgCount() < 3)
1304  return {"ERROR: Missing widget name."};
1305 
1306  if (nc->getArgCount() < 7)
1307  return {"ERROR: Missing X, Y, Width or Height."};
1308 
1309  QString widgetName = nc->getArg(2);
1310  QStringList path = widgetName.split('/');
1311  QString x = nc->getArg(3);
1312  QString y = nc->getArg(4);
1313  QString w = nc->getArg(5);
1314  QString h = nc->getArg(6);
1315 
1316  MythScreenStack *stack = GetMythMainWindow()->GetStack("popup stack");
1317  MythScreenType *topScreen = stack->GetTopScreen();
1318 
1319  if (!topScreen)
1320  {
1321  stack = GetMythMainWindow()->GetMainStack();
1322  topScreen = stack->GetTopScreen();
1323  }
1324 
1325  MythUIType *currType = topScreen;
1326  if (!topScreen)
1327  return {"ERROR: no top screen found!"};
1328 
1329  while (path.count() > 1)
1330  {
1331  QString childName = path.takeFirst();
1332  currType = currType->GetChild(childName);
1333  if (!currType)
1334  return QString("ERROR: Failed to find child '%1'").arg(childName);
1335  }
1336 
1337  MythUIType* type = currType->GetChild(path.first());
1338  if (!type)
1339  return QString("ERROR: widget '%1' not found!").arg(widgetName);
1340 
1341  type->SetArea(MythRect(x, y, w, h));
1342 
1343  return QString("Changed area of '%1' to x:%2, y:%3, w:%4, h:%5")
1344  .arg(widgetName, x, y, w, h);
1345  }
1346 
1347  return QString("ERROR: See 'help %1' for usage information")
1348  .arg(nc->getArg(0));
1349 }
1350 
1352 {
1353  QString command;
1354  QString helpText;
1355 
1356  if (nc->getArgCount() >= 1)
1357  {
1358  if (is_abbrev("help", nc->getArg(0)))
1359  {
1360  if (nc->getArgCount() >= 2)
1361  command = nc->getArg(1);
1362  else
1363  command.clear();
1364  }
1365  else
1366  {
1367  command = nc->getArg(0);
1368  }
1369  }
1370 
1371  if (is_abbrev("jump", command))
1372  {
1373  QMap<QString, QString>::Iterator it;
1374  helpText +=
1375  "Usage: jump JUMPPOINT\r\n"
1376  "\r\n"
1377  "Where JUMPPOINT is one of the following:\r\n";
1378 
1379  for (it = m_jumpMap.begin(); it != m_jumpMap.end(); ++it)
1380  {
1381  helpText += it.key().leftJustified(20, ' ', true) + " - " +
1382  *it + "\r\n";
1383  }
1384  }
1385  else if (is_abbrev("key", command))
1386  {
1387  helpText +=
1388  "key LETTER - Send the letter key specified\r\n"
1389  "key NUMBER - Send the number key specified\r\n"
1390  "key CODE - Send one of the following key codes\r\n"
1391  "\r\n";
1392 
1393  QMap<QString, int>::Iterator it;
1394  bool first = true;
1395  for (it = m_keyMap.begin(); it != m_keyMap.end(); ++it)
1396  {
1397  if (first)
1398  first = false;
1399  else
1400  helpText += ", ";
1401 
1402  helpText += it.key();
1403  }
1404  helpText += "\r\n";
1405  }
1406  else if (is_abbrev("play", command))
1407  {
1408  helpText +=
1409  "play volume NUMBER% - Change volume to given percentage value\r\n"
1410  "play channel up - Change channel Up\r\n"
1411  "play channel down - Change channel Down\r\n"
1412  "play channel NUMBER - Change to a specific channel number\r\n"
1413  "play chanid NUMBER - Change to a specific channel id (chanid)\r\n"
1414  "play file FILENAME - Play FILENAME (FILENAME may be a file or a myth:// URL)\r\n"
1415  "play program CHANID yyyy-MM-ddThh:mm:ss\r\n"
1416  " - Play program with chanid & starttime\r\n"
1417  "play program CHANID yyyy-MM-ddThh:mm:ss resume\r\n"
1418  " - Resume program with chanid & starttime\r\n"
1419  "play save preview\r\n"
1420  " - Save preview image from current position\r\n"
1421  "play save preview FILENAME\r\n"
1422  " - Save preview image to FILENAME\r\n"
1423  "play save preview FILENAME WxH\r\n"
1424  " - Save preview image of size WxH\r\n"
1425  "play seek beginning - Seek to the beginning of the recording\r\n"
1426  "play seek forward - Skip forward in the video\r\n"
1427  "play seek backward - Skip backwards in the video\r\n"
1428  "play seek HH:MM:SS - Seek to a specific position\r\n"
1429  "play speed pause - Pause playback\r\n"
1430  "play speed normal - Playback at normal speed\r\n"
1431  "play speed 1x - Playback at normal speed\r\n"
1432  "play speed SPEEDx - Playback where SPEED must be a decimal\r\n"
1433  "play speed 1/8x - Playback at 1/8x speed\r\n"
1434  "play speed 1/4x - Playback at 1/4x speed\r\n"
1435  "play speed 1/3x - Playback at 1/3x speed\r\n"
1436  "play speed 1/2x - Playback at 1/2x speed\r\n"
1437  "play stop - Stop playback\r\n"
1438  "play subtitles [#] - Switch on indicated subtitle tracks\r\n"
1439  "play music play - Resume playback (MythMusic)\r\n"
1440  "play music pause - Pause playback (MythMusic)\r\n"
1441  "play music stop - Stop Playback (MythMusic)\r\n"
1442  "play music setvolume N - Set volume to number (MythMusic)\r\n"
1443  "play music getvolume - Get current volume (MythMusic)\r\n"
1444  "play music getmeta - Get metadata for current track (MythMusic)\r\n"
1445  "play music getstatus - Get music player status playing/paused/stopped (MythMusic)\r\n"
1446  "play music file NAME - Play specified file (MythMusic)\r\n"
1447  "play music track N - Switch to specified track (MythMusic)\r\n"
1448  "play music url URL - Play specified URL (MythMusic)\r\n";
1449  }
1450  else if (is_abbrev("query", command))
1451  {
1452  helpText +=
1453  "query location - Query current screen or location\r\n"
1454  "query volume - Query the current playback volume\r\n"
1455  "query recordings - List currently available recordings\r\n"
1456  "query recording CHANID STARTTIME\r\n"
1457  " - List info about the specified program\r\n"
1458  "query liveTV - List current TV schedule\r\n"
1459  "query liveTV CHANID - Query current program for specified channel\r\n"
1460  "query load - List 1/5/15 load averages\r\n"
1461  "query memstats - List free and total, physical and swap memory\r\n"
1462  "query time - Query current time on frontend\r\n"
1463  "query uptime - Query machine uptime\r\n"
1464  "query verbose - Get current VERBOSE mask\r\n"
1465  "query version - Query Frontend version details\r\n"
1466  "query channels - Query available channels\r\n"
1467  "query channels START LIMIT - Query available channels from START and limit results to LIMIT lines\r\n";
1468  }
1469  else if (is_abbrev("set", command))
1470  {
1471  helpText +=
1472  "set verbose debug-mask - "
1473  "Change the VERBOSE mask to 'debug-mask'\r\n"
1474  " (i.e. 'set verbose playback,audio')\r\n"
1475  " use 'set verbose default' to revert\r\n"
1476  " back to the default level of\r\n";
1477  }
1478  else if (is_abbrev("screenshot", command))
1479  {
1480  helpText +=
1481  "screenshot - Takes a screenshot and saves it as screenshot.png\r\n"
1482  "screenshot WxH - Saves the screenshot as a WxH size image\r\n";
1483  }
1484  else if (command == "exit")
1485  {
1486  helpText +=
1487  "exit - Terminates session\r\n\r\n";
1488  }
1489  else if ((is_abbrev("message", command)))
1490  {
1491  helpText +=
1492  "message - Displays a simple text message popup\r\n";
1493  }
1494  else if ((is_abbrev("notification", command)))
1495  {
1496  helpText +=
1497  "notification - Displays a simple text message notification\r\n";
1498  }
1499  else if (is_abbrev("theme", command))
1500  {
1501  helpText +=
1502  "theme getthemeinfo - Display the name and location of the current theme\r\n"
1503  "theme reload - Reload the theme\r\n"
1504  "theme showborders - Toggle showing widget borders\r\n"
1505  "theme shownames ON/OFF - Toggle showing widget names\r\n"
1506  "theme getwidgetnames PATH - Display the name and type of all the child widgets from PATH\r\n"
1507  "theme getarea WIDGETNAME - Get the area of widget WIDGET on the active screen\r\n"
1508  "theme setarea WIDGETNAME X Y W H - Change the area of widget WIDGET to X Y W H on the active screen\r\n";
1509  }
1510 
1511  if (!helpText.isEmpty())
1512  return helpText;
1513 
1514  if (!command.isEmpty())
1515  helpText += QString("Unknown command '%1'\r\n\r\n").arg(command);
1516 
1517  helpText +=
1518  "Valid Commands:\r\n"
1519  "---------------\r\n"
1520  "jump - Jump to a specified location in Myth\r\n"
1521  "key - Send a keypress to the program\r\n"
1522  "play - Playback related commands\r\n"
1523  "query - Queries\r\n"
1524  "set - Changes\r\n"
1525  "screenshot - Capture screenshot\r\n"
1526  "message - Display a simple text message\r\n"
1527  "notification - Display a simple text notification\r\n"
1528  "theme - Theme related commands\r\n"
1529  "exit - Exit Network Control\r\n"
1530  "\r\n"
1531  "Type 'help COMMANDNAME' for help on any specific command.\r\n";
1532 
1533  return helpText;
1534 }
1535 
1537 {
1538  if (nc->getArgCount() < 2)
1539  return QString("ERROR: See 'help %1' for usage information")
1540  .arg(nc->getArg(0));
1541 
1542  QString message = nc->getCommand().remove(0, 7).trimmed();
1543  MythMainWindow *window = GetMythMainWindow();
1544  auto* me = new MythEvent(MythEvent::MythUserMessage, message);
1545  qApp->postEvent(window, me);
1546  return {"OK"};
1547 }
1548 
1550 {
1551  if (nc->getArgCount() < 2)
1552  return QString("ERROR: See 'help %1' for usage information")
1553  .arg(nc->getArg(0));
1554 
1555  QString message = nc->getCommand().remove(0, 12).trimmed();
1556  MythNotification n(message, tr("Network Control"));
1558  return {"OK"};
1559 }
1560 
1562 {
1563  QCoreApplication::postEvent(
1564  this, new QEvent(kNetworkControlDataReadyEvent));
1565 }
1566 
1568  const QString &reply)
1569 {
1570  if (!m_clients.contains(ncc))
1571  {
1572  // NetworkControl instance is unaware of control client
1573  // assume connection to client has been terminated and bail
1574  return;
1575  }
1576 
1577  static const QRegularExpression crlfRegEx("\r\n$");
1578  static const QRegularExpression crlfcrlfRegEx("\r\n.*\r\n");
1579 
1580  QTcpSocket *client = ncc->getSocket();
1581  QTextStream *clientStream = ncc->getTextStream();
1582 
1583  if (client && clientStream && client->state() == QTcpSocket::ConnectedState)
1584  {
1585  *clientStream << reply;
1586 
1587  if ((!reply.contains(crlfRegEx)) ||
1588  ( reply.contains(crlfcrlfRegEx)))
1589  *clientStream << "\r\n" << m_prompt;
1590 
1591  clientStream->flush();
1592  client->flush();
1593  }
1594 }
1595 
1597 {
1598  if (e->type() == MythEvent::MythEventMessage)
1599  {
1600  auto *me = dynamic_cast<MythEvent *>(e);
1601  if (me == nullptr)
1602  return;
1603 
1604  const QString& message = me->Message();
1605 
1606  if (message.startsWith("MUSIC_CONTROL"))
1607  {
1608  QStringList tokens = message.simplified().split(" ");
1609  if ((tokens.size() >= 4) &&
1610  (tokens[1] == "ANSWER") &&
1611  (tokens[2] == gCoreContext->GetHostName()))
1612  {
1613  m_answer = tokens[3];
1614  for (int i = 4; i < tokens.size(); i++)
1615  m_answer += QString(" ") + tokens[i];
1616  m_gotAnswer = true;
1617  }
1618 
1619  }
1620  else if (message.startsWith("NETWORK_CONTROL"))
1621  {
1622  QStringList tokens = message.simplified().split(" ");
1623  if ((tokens.size() >= 3) &&
1624  (tokens[1] == "ANSWER"))
1625  {
1626  m_answer = tokens[2];
1627  for (int i = 3; i < tokens.size(); i++)
1628  m_answer += QString(" ") + tokens[i];
1629  m_gotAnswer = true;
1630  }
1631  else if ((tokens.size() >= 4) &&
1632  (tokens[1] == "RESPONSE"))
1633  {
1634 // int clientID = tokens[2].toInt();
1635  m_answer = tokens[3];
1636  for (int i = 4; i < tokens.size(); i++)
1637  m_answer += QString(" ") + tokens[i];
1638  m_gotAnswer = true;
1639  }
1640  }
1641  }
1642  else if (e->type() == kNetworkControlDataReadyEvent)
1643  {
1644  QString reply;
1645 
1646  QMutexLocker locker(&m_clientLock);
1647  QMutexLocker nrLocker(&m_nrLock);
1648 
1649  while (!m_networkControlReplies.isEmpty())
1650  {
1652  m_networkControlReplies.pop_front();
1653 
1654  reply = nc->getCommand();
1655 
1656  NetworkControlClient * ncc = nc->getClient();
1657  if (ncc)
1658  {
1659  sendReplyToClient(ncc, reply);
1660  }
1661  else //send to all clients
1662  {
1663  for (auto * ncc2 : qAsConst(m_clients))
1664  {
1665  if (ncc2)
1666  sendReplyToClient(ncc2, reply);
1667  }
1668  }
1669  delete nc;
1670  }
1671  }
1672  else if (e->type() == NetworkControlCloseEvent::kEventType)
1673  {
1674  auto *ncce = dynamic_cast<NetworkControlCloseEvent*>(e);
1675  if (ncce == nullptr)
1676  return;
1677 
1678  NetworkControlClient *ncc = ncce->getClient();
1679  deleteClient(ncc);
1680  }
1681 }
1682 
1683 QString NetworkControl::listSchedule(const QString& chanID)
1684 {
1685  QString result("");
1686  MSqlQuery query(MSqlQuery::InitCon());
1687  bool appendCRLF = true;
1688  QString queryStr("SELECT chanid, starttime, endtime, title, subtitle "
1689  "FROM program "
1690  "WHERE starttime < :START AND endtime > :END ");
1691 
1692  if (!chanID.isEmpty())
1693  {
1694  queryStr += " AND chanid = :CHANID";
1695  appendCRLF = false;
1696  }
1697 
1698  queryStr += " ORDER BY starttime, endtime, chanid";
1699 
1700  query.prepare(queryStr);
1701  query.bindValue(":START", MythDate::current());
1702  query.bindValue(":END", MythDate::current());
1703  if (!chanID.isEmpty())
1704  {
1705  query.bindValue(":CHANID", chanID);
1706  }
1707 
1708  if (query.exec())
1709  {
1710  while (query.next())
1711  {
1712  QString title = query.value(3).toString();
1713  QString subtitle = query.value(4).toString();
1714 
1715  if (!subtitle.isEmpty())
1716  title += QString(" -\"%1\"").arg(subtitle);
1717  QByteArray atitle = title.toLocal8Bit();
1718 
1719  result +=
1720  QString("%1 %2 %3 %4")
1721  .arg(QString::number(query.value(0).toInt()).rightJustified(5, ' '),
1722  MythDate::as_utc(query.value(1).toDateTime()).toString(Qt::ISODate),
1723  MythDate::as_utc(query.value(2).toDateTime()).toString(Qt::ISODate),
1724  atitle);
1725 
1726  if (appendCRLF)
1727  result += "\r\n";
1728  }
1729  }
1730  else
1731  {
1732  result = "ERROR: Unable to retrieve current schedule list.";
1733  }
1734  return result;
1735 }
1736 
1737 QString NetworkControl::listRecordings(const QString& chanid, const QString& starttime)
1738 {
1739  QString result;
1740  MSqlQuery query(MSqlQuery::InitCon());
1741  QString queryStr;
1742  bool appendCRLF = true;
1743 
1744  queryStr = "SELECT chanid, starttime, title, subtitle "
1745  "FROM recorded WHERE deletepending = 0 ";
1746 
1747  if ((!chanid.isEmpty()) && (!starttime.isEmpty()))
1748  {
1749  queryStr += "AND chanid = " + chanid + " "
1750  "AND starttime = '" + starttime + "' ";
1751  appendCRLF = false;
1752  }
1753 
1754  queryStr += "ORDER BY starttime, title;";
1755 
1756  query.prepare(queryStr);
1757  if (query.exec())
1758  {
1759  QString episode;
1760  QString title;
1761  QString subtitle;
1762  while (query.next())
1763  {
1764  title = query.value(2).toString();
1765  subtitle = query.value(3).toString();
1766 
1767  if (!subtitle.isEmpty())
1768  {
1769  episode = QString("%1 -\"%2\"").arg(title, subtitle);
1770  }
1771  else
1772  {
1773  episode = title;
1774  }
1775 
1776  result +=
1777  QString("%1 %2 %3")
1778  .arg(query.value(0).toString(),
1779  MythDate::as_utc(query.value(1).toDateTime()).toString(Qt::ISODate),
1780  episode);
1781 
1782  if (appendCRLF)
1783  result += "\r\n";
1784  }
1785  }
1786  else
1787  result = "ERROR: Unable to retrieve recordings list.";
1788 
1789  return result;
1790 }
1791 
1792 QString NetworkControl::listChannels(const uint start, const uint limit)
1793 {
1794  QString result;
1795  MSqlQuery query(MSqlQuery::InitCon());
1796  QString queryStr;
1797  uint sqlStart = start;
1798 
1799  // sql starts at zero, we want to start at 1
1800  if (sqlStart > 0)
1801  sqlStart--;
1802 
1803  queryStr = "select chanid, callsign, name from channel "
1804  "where deleted IS NULL and visible > 0 "
1805  "ORDER BY callsign";
1806 
1807  if (limit > 0) // only if a limit is specified, we limit the results
1808  {
1809  QString limitStr = QString(" LIMIT %1,%2").arg(sqlStart).arg(limit);
1810  queryStr += limitStr;
1811  }
1812 
1813  query.prepare(queryStr);
1814  if (!query.exec())
1815  {
1816  result = "ERROR: Unable to retrieve channel list.";
1817  return result;
1818  }
1819 
1820  uint maxcnt = query.size();
1821  uint cnt = 0;
1822  if (maxcnt == 0) // Feedback we have no usefull information
1823  {
1824  result += QString(R"(0:0 0 "Invalid" "Invalid")");
1825  return result;
1826  }
1827 
1828  while (query.next())
1829  {
1830  // Feedback is as follow:
1831  // <current line count>:<max line count to expect> <channelid> <callsign name> <channel name>\r\n
1832  cnt++;
1833  result += QString("%1:%2 %3 \"%4\" \"%5\"\r\n")
1834  .arg(cnt).arg(maxcnt)
1835  .arg(query.value(0).toString(),
1836  query.value(1).toString(),
1837  query.value(2).toString());
1838  }
1839 
1840  return result;
1841 }
1842 
1844 {
1845  int width = 0;
1846  int height = 0;
1847 
1848  if (nc->getArgCount() == 2)
1849  {
1850  QStringList size = nc->getArg(1).split('x');
1851  if (size.size() == 2)
1852  {
1853  width = size[0].toInt();
1854  height = size[1].toInt();
1855  }
1856  }
1857 
1858  MythMainWindow *window = GetMythMainWindow();
1859  QStringList args;
1860  if (width && height)
1861  {
1862  args << QString::number(width);
1863  args << QString::number(height);
1864  }
1865  auto *me = new MythEvent(MythEvent::MythEventMessage,
1867  qApp->postEvent(window, me);
1868  return "OK";
1869 }
1870 
1871 QString NetworkCommand::getFrom(int arg)
1872 {
1873  QString c = m_command;
1874  for(int i=0 ; i<arg ; i++) {
1875  QString argstr = c.simplified().split(" ")[0];
1876  c = c.mid(argstr.length()).trimmed();
1877  }
1878  return c;
1879 }
1880 
1881 /* vim: set expandtab tabstop=4 shiftwidth=4: */
mythuibuttontree.h
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:807
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
build_compdb.args
args
Definition: build_compdb.py:11
MythMainWindow::GetMainStack
MythScreenStack * GetMainStack()
Definition: mythmainwindow.cpp:315
NetworkCommand
Definition: networkcontrol.h:47
badChars
static const QRegularExpression badChars
Definition: mytharchivehelper.cpp:379
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:215
MythEvent::MythEventMessage
static Type MythEventMessage
Definition: mythevent.h:79
MThread::start
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
NetworkControl::newControlConnection
void newControlConnection(QTcpSocket *client)
Definition: networkcontrol.cpp:377
mythuiprogressbar.h
NetworkControl::m_networkControlCommands
QList< NetworkCommand * > m_networkControlCommands
Definition: networkcontrol.h:154
NetworkControl::saveScreenshot
static QString saveScreenshot(NetworkCommand *nc)
Definition: networkcontrol.cpp:1843
MythUIImage
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:97
NetworkControl::notifyDataAvailable
void notifyDataAvailable(void)
Definition: networkcontrol.cpp:1561
mythuivideo.h
NetworkControl::m_ncLock
QMutex m_ncLock
Definition: networkcontrol.h:155
MythDate::as_utc
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
Definition: mythdate.cpp:27
MythUIThemeHelper::GetThemeName
QString GetThemeName()
Definition: mythuithemehelper.cpp:146
ACTION_SCREENSHOT
static constexpr const char * ACTION_SCREENSHOT
Definition: mythuiactions.h:22
NetworkControl::m_keyMap
QMap< QString, int > m_keyMap
Definition: networkcontrol.h:144
MThread::wait
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
NetworkControlClient::m_textStream
QTextStream * m_textStream
Definition: networkcontrol.h:44
MythUIType::GetChild
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:133
NetworkControlClient::readClient
void readClient()
Definition: networkcontrol.cpp:425
NetworkControl::receiveCommand
void receiveCommand(QString &command)
Definition: networkcontrol.cpp:450
NetworkControl::sendReplyToClient
void sendReplyToClient(NetworkControlClient *ncc, const QString &reply)
Definition: networkcontrol.cpp:1567
MythMainWindow::JumpTo
void JumpTo(const QString &Destination, bool Pop=true)
Definition: mythmainwindow.cpp:1450
NetworkControl::getWidgetType
static QString getWidgetType(MythUIType *type)
Definition: networkcontrol.cpp:1144
MythUIEditBar
A narrow purpose widget used to represent cut positions and regions when editing a video.
Definition: mythuieditbar.h:16
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:16
getLoadAvgs
loadArray getLoadAvgs(void)
Returns the system load averages.
Definition: mythmiscutil.cpp:176
GetMythSourceVersion
const char * GetMythSourceVersion()
Definition: mythversion.cpp:5
NetworkCommand::getFrom
QString getFrom(int arg)
Definition: networkcontrol.cpp:1871
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:205
MythScreenStack
Definition: mythscreenstack.h:16
NetworkControlClient::commandReceived
void commandReceived(QString &)
NetworkControl::m_gotAnswer
bool m_gotAnswer
Definition: networkcontrol.h:141
kStartTimeRE
static const QRegularExpression kStartTimeRE
Definition: networkcontrol.cpp:66
MythNotification
Definition: mythnotification.h:29
mythuiscrollbar.h
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:608
MythUITextEdit
A text entry and edit widget.
Definition: mythuitextedit.h:34
MythUIGroup
Create a group of widgets.
Definition: mythuigroup.h:11
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
NetworkControlClient
Definition: networkcontrol.h:26
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:45
MythUIType::GetAllChildren
QList< MythUIType * > * GetAllChildren(void)
Return a list of all child widgets.
Definition: mythuitype.cpp:197
mythsystemevent.h
mythdirs.h
MythUIGuideGrid
A narrow purpose widget used to show television programs and the timeslots they occupy on channels....
Definition: mythuiguidegrid.h:43
MythUIClock
A simple text clock widget.
Definition: mythuiclock.h:25
MythRect
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
mythuieditbar.h
networkcontrol.h
remoteutil.h
mythuiimage.h
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:14
mythuiclock.h
MythEvent::Message
const QString & Message() const
Definition: mythevent.h:65
mythuiguidegrid.h
ServerPool::newConnection
void newConnection(QTcpSocket *)
MythEvent::MythUserMessage
static Type MythUserMessage
Definition: mythevent.h:80
NetworkControl::processSet
static QString processSet(NetworkCommand *nc)
Definition: networkcontrol.cpp:1103
mythversion.h
MythObservable::addListener
void addListener(QObject *listener)
Add a listener to the observable.
Definition: mythobservable.cpp:38
verboseString
QString verboseString
Definition: logging.cpp:102
MYTH_PROTO_VERSION
static constexpr const char * MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:47
NetworkControl::processHelp
QString processHelp(NetworkCommand *nc)
Definition: networkcontrol.cpp:1351
programinfo.h
NetworkControl::m_stopCommandThread
bool m_stopCommandThread
Definition: networkcontrol.h:162
mythlogging.h
NetworkControlCloseEvent
Definition: networkcontrol.h:80
NetworkControl::deleteClient
void deleteClient(void)
Definition: networkcontrol.cpp:346
MythUIProgressBar
Progress bar widget.
Definition: mythuiprogressbar.h:12
MythCoreContext::SendSystemEvent
void SendSystemEvent(const QString &msg)
Definition: mythcorecontext.cpp:1544
NetworkControl::listChannels
static QString listChannels(uint start, uint limit)
Definition: networkcontrol.cpp:1792
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:540
MythUIThemeHelper::GetThemeDir
QString GetThemeDir()
Definition: mythuithemehelper.cpp:141
compat.h
kNetworkControlDataReadyEvent
static QEvent::Type kNetworkControlDataReadyEvent
Definition: networkcontrol.cpp:59
NetworkControlClient::m_socket
QTcpSocket * m_socket
Definition: networkcontrol.h:43
NetworkControl::processNetworkControlCommand
void processNetworkControlCommand(NetworkCommand *nc)
Definition: networkcontrol.cpp:301
MythUIButton
A single button widget.
Definition: mythuibutton.h:21
NetworkControl::~NetworkControl
~NetworkControl() override
Definition: networkcontrol.cpp:250
kChanID1RE
static const QRegularExpression kChanID1RE
Definition: networkcontrol.cpp:64
MythUIButtonTree
A tree widget for displaying and navigating a MythGenericTree()
Definition: mythuibuttontree.h:16
loadArray
std::array< double, 3 > loadArray
Definition: mythmiscutil.h:22
MythMainWindow::IsTopScreenInitialized
static bool IsTopScreenInitialized()
Definition: mythmainwindow.cpp:603
NetworkControl::processQuery
QString processQuery(NetworkCommand *nc)
Definition: networkcontrol.cpp:947
MYTH_BINARY_VERSION
static constexpr const char * MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:15
uint
unsigned int uint
Definition: compat.h:79
MythUILocation::GetCurrentLocation
QString GetCurrentLocation(bool FullPath=false, bool MainStackOnly=true)
Definition: mythuilocation.cpp:20
MythUIScrollBar
Scroll bar widget.
Definition: mythuiscrollbar.h:15
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
MythUICheckBox
A checkbox widget supporting three check states - on,off,half and two conditions - selected and unsel...
Definition: mythuicheckbox.h:15
NetworkControl::m_keyTextMap
QMap< int, QString > m_keyTextMap
Definition: networkcontrol.h:145
getUptime
bool getUptime(std::chrono::seconds &uptime)
Definition: mythmiscutil.cpp:66
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:912
NetworkControl::m_nrLock
QMutex m_nrLock
Definition: networkcontrol.h:159
ACTION_HANDLEMEDIA
static constexpr const char * ACTION_HANDLEMEDIA
Definition: mythuiactions.h:21
mythuigroup.h
MythUIType
The base class on which all widgets and screens are based.
Definition: mythuitype.h:85
GetMythSourcePath
const char * GetMythSourcePath()
Definition: mythversion.cpp:10
MythUIShape
A widget for rendering primitive shapes and lines.
Definition: mythuishape.h:21
verboseArgParse
int verboseArgParse(const QString &arg)
Parse the –verbose commandline argument and set the verbose level.
Definition: logging.cpp:944
NetworkCommand::getArgCount
int getArgCount()
Definition: networkcontrol.h:71
mythuispinbox.h
mythburn.themeName
string themeName
Definition: mythburn.py:218
NetworkControl::listRecordings
static QString listRecordings(const QString &chanid="", const QString &starttime="")
Definition: networkcontrol.cpp:1737
mythuihelper.h
NetworkControlClient::~NetworkControlClient
~NetworkControlClient() override
Definition: networkcontrol.cpp:417
NetworkCommand::m_command
QString m_command
Definition: networkcontrol.h:75
NetworkControl::m_clientLock
QRecursiveMutex m_clientLock
Definition: networkcontrol.h:150
NetworkControl::m_commandThread
MThread * m_commandThread
Definition: networkcontrol.h:161
MythUIText
All purpose text widget, displays a text string.
Definition: mythuitext.h:28
NetworkControl::m_prompt
QString m_prompt
Definition: networkcontrol.h:140
NetworkControlClient::NetworkControlClient
NetworkControlClient(QTcpSocket *s)
Definition: networkcontrol.cpp:405
mythmiscutil.h
NetworkControlClient::getTextStream
QTextStream * getTextStream()
Definition: networkcontrol.h:34
MythUIVideo
Video widget, displays raw image data.
Definition: mythuivideo.h:14
mythcorecontext.h
mythuitextedit.h
NetworkControl::processNotification
static QString processNotification(NetworkCommand *nc)
Definition: networkcontrol.cpp:1549
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:883
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:17
NetworkControl::m_jumpMap
QMap< QString, QString > m_jumpMap
Definition: networkcontrol.h:143
NetworkControl::NetworkControl
NetworkControl()
Definition: networkcontrol.cpp:83
GetNotificationCenter
MythNotificationCenter * GetNotificationCenter(void)
Definition: mythmainwindow.cpp:122
MThread
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:48
NetworkControl::processKey
QString processKey(NetworkCommand *nc)
Definition: networkcontrol.cpp:485
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:102
build_compdb.action
action
Definition: build_compdb.py:9
MythUISpinBox
A widget for offering a range of numerical values where only the the bounding values and interval are...
Definition: mythuispinbox.h:16
NetworkControl::processTheme
QString processTheme(NetworkCommand *nc)
Definition: networkcontrol.cpp:1188
MythDate::current_iso_string
QString current_iso_string(bool stripped)
Returns current Date and Time in UTC as a string.
Definition: mythdate.cpp:22
NetworkControl::run
void run(void) override
Definition: networkcontrol.cpp:281
NetworkControl::m_ncCond
QWaitCondition m_ncCond
Definition: networkcontrol.h:156
NetworkControl::processMessage
static QString processMessage(NetworkCommand *nc)
Definition: networkcontrol.cpp:1536
LOC
#define LOC
Definition: networkcontrol.cpp:53
NetworkControl::m_answer
QString m_answer
Definition: networkcontrol.h:142
FE_LONG_TO
static constexpr qint64 FE_LONG_TO
Definition: networkcontrol.cpp:57
NetworkControl::listSchedule
static QString listSchedule(const QString &chanID="")
Definition: networkcontrol.cpp:1683
mythuibutton.h
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:320
NetworkCommand::getClient
NetworkControlClient * getClient()
Definition: networkcontrol.h:69
NetworkControl::processJump
QString processJump(NetworkCommand *nc)
Definition: networkcontrol.cpp:464
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:838
getMemStats
bool getMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
Returns memory statistics in megabytes.
Definition: mythmiscutil.cpp:109
NetworkControlCloseEvent::kEventType
static Type kEventType
Definition: networkcontrol.h:88
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
NetworkControl::m_clients
QList< NetworkControlClient * > m_clients
Definition: networkcontrol.h:152
MythUIWebBrowser
Web browsing widget.
Definition: mythuiwebbrowser.h:132
NetworkControl::processPlay
QString processPlay(NetworkCommand *nc, int clientID)
Definition: networkcontrol.cpp:590
MythMainWindow::ResetScreensaver
static void ResetScreensaver()
Definition: mythmainwindow.cpp:586
mythuicheckbox.h
previewgenerator.h
NetworkCommand::getCommand
QString getCommand()
Definition: networkcontrol.h:68
GetMythUI
MythUIHelper * GetMythUI()
Definition: mythuihelper.cpp:66
MythUIButtonList
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
Definition: mythuibuttonlist.h:191
mythuiwebbrowser.h
mythmainwindow.h
NetworkControl::customEvent
void customEvent(QEvent *e) override
Definition: networkcontrol.cpp:1596
NetworkCommand::getArg
QString getArg(int arg)
Definition: networkcontrol.h:70
NetworkControlClient::getSocket
QTcpSocket * getSocket()
Definition: networkcontrol.h:33
MythCoreContext::dispatch
void dispatch(const MythEvent &event)
Definition: mythcorecontext.cpp:1725
MythObservable::removeListener
void removeListener(QObject *listener)
Remove a listener to the observable.
Definition: mythobservable.cpp:55
is_abbrev
static bool is_abbrev(QString const &command, QString const &test, int minchars=1)
Is test an abbreviation of command ? The test substring must be at least minchars long.
Definition: networkcontrol.cpp:75
MythMainWindow
Definition: mythmainwindow.h:28
mythuishape.h
MythUIStateType
This widget is used for grouping other widgets for display when a particular named state is called....
Definition: mythuistatetype.h:22
FE_SHORT_TO
static constexpr qint64 FE_SHORT_TO
Definition: networkcontrol.cpp:56
NetworkControl::m_networkControlReplies
QList< NetworkCommand * > m_networkControlReplies
Definition: networkcontrol.h:158
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:832
MythScreenStack::GetTopScreen
virtual MythScreenType * GetTopScreen(void) const
Definition: mythscreenstack.cpp:180
MythNotificationCenter::Queue
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
Definition: mythnotificationcenter.cpp:1350