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