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