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 "libmythbase/compat.h"
21 #include "libmythbase/mythdirs.h"
24 #include "libmythbase/mythversion.h"
26 #include "libmythbase/remoteutil.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 const QEvent::Type kNetworkControlDataReadyEvent =
60  (QEvent::Type) QEvent::registerEventType();
61 const QEvent::Type NetworkControlCloseEvent::kEventType =
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 : std::as_const(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  {
374  LOG(VB_GENERAL, LOG_ERR, LOC + QString("deleteClient(%1), unable to "
375  "locate specified NetworkControlClient").arg((long long)ncc));
376  }
377 }
378 
379 void NetworkControl::newControlConnection(QTcpSocket *client)
380 {
381  QString welcomeStr;
382 
383  LOG(VB_GENERAL, LOG_INFO, LOC + QString("New connection established."));
384 
385  gCoreContext->SendSystemEvent("NET_CTRL_CONNECTED");
386 
387  auto *ncc = new NetworkControlClient(client);
388 
389  QMutexLocker locker(&m_clientLock);
390  m_clients.push_back(ncc);
391 
394  connect(client, &QAbstractSocket::disconnected,
395  this, qOverload<>(&NetworkControl::deleteClient));
396 
397  welcomeStr = "MythFrontend Network Control\r\n";
398  welcomeStr += "Type 'help' for usage information\r\n"
399  "---------------------------------";
400  m_nrLock.lock();
401  m_networkControlReplies.push_back(new NetworkCommand(ncc,welcomeStr));
402  m_nrLock.unlock();
403 
405 }
406 
408  : m_socket(s),
409  m_textStream(new QTextStream(s))
410 {
411 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
412  m_textStream->setCodec("UTF-8");
413 #else
414  m_textStream->setEncoding(QStringConverter::Utf8);
415 #endif
416  connect(m_socket, &QIODevice::readyRead, this, &NetworkControlClient::readClient);
417 }
418 
420 {
421  m_socket->close();
422  m_socket->deleteLater();
423 
424  delete m_textStream;
425 }
426 
428 {
429  auto *socket = (QTcpSocket *)sender();
430  if (!socket)
431  return;
432 
433  while (socket->canReadLine())
434  {
435  QString lineIn = socket->readLine();
436 #if 0
437  static const QRegularExpression badChars
438  { "[^-a-zA-Z0-9\\s\\.:_#/$%&()*+,;<=>?\\[\\]\\|]" };
439  lineIn.remove(badChars);
440 #endif
441 
442  lineIn = lineIn.simplified();
443  if (lineIn.isEmpty())
444  continue;
445 
446  LOG(VB_NETWORK, LOG_INFO, LOC +
447  QString("emit commandReceived(%1)").arg(lineIn));
448  emit commandReceived(lineIn);
449  }
450 }
451 
452 void NetworkControl::receiveCommand(QString &command)
453 {
454  LOG(VB_NETWORK, LOG_INFO, LOC +
455  QString("NetworkControl::receiveCommand(%1)").arg(command));
456  auto *ncc = qobject_cast<NetworkControlClient *>(sender());
457  if (!ncc)
458  return;
459 
460  m_ncLock.lock();
461  m_networkControlCommands.push_back(new NetworkCommand(ncc,command));
462  m_ncCond.wakeOne();
463  m_ncLock.unlock();
464 }
465 
467 {
468  QString result = "OK";
469 
470  if ((nc->getArgCount() < 2) || (!m_jumpMap.contains(nc->getArg(1))))
471  return QString("ERROR: See 'help %1' for usage information")
472  .arg(nc->getArg(0));
473 
475 
476  // Fixme, should do some better checking here, but that would
477  // depend on all Locations matching their jumppoints
478  QElapsedTimer timer;
479  timer.start();
480  while (!timer.hasExpired(FE_SHORT_TO) &&
481  (GetMythUI()->GetCurrentLocation().toLower() != nc->getArg(1)))
482  std::this_thread::sleep_for(10ms);
483 
484  return result;
485 }
486 
488 {
489  QString result = "OK";
490  QKeyEvent *event = nullptr;
491 
492  if (nc->getArgCount() < 2)
493  return QString("ERROR: See 'help %1' for usage information")
494  .arg(nc->getArg(0));
495 
496  QObject *keyDest = nullptr;
497 
498  if (GetMythMainWindow())
499  keyDest = GetMythMainWindow();
500  else
501  return {"ERROR: Application has no main window!\n"};
502 
503  int curToken = 1;
504  while (curToken < nc->getArgCount())
505  {
506  int tokenLen = nc->getArg(curToken).length();
507 
508  if (nc->getArg(curToken) == "sleep")
509  {
510  std::this_thread::sleep_for(1s);
511  }
512  else if (m_keyMap.contains(nc->getArg(curToken)))
513  {
514  int keyCode = m_keyMap[nc->getArg(curToken)];
515  QString keyText;
516 
517  if (m_keyTextMap.contains(keyCode))
518  keyText = m_keyTextMap[keyCode];
519 
521 
522  event = new QKeyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier,
523  keyText);
524  QCoreApplication::postEvent(keyDest, event);
525 
526  event = new QKeyEvent(QEvent::KeyRelease, keyCode, Qt::NoModifier,
527  keyText);
528  QCoreApplication::postEvent(keyDest, event);
529  }
530  else if (((tokenLen == 1) &&
531  (nc->getArg(curToken).at(0).isLetterOrNumber())) ||
532  ((tokenLen >= 1) &&
533  (nc->getArg(curToken).contains("+"))))
534  {
535  QKeySequence a(nc->getArg(curToken));
536 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
537  int keyCode = a[0];
538  Qt::KeyboardModifiers modifiers = Qt::NoModifier;
539 
540  if (tokenLen > 1)
541  {
542  QStringList tokenParts = nc->getArg(curToken).split('+');
543 
544  int partNum = 0;
545  while (partNum < (tokenParts.size() - 1))
546  {
547  if (tokenParts[partNum].toUpper() == "CTRL")
548  modifiers |= Qt::ControlModifier;
549  if (tokenParts[partNum].toUpper() == "SHIFT")
550  modifiers |= Qt::ShiftModifier;
551  if (tokenParts[partNum].toUpper() == "ALT")
552  modifiers |= Qt::AltModifier;
553  if (tokenParts[partNum].toUpper() == "META")
554  modifiers |= Qt::MetaModifier;
555 
556  partNum++;
557  }
558  }
559 #else
560  int keyCode = a[0].key();
561  Qt::KeyboardModifiers modifiers = a[0].keyboardModifiers();
562 #endif
563  if (tokenLen == 1)
564  {
565  if (nc->getArg(curToken) == nc->getArg(curToken).toUpper())
566  modifiers |= Qt::ShiftModifier;
567  }
568 
570 
571  event = new QKeyEvent(QEvent::KeyPress, keyCode, modifiers,
572  nc->getArg(curToken));
573  QCoreApplication::postEvent(keyDest, event);
574 
575  event = new QKeyEvent(QEvent::KeyRelease, keyCode, modifiers,
576  nc->getArg(curToken));
577  QCoreApplication::postEvent(keyDest, event);
578  }
579  else
580  {
581  return QString("ERROR: Invalid syntax at '%1', see 'help %2' for "
582  "usage information")
583  .arg(nc->getArg(curToken), nc->getArg(0));
584  }
585 
586  curToken++;
587  }
588 
589  return result;
590 }
591 
592 QString NetworkControl::processPlay(NetworkCommand *nc, int clientID)
593 {
594  QString result = "OK";
595  QString message;
596 
597  if (nc->getArgCount() < 2)
598  return QString("ERROR: See 'help %1' for usage information")
599  .arg(nc->getArg(0));
600 
601  if ((nc->getArgCount() >= 3) &&
602  (is_abbrev("file", nc->getArg(1))))
603  {
604  if (GetMythUI()->GetCurrentLocation().toLower() != "mainmenu")
605  {
606  GetMythMainWindow()->JumpTo(m_jumpMap["mainmenu"]);
607 
608  QElapsedTimer timer;
609  timer.start();
610  while (!timer.hasExpired(FE_LONG_TO) &&
611  (GetMythUI()->GetCurrentLocation().toLower() != "mainmenu"))
612  std::this_thread::sleep_for(10ms);
613  }
614 
615  if (GetMythUI()->GetCurrentLocation().toLower() == "mainmenu")
616  {
617  QStringList args;
618  args << nc->getFrom(2);
619  auto *me = new MythEvent(ACTION_HANDLEMEDIA, args);
620  qApp->postEvent(GetMythMainWindow(), me);
621  }
622  else
623  {
624  return {"Unable to change to main menu to start playback!"};
625  }
626  }
627  else if ((nc->getArgCount() >= 4) &&
628  (is_abbrev("program", nc->getArg(1))) &&
629  (nc->getArg(2).contains(kChanID1RE)) &&
630  (nc->getArg(3).contains(kStartTimeRE)))
631  {
632  if (GetMythUI()->GetCurrentLocation().toLower() == "playback")
633  {
634  QString msg = QString("NETWORK_CONTROL STOP");
635  MythEvent me(msg);
636  gCoreContext->dispatch(me);
637 
638  QElapsedTimer timer;
639  timer.start();
640  while (!timer.hasExpired(FE_LONG_TO) &&
641  (GetMythUI()->GetCurrentLocation().toLower() == "playback"))
642  std::this_thread::sleep_for(10ms);
643  }
644 
645  if (GetMythUI()->GetCurrentLocation().toLower() != "playbackbox")
646  {
647  GetMythMainWindow()->JumpTo(m_jumpMap["playbackbox"]);
648 
649  QElapsedTimer timer;
650  timer.start();
651  while (!timer.hasExpired(10000) &&
652  (GetMythUI()->GetCurrentLocation().toLower() != "playbackbox"))
653  std::this_thread::sleep_for(10ms);
654 
655  timer.start();
656  while (!timer.hasExpired(10000) && (!MythMainWindow::IsTopScreenInitialized()))
657  std::this_thread::sleep_for(10ms);
658  }
659 
660  if (GetMythUI()->GetCurrentLocation().toLower() == "playbackbox")
661  {
662  QString action = "PLAY";
663  if (nc->getArgCount() == 5 && nc->getArg(4) == "resume")
664  action = "RESUME";
665 
666  QString msg = QString("NETWORK_CONTROL %1 PROGRAM %2 %3 %4")
667  .arg(action, nc->getArg(2),
668  nc->getArg(3).toUpper(),
669  QString::number(clientID));
670 
671  result.clear();
672  m_gotAnswer = false;
673  QElapsedTimer timer;
674  timer.start();
675 
676  MythEvent me(msg);
677  gCoreContext->dispatch(me);
678 
679  while (!timer.hasExpired(FE_LONG_TO) && !m_gotAnswer)
680  std::this_thread::sleep_for(10ms);
681 
682  if (m_gotAnswer)
683  result += m_answer;
684  else
685  result = "ERROR: Timed out waiting for reply from player";
686 
687  }
688  else
689  {
690  result = QString("ERROR: Unable to change to PlaybackBox from "
691  "%1, cannot play requested file.")
692  .arg(GetMythUI()->GetCurrentLocation());
693  }
694  }
695  else if (is_abbrev("music", nc->getArg(1)))
696  {
697 #if 0
698  if (GetMythUI()->GetCurrentLocation().toLower() != "playmusic")
699  {
700  return QString("ERROR: You are in %1 mode and this command is "
701  "only for MythMusic")
702  .arg(GetMythUI()->GetCurrentLocation());
703  }
704 #endif
705 
706  QString hostname = gCoreContext->GetHostName();
707 
708  if (nc->getArgCount() == 3)
709  {
710  if (is_abbrev("play", nc->getArg(2)))
711  message = QString("MUSIC_COMMAND %1 PLAY").arg(hostname);
712  else if (is_abbrev("pause", nc->getArg(2)))
713  message = QString("MUSIC_COMMAND %1 PAUSE").arg(hostname);
714  else if (is_abbrev("stop", nc->getArg(2)))
715  message = QString("MUSIC_COMMAND %1 STOP").arg(hostname);
716  else if (is_abbrev("getvolume", nc->getArg(2)))
717  {
718  m_gotAnswer = false;
719 
720  MythEvent me(QString("MUSIC_COMMAND %1 GET_VOLUME").arg(hostname));
721  gCoreContext->dispatch(me);
722 
723  QElapsedTimer timer;
724  timer.start();
725  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
726  {
727  qApp->processEvents();
728  std::this_thread::sleep_for(10ms);
729  }
730 
731  if (m_gotAnswer)
732  return m_answer;
733 
734  return "unknown";
735  }
736  else if (is_abbrev("getmeta", nc->getArg(2)))
737  {
738  m_gotAnswer = false;
739 
740  MythEvent me(QString("MUSIC_COMMAND %1 GET_METADATA").arg(hostname));
741  gCoreContext->dispatch(me);
742 
743  QElapsedTimer timer;
744  timer.start();
745  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
746  {
747  qApp->processEvents();
748  std::this_thread::sleep_for(10ms);
749  }
750 
751  if (m_gotAnswer)
752  return m_answer;
753 
754  return "unknown";
755  }
756  else if (is_abbrev("getstatus", nc->getArg(2)))
757  {
758  m_gotAnswer = false;
759 
760  MythEvent me(QString("MUSIC_COMMAND %1 GET_STATUS").arg(hostname));
761  gCoreContext->dispatch(me);
762 
763  QElapsedTimer timer;
764  timer.start();
765  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
766  {
767  qApp->processEvents();
768  std::this_thread::sleep_for(10ms);
769  }
770 
771  if (m_gotAnswer)
772  return m_answer;
773 
774  return "unknown";
775  }
776  else
777  {
778  return {"ERROR: Invalid 'play music' command"};
779  }
780  }
781  else if (nc->getArgCount() > 3)
782  {
783  if (is_abbrev("setvolume", nc->getArg(2)))
784  {
785  message = QString("MUSIC_COMMAND %1 SET_VOLUME %2")
786  .arg(hostname, nc->getArg(3));
787  }
788  else if (is_abbrev("track", nc->getArg(2)))
789  {
790  message = QString("MUSIC_COMMAND %1 PLAY_TRACK %2")
791  .arg(hostname, nc->getArg(3));
792  }
793  else if (is_abbrev("url", nc->getArg(2)))
794  {
795  message = QString("MUSIC_COMMAND %1 PLAY_URL %2")
796  .arg(hostname, nc->getArg(3));
797  }
798  else if (is_abbrev("file", nc->getArg(2)))
799  {
800  message = QString("MUSIC_COMMAND %1 PLAY_FILE '%2'")
801  .arg(hostname, nc->getFrom(3));
802  }
803  else
804  {
805  return {"ERROR: Invalid 'play music' command"};
806  }
807  }
808  else
809  {
810  return {"ERROR: Invalid 'play music' command"};
811  }
812  }
813  // Everything below here requires us to be in playback mode so check to
814  // see if we are
815  else if (GetMythUI()->GetCurrentLocation().toLower() != "playback")
816  {
817  return QString("ERROR: You are in %1 mode and this command is only "
818  "for playback mode")
819  .arg(GetMythUI()->GetCurrentLocation());
820  }
821  else if (is_abbrev("chanid", nc->getArg(1), 5))
822  {
823  if (nc->getArg(2).contains(kChanID1RE))
824  message = QString("NETWORK_CONTROL CHANID %1").arg(nc->getArg(2));
825  else
826  return QString("ERROR: See 'help %1' for usage information")
827  .arg(nc->getArg(0));
828  }
829  else if (is_abbrev("channel", nc->getArg(1), 5))
830  {
831  static const QRegularExpression kChanID2RE { "^[-\\.\\d_#]+$" };
832 
833  if (nc->getArgCount() < 3)
834  return "ERROR: See 'help play' for usage information";
835 
836  if (is_abbrev("up", nc->getArg(2)))
837  message = "NETWORK_CONTROL CHANNEL UP";
838  else if (is_abbrev("down", nc->getArg(2)))
839  message = "NETWORK_CONTROL CHANNEL DOWN";
840  else if (nc->getArg(2).contains(kChanID2RE))
841  message = QString("NETWORK_CONTROL CHANNEL %1").arg(nc->getArg(2));
842  else
843  return QString("ERROR: See 'help %1' for usage information")
844  .arg(nc->getArg(0));
845  }
846  else if (is_abbrev("seek", nc->getArg(1), 2))
847  {
848  static const QRegularExpression kSeekTimeRE { R"(^\d\d:\d\d:\d\d$)" };
849 
850  if (nc->getArgCount() < 3)
851  return QString("ERROR: See 'help %1' for usage information")
852  .arg(nc->getArg(0));
853 
854  if (is_abbrev("beginning", nc->getArg(2)))
855  message = "NETWORK_CONTROL SEEK BEGINNING";
856  else if (is_abbrev("forward", nc->getArg(2)))
857  message = "NETWORK_CONTROL SEEK FORWARD";
858  else if (is_abbrev("rewind", nc->getArg(2)) ||
859  is_abbrev("backward", nc->getArg(2)))
860  message = "NETWORK_CONTROL SEEK BACKWARD";
861  else if (nc->getArg(2).contains(kSeekTimeRE))
862  {
863  int hours = nc->getArg(2).mid(0, 2).toInt();
864  int minutes = nc->getArg(2).mid(3, 2).toInt();
865  int seconds = nc->getArg(2).mid(6, 2).toInt();
866  message = QString("NETWORK_CONTROL SEEK POSITION %1")
867  .arg((hours * 3600) + (minutes * 60) + seconds);
868  }
869  else
870  {
871  return QString("ERROR: See 'help %1' for usage information")
872  .arg(nc->getArg(0));
873  }
874  }
875  else if (is_abbrev("speed", nc->getArg(1), 2))
876  {
877  static const QRegularExpression kSpeed1RE { R"(^\-*\d+x$)" };
878  static const QRegularExpression kSpeed2RE { R"(^\-*\d+\/\d+x$)" };
879  static const QRegularExpression kSpeed3RE { R"(^\-*\d*\.\d+x$)" };
880 
881  if (nc->getArgCount() < 3)
882  return QString("ERROR: See 'help %1' for usage information")
883  .arg(nc->getArg(0));
884 
885  QString token2 = nc->getArg(2).toLower();
886  if ((token2.contains(kSpeed1RE)) ||
887  (token2.contains(kSpeed2RE)) ||
888  (token2.contains(kSpeed3RE)))
889  message = QString("NETWORK_CONTROL SPEED %1").arg(token2);
890  else if (is_abbrev("normal", token2))
891  message = QString("NETWORK_CONTROL SPEED normal");
892  else if (is_abbrev("pause", token2))
893  message = QString("NETWORK_CONTROL SPEED 0x");
894  else
895  return QString("ERROR: See 'help %1' for usage information")
896  .arg(nc->getArg(0));
897  }
898  else if (is_abbrev("save", nc->getArg(1), 2))
899  {
900  if (is_abbrev("screenshot", nc->getArg(2), 2))
901  return saveScreenshot(nc);
902  }
903  else if (is_abbrev("stop", nc->getArg(1), 2))
904  {
905  message = QString("NETWORK_CONTROL STOP");
906  }
907  else if (is_abbrev("volume", nc->getArg(1), 2))
908  {
909  static const QRegularExpression kVolumeRE { "^\\d+%?$" };
910 
911  if ((nc->getArgCount() < 3) ||
912  (!nc->getArg(2).toLower().contains(kVolumeRE)))
913  {
914  return QString("ERROR: See 'help %1' for usage information")
915  .arg(nc->getArg(0));
916  }
917 
918  message = QString("NETWORK_CONTROL VOLUME %1")
919  .arg(nc->getArg(2).toLower());
920  }
921  else if (is_abbrev("subtitles", nc->getArg(1), 2))
922  {
923  static const QRegularExpression kNumberRE { "^\\d+$" };
924  if (nc->getArgCount() < 3)
925  message = QString("NETWORK_CONTROL SUBTITLES 0");
926  else if (!nc->getArg(2).toLower().contains(kNumberRE))
927  {
928  return QString("ERROR: See 'help %1' for usage information")
929  .arg(nc->getArg(0));
930  }
931  else
932  {
933  message = QString("NETWORK_CONTROL SUBTITLES %1")
934  .arg(nc->getArg(2));
935  }
936  }
937  else
938  {
939  return QString("ERROR: See 'help %1' for usage information")
940  .arg(nc->getArg(0));
941  }
942 
943  if (!message.isEmpty())
944  {
945  // Don't broadcast this event as the TV object will see it
946  // twice: once directly from the Qt event system, and once
947  // because its filtering all events before they are sent to
948  // the main window. Its much easier to get a pointer to the
949  // MainWindow object than is is to a pointer to the TV object,
950  // so send the event directly there. The TV object will get
951  // it anyway because because of the filter hook. (The last
952  // third of this function requires you to be in playback mode
953  // so the TV object is guaranteed to exist.)
954  auto *me = new MythEvent(message);
955  qApp->postEvent(GetMythMainWindow(), me);
956  }
957 
958  return result;
959 }
960 
962 {
963  QString result = "OK";
964 
965  if (nc->getArgCount() < 2)
966  return QString("ERROR: See 'help %1' for usage information")
967  .arg(nc->getArg(0));
968 
969  if (is_abbrev("location", nc->getArg(1)))
970  {
971  bool fullPath = false;
972  bool mainStackOnly = true;
973 
974  if (nc->getArgCount() > 2)
975  fullPath = (nc->getArg(2).toLower() == "true" || nc->getArg(2) == "1");
976  if (nc->getArgCount() > 3)
977  mainStackOnly = (nc->getArg(3).toLower() == "true" || nc->getArg(3) == "1");
978 
979  QString location = GetMythUI()->GetCurrentLocation(fullPath, mainStackOnly);
980  result = location;
981 
982  // if we're playing something, then find out what
983  if (location == "Playback")
984  {
985  result += " ";
986  m_gotAnswer = false;
987  QString message = QString("NETWORK_CONTROL QUERY POSITION");
988  MythEvent me(message);
989  gCoreContext->dispatch(me);
990 
991  QElapsedTimer timer;
992  timer.start();
993  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
994  std::this_thread::sleep_for(10ms);
995 
996  if (m_gotAnswer)
997  result += m_answer;
998  else
999  result = "ERROR: Timed out waiting for reply from player";
1000  }
1001  }
1002  else if (is_abbrev("verbose", nc->getArg(1)))
1003  {
1004  return verboseString;
1005  }
1006  else if (is_abbrev("liveTV", nc->getArg(1)))
1007  {
1008  if(nc->getArgCount() == 3) // has a channel ID
1009  return listSchedule(nc->getArg(2));
1010  return listSchedule();
1011  }
1012  else if (is_abbrev("version", nc->getArg(1)))
1013  {
1014  int dbSchema = gCoreContext->GetNumSetting("DBSchemaVer");
1015 
1016  return QString("VERSION: %1/%2 %3 %4 QT/%5 DBSchema/%6")
1017  .arg(GetMythSourceVersion(),
1019  MYTH_BINARY_VERSION,
1020  MYTH_PROTO_VERSION,
1021  QT_VERSION_STR,
1022  QString::number(dbSchema));
1023 
1024  }
1025  else if(is_abbrev("time", nc->getArg(1)))
1026  {
1028  }
1029  else if (is_abbrev("uptime", nc->getArg(1)))
1030  {
1031  QString str;
1032  std::chrono::seconds uptime = 0s;
1033 
1034  if (getUptime(uptime))
1035  str = QString::number(uptime.count());
1036  else
1037  str = QString("Could not determine uptime.");
1038  return str;
1039  }
1040  else if (is_abbrev("load", nc->getArg(1)))
1041  {
1042  QString str;
1043  loadArray loads = getLoadAvgs();
1044  if (loads[0] == -1)
1045  str = QString("getloadavg() failed");
1046  else
1047  str = QString("%1 %2 %3").arg(loads[0]).arg(loads[1]).arg(loads[2]);
1048  return str;
1049  }
1050  else if (is_abbrev("memstats", nc->getArg(1)))
1051  {
1052  QString str;
1053  int totalMB = 0;
1054  int freeMB = 0;
1055  int totalVM = 0;
1056  int freeVM = 0;
1057 
1058  if (getMemStats(totalMB, freeMB, totalVM, freeVM))
1059  {
1060  str = QString("%1 %2 %3 %4")
1061  .arg(totalMB).arg(freeMB).arg(totalVM).arg(freeVM);
1062  }
1063  else
1064  {
1065  str = QString("Could not determine memory stats.");
1066  }
1067  return str;
1068  }
1069  else if (is_abbrev("volume", nc->getArg(1)))
1070  {
1071  QString str = "0%";
1072 
1073  QString location = GetMythUI()->GetCurrentLocation(false, false);
1074 
1075  if (location != "Playback")
1076  return str;
1077 
1078  m_gotAnswer = false;
1079  QString message = QString("NETWORK_CONTROL QUERY VOLUME");
1080  MythEvent me(message);
1081  gCoreContext->dispatch(me);
1082 
1083  QElapsedTimer timer;
1084  timer.start();
1085  while (!timer.hasExpired(FE_SHORT_TO) && !m_gotAnswer)
1086  std::this_thread::sleep_for(10ms);
1087 
1088  if (m_gotAnswer)
1089  str = m_answer;
1090  else
1091  str = "ERROR: Timed out waiting for reply from player";
1092 
1093  return str;
1094  }
1095  else if ((nc->getArgCount() == 4) &&
1096  is_abbrev("recording", nc->getArg(1)) &&
1097  (nc->getArg(2).contains(kChanID1RE)) &&
1098  (nc->getArg(3).contains(kStartTimeRE)))
1099  {
1100  return listRecordings(nc->getArg(2), nc->getArg(3).toUpper());
1101  }
1102  else if (is_abbrev("recordings", nc->getArg(1)))
1103  {
1104  return listRecordings();
1105  }
1106  else if (is_abbrev("channels", nc->getArg(1)))
1107  {
1108  if (nc->getArgCount() == 2)
1109  return listChannels(0, 0); // give us all you can
1110  if (nc->getArgCount() == 4)
1111  return listChannels(nc->getArg(2).toLower().toUInt(),
1112  nc->getArg(3).toLower().toUInt());
1113  return QString("ERROR: See 'help %1' for usage information "
1114  "(parameters mismatch)").arg(nc->getArg(0));
1115  }
1116  else
1117  {
1118  return QString("ERROR: See 'help %1' for usage information")
1119  .arg(nc->getArg(0));
1120  }
1121 
1122  return result;
1123 }
1124 
1126 {
1127  if (nc->getArgCount() == 1)
1128  return QString("ERROR: See 'help %1' for usage information")
1129  .arg(nc->getArg(0));
1130 
1131  if (nc->getArg(1) == "verbose")
1132  {
1133  if (nc->getArgCount() < 3)
1134  return {"ERROR: Missing filter name."};
1135 
1136  if (nc->getArgCount() > 3)
1137  {
1138  return QString("ERROR: Separate filters with commas with no "
1139  "space: playback,audio\r\n See 'help %1' for usage "
1140  "information").arg(nc->getArg(0));
1141  }
1142 
1143  QString oldVerboseString = verboseString;
1144  QString result = "OK";
1145 
1146  int pva_result = verboseArgParse(nc->getArg(2));
1147 
1148  if (pva_result != 0 /*GENERIC_EXIT_OK */)
1149  result = "Failed";
1150 
1151  result += "\r\n";
1152  result += " Previous filter: " + oldVerboseString + "\r\n";
1153  result += " New Filter: " + verboseString + "\r\n";
1154 
1155  LOG(VB_GENERAL, LOG_NOTICE,
1156  QString("Verbose mask changed, new level is: %1")
1157  .arg(verboseString));
1158 
1159  return result;
1160  }
1161 
1162  return QString("ERROR: See 'help %1' for usage information")
1163  .arg(nc->getArg(0));
1164 }
1165 
1167 {
1168  if (dynamic_cast<MythUIText *>(type))
1169  return "MythUIText";
1170  if (dynamic_cast<MythUITextEdit *>(type))
1171  return "MythUITextEdit";
1172  if (dynamic_cast<MythUIGroup *>(type))
1173  return "MythUIGroup";
1174  if (dynamic_cast<MythUIButton *>(type))
1175  return "MythUIButton";
1176  if (dynamic_cast<MythUICheckBox *>(type))
1177  return "MythUICheckBox";
1178  if (dynamic_cast<MythUIShape *>(type))
1179  return "MythUIShape";
1180  if (dynamic_cast<MythUIButtonList *>(type))
1181  return "MythUIButtonList";
1182  if (dynamic_cast<MythUIImage *>(type))
1183  return "MythUIImage";
1184  if (dynamic_cast<MythUISpinBox *>(type))
1185  return "MythUISpinBox";
1186 #if CONFIG_QTWEBKIT
1187  if (dynamic_cast<MythUIWebBrowser *>(type))
1188  return "MythUIWebBrowser";
1189 #endif
1190  if (dynamic_cast<MythUIClock *>(type))
1191  return "MythUIClock";
1192  if (dynamic_cast<MythUIStateType *>(type))
1193  return "MythUIStateType";
1194  if (dynamic_cast<MythUIProgressBar *>(type))
1195  return "MythUIProgressBar";
1196  if (dynamic_cast<MythUIButtonTree *>(type))
1197  return "MythUIButtonTree";
1198  if (dynamic_cast<MythUIScrollBar *>(type))
1199  return "MythUIScrollBar";
1200  if (dynamic_cast<MythUIVideo *>(type))
1201  return "MythUIVideo";
1202  if (dynamic_cast<MythUIGuideGrid *>(type))
1203  return "MythUIGuideGrid";
1204  if (dynamic_cast<MythUIEditBar *>(type))
1205  return "MythUIEditBar";
1206 
1207  return "Unknown";
1208 }
1209 
1211 {
1212  if (nc->getArgCount() == 1)
1213  return QString("ERROR: See 'help %1' for usage information")
1214  .arg(nc->getArg(0));
1215 
1216  if (nc->getArg(1) == "getthemeinfo")
1217  {
1218  QString themeName = GetMythUI()->GetThemeName();
1219  QString themeDir = GetMythUI()->GetThemeDir();
1220  return QString("%1 - %2").arg(themeName, themeDir);
1221  }
1222  if (nc->getArg(1) == "reload")
1223  {
1224  GetMythMainWindow()->JumpTo(m_jumpMap["reloadtheme"]);
1225 
1226  return "OK";
1227  }
1228  if (nc->getArg(1) == "showborders")
1229  {
1230  GetMythMainWindow()->JumpTo(m_jumpMap["showborders"]);
1231 
1232  return "OK";
1233  }
1234  if (nc->getArg(1) == "shownames")
1235  {
1236  GetMythMainWindow()->JumpTo(m_jumpMap["shownames"]);
1237 
1238  return "OK";
1239  }
1240  if (nc->getArg(1) == "getwidgetnames")
1241  {
1242  QStringList path;
1243 
1244  if (nc->getArgCount() >= 3)
1245  path = nc->getArg(2).split('/');
1246 
1247  MythScreenStack *stack = GetMythMainWindow()->GetStack("popup stack");
1248  MythScreenType *topScreen = stack->GetTopScreen();
1249 
1250  if (!topScreen)
1251  {
1252  stack = GetMythMainWindow()->GetMainStack();
1253  topScreen = stack->GetTopScreen();
1254  }
1255 
1256  if (!topScreen)
1257  return {"ERROR: no top screen found!"};
1258 
1259  MythUIType *currType = topScreen;
1260 
1261  while (!path.isEmpty())
1262  {
1263  QString childName = path.takeFirst();
1264  currType = currType->GetChild(childName);
1265  if (!currType)
1266  return QString("ERROR: Failed to find child '%1'").arg(childName);
1267  }
1268 
1269  QList<MythUIType*> *children = currType->GetAllChildren();
1270  QString result;
1271 
1272  for (int i = 0; i < children->count(); i++)
1273  {
1274  MythUIType *type = children->at(i);
1275  QString widgetName = type->objectName();
1276  QString widgetType = getWidgetType(type);
1277  result += QString("%1 - %2\n\r").arg(widgetName, -20).arg(widgetType);
1278  }
1279 
1280  return result;
1281  }
1282  if (nc->getArg(1) == "getarea")
1283  {
1284  if (nc->getArgCount() < 3)
1285  return {"ERROR: Missing widget name."};
1286 
1287  QString widgetName = nc->getArg(2);
1288  QStringList path = widgetName.split('/');
1289 
1290  MythScreenStack *stack = GetMythMainWindow()->GetStack("popup stack");
1291  MythScreenType *topScreen = stack->GetTopScreen();
1292 
1293  if (!topScreen)
1294  {
1295  stack = GetMythMainWindow()->GetMainStack();
1296  topScreen = stack->GetTopScreen();
1297  }
1298 
1299  if (!topScreen)
1300  return {"ERROR: no top screen found!"};
1301 
1302  MythUIType *currType = topScreen;
1303 
1304  while (path.count() > 1)
1305  {
1306  QString childName = path.takeFirst();
1307  currType = currType->GetChild(childName);
1308  if (!currType)
1309  return QString("ERROR: Failed to find child '%1'").arg(childName);
1310  }
1311 
1312  MythUIType* type = currType->GetChild(path.first());
1313  if (!type)
1314  return QString("ERROR: widget '%1' not found!").arg(widgetName);
1315 
1316  int x = type->GetFullArea().x();
1317  int y = type->GetFullArea().y();
1318  int w = type->GetFullArea().width();
1319  int h = type->GetFullArea().height();
1320  return QString("The area of '%1' is x:%2, y:%3, w:%4, h:%5")
1321  .arg(widgetName).arg(x).arg(y).arg(w).arg(h);
1322  }
1323  if (nc->getArg(1) == "setarea")
1324  {
1325  if (nc->getArgCount() < 3)
1326  return {"ERROR: Missing widget name."};
1327 
1328  if (nc->getArgCount() < 7)
1329  return {"ERROR: Missing X, Y, Width or Height."};
1330 
1331  QString widgetName = nc->getArg(2);
1332  QStringList path = widgetName.split('/');
1333  QString x = nc->getArg(3);
1334  QString y = nc->getArg(4);
1335  QString w = nc->getArg(5);
1336  QString h = nc->getArg(6);
1337 
1338  MythScreenStack *stack = GetMythMainWindow()->GetStack("popup stack");
1339  MythScreenType *topScreen = stack->GetTopScreen();
1340 
1341  if (!topScreen)
1342  {
1343  stack = GetMythMainWindow()->GetMainStack();
1344  topScreen = stack->GetTopScreen();
1345  }
1346 
1347  MythUIType *currType = topScreen;
1348  if (!topScreen)
1349  return {"ERROR: no top screen found!"};
1350 
1351  while (path.count() > 1)
1352  {
1353  QString childName = path.takeFirst();
1354  currType = currType->GetChild(childName);
1355  if (!currType)
1356  return QString("ERROR: Failed to find child '%1'").arg(childName);
1357  }
1358 
1359  MythUIType* type = currType->GetChild(path.first());
1360  if (!type)
1361  return QString("ERROR: widget '%1' not found!").arg(widgetName);
1362 
1363  type->SetArea(MythRect(x, y, w, h));
1364 
1365  return QString("Changed area of '%1' to x:%2, y:%3, w:%4, h:%5")
1366  .arg(widgetName, x, y, w, h);
1367  }
1368 
1369  return QString("ERROR: See 'help %1' for usage information")
1370  .arg(nc->getArg(0));
1371 }
1372 
1374 {
1375  QString command;
1376  QString helpText;
1377 
1378  if (nc->getArgCount() >= 1)
1379  {
1380  if (is_abbrev("help", nc->getArg(0)))
1381  {
1382  if (nc->getArgCount() >= 2)
1383  command = nc->getArg(1);
1384  else
1385  command.clear();
1386  }
1387  else
1388  {
1389  command = nc->getArg(0);
1390  }
1391  }
1392 
1393  if (is_abbrev("jump", command))
1394  {
1395  QMap<QString, QString>::Iterator it;
1396  helpText +=
1397  "Usage: jump JUMPPOINT\r\n"
1398  "\r\n"
1399  "Where JUMPPOINT is one of the following:\r\n";
1400 
1401  for (it = m_jumpMap.begin(); it != m_jumpMap.end(); ++it)
1402  {
1403  helpText += it.key().leftJustified(20, ' ', true) + " - " +
1404  *it + "\r\n";
1405  }
1406  }
1407  else if (is_abbrev("key", command))
1408  {
1409  helpText +=
1410  "key LETTER - Send the letter key specified\r\n"
1411  "key NUMBER - Send the number key specified\r\n"
1412  "key CODE - Send one of the following key codes\r\n"
1413  "\r\n";
1414 
1415  QMap<QString, int>::Iterator it;
1416  bool first = true;
1417  for (it = m_keyMap.begin(); it != m_keyMap.end(); ++it)
1418  {
1419  if (first)
1420  first = false;
1421  else
1422  helpText += ", ";
1423 
1424  helpText += it.key();
1425  }
1426  helpText += "\r\n";
1427  }
1428  else if (is_abbrev("play", command))
1429  {
1430  helpText +=
1431  "play volume NUMBER% - Change volume to given percentage value\r\n"
1432  "play channel up - Change channel Up\r\n"
1433  "play channel down - Change channel Down\r\n"
1434  "play channel NUMBER - Change to a specific channel number\r\n"
1435  "play chanid NUMBER - Change to a specific channel id (chanid)\r\n"
1436  "play file FILENAME - Play FILENAME (FILENAME may be a file or a myth:// URL)\r\n"
1437  "play program CHANID yyyy-MM-ddThh:mm:ss\r\n"
1438  " - Play program with chanid & starttime\r\n"
1439  "play program CHANID yyyy-MM-ddThh:mm:ss resume\r\n"
1440  " - Resume program with chanid & starttime\r\n"
1441  "play save preview\r\n"
1442  " - Save preview image from current position\r\n"
1443  "play save preview FILENAME\r\n"
1444  " - Save preview image to FILENAME\r\n"
1445  "play save preview FILENAME WxH\r\n"
1446  " - Save preview image of size WxH\r\n"
1447  "play seek beginning - Seek to the beginning of the recording\r\n"
1448  "play seek forward - Skip forward in the video\r\n"
1449  "play seek backward - Skip backwards in the video\r\n"
1450  "play seek HH:MM:SS - Seek to a specific position\r\n"
1451  "play speed pause - Pause playback\r\n"
1452  "play speed normal - Playback at normal speed\r\n"
1453  "play speed 1x - Playback at normal speed\r\n"
1454  "play speed SPEEDx - Playback where SPEED must be a decimal\r\n"
1455  "play speed 1/8x - Playback at 1/8x speed\r\n"
1456  "play speed 1/4x - Playback at 1/4x speed\r\n"
1457  "play speed 1/3x - Playback at 1/3x speed\r\n"
1458  "play speed 1/2x - Playback at 1/2x speed\r\n"
1459  "play stop - Stop playback\r\n"
1460  "play subtitles [#] - Switch on indicated subtitle tracks\r\n"
1461  "play music play - Resume playback (MythMusic)\r\n"
1462  "play music pause - Pause playback (MythMusic)\r\n"
1463  "play music stop - Stop Playback (MythMusic)\r\n"
1464  "play music setvolume N - Set volume to number (MythMusic)\r\n"
1465  "play music getvolume - Get current volume (MythMusic)\r\n"
1466  "play music getmeta - Get metadata for current track (MythMusic)\r\n"
1467  "play music getstatus - Get music player status playing/paused/stopped (MythMusic)\r\n"
1468  "play music file NAME - Play specified file (MythMusic)\r\n"
1469  "play music track N - Switch to specified track (MythMusic)\r\n"
1470  "play music url URL - Play specified URL (MythMusic)\r\n";
1471  }
1472  else if (is_abbrev("query", command))
1473  {
1474  helpText +=
1475  "query location - Query current screen or location\r\n"
1476  "query volume - Query the current playback volume\r\n"
1477  "query recordings - List currently available recordings\r\n"
1478  "query recording CHANID STARTTIME\r\n"
1479  " - List info about the specified program\r\n"
1480  "query liveTV - List current TV schedule\r\n"
1481  "query liveTV CHANID - Query current program for specified channel\r\n"
1482  "query load - List 1/5/15 load averages\r\n"
1483  "query memstats - List free and total, physical and swap memory\r\n"
1484  "query time - Query current time on frontend\r\n"
1485  "query uptime - Query machine uptime\r\n"
1486  "query verbose - Get current VERBOSE mask\r\n"
1487  "query version - Query Frontend version details\r\n"
1488  "query channels - Query available channels\r\n"
1489  "query channels START LIMIT - Query available channels from START and limit results to LIMIT lines\r\n";
1490  }
1491  else if (is_abbrev("set", command))
1492  {
1493  helpText +=
1494  "set verbose debug-mask - "
1495  "Change the VERBOSE mask to 'debug-mask'\r\n"
1496  " (i.e. 'set verbose playback,audio')\r\n"
1497  " use 'set verbose default' to revert\r\n"
1498  " back to the default level of\r\n";
1499  }
1500  else if (is_abbrev("screenshot", command))
1501  {
1502  helpText +=
1503  "screenshot - Takes a screenshot and saves it as screenshot.png\r\n"
1504  "screenshot WxH - Saves the screenshot as a WxH size image\r\n";
1505  }
1506  else if (command == "exit")
1507  {
1508  helpText +=
1509  "exit - Terminates session\r\n\r\n";
1510  }
1511  else if ((is_abbrev("message", command)))
1512  {
1513  helpText +=
1514  "message - Displays a simple text message popup\r\n";
1515  }
1516  else if ((is_abbrev("notification", command)))
1517  {
1518  helpText +=
1519  "notification - Displays a simple text message notification\r\n";
1520  }
1521  else if (is_abbrev("theme", command))
1522  {
1523  helpText +=
1524  "theme getthemeinfo - Display the name and location of the current theme\r\n"
1525  "theme reload - Reload the theme\r\n"
1526  "theme showborders - Toggle showing widget borders\r\n"
1527  "theme shownames ON/OFF - Toggle showing widget names\r\n"
1528  "theme getwidgetnames PATH - Display the name and type of all the child widgets from PATH\r\n"
1529  "theme getarea WIDGETNAME - Get the area of widget WIDGET on the active screen\r\n"
1530  "theme setarea WIDGETNAME X Y W H - Change the area of widget WIDGET to X Y W H on the active screen\r\n";
1531  }
1532 
1533  if (!helpText.isEmpty())
1534  return helpText;
1535 
1536  if (!command.isEmpty())
1537  helpText += QString("Unknown command '%1'\r\n\r\n").arg(command);
1538 
1539  helpText +=
1540  "Valid Commands:\r\n"
1541  "---------------\r\n"
1542  "jump - Jump to a specified location in Myth\r\n"
1543  "key - Send a keypress to the program\r\n"
1544  "play - Playback related commands\r\n"
1545  "query - Queries\r\n"
1546  "set - Changes\r\n"
1547  "screenshot - Capture screenshot\r\n"
1548  "message - Display a simple text message\r\n"
1549  "notification - Display a simple text notification\r\n"
1550  "theme - Theme related commands\r\n"
1551  "exit - Exit Network Control\r\n"
1552  "\r\n"
1553  "Type 'help COMMANDNAME' for help on any specific command.\r\n";
1554 
1555  return helpText;
1556 }
1557 
1559 {
1560  if (nc->getArgCount() < 2)
1561  return QString("ERROR: See 'help %1' for usage information")
1562  .arg(nc->getArg(0));
1563 
1564  QString message = nc->getCommand().remove(0, 7).trimmed();
1565  MythMainWindow *window = GetMythMainWindow();
1566  auto* me = new MythEvent(MythEvent::kMythUserMessage, message);
1567  qApp->postEvent(window, me);
1568  return {"OK"};
1569 }
1570 
1572 {
1573  if (nc->getArgCount() < 2)
1574  return QString("ERROR: See 'help %1' for usage information")
1575  .arg(nc->getArg(0));
1576 
1577  QString message = nc->getCommand().remove(0, 12).trimmed();
1578  MythNotification n(message, tr("Network Control"));
1580  return {"OK"};
1581 }
1582 
1584 {
1585  QCoreApplication::postEvent(
1586  this, new QEvent(kNetworkControlDataReadyEvent));
1587 }
1588 
1590  const QString &reply)
1591 {
1592  if (!m_clients.contains(ncc))
1593  {
1594  // NetworkControl instance is unaware of control client
1595  // assume connection to client has been terminated and bail
1596  return;
1597  }
1598 
1599  static const QRegularExpression crlfRegEx("\r\n$");
1600  static const QRegularExpression crlfcrlfRegEx("\r\n.*\r\n");
1601 
1602  QTcpSocket *client = ncc->getSocket();
1603  QTextStream *clientStream = ncc->getTextStream();
1604 
1605  if (client && clientStream && client->state() == QTcpSocket::ConnectedState)
1606  {
1607  *clientStream << reply;
1608 
1609  if ((!reply.contains(crlfRegEx)) ||
1610  ( reply.contains(crlfcrlfRegEx)))
1611  *clientStream << "\r\n" << m_prompt;
1612 
1613  clientStream->flush();
1614  client->flush();
1615  }
1616 }
1617 
1619 {
1620  if (e->type() == MythEvent::kMythEventMessage)
1621  {
1622  auto *me = dynamic_cast<MythEvent *>(e);
1623  if (me == nullptr)
1624  return;
1625 
1626  const QString& message = me->Message();
1627 
1628  if (message.startsWith("MUSIC_CONTROL"))
1629  {
1630  QStringList tokens = message.simplified().split(" ");
1631  if ((tokens.size() >= 4) &&
1632  (tokens[1] == "ANSWER") &&
1633  (tokens[2] == gCoreContext->GetHostName()))
1634  {
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 (message.startsWith("NETWORK_CONTROL"))
1643  {
1644  QStringList tokens = message.simplified().split(" ");
1645  if ((tokens.size() >= 3) &&
1646  (tokens[1] == "ANSWER"))
1647  {
1648  m_answer = tokens[2];
1649  for (int i = 3; i < tokens.size(); i++)
1650  m_answer += QString(" ") + tokens[i];
1651  m_gotAnswer = true;
1652  }
1653  else if ((tokens.size() >= 4) &&
1654  (tokens[1] == "RESPONSE"))
1655  {
1656 // int clientID = tokens[2].toInt();
1657  m_answer = tokens[3];
1658  for (int i = 4; i < tokens.size(); i++)
1659  m_answer += QString(" ") + tokens[i];
1660  m_gotAnswer = true;
1661  }
1662  }
1663  }
1664  else if (e->type() == kNetworkControlDataReadyEvent)
1665  {
1666  QString reply;
1667 
1668  QMutexLocker locker(&m_clientLock);
1669  QMutexLocker nrLocker(&m_nrLock);
1670 
1671  while (!m_networkControlReplies.isEmpty())
1672  {
1674  m_networkControlReplies.pop_front();
1675 
1676  reply = nc->getCommand();
1677 
1678  NetworkControlClient * ncc = nc->getClient();
1679  if (ncc)
1680  {
1681  sendReplyToClient(ncc, reply);
1682  }
1683  else //send to all clients
1684  {
1685  for (auto * ncc2 : std::as_const(m_clients))
1686  {
1687  if (ncc2)
1688  sendReplyToClient(ncc2, reply);
1689  }
1690  }
1691  delete nc;
1692  }
1693  }
1694  else if (e->type() == NetworkControlCloseEvent::kEventType)
1695  {
1696  auto *ncce = dynamic_cast<NetworkControlCloseEvent*>(e);
1697  if (ncce == nullptr)
1698  return;
1699 
1700  NetworkControlClient *ncc = ncce->getClient();
1701  deleteClient(ncc);
1702  }
1703 }
1704 
1705 QString NetworkControl::listSchedule(const QString& chanID)
1706 {
1707  QString result("");
1708  MSqlQuery query(MSqlQuery::InitCon());
1709  bool appendCRLF = true;
1710  QString queryStr("SELECT chanid, starttime, endtime, title, subtitle "
1711  "FROM program "
1712  "WHERE starttime < :START AND endtime > :END ");
1713 
1714  if (!chanID.isEmpty())
1715  {
1716  queryStr += " AND chanid = :CHANID";
1717  appendCRLF = false;
1718  }
1719 
1720  queryStr += " ORDER BY starttime, endtime, chanid";
1721 
1722  query.prepare(queryStr);
1723  query.bindValue(":START", MythDate::current());
1724  query.bindValue(":END", MythDate::current());
1725  if (!chanID.isEmpty())
1726  {
1727  query.bindValue(":CHANID", chanID);
1728  }
1729 
1730  if (query.exec())
1731  {
1732  while (query.next())
1733  {
1734  QString title = query.value(3).toString();
1735  QString subtitle = query.value(4).toString();
1736 
1737  if (!subtitle.isEmpty())
1738  title += QString(" -\"%1\"").arg(subtitle);
1739  QByteArray atitle = title.toLocal8Bit();
1740 
1741  result +=
1742  QString("%1 %2 %3 %4")
1743  .arg(QString::number(query.value(0).toInt()).rightJustified(5, ' '),
1744  MythDate::as_utc(query.value(1).toDateTime()).toString(Qt::ISODate),
1745  MythDate::as_utc(query.value(2).toDateTime()).toString(Qt::ISODate),
1746  atitle);
1747 
1748  if (appendCRLF)
1749  result += "\r\n";
1750  }
1751  }
1752  else
1753  {
1754  result = "ERROR: Unable to retrieve current schedule list.";
1755  }
1756  return result;
1757 }
1758 
1759 QString NetworkControl::listRecordings(const QString& chanid, const QString& starttime)
1760 {
1761  QString result;
1762  MSqlQuery query(MSqlQuery::InitCon());
1763  QString queryStr;
1764  bool appendCRLF = true;
1765 
1766  queryStr = "SELECT chanid, starttime, title, subtitle "
1767  "FROM recorded WHERE deletepending = 0 ";
1768 
1769  if ((!chanid.isEmpty()) && (!starttime.isEmpty()))
1770  {
1771  queryStr += "AND chanid = " + chanid + " "
1772  "AND starttime = '" + starttime + "' ";
1773  appendCRLF = false;
1774  }
1775 
1776  queryStr += "ORDER BY starttime, title;";
1777 
1778  query.prepare(queryStr);
1779  if (query.exec())
1780  {
1781  QString episode;
1782  QString title;
1783  QString subtitle;
1784  while (query.next())
1785  {
1786  title = query.value(2).toString();
1787  subtitle = query.value(3).toString();
1788 
1789  if (!subtitle.isEmpty())
1790  {
1791  episode = QString("%1 -\"%2\"").arg(title, subtitle);
1792  }
1793  else
1794  {
1795  episode = title;
1796  }
1797 
1798  result +=
1799  QString("%1 %2 %3")
1800  .arg(query.value(0).toString(),
1801  MythDate::as_utc(query.value(1).toDateTime()).toString(Qt::ISODate),
1802  episode);
1803 
1804  if (appendCRLF)
1805  result += "\r\n";
1806  }
1807  }
1808  else
1809  {
1810  result = "ERROR: Unable to retrieve recordings list.";
1811  }
1812 
1813  return result;
1814 }
1815 
1816 QString NetworkControl::listChannels(const uint start, const uint limit)
1817 {
1818  QString result;
1819  MSqlQuery query(MSqlQuery::InitCon());
1820  QString queryStr;
1821  uint sqlStart = start;
1822 
1823  // sql starts at zero, we want to start at 1
1824  if (sqlStart > 0)
1825  sqlStart--;
1826 
1827  queryStr = "select chanid, callsign, name from channel "
1828  "where deleted IS NULL and visible > 0 "
1829  "ORDER BY callsign";
1830 
1831  if (limit > 0) // only if a limit is specified, we limit the results
1832  {
1833  QString limitStr = QString(" LIMIT %1,%2").arg(sqlStart).arg(limit);
1834  queryStr += limitStr;
1835  }
1836 
1837  query.prepare(queryStr);
1838  if (!query.exec())
1839  {
1840  result = "ERROR: Unable to retrieve channel list.";
1841  return result;
1842  }
1843 
1844  uint maxcnt = query.size();
1845  uint cnt = 0;
1846  if (maxcnt == 0) // Feedback we have no usefull information
1847  {
1848  result += QString(R"(0:0 0 "Invalid" "Invalid")");
1849  return result;
1850  }
1851 
1852  while (query.next())
1853  {
1854  // Feedback is as follow:
1855  // <current line count>:<max line count to expect> <channelid> <callsign name> <channel name>\r\n
1856  cnt++;
1857  result += QString("%1:%2 %3 \"%4\" \"%5\"\r\n")
1858  .arg(cnt).arg(maxcnt)
1859  .arg(query.value(0).toString(),
1860  query.value(1).toString(),
1861  query.value(2).toString());
1862  }
1863 
1864  return result;
1865 }
1866 
1868 {
1869  int width = 0;
1870  int height = 0;
1871 
1872  if (nc->getArgCount() == 2)
1873  {
1874  QStringList size = nc->getArg(1).split('x');
1875  if (size.size() == 2)
1876  {
1877  width = size[0].toInt();
1878  height = size[1].toInt();
1879  }
1880  }
1881 
1882  MythMainWindow *window = GetMythMainWindow();
1883  QStringList args;
1884  if (width && height)
1885  {
1886  args << QString::number(width);
1887  args << QString::number(height);
1888  }
1889  auto *me = new MythEvent(MythEvent::kMythEventMessage,
1891  qApp->postEvent(window, me);
1892  return "OK";
1893 }
1894 
1895 QString NetworkCommand::getFrom(int arg)
1896 {
1897  QString c = m_command;
1898  for(int i=0 ; i<arg ; i++) {
1899  QString argstr = c.simplified().split(" ")[0];
1900  c = c.mid(argstr.length()).trimmed();
1901  }
1902  return c;
1903 }
1904 
1905 /* 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:812
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
build_compdb.args
args
Definition: build_compdb.py:11
MythMainWindow::GetMainStack
MythScreenStack * GetMainStack()
Definition: mythmainwindow.cpp:317
NetworkCommand
Definition: networkcontrol.h:45
badChars
static const QRegularExpression badChars
Definition: mytharchivehelper.cpp:373
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:214
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:379
MythEvent::kMythUserMessage
static const Type kMythUserMessage
Definition: mythevent.h:80
mythuiprogressbar.h
MythEvent::kMythEventMessage
static const Type kMythEventMessage
Definition: mythevent.h:79
NetworkControl::m_networkControlCommands
QList< NetworkCommand * > m_networkControlCommands
Definition: networkcontrol.h:148
NetworkControl::saveScreenshot
static QString saveScreenshot(NetworkCommand *nc)
Definition: networkcontrol.cpp:1867
MythUIImage
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:97
NetworkControl::notifyDataAvailable
void notifyDataAvailable(void)
Definition: networkcontrol.cpp:1583
mythuivideo.h
NetworkControl::m_ncLock
QMutex m_ncLock
Definition: networkcontrol.h:149
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:142
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:42
MythUIType::GetChild
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:138
NetworkControlClient::readClient
void readClient()
Definition: networkcontrol.cpp:427
NetworkControl::receiveCommand
void receiveCommand(QString &command)
Definition: networkcontrol.cpp:452
NetworkControl::sendReplyToClient
void sendReplyToClient(NetworkControlClient *ncc, const QString &reply)
Definition: networkcontrol.cpp:1589
MythMainWindow::JumpTo
void JumpTo(const QString &Destination, bool Pop=true)
Definition: mythmainwindow.cpp:1457
NetworkControl::getWidgetType
static QString getWidgetType(MythUIType *type)
Definition: networkcontrol.cpp:1166
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:175
getMemStats
bool getMemStats([[maybe_unused]] int &totalMB, [[maybe_unused]] int &freeMB, [[maybe_unused]] int &totalVM, [[maybe_unused]] int &freeVM)
Definition: mythmiscutil.cpp:109
GetMythSourceVersion
const char * GetMythSourceVersion()
Definition: mythversion.cpp:5
NetworkCommand::getFrom
QString getFrom(int arg)
Definition: networkcontrol.cpp:1895
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
MythScreenStack
Definition: mythscreenstack.h:16
NetworkControlClient::commandReceived
void commandReceived(QString &)
NetworkControl::m_gotAnswer
bool m_gotAnswer
Definition: networkcontrol.h:139
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:618
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:24
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:202
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 *)
NetworkControl::processSet
static QString processSet(NetworkCommand *nc)
Definition: networkcontrol.cpp:1125
MythObservable::addListener
void addListener(QObject *listener)
Add a listener to the observable.
Definition: mythobservable.cpp:38
verboseString
QString verboseString
Definition: logging.cpp:98
NetworkControlCloseEvent::kEventType
static const Type kEventType
Definition: networkcontrol.h:86
NetworkControl::processHelp
QString processHelp(NetworkCommand *nc)
Definition: networkcontrol.cpp:1373
programinfo.h
NetworkControl::m_stopCommandThread
bool m_stopCommandThread
Definition: networkcontrol.h:156
mythlogging.h
NetworkControlCloseEvent
Definition: networkcontrol.h:78
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:1552
NetworkControl::listChannels
static QString listChannels(uint start, uint limit)
Definition: networkcontrol.cpp:1816
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
kNetworkControlDataReadyEvent
static const QEvent::Type kNetworkControlDataReadyEvent
Definition: networkcontrol.cpp:59
MythUIThemeHelper::GetThemeDir
QString GetThemeDir()
Definition: mythuithemehelper.cpp:141
compat.h
NetworkControlClient::m_socket
QTcpSocket * m_socket
Definition: networkcontrol.h:41
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:605
NetworkControl::processQuery
QString processQuery(NetworkCommand *nc)
Definition: networkcontrol.cpp:961
uint
unsigned int uint
Definition: compat.h:81
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:143
getUptime
bool getUptime(std::chrono::seconds &uptime)
Definition: mythmiscutil.cpp:66
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:916
NetworkControl::m_nrLock
QMutex m_nrLock
Definition: networkcontrol.h:153
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:894
NetworkCommand::getArgCount
int getArgCount()
Definition: networkcontrol.h:69
mythuispinbox.h
mythburn.themeName
string themeName
Definition: mythburn.py:218
NetworkControl::listRecordings
static QString listRecordings(const QString &chanid="", const QString &starttime="")
Definition: networkcontrol.cpp:1759
mythuihelper.h
NetworkControlClient::~NetworkControlClient
~NetworkControlClient() override
Definition: networkcontrol.cpp:419
NetworkCommand::m_command
QString m_command
Definition: networkcontrol.h:73
NetworkControl::m_clientLock
QRecursiveMutex m_clientLock
Definition: networkcontrol.h:145
NetworkControl::m_commandThread
MThread * m_commandThread
Definition: networkcontrol.h:155
MythUIText
All purpose text widget, displays a text string.
Definition: mythuitext.h:28
NetworkControl::m_prompt
QString m_prompt
Definition: networkcontrol.h:138
NetworkControlClient::NetworkControlClient
NetworkControlClient(QTcpSocket *s)
Definition: networkcontrol.cpp:407
mythmiscutil.h
NetworkControlClient::getTextStream
QTextStream * getTextStream()
Definition: networkcontrol.h:32
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:1571
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:17
NetworkControl::m_jumpMap
QMap< QString, QString > m_jumpMap
Definition: networkcontrol.h:141
NetworkControl::NetworkControl
NetworkControl()
Definition: networkcontrol.cpp:83
GetNotificationCenter
MythNotificationCenter * GetNotificationCenter(void)
Definition: mythmainwindow.cpp:124
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:487
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
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:1210
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:150
NetworkControl::processMessage
static QString processMessage(NetworkCommand *nc)
Definition: networkcontrol.cpp:1558
LOC
#define LOC
Definition: networkcontrol.cpp:53
NetworkControl::m_answer
QString m_answer
Definition: networkcontrol.h:140
FE_LONG_TO
static constexpr qint64 FE_LONG_TO
Definition: networkcontrol.cpp:57
NetworkControl::listSchedule
static QString listSchedule(const QString &chanID="")
Definition: networkcontrol.cpp:1705
mythuibutton.h
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:322
NetworkCommand::getClient
NetworkControlClient * getClient()
Definition: networkcontrol.h:67
NetworkControl::processJump
QString processJump(NetworkCommand *nc)
Definition: networkcontrol.cpp:466
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:842
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
NetworkControl::m_clients
QList< NetworkControlClient * > m_clients
Definition: networkcontrol.h:146
MythUIWebBrowser
Web browsing widget.
Definition: mythuiwebbrowser.h:132
NetworkControl::processPlay
QString processPlay(NetworkCommand *nc, int clientID)
Definition: networkcontrol.cpp:592
MythMainWindow::ResetScreensaver
static void ResetScreensaver()
Definition: mythmainwindow.cpp:588
mythuicheckbox.h
previewgenerator.h
NetworkCommand::getCommand
QString getCommand()
Definition: networkcontrol.h:66
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:1618
NetworkCommand::getArg
QString getArg(int arg)
Definition: networkcontrol.h:68
NetworkControlClient::getSocket
QTcpSocket * getSocket()
Definition: networkcontrol.h:31
MythCoreContext::dispatch
void dispatch(const MythEvent &event)
Definition: mythcorecontext.cpp:1727
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:152
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
MythScreenStack::GetTopScreen
virtual MythScreenType * GetTopScreen(void) const
Definition: mythscreenstack.cpp:182
MythNotificationCenter::Queue
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
Definition: mythnotificationcenter.cpp:1349