summaryrefslogtreecommitdiffstats
path: root/xbmc/interfaces/legacy/Window.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/interfaces/legacy/Window.cpp')
-rw-r--r--xbmc/interfaces/legacy/Window.cpp767
1 files changed, 767 insertions, 0 deletions
diff --git a/xbmc/interfaces/legacy/Window.cpp b/xbmc/interfaces/legacy/Window.cpp
new file mode 100644
index 0000000..100803e
--- /dev/null
+++ b/xbmc/interfaces/legacy/Window.cpp
@@ -0,0 +1,767 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "Window.h"
+
+#include "ServiceBroker.h"
+#include "WindowException.h"
+#include "WindowInterceptor.h"
+#include "application/Application.h"
+#include "guilib/GUIButtonControl.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIEditControl.h"
+#include "guilib/GUIRadioButtonControl.h"
+#include "guilib/GUIWindowManager.h"
+#include "messaging/ApplicationMessenger.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+
+#define ACTIVE_WINDOW CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()
+
+namespace XBMCAddon
+{
+ namespace xbmcgui
+ {
+ thread_local ref* InterceptorBase::upcallTls;
+
+ /**
+ * Used in add/remove control. It only locks if it's given a
+ * non-NULL CCriticalSection. It's given a NULL CCriticalSection
+ * when a function higher in the call stack already has a
+ */
+ class MaybeLock
+ {
+ CCriticalSection* lock;
+ public:
+ inline explicit MaybeLock(CCriticalSection* p_lock) : lock(p_lock) { if (lock) lock->lock(); }
+ inline ~MaybeLock() { if (lock) lock->unlock(); }
+ };
+
+ class SingleLockWithDelayGuard
+ {
+ DelayedCallGuard dcg;
+ CCriticalSection& lock;
+ public:
+ inline SingleLockWithDelayGuard(CCriticalSection& ccrit, LanguageHook* lh) : dcg(lh), lock(ccrit) { lock.lock(); }
+ inline ~SingleLockWithDelayGuard() { lock.unlock(); }
+ };
+
+ /**
+ * Explicit template instantiation
+ */
+ template class Interceptor<CGUIWindow>;
+
+ /**
+ * This interceptor is a simple, non-callbackable (is that a word?)
+ * Interceptor to satisfy the Window requirements for upcalling
+ * for the purposes of instantiating a Window instance from
+ * an already existing window.
+ */
+ class ProxyExistingWindowInterceptor : public InterceptorBase
+ {
+ CGUIWindow* cguiwindow;
+
+ public:
+ inline ProxyExistingWindowInterceptor(CGUIWindow* window) :
+ cguiwindow(window) { XBMC_TRACE; }
+
+ CGUIWindow* get() override;
+ };
+
+ CGUIWindow* ProxyExistingWindowInterceptor::get() { XBMC_TRACE; return cguiwindow; }
+
+ Window::Window(bool discrim):
+ window(NULL),
+ m_actionEvent(true),
+ canPulse(true), existingWindow(false)
+ {
+ XBMC_TRACE;
+ }
+
+ /**
+ * This just creates a default window.
+ */
+ Window::Window(int existingWindowId) :
+ window(NULL),
+ m_actionEvent(true)
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook);
+
+ if (existingWindowId == -1)
+ {
+ // in this case just do the other constructor.
+ canPulse = true;
+ existingWindow = false;
+
+ setWindow(new Interceptor<CGUIWindow>("CGUIWindow",this,getNextAvailableWindowId()));
+ }
+ else
+ {
+ // user specified window id, use this one if it exists
+ // It is not possible to capture key presses or button presses
+ CGUIWindow* pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(existingWindowId);
+ if (!pWindow)
+ throw WindowException("Window id does not exist");
+
+ setWindow(new ProxyExistingWindowInterceptor(pWindow));
+ }
+ }
+
+ Window::~Window()
+ {
+ XBMC_TRACE;
+
+ deallocating();
+ }
+
+ void Window::deallocating()
+ {
+ AddonCallback::deallocating();
+
+ dispose();
+ }
+
+ void Window::dispose()
+ {
+ XBMC_TRACE;
+
+ //! @todo rework locking
+ // Python GIL and CServiceBroker::GetWinSystem()->GetGfxContext() are deadlock happy
+ // dispose is called from GUIWindowManager and in this case DelayGuard must not be used.
+ if (!CServiceBroker::GetAppMessenger()->IsProcessThread())
+ {
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(), languageHook);
+ }
+
+ if (!isDisposed)
+ {
+ isDisposed = true;
+
+ // no callbacks are possible any longer
+ // - this will be handled by the parent constructor
+
+ // first change to an existing window
+ if (!existingWindow)
+ {
+ if (ACTIVE_WINDOW == iWindowId && !g_application.m_bStop)
+ {
+ if(CServiceBroker::GetGUI()->GetWindowManager().GetWindow(iOldWindowId))
+ {
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(iOldWindowId);
+ }
+ // old window does not exist anymore, switch to home
+ else CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_HOME);
+ }
+
+ }
+ else
+ {
+ //! @bug
+ //! This is an existing window, so no resources are free'd. Note that
+ //! THIS WILL FAIL for any controls newly created by python - they will
+ //! remain after the script ends. Ideally this would be remedied by
+ //! a flag in Control that specifies that it was python created - any python
+ //! created controls could then be removed + free'd from the window.
+ //! how this works with controlgroups though could be a bit tricky.
+ }
+
+ // and free our list of controls
+ std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
+ while (it != vecControls.end())
+ {
+ AddonClass::Ref<Control> pControl = *it;
+ // initialize control to zero
+ pControl->pGUIControl = NULL;
+ pControl->iControlId = 0;
+ pControl->iParentId = 0;
+ ++it;
+ }
+
+ if (!existingWindow)
+ {
+ if (window)
+ {
+ if (CServiceBroker::GetGUI()->GetWindowManager().IsWindowVisible(ref(window)->GetID()))
+ {
+ destroyAfterDeInit = true;
+ close();
+ }
+ else
+ CServiceBroker::GetGUI()->GetWindowManager().Delete(ref(window)->GetID());
+ }
+ }
+
+ vecControls.clear();
+ }
+ }
+
+ void Window::setWindow(InterceptorBase* _window)
+ {
+ XBMC_TRACE;
+ window = _window;
+ iWindowId = _window->get()->GetID();
+
+ if (!existingWindow)
+ CServiceBroker::GetGUI()->GetWindowManager().Add(window->get());
+ }
+
+ int Window::getNextAvailableWindowId()
+ {
+ XBMC_TRACE;
+ // window id's 13000 - 13100 are reserved for python
+ // get first window id that is not in use
+ int id = WINDOW_PYTHON_START;
+ // if window 13099 is in use it means python can't create more windows
+ if (CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_PYTHON_END))
+ throw WindowException("maximum number of windows reached");
+
+ while(id < WINDOW_PYTHON_END && CServiceBroker::GetGUI()->GetWindowManager().GetWindow(id) != NULL) id++;
+ return id;
+ }
+
+ void Window::popActiveWindowId()
+ {
+ XBMC_TRACE;
+ if (iOldWindowId != iWindowId &&
+ iWindowId != ACTIVE_WINDOW)
+ iOldWindowId = ACTIVE_WINDOW;
+ }
+
+ // Internal helper method
+ /* Searches for a control in Window->vecControls
+ * If we can't find any but the window has the controlId (in case of a not python window)
+ * we create a new control with basic functionality
+ */
+ Control* Window::GetControlById(int iControlId, CCriticalSection* gc)
+ {
+ XBMC_TRACE;
+
+ // find in window vector first!!!
+ // this saves us from creating a complete new control
+ std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
+ while (it != vecControls.end())
+ {
+ AddonClass::Ref<Control> control = (*it);
+ if (control->iControlId == iControlId)
+ {
+ return control.get();
+ } else ++it;
+ }
+
+ // lock xbmc GUI before accessing data from it
+ MaybeLock lock(gc);
+
+ // check if control exists
+ CGUIControl* pGUIControl = ref(window)->GetControl(iControlId);
+ if (!pGUIControl)
+ {
+ // control does not exist.
+ throw WindowException("Non-Existent Control %d",iControlId);
+ }
+
+ // allocate a new control with a new reference
+ CLabelInfo li;
+
+ Control* pControl = NULL;
+
+ //! @todo Yuck! Should probably be done with a Factory pattern
+ switch(pGUIControl->GetControlType())
+ {
+ case CGUIControl::GUICONTROL_BUTTON:
+ pControl = new ControlButton();
+
+ li = ((CGUIButtonControl *)pGUIControl)->GetLabelInfo();
+
+ // note: conversion from infocolors -> plain colors here
+ ((ControlButton*)pControl)->disabledColor = li.disabledColor;
+ ((ControlButton*)pControl)->focusedColor = li.focusedColor;
+ ((ControlButton*)pControl)->textColor = li.textColor;
+ ((ControlButton*)pControl)->shadowColor = li.shadowColor;
+ if (li.font) ((ControlButton*)pControl)->strFont = li.font->GetFontName();
+ ((ControlButton*)pControl)->align = li.align;
+ break;
+ case CGUIControl::GUICONTROL_LABEL:
+ pControl = new ControlLabel();
+ break;
+ case CGUIControl::GUICONTROL_SPIN:
+ pControl = new ControlSpin();
+ break;
+ case CGUIControl::GUICONTROL_FADELABEL:
+ pControl = new ControlFadeLabel();
+ break;
+ case CGUIControl::GUICONTROL_TEXTBOX:
+ pControl = new ControlTextBox();
+ break;
+ case CGUIControl::GUICONTROL_IMAGE:
+ case CGUIControl::GUICONTROL_BORDEREDIMAGE:
+ pControl = new ControlImage();
+ break;
+ case CGUIControl::GUICONTROL_PROGRESS:
+ pControl = new ControlProgress();
+ break;
+ case CGUIControl::GUICONTROL_SLIDER:
+ pControl = new ControlSlider();
+ break;
+ case CGUIControl::GUICONTAINER_LIST:
+ case CGUIControl::GUICONTAINER_WRAPLIST:
+ case CGUIControl::GUICONTAINER_FIXEDLIST:
+ case CGUIControl::GUICONTAINER_PANEL:
+ pControl = new ControlList();
+ // create a python spin control
+ ((ControlList*)pControl)->pControlSpin = new ControlSpin();
+ break;
+ case CGUIControl::GUICONTROL_GROUP:
+ pControl = new ControlGroup();
+ break;
+ case CGUIControl::GUICONTROL_RADIO:
+ pControl = new ControlRadioButton();
+
+ li = ((CGUIRadioButtonControl *)pGUIControl)->GetLabelInfo();
+
+ // note: conversion from infocolors -> plain colors here
+ ((ControlRadioButton*)pControl)->disabledColor = li.disabledColor;
+ ((ControlRadioButton*)pControl)->focusedColor = li.focusedColor;
+ ((ControlRadioButton*)pControl)->textColor = li.textColor;
+ ((ControlRadioButton*)pControl)->shadowColor = li.shadowColor;
+ if (li.font) ((ControlRadioButton*)pControl)->strFont = li.font->GetFontName();
+ ((ControlRadioButton*)pControl)->align = li.align;
+ break;
+ case CGUIControl::GUICONTROL_EDIT:
+ pControl = new ControlEdit();
+
+ li = ((CGUIEditControl *)pGUIControl)->GetLabelInfo();
+
+ // note: conversion from infocolors -> plain colors here
+ ((ControlEdit*)pControl)->disabledColor = li.disabledColor;
+ ((ControlEdit*)pControl)->textColor = li.textColor;
+ if (li.font) ((ControlEdit*)pControl)->strFont = li.font->GetFontName();
+ ((ControlButton*)pControl)->align = li.align;
+ break;
+ default:
+ break;
+ }
+
+ if (!pControl)
+ // throw an exception
+ throw WindowException("Unknown control type for python");
+
+ // we have a valid control here, fill in all the 'Control' data
+ pControl->pGUIControl = pGUIControl;
+ pControl->iControlId = pGUIControl->GetID();
+ pControl->iParentId = iWindowId;
+ pControl->dwHeight = (int)pGUIControl->GetHeight();
+ pControl->dwWidth = (int)pGUIControl->GetWidth();
+ pControl->dwPosX = (int)pGUIControl->GetXPosition();
+ pControl->dwPosY = (int)pGUIControl->GetYPosition();
+ pControl->iControlUp = pGUIControl->GetAction(ACTION_MOVE_UP).GetNavigation();
+ pControl->iControlDown = pGUIControl->GetAction(ACTION_MOVE_DOWN).GetNavigation();
+ pControl->iControlLeft = pGUIControl->GetAction(ACTION_MOVE_LEFT).GetNavigation();
+ pControl->iControlRight = pGUIControl->GetAction(ACTION_MOVE_RIGHT).GetNavigation();
+
+ // It got this far so means the control isn't actually in the vector of controls
+ // so lets add it to save doing all that next time
+ vecControls.emplace_back(pControl);
+
+ // return the control with increased reference (+1)
+ return pControl;
+ }
+
+ void Window::PulseActionEvent()
+ {
+ XBMC_TRACE;
+ if (canPulse)
+ m_actionEvent.Set();
+ }
+
+ bool Window::WaitForActionEvent(unsigned int milliseconds)
+ {
+ XBMC_TRACE;
+ // DO NOT MAKE THIS A DELAYED CALL!!!!
+ bool ret = languageHook == NULL ? m_actionEvent.Wait(std::chrono::milliseconds(milliseconds))
+ : languageHook->WaitForEvent(m_actionEvent, milliseconds);
+ if (ret)
+ m_actionEvent.Reset();
+ return ret;
+ }
+
+ bool Window::OnAction(const CAction &action)
+ {
+ XBMC_TRACE;
+ // do the base class window first, and the call to python after this
+ bool ret = ref(window)->OnAction(action);
+
+ // workaround - for scripts which try to access the active control (focused) when there is none.
+ // for example - the case when the mouse enters the screen.
+ CGUIControl *pControl = ref(window)->GetFocusedControl();
+ if (action.IsMouse() && !pControl)
+ return ret;
+
+ AddonClass::Ref<Action> inf(new Action(action));
+ invokeCallback(new CallbackFunction<Window,AddonClass::Ref<Action> >(this,&Window::onAction,inf.get()));
+ PulseActionEvent();
+
+ return ret;
+ }
+
+ bool Window::OnBack(int actionID)
+ {
+ // we are always a Python window ... keep that in mind when reviewing the old code
+ return true;
+ }
+
+ void Window::OnDeinitWindow(int nextWindowID /*= 0*/)
+ {
+ // NOTE!: This handle child classes correctly. XML windows will call
+ // the OnDeinitWindow from CGUIMediaWindow while non-XML classes will
+ // call the OnDeinitWindow on CGUIWindow
+ ref(window)->OnDeinitWindow(nextWindowID);
+ if (destroyAfterDeInit)
+ CServiceBroker::GetGUI()->GetWindowManager().Delete(window->get()->GetID());
+ }
+
+ void Window::onAction(Action* action)
+ {
+ XBMC_TRACE;
+ // default onAction behavior
+ if(action->id == ACTION_PREVIOUS_MENU || action->id == ACTION_NAV_BACK)
+ close();
+ }
+
+ bool Window::OnMessage(CGUIMessage& message)
+ {
+ XBMC_TRACE;
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_CLICKED:
+ {
+ int iControl=message.GetSenderId();
+ AddonClass::Ref<Control> inf;
+ // find python control object with same iControl
+ std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
+ while (it != vecControls.end())
+ {
+ AddonClass::Ref<Control> pControl = (*it);
+ if (pControl->iControlId == iControl)
+ {
+ inf = pControl.get();
+ break;
+ }
+ ++it;
+ }
+
+ // did we find our control?
+ if (inf.isNotNull())
+ {
+ // currently we only accept messages from a button or controllist with a select action
+ if (inf->canAcceptMessages(message.GetParam1()))
+ {
+ invokeCallback(new CallbackFunction<Window,AddonClass::Ref<Control> >(this,&Window::onControl,inf.get()));
+ PulseActionEvent();
+
+ // return true here as we are handling the event
+ return true;
+ }
+ }
+ // if we get here, we didn't add the action
+ }
+ break;
+ }
+
+ return ref(window)->OnMessage(message);
+ }
+
+ void Window::onControl(Control* action) { XBMC_TRACE; /* do nothing by default */ }
+ void Window::onClick(int controlId) { XBMC_TRACE; /* do nothing by default */ }
+ void Window::onDoubleClick(int controlId) { XBMC_TRACE; /* do nothing by default */ }
+ void Window::onFocus(int controlId) { XBMC_TRACE; /* do nothing by default */ }
+ void Window::onInit() { XBMC_TRACE; /* do nothing by default */ }
+
+ void Window::show()
+ {
+ XBMC_TRACE;
+ DelayedCallGuard dcguard(languageHook);
+ popActiveWindowId();
+
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTIVATE_WINDOW, iWindowId, 0);
+ }
+
+ void Window::setFocus(Control* pControl)
+ {
+ XBMC_TRACE;
+ if(pControl == NULL)
+ throw WindowException("Object should be of type Control");
+
+ CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS,pControl->iParentId, pControl->iControlId);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, pControl->iParentId);
+ }
+
+ void Window::setFocusId(int iControlId)
+ {
+ XBMC_TRACE;
+ CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS,iWindowId,iControlId);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iWindowId);
+ }
+
+ Control* Window::getFocus()
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook);
+
+ int iControlId = ref(window)->GetFocusedControlID();
+ if(iControlId == -1)
+ throw WindowException("No control in this window has focus");
+ // Sine I'm already holding the lock theres no reason to give it to GetFocusedControlID
+ return GetControlById(iControlId,NULL);
+ }
+
+ long Window::getFocusId()
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook);
+ int iControlId = ref(window)->GetFocusedControlID();
+ if(iControlId == -1)
+ throw WindowException("No control in this window has focus");
+ return (long)iControlId;
+ }
+
+ void Window::removeControl(Control* pControl)
+ {
+ XBMC_TRACE;
+ DelayedCallGuard dg(languageHook);
+ doRemoveControl(pControl,&CServiceBroker::GetWinSystem()->GetGfxContext(),true);
+ }
+
+ void Window::doRemoveControl(Control* pControl, CCriticalSection* gcontext, bool wait)
+ {
+ XBMC_TRACE;
+ // type checking, object should be of type Control
+ if(pControl == NULL)
+ throw WindowException("Object should be of type Control");
+
+ {
+ MaybeLock mlock(gcontext);
+ if(!ref(window)->GetControl(pControl->iControlId))
+ throw WindowException("Control does not exist in window");
+ }
+
+ // delete control from vecControls in window object
+ std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin();
+ while (it != vecControls.end())
+ {
+ AddonClass::Ref<Control> control = (*it);
+ if (control->iControlId == pControl->iControlId)
+ {
+ it = vecControls.erase(it);
+ } else ++it;
+ }
+
+ CGUIMessage msg(GUI_MSG_REMOVE_CONTROL, 0, 0);
+ msg.SetPointer(pControl->pGUIControl);
+ CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, iWindowId, wait);
+
+ // initialize control to zero
+ pControl->pGUIControl = NULL;
+ pControl->iControlId = 0;
+ pControl->iParentId = 0;
+ }
+
+ void Window::removeControls(std::vector<Control*> pControls)
+ {
+ XBMC_TRACE;
+ DelayedCallGuard dg(languageHook);
+ int count = 1; int size = pControls.size();
+ for (std::vector<Control*>::iterator iter = pControls.begin(); iter != pControls.end(); count++, ++iter)
+ doRemoveControl(*iter,NULL, count == size);
+ }
+
+ long Window::getHeight()
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(), languageHook);
+ RESOLUTION_INFO resInfo = ref(window)->GetCoordsRes();
+ return resInfo.iHeight;
+ }
+
+ long Window::getWidth()
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(), languageHook);
+ RESOLUTION_INFO resInfo = ref(window)->GetCoordsRes();
+ return resInfo.iWidth;
+ }
+
+ void Window::setProperty(const char* key, const String& value)
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook);
+ std::string lowerKey = key;
+ StringUtils::ToLower(lowerKey);
+
+ ref(window)->SetProperty(lowerKey, value);
+ }
+
+ String Window::getProperty(const char* key)
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook);
+ std::string lowerKey = key;
+ StringUtils::ToLower(lowerKey);
+ std::string value = ref(window)->GetProperty(lowerKey).asString();
+ return value;
+ }
+
+ void Window::clearProperty(const char* key)
+ {
+ XBMC_TRACE;
+ if (!key) return;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook);
+
+ std::string lowerKey = key;
+ StringUtils::ToLower(lowerKey);
+ ref(window)->SetProperty(lowerKey, "");
+ }
+
+ void Window::clearProperties()
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook);
+ ref(window)->ClearProperties();
+ }
+
+ void Window::close()
+ {
+ XBMC_TRACE;
+ bModal = false;
+
+ if (!existingWindow)
+ PulseActionEvent();
+
+ {
+ DelayedCallGuard dcguard(languageHook);
+ CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_PREVIOUS_WINDOW, iOldWindowId, 0);
+ }
+
+ iOldWindowId = 0;
+ }
+
+ void Window::doModal()
+ {
+ XBMC_TRACE;
+ if (!existingWindow)
+ {
+ bModal = true;
+
+ if(iWindowId != ACTIVE_WINDOW)
+ show();
+
+ while (bModal && !g_application.m_bStop)
+ {
+//! @todo garbear added this code to the python window.cpp class and
+//! commented in XBPyThread.cpp. I'm not sure how to handle this
+//! in this native implementation.
+// // Check if XBPyThread::stop() raised a SystemExit exception
+// if (PyThreadState_Get()->async_exc == PyExc_SystemExit)
+// {
+// CLog::Log(LOGDEBUG, "PYTHON: doModal() encountered a SystemExit exception, closing window and returning");
+// Window_Close(self, NULL);
+// break;
+// }
+ languageHook->MakePendingCalls(); // MakePendingCalls
+
+ bool stillWaiting;
+ do
+ {
+ {
+ DelayedCallGuard dcguard(languageHook);
+ stillWaiting = WaitForActionEvent(100) ? false : true;
+ }
+ languageHook->MakePendingCalls();
+ } while (stillWaiting);
+ }
+ }
+ }
+
+ void Window::addControl(Control* pControl)
+ {
+ XBMC_TRACE;
+ DelayedCallGuard dg(languageHook);
+ doAddControl(pControl,&CServiceBroker::GetWinSystem()->GetGfxContext(),true);
+ }
+
+ void Window::doAddControl(Control* pControl, CCriticalSection* gcontext, bool wait)
+ {
+ XBMC_TRACE;
+ if(pControl == NULL)
+ throw WindowException("NULL Control passed to WindowBase::addControl");
+
+ if(pControl->iControlId != 0)
+ throw WindowException("Control is already used");
+
+ // lock kodi GUI before accessing data from it
+ pControl->iParentId = iWindowId;
+
+ {
+ MaybeLock mlock(gcontext);
+ // assign control id, if id is already in use, try next id
+ do pControl->iControlId = ++iCurrentControlId;
+ while (ref(window)->GetControl(pControl->iControlId));
+ }
+
+ pControl->Create();
+
+ // set default navigation for control
+ pControl->iControlUp = pControl->iControlId;
+ pControl->iControlDown = pControl->iControlId;
+ pControl->iControlLeft = pControl->iControlId;
+ pControl->iControlRight = pControl->iControlId;
+
+ pControl->pGUIControl->SetAction(ACTION_MOVE_UP, CGUIAction(pControl->iControlUp));
+ pControl->pGUIControl->SetAction(ACTION_MOVE_DOWN, CGUIAction(pControl->iControlDown));
+ pControl->pGUIControl->SetAction(ACTION_MOVE_LEFT, CGUIAction(pControl->iControlLeft));
+ pControl->pGUIControl->SetAction(ACTION_MOVE_RIGHT, CGUIAction(pControl->iControlRight));
+
+ // add control to list and allocate resources for the control
+ vecControls.emplace_back(pControl);
+ pControl->pGUIControl->AllocResources();
+
+ // This calls the CGUIWindow parent class to do the final add
+ CGUIMessage msg(GUI_MSG_ADD_CONTROL, 0, 0);
+ msg.SetPointer(pControl->pGUIControl);
+ CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, iWindowId, wait);
+ }
+
+ void Window::addControls(std::vector<Control*> pControls)
+ {
+ XBMC_TRACE;
+ SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook);
+ int count = 1; int size = pControls.size();
+ for (std::vector<Control*>::iterator iter = pControls.begin(); iter != pControls.end(); count++, ++iter)
+ doAddControl(*iter,NULL, count == size);
+ }
+
+ Control* Window::getControl(int iControlId)
+ {
+ XBMC_TRACE;
+ DelayedCallGuard dg(languageHook);
+ return GetControlById(iControlId,&CServiceBroker::GetWinSystem()->GetGfxContext());
+ }
+
+ void Action::setFromCAction(const CAction& action)
+ {
+ XBMC_TRACE;
+ id = action.GetID();
+ buttonCode = action.GetButtonCode();
+ fAmount1 = action.GetAmount(0);
+ fAmount2 = action.GetAmount(1);
+ fRepeat = action.GetRepeat();
+ strAction = action.GetName();
+ }
+
+ }
+}