MythTV  master
Engine.cpp
Go to the documentation of this file.
1 /* Engine.cpp
2 
3  Copyright (C) David C. J. Matthews 2004, 2008 dm at prolingua.co.uk
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU General Public License
7  as published by the Free Software Foundation; either version 2
8  of the License, or (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18  Or, point your browser to http://www.gnu.org/copyleft/gpl.html
19 
20 */
21 
22 #include <QStringList>
23 #include <QRegularExpression>
24 #include <QThread>
25 
26 #include "Engine.h"
27 #include "ParseNode.h"
28 #include "ParseBinary.h"
29 #include "ParseText.h"
30 #include "Root.h"
31 #include "Groups.h"
32 #include "ASN1Codes.h"
33 #include "Logging.h"
34 #include "freemheg.h"
35 #include "Visible.h" // For MHInteractible
36 #include "Stream.h"
37 
38 #include <cstdarg>
39 #include <cstdio>
40 #include <cstdlib>
41 
42 // External creation function.
44 {
45  return new MHEngine(context);
46 }
47 
48 MHEngine::MHEngine(MHContext *context): m_context(context)
49 {
50  // Required for BBC Freeview iPlayer
51  auto *pEntry = new MHPSEntry;
52  pEntry->m_FileName.Copy("ram://bbcipstr");
53  pEntry->m_Data.Append(new MHUnion(true)); // Default true
54  // The next value must be true to enable Freeview interaction channel
55  pEntry->m_Data.Append(new MHUnion(true)); // Default false
56  m_persistentStore.Append(pEntry);
57 }
58 
60 {
61  while (!m_applicationStack.isEmpty())
62  {
63  delete m_applicationStack.pop();
64  }
65 
66  while (!m_eventQueue.isEmpty())
67  {
68  delete m_eventQueue.dequeue();
69  }
70 
71  while (!m_externContentTable.isEmpty())
72  {
73  delete m_externContentTable.takeFirst();
74  }
75 }
76 
77 // Check for external content every 2 seconds.
78 static constexpr std::chrono::milliseconds CONTENT_CHECK_TIME { 2s };
79 
80 // This is the main loop of the engine.
81 std::chrono::milliseconds MHEngine::RunAll()
82 {
83  // Request to boot or reboot
84  if (m_fBooting)
85  {
86  // Reset everything
87  while (!m_applicationStack.isEmpty())
88  {
89  delete m_applicationStack.pop();
90  }
91 
92  while (!m_eventQueue.isEmpty())
93  {
94  delete m_eventQueue.dequeue();
95  }
96 
97  while (!m_externContentTable.isEmpty())
98  {
99  delete m_externContentTable.takeFirst();
100  }
101 
102  m_linkTable.clear();
103 
104  // UK MHEG applications boot from ~//a or ~//startup. Actually the initial
105  // object can also be explicitly given in the
106  MHObjectRef startObj;
107  startObj.m_nObjectNo = 0;
108  startObj.m_groupId.Copy(MHOctetString("~//a"));
109 
110  // Launch will block until either it finds the appropriate object and
111  // begins the application or discovers that the file definitely isn't
112  // present in the carousel. It is possible that the object might appear
113  // if one of the containing directories is updated.
114  if (! Launch(startObj))
115  {
116  startObj.m_groupId.Copy(MHOctetString("~//startup"));
117 
118  if (! Launch(startObj))
119  {
120  MHLOG(MHLogNotifications, "NOTE Engine auto-boot failed");
121  return -1ms;
122  }
123  }
124 
125  m_fBooting = false;
126  }
127 
128  std::chrono::milliseconds nNextTime = 0ms;
129 
130  do
131  {
132  // Check to see if we need to close.
133  if (m_context->CheckStop())
134  {
135  return 0ms;
136  }
137 
138  // Run queued actions.
139  RunActions();
140  // Now the action stack is empty process the next asynchronous event.
141  // Processing one event may affect how subsequent events are handled.
142 
143  // Check to see if some files we're waiting for have arrived.
144  // This could result in ContentAvailable events.
146 
147  // Check the timers. This may result in timer events being raised.
148  nNextTime = CurrentScene() ? CurrentScene()->CheckTimers(this) : 0ms;
149 
150  if (CurrentApp())
151  {
152  // The UK MHEG profile allows applications to have timers.
153  std::chrono::milliseconds nAppTime = CurrentApp()->CheckTimers(this);
154 
155  if (nAppTime != 0ms && (nNextTime == 0ms || nAppTime < nNextTime))
156  {
157  nNextTime = nAppTime;
158  }
159  }
160 
161  if (! m_externContentTable.isEmpty())
162  {
163  // If we have an outstanding request for external content we need to set a timer.
164  if (nNextTime == 0ms || nNextTime > CONTENT_CHECK_TIME)
165  {
166  nNextTime = CONTENT_CHECK_TIME;
167  }
168  }
169 
170  if (! m_eventQueue.isEmpty())
171  {
172  MHAsynchEvent *pEvent = m_eventQueue.dequeue();
173  MHLOG(MHLogLinks, QString("Asynchronous event dequeued - %1 from %2")
174  .arg(MHLink::EventTypeToString(pEvent->m_eventType),
177  pEvent->m_eventType, pEvent->m_eventData);
178  delete pEvent;
179  }
180  }
181  while (! m_eventQueue.isEmpty() || ! m_actionStack.isEmpty());
182 
183  // Redraw the display if necessary.
184  if (! m_redrawRegion.isEmpty())
185  {
187  m_redrawRegion = QRegion();
188  }
189 
190  return nNextTime;
191 }
192 
193 
194 // Convert the parse tree for an application or scene into an object node.
195 MHGroup *MHEngine::ParseProgram(QByteArray &text)
196 {
197  if (text.size() == 0)
198  {
199  return nullptr;
200  }
201 
202  // Look at the first byte to decide whether this is text or binary. Binary
203  // files will begin with 0xA0 or 0xA1, text files with white space, comment ('/')
204  // or curly bracket.
205  // This is only there for testing: all downloaded objects will be in ASN1
206  unsigned char ch = text[0];
207  MHParseBase *parser = nullptr;
208  MHParseNode *pTree = nullptr;
209  MHGroup *pRes = nullptr;
210 
211  if (ch >= 128)
212  {
213  parser = new MHParseBinary(text);
214  }
215  else
216  {
217  parser = new MHParseText(text);
218  }
219 
220  try
221  {
222  // Parse the binary or text.
223  pTree = parser->Parse();
224 
225  switch (pTree->GetTagNo()) // The parse node should be a tagged item.
226  {
227  case C_APPLICATION:
228  pRes = new MHApplication;
229  break;
230  case C_SCENE:
231  pRes = new MHScene;
232  break;
233  default:
234  MHParseNode::Failure("Expected Application or Scene"); // throws exception.
235  }
236 
237  pRes->Initialise(pTree, this); // Convert the parse tree.
238  delete(pTree);
239  delete(parser);
240  }
241  catch (...)
242  {
243  delete(parser);
244  delete(pTree);
245  delete(pRes);
246  throw;
247  }
248 
249  return pRes;
250 }
251 
252 // Determine protocol for a file
254 static EProtocol PathProtocol(const QString& csPath)
255 {
256  if (csPath.isEmpty() || csPath.startsWith("DSM:") || csPath.startsWith("~"))
257  return kProtoDSM;
258  if (csPath.startsWith("hybrid:"))
259  return kProtoHybrid;
260  if (csPath.startsWith("http:") || csPath.startsWith("https:"))
261  return kProtoHTTP;
262  if (csPath.startsWith("CI:"))
263  return kProtoCI;
264 
265  int firstColon = csPath.indexOf(':');
266  int firstSlash = csPath.indexOf('/');
267  if (firstColon > 0 && firstSlash > 0 && firstColon < firstSlash)
268  return kProtoUnknown;
269 
270  return kProtoDSM;
271 }
272 
273 // Launch and Spawn
274 bool MHEngine::Launch(const MHObjectRef &target, bool fIsSpawn)
275 {
276  if (m_fInTransition)
277  {
278  MHLOG(MHLogWarning, "WARN Launch during transition - ignoring");
279  return false;
280  }
281 
282  if (target.m_groupId.Size() == 0) return false; // No file name.
283  QString csPath = GetPathName(target.m_groupId); // Get path relative to root.
284  MHLOG(MHLogNotifications, "NOTE Launching " + csPath);
285 
286  // Check that the file exists before we commit to the transition.
287  // This may block if we cannot be sure whether the object is present.
288  QByteArray text;
289  if (! m_context->GetCarouselData(csPath, text))
290  {
291  if (!m_fBooting)
292  EngineEvent(2); // GroupIDRefError
293  MHLOG(MHLogWarning, "WARN Launch not found " + csPath);
294  return false;
295  }
296 
297  auto *pProgram = dynamic_cast<MHApplication*>(ParseProgram(text));
298  if (! pProgram)
299  {
300  MHLOG(MHLogWarning, "Empty application");
301  return false;
302  }
303  if (! pProgram->m_fIsApp)
304  {
305  MHLOG(MHLogWarning, "Expected an application");
306  delete pProgram;
307  return false;
308  }
309  if ((gMHLogoptions & MHLogScenes) && gMHLogStream != nullptr) // Print it so we know what's going on.
310  {
311  pProgram->PrintMe(gMHLogStream, 0);
312  }
313 
314  // Clear the action queue of anything pending.
315  m_actionStack.clear();
316 
317  m_fInTransition = true; // Starting a transition
318 
319  try
320  {
321  if (CurrentApp())
322  {
323  if (fIsSpawn) // Run the CloseDown actions.
324  {
325  AddActions(CurrentApp()->m_closeDown);
326  RunActions();
327  }
328 
329  if (CurrentScene())
330  {
331  CurrentScene()->Destruction(this);
332  }
333 
334  CurrentApp()->Destruction(this);
335 
336  if (!fIsSpawn)
337  {
338  delete m_applicationStack.pop(); // Pop and delete the current app.
339  }
340  }
341 
342  // Save the path we use for this app.
343  pProgram->m_path = csPath; // Record the path
344  int nPos = pProgram->m_path.lastIndexOf('/');
345 
346  if (nPos < 0)
347  {
348  pProgram->m_path = "";
349  }
350  else
351  {
352  pProgram->m_path = pProgram->m_path.left(nPos);
353  }
354 
355  // Have now got the application.
356  m_applicationStack.push(pProgram);
357 
358  // This isn't in the standard as far as I can tell but we have to do this because
359  // we may have events referring to the old application.
360  while (!m_eventQueue.isEmpty())
361  {
362  delete m_eventQueue.dequeue();
363  }
364 
365  // Activate the application. ....
366  CurrentApp()->Activation(this);
367  m_fInTransition = false; // The transition is complete
368  MHLOG(MHLogNotifications, "NOTE Launch completed OK");
369  return true;
370  }
371  catch (...)
372  {
373  m_fInTransition = false; // The transition is complete
374  return false;
375  }
376 }
377 
379 {
380  if (m_fInTransition)
381  {
382  MHLOG(MHLogWarning, "WARN Quit during transition - ignoring");
383  return;
384  }
385 
386  m_fInTransition = true; // Starting a transition
387 
388  MHScene *scene = CurrentScene();
389  if (scene)
390  scene->Destruction(this);
391 
392  MHApplication *pApp = CurrentApp();
393  if (nullptr != pApp)
394  pApp->Destruction(this);
395 
396  // This isn't in the standard as far as I can tell but we have to do this because
397  // we may have events referring to the old application.
398  while (!m_eventQueue.isEmpty())
399  {
400  delete m_eventQueue.dequeue();
401  }
402 
403  delete m_applicationStack.pop();
404 
405  // If the stack is now empty we return to boot mode.
406  if (m_applicationStack.isEmpty())
407  {
408  m_fBooting = true;
409  }
410  else if (nullptr != pApp)
411  {
412  pApp->m_fRestarting = true;
413  pApp->Activation(this); // This will do any OnRestart actions.
414  // Note - this doesn't activate the previously active scene.
415  }
416 
417  m_fInTransition = false; // The transition is complete
418 }
419 
421 {
422  if (m_fInTransition)
423  {
424  // TransitionTo is not allowed in OnStartUp or OnCloseDown actions.
425  MHLOG(MHLogWarning, "WARN TransitionTo during transition - ignoring");
426  return;
427  }
428 
429  if (target.m_groupId.Size() == 0)
430  {
431  return; // No file name.
432  }
433 
434  QString csPath = GetPathName(target.m_groupId);
435 
436  // Check that the file exists before we commit to the transition.
437  // This may block if we cannot be sure whether the object is present.
438  QByteArray text;
439  if (! m_context->GetCarouselData(csPath, text)) {
440  EngineEvent(2); // GroupIDRefError
441  return;
442  }
443 
444  // Parse and run the file.
445  MHGroup *pProgram = ParseProgram(text);
446 
447  if (!pProgram )
448  MHERROR("Empty scene");
449 
450  if (pProgram->m_fIsApp)
451  {
452  delete pProgram;
453  MHERROR("Expected a scene");
454  }
455 
456  // Clear the action queue of anything pending.
457  m_actionStack.clear();
458 
459  // At this point we have managed to load the scene.
460  // Deactivate any non-shared ingredients in the application.
461  MHApplication *pApp = CurrentApp();
462  if (nullptr == pApp)
463  {
464  delete pProgram;
465  MHERROR("Application disappeared");
466  }
467 
468  for (int i = pApp->m_items.Size(); i > 0; i--)
469  {
470  MHIngredient *pItem = pApp->m_items.GetAt(i - 1);
471 
472  if (! pItem->IsShared())
473  {
474  pItem->Deactivation(this); // This does not remove them from the display stack.
475  }
476  }
477 
478  m_fInTransition = true; // TransitionTo etc are not allowed.
479 
480  if (pApp->m_pCurrentScene)
481  {
482  pApp->m_pCurrentScene->Deactivation(this); // This may involve a call to RunActions
483  pApp->m_pCurrentScene->Destruction(this);
484  }
485 
486  // Everything that belongs to the previous scene should have been removed from the display stack.
487 
488  // At this point we may have added actions to the queue as a result of synchronous
489  // events during the deactivation.
490 
491  // Remove any events from the asynch event queue unless they derive from
492  // the application itself or a shared ingredient.
493 
494  // This was causing crashes with leftover events being invalid.
495  // Changed to clear all events at this point.
496 
497  while (!m_eventQueue.isEmpty())
498  delete m_eventQueue.dequeue();
499 
500  // Can now actually delete the old scene.
501  if (pApp->m_pCurrentScene)
502  {
503  delete(pApp->m_pCurrentScene);
504  pApp->m_pCurrentScene = nullptr;
505  }
506 
507  m_interacting = nullptr;
508 
509  // Switch to the new scene.
510  pApp->m_pCurrentScene = dynamic_cast< MHScene* >(pProgram);
511  MHScene *pScene = CurrentScene();
512  if (nullptr != pScene)
513  {
514  SetInputRegister(pScene->m_nEventReg);
515  m_redrawRegion = QRegion(0, 0, pScene->m_nSceneCoordX, pScene->m_nSceneCoordY); // Redraw the whole screen
516  }
517 
518  if ((gMHLogoptions & MHLogScenes) && gMHLogStream != nullptr) // Print it so we know what's going on.
519  {
520  pProgram->PrintMe(gMHLogStream, 0);
521  }
522 
523  pProgram->Preparation(this);
524  pProgram->Activation(this);
525  m_fInTransition = false; // The transition is complete
526 }
527 
529 {
530  m_context->SetInputRegister(nReg); // Enable the appropriate buttons
531 }
532 
533 // Create a canonical path name. The rules are given in the UK MHEG document.
535 {
536  if (str.Size() == 0)
537  return {};
538 
539  QString csPath = QString::fromUtf8((const char *)str.Bytes(), str.Size());
540  switch (PathProtocol(csPath))
541  {
542  default:
543  case kProtoUnknown:
544  case kProtoHybrid:
545  case kProtoHTTP:
546  case kProtoCI:
547  return csPath;
548  case kProtoDSM:
549  break;
550  }
551 
552  if (csPath.startsWith("DSM:"))
553  csPath = csPath.mid(4); // Remove DSM:
554  else if (csPath.startsWith("~"))
555  csPath = csPath.mid(1); // Remove ~
556  if (!csPath.startsWith("//"))
557  {
558  // Add the current application's path name
559  MHApplication *pApp = CurrentApp();
560  if (nullptr != pApp)
561  {
562  csPath = pApp->m_path + csPath;
563  }
564  }
565 
566  // Remove any occurrences of x/../
567  int nPos = -1;
568 
569  while ((nPos = csPath.indexOf("/../")) >= 0)
570  {
571  int nEnd = nPos + 4;
572 
573  while (nPos >= 1 && csPath[nPos-1] != '/')
574  {
575  nPos--;
576  }
577 
578  csPath = csPath.left(nPos) + csPath.mid(nEnd);
579  }
580 
581  return csPath;
582 }
583 
584 // Look up an object. In most cases we just want to fail if the object isn't found.
585 MHRoot *MHEngine::FindObject(const MHObjectRef &oRef, bool failOnNotFound)
586 {
587  // It should match either the application or the scene.
588  MHGroup *pSearch = nullptr;
589  MHGroup *pScene = CurrentScene();
590  MHGroup *pApp = CurrentApp();
591 
592  if (pScene && GetPathName(pScene->m_ObjectReference.m_groupId) == GetPathName(oRef.m_groupId))
593  {
594  pSearch = pScene;
595  }
596  else if (pApp && GetPathName(pApp->m_ObjectReference.m_groupId) == GetPathName(oRef.m_groupId))
597  {
598  pSearch = pApp;
599  }
600 
601  if (pSearch)
602  {
603  MHRoot *pItem = pSearch->FindByObjectNo(oRef.m_nObjectNo);
604 
605  if (pItem)
606  {
607  return pItem;
608  }
609  }
610 
611  if (failOnNotFound)
612  {
613  // I've seen at least one case where MHEG code has quite deliberately referred to
614  // an object that may or may not exist at a particular time.
615  // Another case was a call to CallActionSlot with an object reference variable
616  // that had been initialised to zero.
617  MHLOG(MHLogWarning, QString("WARN Reference %1 not found").arg(oRef.m_nObjectNo));
618  throw "FindObject failed";
619  }
620 
621  return nullptr; // If we don't generate an error.
622 }
623 
624 // Run queued actions.
626 {
627  while (! m_actionStack.isEmpty())
628  {
629  // Remove the first action.
630  MHElemAction *pAction = m_actionStack.pop();
631 
632  // Run it. If it fails and throws an exception catch it and continue with the next.
633  try
634  {
635  if ((gMHLogoptions & MHLogActions) && gMHLogStream != nullptr) // Debugging
636  {
637  fprintf(gMHLogStream, "[freemheg] Action - ");
638  pAction->PrintMe(gMHLogStream, 0);
639  fflush(gMHLogStream);
640  }
641 
642  pAction->Perform(this);
643  }
644  catch (...)
645  {
646  }
647  }
648 }
649 
650 // Called when an event is triggered. Either queues the event or finds a link that matches.
651 void MHEngine::EventTriggered(MHRoot *pSource, enum EventType ev, const MHUnion &evData)
652 {
653  MHLOG(MHLogLinks, QString("Event - %1 from %2")
655 
656  switch (ev)
657  {
659  case EventHeadItems:
660  case EventHighlightOff:
661  case EventHighlightOn:
662  case EventIsAvailable:
663  case EventIsDeleted:
664  case EventIsDeselected:
665  case EventIsRunning:
666  case EventIsSelected:
667  case EventIsStopped:
668  case EventItemDeselected:
669  case EventItemSelected:
671  case EventTailItems:
672  case EventTestEvent:
673  case EventTokenMovedFrom:
674  case EventTokenMovedTo:
675  // Synchronous events. Fire any links that are waiting.
676  // The UK MHEG describes this as the preferred interpretation. We are checking the link
677  // at the time we generate the event rather than queuing the synchronous events until
678  // this elementary action is complete. That matters if we are processing an elementary action
679  // which will activate or deactivate links.
680  CheckLinks(pSource->m_ObjectReference, ev, evData);
681  break;
682  case EventAnchorFired:
683  case EventAsyncStopped:
685  case EventCounterTrigger:
686  case EventCursorEnter:
687  case EventCursorLeave:
688  case EventEngineEvent:
689  case EventEntryFieldFull:
691  case EventStreamEvent:
692  case EventStreamPlaying:
693  case EventStreamStopped:
694  case EventTimerFired:
695  case EventUserInput:
696  case EventFocusMoved: // UK MHEG. Generated by HyperText class
697  case EventSliderValueChanged: // UK MHEG. Generated by Slider class
698  default:
699  {
700  // Asynchronous events. Add to the event queue.
701  auto *pEvent = new MHAsynchEvent;
702  pEvent->m_pEventSource = pSource;
703  pEvent->m_eventType = ev;
704  pEvent->m_eventData = evData;
705  m_eventQueue.enqueue(pEvent);
706  }
707  break;
708  }
709 }
710 
711 
712 // TO CHECK: If two actions both depend on the same event is the order in which the links are
713 // searched defined? This processes items in the order in which they were activated rather
714 // than their static position in the group.
715 
716 // Check all the links in the application and scene and fire any that match this event.
717 void MHEngine::CheckLinks(const MHObjectRef &sourceRef, enum EventType ev, const MHUnion &un)
718 {
719  for (auto *link : std::as_const(m_linkTable))
720  link->MatchEvent(sourceRef, ev, un, this);
721 }
722 
723 // Add and remove links to and from the active link table.
725 {
726  m_linkTable.append(pLink);
727 }
728 
730 {
731  m_linkTable.removeAll(pLink);
732 }
733 
734 // Called when a link fires to add the actions to the action stack.
736 {
737  // Put them on the stack in reverse order so that we will pop the first.
738  for (int i = actions.Size(); i > 0; i--)
739  {
740  m_actionStack.push(actions.GetAt(i - 1));
741  }
742 }
743 
744 // Add a visible to the display stack if it isn't already there.
746 {
747  MHApplication *pApp = CurrentApp();
748  if (nullptr == pApp)
749  return;
750  if (pApp->FindOnStack(pVis) != -1)
751  {
752  return; // Return if it's already there.
753  }
754 
755  pApp->m_displayStack.Append(pVis);
756  Redraw(pVis->GetVisibleArea()); // Request a redraw
757 }
758 
759 // Remove a visible from the display stack if it is there.
761 {
762  MHApplication *pApp = CurrentApp();
763  if (nullptr == pApp)
764  return;
765  int nPos = pApp->FindOnStack(pVis);
766 
767  if (nPos == -1)
768  {
769  return;
770  }
771 
772  pApp->m_displayStack.RemoveAt(nPos);
773  Redraw(pVis->GetVisibleArea()); // Request a redraw
774 }
775 
776 // Functions to alter the Z-order.
778 {
779  MHApplication *pApp = CurrentApp();
780  if (nullptr == pApp)
781  return;
782  int nPos = pApp->FindOnStack(p);
783 
784  if (nPos == -1)
785  {
786  return; // If it's not there do nothing
787  }
788 
789  auto *pVis = (MHVisible *)p; // Can now safely cast it.
790  pApp->m_displayStack.RemoveAt(nPos); // Remove it from its present posn
791  pApp->m_displayStack.Append(pVis); // Push it on the top.
792  Redraw(pVis->GetVisibleArea()); // Request a redraw
793 }
794 
796 {
797  MHApplication *pApp = CurrentApp();
798  if (nullptr == pApp)
799  return;
800  int nPos = pApp->FindOnStack(p);
801 
802  if (nPos == -1)
803  {
804  return; // If it's not there do nothing
805  }
806 
807  auto *pVis = (MHVisible *)p; // Can now safely cast it.
808  pApp->m_displayStack.RemoveAt(nPos); // Remove it from its present posn
809  pApp->m_displayStack.InsertAt(pVis, 0); // Put it on the bottom.
810  Redraw(pVis->GetVisibleArea()); // Request a redraw
811 }
812 
813 void MHEngine::PutBefore(const MHRoot *p, const MHRoot *pRef)
814 {
815  MHApplication *pApp = CurrentApp();
816  if (nullptr == pApp)
817  return;
818  int nPos = pApp->FindOnStack(p);
819 
820  if (nPos == -1)
821  {
822  return; // If it's not there do nothing
823  }
824 
825  auto *pVis = (MHVisible *)p; // Can now safely cast it.
826  int nRef = pApp->FindOnStack(pRef);
827 
828  if (nRef == -1)
829  {
830  return; // If the reference visible isn't there do nothing.
831  }
832 
833  pApp->m_displayStack.RemoveAt(nPos);
834 
835  if (nRef >= nPos)
836  {
837  nRef--; // The position of the reference may have shifted
838  }
839 
840  pApp->m_displayStack.InsertAt(pVis, nRef + 1);
841  // Redraw the area occupied by the moved item. We might be able to reduce
842  // the area to be redrawn by looking at the way it is affected by other items
843  // in the stack. We could also see whether it's currently active.
844  Redraw(pVis->GetVisibleArea()); // Request a redraw
845 }
846 
847 void MHEngine::PutBehind(const MHRoot *p, const MHRoot *pRef)
848 {
849  MHApplication *pApp = CurrentApp();
850  if (nullptr == pApp)
851  return;
852  int nPos = pApp->FindOnStack(p);
853 
854  if (nPos == -1)
855  {
856  return; // If it's not there do nothing
857  }
858 
859  int nRef = pApp->FindOnStack(pRef);
860 
861  if (nRef == -1)
862  {
863  return; // If the reference visible isn't there do nothing.
864  }
865 
866  auto *pVis = (MHVisible *)p; // Can now safely cast it.
867  pApp->m_displayStack.RemoveAt(nPos);
868 
869  if (nRef >= nPos)
870  {
871  nRef--; // The position of the reference may have shifted
872  }
873 
874  pApp->m_displayStack.InsertAt(pVis, nRef); // Shift the reference and anything above up.
875  Redraw(pVis->GetVisibleArea()); // Request a redraw
876 }
877 
878 // Draw a region of the screen. This attempts to minimise the drawing by eliminating items
879 // that are completely obscured by items above them. We have to take into account the
880 // transparency of items since items higher up the stack may be semi-transparent.
881 void MHEngine::DrawRegion(const QRegion& toDraw, int nStackPos)
882 {
883  if (toDraw.isEmpty())
884  {
885  return; // Nothing left to draw.
886  }
887 
888  while (nStackPos >= 0)
889  {
890  MHVisible *pItem = CurrentApp()->m_displayStack.GetAt(nStackPos);
891  // Work out how much of the area we want to draw is included in this visible.
892  // The visible area will be empty if the item is transparent or not active.
893  QRegion drawArea = pItem->GetVisibleArea() & toDraw;
894 
895  if (! drawArea.isEmpty()) // It contributes something.
896  {
897  // Remove the opaque area of this item from the region we have left.
898  // If this item is (semi-)transparent this will not remove anything.
899  QRegion newDraw = toDraw - pItem->GetOpaqueArea();
900  DrawRegion(newDraw, nStackPos - 1); // Do the items further down if any.
901  // Now we've drawn anything below this we can draw this item on top.
902  pItem->Display(this);
903  return;
904  }
905 
906  nStackPos--;
907  }
908 
909  // We've drawn all the visibles and there's still some undrawn area.
910  // Fill it with black.
911  m_context->DrawBackground(toDraw);
912 }
913 
914 // Redraw an area of the display. This will be called via the context from Redraw.
915 void MHEngine::DrawDisplay(const QRegion& toDraw)
916 {
917  if (m_fBooting)
918  {
919  return;
920  }
921 
922  MHApplication *pApp = CurrentApp();
923  int nTopStack = (pApp == nullptr) ? -1 : pApp->m_displayStack.Size() - 1;
924  DrawRegion(toDraw, nTopStack);
925 }
926 
927 // An area of the screen needs to be redrawn. We simply remember this and redraw it
928 // in one go when the timer expires.
929 void MHEngine::Redraw(const QRegion& region)
930 {
931  m_redrawRegion += region;
932 }
933 
934 // Called to decrement the lock count.
936 {
937  MHApplication *pApp = CurrentApp();
938  if (nullptr == pApp)
939  return;
940  if (pApp->m_nLockCount > 0)
941  {
942  pApp->m_nLockCount--;
943  }
944 }
945 
946 
947 // Called from the windowing application, this generates a user event as the result of a button push.
949 {
950  MHScene *pScene = CurrentScene();
951 
952  if (! pScene)
953  {
954  return;
955  }
956 
957  // Various keys generate engine events as well as user events.
958  // These are generated before the user events and even if there
959  // is an interactible.
960  switch (nCode)
961  {
962  case 104:
963  case 105: // Text key
964  EngineEvent(4);
965  break;
966  case 16: // Text Exit/Cancel key
967  case 100: // Red
968  case 101: // Green
969  case 102: // Yellow
970  case 103: // Blue
971  case 300: // EPG
972  EngineEvent(nCode);
973  break;
974  }
975 
976  // If we are interacting with an interactible send the key
977  // there otherwise generate a user event.
978  if (m_interacting)
979  {
980  m_interacting->KeyEvent(this, nCode);
981  }
982  else
983  {
984  EventTriggered(pScene, EventUserInput, nCode);
985  }
986 }
987 
988 void MHEngine::EngineEvent(int nCode)
989 {
990  if (CurrentApp())
992  else if (!m_fBooting)
993  MHLOG(MHLogWarning, QString("WARN EngineEvent %1 but no app").arg(nCode));
994 }
995 
996 void MHEngine::StreamStarted(MHStream *stream, bool bStarted)
997 {
999 }
1000 
1001 // Called by an ingredient wanting external content.
1003 {
1004  // It seems that some MHEG applications contain active ingredients with empty contents
1005  // This isn't correct but we simply ignore that.
1006  if (! pRequester->m_contentRef.IsSet())
1007  {
1008  return;
1009  }
1010 
1011  // Remove any existing content requests for this ingredient.
1012  CancelExternalContentRequest(pRequester);
1013 
1014  QString csPath = GetPathName(pRequester->m_contentRef.m_contentRef);
1015 
1016  if (csPath.isEmpty())
1017  {
1018  MHLOG(MHLogWarning, "RequestExternalContent empty path");
1019  return;
1020  }
1021 
1022  if (m_context->CheckCarouselObject(csPath))
1023  {
1024  // Available now - pass it to the ingredient.
1025  QByteArray text;
1026  if (m_context->GetCarouselData(csPath, text))
1027  {
1028  // If the content is not recognized catch the exception and continue
1029  try
1030  {
1031  pRequester->ContentArrived(
1032  reinterpret_cast< const unsigned char * >(text.constData()),
1033  text.size(), this);
1034  }
1035  catch (...)
1036  {}
1037  }
1038  else
1039  {
1040  MHLOG(MHLogWarning, QString("WARN No file content %1 <= %2")
1041  .arg(pRequester->m_ObjectReference.Printable(), csPath));
1042  if (kProtoHTTP == PathProtocol(csPath))
1043  EngineEvent(203); // 203=RemoteNetworkError if 404 reply
1044  EngineEvent(3); // ContentRefError
1045  }
1046  }
1047  else
1048  {
1049  // Need to record this and check later.
1050  MHLOG(MHLogNotifications, QString("Waiting for %1 <= %2")
1051  .arg(pRequester->m_ObjectReference.Printable(), csPath.left(128)) );
1052  auto *pContent = new MHExternContent;
1053  pContent->m_FileName = csPath;
1054  pContent->m_pRequester = pRequester;
1055  pContent->m_time.start();
1056  m_externContentTable.append(pContent);
1057  }
1058 }
1059 
1060 // Remove any pending requests from the queue.
1062 {
1063  QList<MHExternContent *>::iterator it = m_externContentTable.begin();
1064 
1065  while (it != m_externContentTable.end())
1066  {
1067  MHExternContent *pContent = *it;
1068 
1069  if (pContent->m_pRequester == pRequester)
1070  {
1071  MHLOG(MHLogNotifications, QString("Cancelled wait for %1")
1072  .arg(pRequester->m_ObjectReference.Printable()) );
1073  it = m_externContentTable.erase(it);
1074  delete pContent;
1075  return;
1076  }
1077  ++it;
1078  }
1079 }
1080 
1081 // See if we can satisfy any of the outstanding requests.
1083 {
1084  QList<MHExternContent*>::iterator it = m_externContentTable.begin();
1085  while (it != m_externContentTable.end())
1086  {
1087  MHExternContent *pContent = *it;
1088  if (m_context->CheckCarouselObject(pContent->m_FileName))
1089  {
1090  // Remove from the list.
1091  it = m_externContentTable.erase(it);
1092 
1093  QByteArray text;
1094  if (m_context->GetCarouselData(pContent->m_FileName, text))
1095  {
1096  MHLOG(MHLogNotifications, QString("Received %1 len %2")
1097  .arg(pContent->m_pRequester->m_ObjectReference.Printable())
1098  .arg(text.size()) );
1099  // If the content is not recognized catch the exception and continue
1100  try
1101  {
1102  pContent->m_pRequester->ContentArrived(
1103  reinterpret_cast< const unsigned char * >(text.constData()),
1104  text.size(), this);
1105  }
1106  catch (...)
1107  {}
1108  }
1109  else
1110  {
1111  MHLOG(MHLogWarning, QString("WARN No file content %1 <= %2")
1112  .arg(pContent->m_pRequester->m_ObjectReference.Printable(),
1113  pContent->m_FileName));
1114  if (kProtoHTTP == PathProtocol(pContent->m_FileName))
1115  EngineEvent(203); // 203=RemoteNetworkError if 404 reply
1116  EngineEvent(3); // ContentRefError
1117  }
1118 
1119  delete pContent;
1120  }
1121  else if (pContent->m_time.elapsed() > 60000) // TODO Get this from carousel
1122  {
1123  // Remove from the list.
1124  it = m_externContentTable.erase(it);
1125 
1126  MHLOG(MHLogWarning, QString("WARN File timed out %1 <= %2")
1127  .arg(pContent->m_pRequester->m_ObjectReference.Printable(),
1128  pContent->m_FileName));
1129 
1130  if (kProtoHTTP == PathProtocol(pContent->m_FileName))
1131  EngineEvent(203); // 203=RemoteNetworkError if 404 reply
1132  EngineEvent(3); // ContentRefError
1133 
1134  delete pContent;
1135  }
1136  else
1137  {
1138  ++it;
1139  }
1140  }
1141 }
1142 
1143 bool MHEngine::LoadStorePersistent(bool fIsLoad, const MHOctetString &fileName, const MHSequence<MHObjectRef *> &variables)
1144 {
1145  QString const csFile = QString::fromUtf8(
1146  (const char *)fileName.Bytes(), fileName.Size() );
1147 
1148  // See if there is an entry there already.
1149  MHPSEntry *pEntry = nullptr;
1150  int i = 0;
1151 
1152  for (i = 0; i < m_persistentStore.Size(); i++)
1153  {
1154  pEntry = m_persistentStore.GetAt(i);
1155 
1156  if (pEntry->m_FileName.Equal(fileName))
1157  {
1158  break;
1159  }
1160  }
1161 
1162  if (i == m_persistentStore.Size()) // Not there.
1163  {
1164  // If we're loading then we've failed.
1165  if (fIsLoad)
1166  {
1167  MHLOG(MHLogNotifications, QString(
1168  "Load Persistent(%1) #%2: no such file")
1169  .arg(csFile).arg(variables.Size()) );
1170  return false;
1171  }
1172 
1173  // If we're storing we make a new entry.
1174  pEntry = new MHPSEntry;
1175  pEntry->m_FileName.Copy(fileName);
1176  m_persistentStore.Append(pEntry);
1177  }
1178 
1179  // This should never happen
1180  if (pEntry == nullptr)
1181  return false;
1182 
1183  if (fIsLoad) // Copy the data into the variables.
1184  {
1185  // Check that we have sufficient data before we continue?
1186  if (pEntry->m_Data.Size() < variables.Size())
1187  {
1188  MHLOG(MHLogWarning, QString(
1189  "Load Persistent(%1): size mismatch").arg(csFile));
1190  return false;
1191  }
1192 
1193  for (i = 0; i < variables.Size(); i++)
1194  {
1195  MHUnion *pValue = pEntry->m_Data.GetAt(i);
1196  MHLOG(MHLogNotifications, QString("Load Persistent(%1) #%2=%3")
1197  .arg(csFile).arg(i).arg(pValue->Printable()) );
1198  FindObject(*(variables.GetAt(i)))->SetVariableValue(*pValue);
1199  }
1200  }
1201 
1202  else // Get the data from the variables into the store.
1203  {
1204  // Remove any existing data.
1205  while (pEntry->m_Data.Size() != 0)
1206  {
1207  pEntry->m_Data.RemoveAt(0);
1208  }
1209 
1210  // Set the store to the values.
1211  for (i = 0; i < variables.Size(); i++)
1212  {
1213  auto *pValue = new MHUnion;
1214  pEntry->m_Data.Append(pValue);
1215  FindObject(*(variables.GetAt(i)))->GetVariableValue(*pValue, this);
1216  MHLOG(MHLogNotifications, QString("Store Persistent(%1) %2=>#%3")
1217  .arg(csFile, pValue->Printable(), QString::number(i)) );
1218  }
1219  }
1220 
1221  return true;
1222 }
1223 
1224 // Find out what we support.
1226 {
1227  QString csFeat = QString::fromUtf8((const char *)feature.Bytes(), feature.Size());
1228  static const QRegularExpression kSeparatorRE { R"([\(\,\)])" };
1229  QStringList strings = csFeat.split(kSeparatorRE);
1230 
1231  MHLOG(MHLogNotifications, "NOTE GetEngineSupport " + csFeat);
1232 
1233  if (strings[0] == "ApplicationStacking" || strings[0] == "ASt")
1234  {
1235  return true;
1236  }
1237 
1238  // We're required to support cloning for Text, Bitmap and Rectangle.
1239  if (strings[0] == "Cloning" || strings[0] == "Clo")
1240  {
1241  return true;
1242  }
1243 
1244  if (strings[0] == "SceneCoordinateSystem" || strings[0] == "SCS")
1245  {
1246  return strings.count() >= 3 && strings[1] == "720" && strings[2] == "576";
1247 
1248  // I've also seen SceneCoordinateSystem(1,1)
1249  }
1250 
1251  if (strings[0] == "MultipleAudioStreams" || strings[0] == "MAS")
1252  {
1253  return strings.count() >= 2 && (strings[1] == "0" || strings[1] == "1");
1254  }
1255 
1256  if (strings[0] == "MultipleVideoStreams" || strings[0] == "MVS")
1257  {
1258  return strings.count() >= 2 && (strings[1] == "0" || strings[1] == "1");
1259  }
1260 
1261  // We're supposed to return true for all values of N
1262  if (strings[0] == "OverlappingVisibles" || strings[0] == "OvV")
1263  {
1264  return true;
1265  }
1266 
1267  if (strings[0] == "SceneAspectRatio" || strings[0] == "SAR")
1268  {
1269  if (strings.count() < 3)
1270  {
1271  return false;
1272  }
1273  return (strings[1] == "4" && strings[2] == "3") || (strings[1] == "16" && strings[2] == "9");
1274  }
1275 
1276  // We're supposed to support these at least. May also support(10,1440,1152)
1277  if (strings[0] == "VideoScaling" || strings[0] == "VSc")
1278  {
1279  if (strings.count() < 4 || strings[1] != "10")
1280  {
1281  return false;
1282  }
1283  return (strings[2] == "720" && strings[3] == "576") || (strings[2] == "360" && strings[3] == "288");
1284  }
1285 
1286  if (strings[0] == "BitmapScaling" || strings[0] == "BSc")
1287  {
1288  if (strings.count() < 4 || strings[1] != "2")
1289  {
1290  return false;
1291  }
1292  return (strings[2] == "720" && strings[3] == "576") || (strings[2] == "360" && strings[3] == "288");
1293  }
1294 
1295  // I think we only support the video fully on screen
1296  if (strings[0] == "VideoDecodeOffset" || strings[0] == "VDO")
1297  {
1298  return strings.count() >= 3 && strings[1] == "10" && strings[1] == "0";
1299  }
1300 
1301  // We support bitmaps that are partially off screen (don't we?)
1302  if (strings[0] == "BitmapDecodeOffset" || strings[0] == "BDO")
1303  {
1304  if (strings.count() >= 3 && strings[1] == "2" && (strings[2] == "0" || strings[2] == "1"))
1305  {
1306  return true;
1307  }
1308  if (strings.count() >= 2 && (strings[1] == "4" || strings[1] == "6"))
1309  {
1310  return true;
1311  }
1312  return false;
1313  }
1314 
1315  if (strings[0] == "UKEngineProfile" || strings[0] == "UniversalEngineProfile" || strings[0] == "UEP")
1316  {
1317  if (strings.count() < 2)
1318  {
1319  return false;
1320  }
1321 
1322  if (strings[1] == MHEGEngineProviderIdString)
1323  {
1324  return true;
1325  }
1326 
1327  if (strings[1] == m_context->GetReceiverId())
1328  {
1329  return true;
1330  }
1331 
1332  if (strings[1] == m_context->GetDSMCCId())
1333  {
1334  return true;
1335  }
1336 
1337  // The UK profile 1.06 seems a bit confused on this point. It is not clear whether
1338  // we are supposed to return true for UKEngineProfile(2) or not.
1339  if (strings[1] == "2")
1340  return true;
1341  if (strings[1] == "1")
1342  return true;
1343  // 'The Space' on Freeview checks this...
1344  if (strings[1] == "PANT11001")
1345  return true;
1346  // Irish DTT expects "1285". From ETSI ES 202 184: UEP(1285) means the receiver has been verified as fully conformant.
1347  if (strings[1] == "1285")
1348  return true;
1349 
1350  return false;
1351  }
1352 
1353  // InteractionChannelExtension.
1354  if (strings[0] == "ICProfile" || strings[0] == "ICP") {
1355  if (strings.count() < 2) return false;
1356  if (strings[1] == "0")
1357  return true; // InteractionChannelExtension.
1358  if (strings[1] == "1")
1359  return true; // ICStreamingExtension. This is a deliberate lie
1360  return false;
1361  }
1362 
1363  if (strings[0] == "HDExtension" || strings[0] == "HDE") {
1364  if (strings.count() < 2) return false;
1365  if (strings[1] == "0")
1366  return false; // HDVideoExtension.
1367  if (strings[1] == "1")
1368  return false; // HDGraphicsPlaneExtension
1369  return false;
1370  }
1371  if (strings[0] == "HDGraphicsPlaneExtension" || strings[0] == "HDG") {
1372  if (strings.count() < 2) return false;
1373  // true if HDGraphicsPlaneExtension
1374  return strings[1] == "0";
1375  }
1376 
1377  // Otherwise return false.
1378  return false;
1379 }
1380 
1381 // Get the various defaults. These are extracted from the current app or the (UK) MHEG defaults.
1383 {
1384  MHApplication *pApp = CurrentApp();
1385 
1386  if (pApp && pApp->m_nCharSet > 0)
1387  {
1388  return pApp->m_nCharSet;
1389  }
1390  return 10; // UK MHEG default.
1391 }
1392 
1394 {
1395  MHApplication *pApp = CurrentApp();
1396 
1397  if (pApp && pApp->m_bgColour.IsSet())
1398  {
1399  colour.Copy(pApp->m_bgColour);
1400  }
1401  else
1402  {
1403  colour.SetFromString("\000\000\000\377", 4); // '=00=00=00=FF' Default - transparent
1404  }
1405 }
1406 
1408 {
1409  MHApplication *pApp = CurrentApp();
1410 
1411  if (pApp && pApp->m_textColour.IsSet())
1412  {
1413  colour.Copy(pApp->m_textColour);
1414  }
1415  else
1416  {
1417  colour.SetFromString("\377\377\377\000", 4); // '=FF=FF=FF=00' UK MHEG Default - white
1418  }
1419 }
1420 
1422 {
1423  MHApplication *pApp = CurrentApp();
1424 
1425  if (pApp && pApp->m_buttonRefColour.IsSet())
1426  {
1427  colour.Copy(pApp->m_buttonRefColour);
1428  }
1429  else
1430  {
1431  colour.SetFromString("\377\377\377\000", 4); // '=FF=FF=FF=00' ??? Not specified in UK MHEG
1432  }
1433 }
1434 
1436 {
1437  MHApplication *pApp = CurrentApp();
1438 
1439  if (pApp && pApp->m_highlightRefColour.IsSet())
1440  {
1441  colour.Copy(pApp->m_highlightRefColour);
1442  }
1443  else
1444  {
1445  colour.SetFromString("\377\377\377\000", 4); // '=FF=FF=FF=00' UK MHEG Default - white
1446  }
1447 }
1448 
1450 {
1451  MHApplication *pApp = CurrentApp();
1452 
1453  if (pApp && pApp->m_sliderRefColour.IsSet())
1454  {
1455  colour.Copy(pApp->m_sliderRefColour);
1456  }
1457  else
1458  {
1459  colour.SetFromString("\377\377\377\000", 4); // '=FF=FF=FF=00' UK MHEG Default - white
1460  }
1461 }
1462 
1464 {
1465  MHApplication *pApp = CurrentApp();
1466 
1467  if (pApp && pApp->m_nTextCHook > 0)
1468  {
1469  return pApp->m_nTextCHook;
1470  }
1471  return 10; // UK MHEG default.
1472 }
1473 
1475 {
1476  MHApplication *pApp = CurrentApp();
1477 
1478  if (pApp && pApp->m_nStrCHook > 0)
1479  {
1480  return pApp->m_nStrCHook;
1481  }
1482  return 10; // UK MHEG default.
1483 }
1484 
1486 {
1487  MHApplication *pApp = CurrentApp();
1488 
1489  if (pApp && pApp->m_nBitmapCHook > 0)
1490  {
1491  return pApp->m_nBitmapCHook;
1492  }
1493  return 4; // UK MHEG default - PNG bitmap
1494 }
1495 
1497 {
1498  MHApplication *pApp = CurrentApp();
1499 
1500  if (pApp && pApp->m_fontAttrs.Size() > 0)
1501  {
1502  str.Copy(pApp->m_fontAttrs);
1503  }
1504  else
1505  {
1506  str.Copy("plain.24.24.0"); // TODO: Check this.
1507  }
1508 }
1509 
1510 // An identifier string required by the UK profile. The "manufacturer" is GNU.
1511 const char *MHEngine::MHEGEngineProviderIdString = "MHGGNU001";
1512 
1513 // Define the logging function and settings
1515 
1516 FILE *gMHLogStream = nullptr;
1517 
1518 // The MHEG engine calls this when it needs to log something.
1519 void mhlog_fn(const QString& logtext)
1520 {
1521  QByteArray tmp = logtext.toLatin1();
1522  fprintf(gMHLogStream, "[freemheg] %s\n", tmp.constData());
1523 }
1524 
1525 // Called from the user of the library to set the logging.
1526 void MHSetLogging(FILE *logStream, unsigned int logLevel)
1527 {
1528  gMHLogStream = logStream;
1530 }
MHContext::GetCarouselData
virtual bool GetCarouselData(const QString &objectPath, QByteArray &result)=0
kProtoCI
@ kProtoCI
Definition: Engine.cpp:253
MHObjectRef
Definition: BaseClasses.h:153
MHGroup
Definition: Groups.h:46
MHContext::SetInputRegister
virtual void SetInputRegister(int nReg)=0
MHEngine::CurrentScene
MHScene * CurrentScene()
Definition: Engine.h:181
MHPSEntry::m_FileName
MHOctetString m_FileName
Definition: Engine.h:58
EventHighlightOff
@ EventHighlightOff
Definition: Root.h:37
EventFocusMoved
@ EventFocusMoved
Definition: Root.h:42
MHEngine::RequestExternalContent
void RequestExternalContent(MHIngredient *pRequester)
Definition: Engine.cpp:1002
MHEngine::GetDefaultBGColour
void GetDefaultBGColour(MHColour &colour)
Definition: Engine.cpp:1393
Groups.h
MHEngine::m_interacting
MHInteractible * m_interacting
Definition: Engine.h:214
EventItemDeselected
@ EventItemDeselected
Definition: Root.h:39
MHRoot::Deactivation
virtual void Deactivation(MHEngine *engine)
Definition: Root.cpp:86
MHStream
Definition: Stream.h:32
MHApplication::m_fRestarting
bool m_fRestarting
Definition: Groups.h:152
MHScene::m_nEventReg
int m_nEventReg
Definition: Groups.h:102
ASN1Codes.h
MHEngine
Definition: Engine.h:72
MHEngine::DrawDisplay
void DrawDisplay(const QRegion &toDraw) override
Definition: Engine.cpp:915
MHApplication
Definition: Groups.h:117
MHEngine::MHEGEngineProviderIdString
static const char * MHEGEngineProviderIdString
Definition: Engine.h:158
MHApplication::Activation
void Activation(MHEngine *engine) override
Definition: Groups.cpp:689
MHEngine::MHEngine
MHEngine(MHContext *context)
Definition: Engine.cpp:48
MHObjectRef::m_groupId
MHOctetString m_groupId
Definition: BaseClasses.h:170
MHApplication::FindOnStack
int FindOnStack(const MHRoot *pVis)
Definition: Groups.cpp:707
MHObjectRef::m_nObjectNo
int m_nObjectNo
Definition: BaseClasses.h:169
logLevel
LogLevel_t logLevel
Definition: logging.cpp:86
MHEngine::ParseProgram
MHGroup * ParseProgram(QByteArray &text)
Definition: Engine.cpp:195
EventHighlightOn
@ EventHighlightOn
Definition: Root.h:37
MHEngine::m_eventQueue
QQueue< MHAsynchEvent * > m_eventQueue
Definition: Engine.h:193
MHEngine::RunAll
std::chrono::milliseconds RunAll(void) override
Definition: Engine.cpp:81
MHGroup::Activation
void Activation(MHEngine *engine) override
Definition: Groups.cpp:275
EventUserInput
@ EventUserInput
Definition: Root.h:35
EventStreamEvent
@ EventStreamEvent
Definition: Root.h:36
PathProtocol
static EProtocol PathProtocol(const QString &csPath)
Definition: Engine.cpp:254
MHContext::DrawBackground
virtual void DrawBackground(const QRegion &reg)=0
MHLogWarning
@ MHLogWarning
Definition: freemheg.h:72
MHOctetString::Bytes
const unsigned char * Bytes() const
Definition: BaseClasses.h:125
MHGroup::Preparation
void Preparation(MHEngine *engine) override
Definition: Groups.cpp:258
MHEngine::SendToBack
void SendToBack(const MHRoot *pVis)
Definition: Engine.cpp:795
MHExternContent::m_FileName
QString m_FileName
Definition: Engine.h:65
gMHLogoptions
int gMHLogoptions
Definition: Engine.cpp:1514
MHLogError
@ MHLogError
Definition: freemheg.h:71
MHAsynchEvent
Definition: Engine.h:45
EventCursorLeave
@ EventCursorLeave
Definition: Root.h:37
MHEngine::GetDefaultButtonRefColour
void GetDefaultButtonRefColour(MHColour &colour)
Definition: Engine.cpp:1421
EventTestEvent
@ EventTestEvent
Definition: Root.h:38
MHEngine::m_fBooting
bool m_fBooting
Definition: Engine.h:212
EventSliderValueChanged
@ EventSliderValueChanged
Definition: Root.h:42
EventItemSelected
@ EventItemSelected
Definition: Root.h:39
MHInteractible::KeyEvent
virtual void KeyEvent(MHEngine *, int)
Definition: Visible.h:151
build_compdb.parser
parser
Definition: build_compdb.py:7
MHEngine::AddLink
void AddLink(MHLink *pLink)
Definition: Engine.cpp:724
Visible.h
MHRoot::SetVariableValue
virtual void SetVariableValue(const MHUnion &)
Definition: Root.h:109
MHEngine::GetDefaultSliderRefColour
void GetDefaultSliderRefColour(MHColour &colour)
Definition: Engine.cpp:1449
MHAsynchEvent::m_eventData
MHUnion m_eventData
Definition: Engine.h:49
EventContentAvailable
@ EventContentAvailable
Definition: Root.h:34
MHApplication::m_pCurrentScene
MHScene * m_pCurrentScene
Definition: Groups.h:151
MHApplication::m_buttonRefColour
MHColour m_buttonRefColour
Definition: Groups.h:134
MHSequence< MHObjectRef * >
MHOctetString
Definition: BaseClasses.h:107
mythburn.FILE
int FILE
Definition: mythburn.py:139
EventAnchorFired
@ EventAnchorFired
Definition: Root.h:35
MHGroup::Destruction
void Destruction(MHEngine *engine) override
Definition: Groups.cpp:319
MHEngine::GetPathName
QString GetPathName(const MHOctetString &str)
Definition: Engine.cpp:534
MHApplication::m_bgColour
MHColour m_bgColour
Definition: Groups.h:134
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
MHGroup::Deactivation
void Deactivation(MHEngine *engine) override
Definition: Groups.cpp:305
EventCursorEnter
@ EventCursorEnter
Definition: Root.h:37
MHEngine::m_context
MHContext * m_context
Definition: Engine.h:211
MHApplication::m_sliderRefColour
MHColour m_sliderRefColour
Definition: Groups.h:134
MHEngine::GetDefaultStreamCHook
int GetDefaultStreamCHook()
Definition: Engine.cpp:1474
ParseBinary.h
MHRoot::m_ObjectReference
MHObjectRef m_ObjectReference
Definition: Root.h:248
C_SCENE
@ C_SCENE
Definition: ASN1Codes.h:37
MHEngine::RemoveFromDisplayStack
void RemoveFromDisplayStack(MHVisible *pVis)
Definition: Engine.cpp:760
MHEngine::Redraw
void Redraw(const QRegion &region)
Definition: Engine.cpp:929
MHColour::SetFromString
void SetFromString(const char *str, int nLen)
Definition: BaseClasses.cpp:245
MHGroup::m_items
MHOwnPtrSequence< MHIngredient > m_items
Definition: Groups.h:71
MHEngine::CancelExternalContentRequest
void CancelExternalContentRequest(MHIngredient *pRequester)
Definition: Engine.cpp:1061
MHEngine::EventTriggered
void EventTriggered(MHRoot *pSource, enum EventType ev)
Definition: Engine.h:94
MHApplication::m_nBitmapCHook
int m_nBitmapCHook
Definition: Groups.h:138
MHScene::m_nSceneCoordY
int m_nSceneCoordY
Definition: Groups.h:104
MHApplication::m_fontAttrs
MHOctetString m_fontAttrs
Definition: Groups.h:141
MHEngine::~MHEngine
~MHEngine() override
Definition: Engine.cpp:59
MHEngine::CheckLinks
void CheckLinks(const MHObjectRef &sourceRef, enum EventType ev, const MHUnion &un)
Definition: Engine.cpp:717
MHEngine::GetDefaultBitmapCHook
int GetDefaultBitmapCHook()
Definition: Engine.cpp:1485
EventEntryFieldFull
@ EventEntryFieldFull
Definition: Root.h:39
MHContentRef::IsSet
bool IsSet() const
Definition: BaseClasses.h:184
MHUnion
Definition: BaseClasses.h:283
EventType
EventType
Definition: Root.h:33
feature
static const std::array< featureStruct, 7 > feature
Definition: audiooutputsettings.cpp:434
hardwareprofile.config.p
p
Definition: config.py:33
mhlog_fn
void mhlog_fn(const QString &logtext)
Definition: Engine.cpp:1519
MHEngine::GetDefaultHighlightRefColour
void GetDefaultHighlightRefColour(MHColour &colour)
Definition: Engine.cpp:1435
ParseNode.h
MHSetLogging
void MHSetLogging(FILE *logStream, unsigned int logLevel)
Definition: Engine.cpp:1526
EventTailItems
@ EventTailItems
Definition: Root.h:39
MHEngine::PutBefore
void PutBefore(const MHRoot *pVis, const MHRoot *pRef)
Definition: Engine.cpp:813
ParseText.h
EventIsRunning
@ EventIsRunning
Definition: Root.h:34
MHLogScenes
@ MHLogScenes
Definition: freemheg.h:74
EventEngineEvent
@ EventEngineEvent
Definition: Root.h:40
EventCounterTrigger
@ EventCounterTrigger
Definition: Root.h:37
MHEngine::FindObject
MHRoot * FindObject(const MHObjectRef &oRef, bool failOnNotFound=true)
Definition: Engine.cpp:585
MHParseNode::GetTagNo
int GetTagNo()
Definition: ParseNode.cpp:49
EventTimerFired
@ EventTimerFired
Definition: Root.h:35
MHColour
Definition: BaseClasses.h:139
MHEngine::GetDefaultTextCHook
int GetDefaultTextCHook()
Definition: Engine.cpp:1463
MHExternContent::m_time
QElapsedTimer m_time
Definition: Engine.h:67
MHScene
Definition: Groups.h:88
MHLOG
#define MHLOG(__level, __text)
Definition: Logging.h:36
EventStreamPlaying
@ EventStreamPlaying
Definition: Root.h:36
MHGroup::FindByObjectNo
MHRoot * FindByObjectNo(int n) override
Definition: Groups.cpp:330
MHVisible::GetVisibleArea
virtual QRegion GetVisibleArea()
Definition: Visible.cpp:203
MHApplication::m_displayStack
MHSequence< MHVisible * > m_displayStack
Definition: Groups.h:148
MHCreateEngine
MHEG * MHCreateEngine(MHContext *context)
Definition: Engine.cpp:43
MHGroup::m_fIsApp
bool m_fIsApp
Definition: Groups.h:72
kProtoUnknown
@ kProtoUnknown
Definition: Engine.cpp:253
MHScene::m_nSceneCoordX
int m_nSceneCoordX
Definition: Groups.h:103
MHSequence::RemoveAt
void RemoveAt(int i)
Definition: BaseClasses.h:66
MHEngine::m_applicationStack
QStack< MHApplication * > m_applicationStack
Definition: Engine.h:175
Engine.h
MHGroup::CheckTimers
std::chrono::milliseconds CheckTimers(MHEngine *engine)
Definition: Groups.cpp:387
MHApplication::m_nTextCHook
int m_nTextCHook
Definition: Groups.h:135
MHGroup::PrintMe
void PrintMe(FILE *fd, int nTabs) const override
Definition: Groups.cpp:217
MHApplication::m_nLockCount
int m_nLockCount
Definition: Groups.h:145
gMHLogStream
FILE * gMHLogStream
Definition: Engine.cpp:1516
MHEngine::GetDefaultTextColour
void GetDefaultTextColour(MHColour &colour)
Definition: Engine.cpp:1407
MHEngine::TransitionToScene
void TransitionToScene(const MHObjectRef &target)
Definition: Engine.cpp:420
MHIngredient
Definition: Ingredients.h:33
MHParseBase
Definition: ParseNode.h:31
kProtoHybrid
@ kProtoHybrid
Definition: Engine.cpp:253
MHSequence::InsertAt
void InsertAt(BASE b, int n)
Definition: BaseClasses.h:53
MHEngine::AddActions
void AddActions(const MHActionSequence &actions)
Definition: Engine.cpp:735
MHEngine::AddToDisplayStack
void AddToDisplayStack(MHVisible *pVis)
Definition: Engine.cpp:745
MHEngine::GetDefaultFontAttrs
void GetDefaultFontAttrs(MHOctetString &str)
Definition: Engine.cpp:1496
MHGroup::Initialise
void Initialise(MHParseNode *p, MHEngine *engine) override
Definition: Groups.cpp:51
MHEngine::m_actionStack
QStack< MHElemAction * > m_actionStack
Definition: Engine.h:189
MHContext::GetDSMCCId
virtual const char * GetDSMCCId(void)=0
Root.h
MHOctetString::Size
int Size() const
Definition: BaseClasses.h:120
MHEngine::GenerateUserAction
void GenerateUserAction(int nCode) override
Definition: Engine.cpp:948
EventHeadItems
@ EventHeadItems
Definition: Root.h:39
MHPSEntry::m_Data
MHOwnPtrSequence< MHUnion > m_Data
Definition: Engine.h:59
kProtoHTTP
@ kProtoHTTP
Definition: Engine.cpp:253
MHPSEntry
Definition: Engine.h:55
MHEngine::m_redrawRegion
QRegion m_redrawRegion
Definition: Engine.h:172
MHLogLinks
@ MHLogLinks
Definition: freemheg.h:76
EventStreamStopped
@ EventStreamStopped
Definition: Root.h:36
MHParseNode::Failure
static void Failure(const char *p)
Definition: ParseNode.cpp:43
MHVisible
Definition: Visible.h:35
MHContentRef::m_contentRef
MHOctetString m_contentRef
Definition: BaseClasses.h:189
MHEngine::m_externContentTable
QList< MHExternContent * > m_externContentTable
Definition: Engine.h:200
MHEngine::UnlockScreen
void UnlockScreen()
Definition: Engine.cpp:935
MHColour::IsSet
bool IsSet() const
Definition: BaseClasses.h:144
MHEngine::CurrentApp
MHApplication * CurrentApp()
Definition: Engine.h:176
MHEngine::GetDefaultCharSet
int GetDefaultCharSet()
Definition: Engine.cpp:1382
MHIngredient::IsShared
bool IsShared() override
Definition: Ingredients.h:44
CONTENT_CHECK_TIME
static constexpr std::chrono::milliseconds CONTENT_CHECK_TIME
Definition: Engine.cpp:78
MHColour::Copy
void Copy(const MHColour &col)
Definition: BaseClasses.cpp:251
MHContext::RequireRedraw
virtual void RequireRedraw(const QRegion &region)=0
MHActionSequence
Definition: Actions.h:28
MHContext
Definition: freemheg.h:98
MHEngine::EngineEvent
void EngineEvent(int nCode) override
Definition: Engine.cpp:988
MHSequence::Size
int Size() const
Definition: BaseClasses.h:47
MHUnion::Printable
QString Printable() const
Definition: BaseClasses.cpp:675
MHParseBinary
Definition: ParseBinary.h:31
C_APPLICATION
@ C_APPLICATION
Definition: ASN1Codes.h:36
MHParseNode
Definition: ParseNode.h:38
MHEngine::StreamStarted
void StreamStarted(MHStream *stream, bool bStarted) override
Definition: Engine.cpp:996
EventIsSelected
@ EventIsSelected
Definition: Root.h:38
EventTokenMovedFrom
@ EventTokenMovedFrom
Definition: Root.h:36
MHSequence::GetAt
BASE GetAt(int i) const
Definition: BaseClasses.h:49
MHEngine::SetInputRegister
void SetInputRegister(int nReg)
Definition: Engine.cpp:528
MHEngine::LoadStorePersistent
bool LoadStorePersistent(bool fIsLoad, const MHOctetString &fileName, const MHSequence< MHObjectRef * > &variables)
Definition: Engine.cpp:1143
EventLastItemPresented
@ EventLastItemPresented
Definition: Root.h:38
MHSequence::Append
void Append(BASE b)
Definition: BaseClasses.h:64
EventIsStopped
@ EventIsStopped
Definition: Root.h:34
MHEG
Definition: freemheg.h:54
EProtocol
EProtocol
Definition: Engine.cpp:253
EventIsDeselected
@ EventIsDeselected
Definition: Root.h:38
freemheg.h
MHOctetString::Equal
bool Equal(const MHOctetString &str) const
Definition: BaseClasses.h:123
MHEngine::BringToFront
void BringToFront(const MHRoot *pVis)
Definition: Engine.cpp:777
MHERROR
#define MHERROR(__text)
Definition: Logging.h:42
MHLogNotifications
@ MHLogNotifications
Definition: freemheg.h:73
MHEngine::PutBehind
void PutBehind(const MHRoot *pVis, const MHRoot *pRef)
Definition: Engine.cpp:847
Logging.h
MHLogActions
@ MHLogActions
Definition: freemheg.h:75
MHExternContent::m_pRequester
MHIngredient * m_pRequester
Definition: Engine.h:66
MHEngine::m_fInTransition
bool m_fInTransition
Definition: Engine.h:205
MHApplication::m_nStrCHook
int m_nStrCHook
Definition: Groups.h:137
EventTokenMovedTo
@ EventTokenMovedTo
Definition: Root.h:36
MHElemAction::Perform
virtual void Perform(MHEngine *engine)=0
Stream.h
MHAsynchEvent::m_pEventSource
MHRoot * m_pEventSource
Definition: Engine.h:47
MHElemAction
Definition: BaseActions.h:34
EventInteractionCompleted
@ EventInteractionCompleted
Definition: Root.h:35
MHIngredient::ContentArrived
virtual void ContentArrived(const unsigned char *, int, MHEngine *)
Definition: Ingredients.h:62
MHEngine::RemoveLink
void RemoveLink(MHLink *pLink)
Definition: Engine.cpp:729
MHContext::CheckCarouselObject
virtual bool CheckCarouselObject(const QString &objectPath)=0
MHEngine::DrawRegion
void DrawRegion(const QRegion &toDraw, int nStackPos)
Definition: Engine.cpp:881
MHExternContent
Definition: Engine.h:63
MHContext::GetReceiverId
virtual const char * GetReceiverId(void)=0
MHEngine::m_persistentStore
MHOwnPtrSequence< MHPSEntry > m_persistentStore
Definition: Engine.h:203
MHContext::CheckStop
virtual bool CheckStop(void)=0
MHEngine::Quit
void Quit()
Definition: Engine.cpp:378
MHVisible::Display
virtual void Display(MHEngine *)=0
MHEngine::m_linkTable
QList< MHLink * > m_linkTable
Definition: Engine.h:196
MHIngredient::m_contentRef
MHContentRef m_contentRef
Definition: Ingredients.h:78
EventFirstItemPresented
@ EventFirstItemPresented
Definition: Root.h:38
MHApplication::m_nCharSet
int m_nCharSet
Definition: Groups.h:133
MHApplication::m_highlightRefColour
MHColour m_highlightRefColour
Definition: Groups.h:134
MHElemAction::PrintMe
virtual void PrintMe(FILE *fd, int nTabs) const
Definition: BaseActions.cpp:37
MHApplication::m_textColour
MHColour m_textColour
Definition: Groups.h:134
MHEngine::RunActions
void RunActions()
Definition: Engine.cpp:625
MHVisible::GetOpaqueArea
virtual QRegion GetOpaqueArea()
Definition: Visible.h:65
EventIsDeleted
@ EventIsDeleted
Definition: Root.h:34
EventIsAvailable
@ EventIsAvailable
Definition: Root.h:34
MHEngine::CheckContentRequests
void CheckContentRequests()
Definition: Engine.cpp:1082
MHEngine::GetEngineSupport
bool GetEngineSupport(const MHOctetString &feature)
Definition: Engine.cpp:1225
kProtoDSM
@ kProtoDSM
Definition: Engine.cpp:253
MHOctetString::Copy
void Copy(const MHOctetString &str)
Definition: BaseClasses.cpp:119
EventAsyncStopped
@ EventAsyncStopped
Definition: Root.h:35
MHRoot
Definition: Root.h:44
MHEngine::Launch
bool Launch(const MHObjectRef &target, bool fIsSpawn=false)
Definition: Engine.cpp:274
MHParseText
Definition: ParseText.h:32
MHRoot::GetVariableValue
virtual void GetVariableValue(MHUnion &, MHEngine *)
Definition: Root.h:107
MHApplication::m_path
QString m_path
Definition: Groups.h:153
MHObjectRef::Printable
QString Printable() const
Definition: BaseClasses.cpp:298