1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #pragma once
10 
11 #include "DirtyRegionTracker.h"
12 #include "GUIWindow.h"
13 #include "IMsgTargetCallback.h"
14 #include "IWindowManagerCallback.h"
15 #include "guilib/WindowIDs.h"
16 #include "messaging/IMessageTarget.h"
17 
18 #include <list>
19 #include <unordered_map>
20 #include <utility>
21 #include <vector>
22 
23 class CGUIDialog;
24 class CGUIMediaWindow;
25 
26 #ifdef TARGET_WINDOWS_STORE
27 #pragma pack(push, 8)
28 #endif
29 enum class DialogModalityType;
30 #ifdef TARGET_WINDOWS_STORE
31 #pragma pack(pop)
32 #endif
33 
34 namespace KODI
35 {
36   namespace MESSAGING
37   {
38     class CApplicationMessenger;
39   }
40 }
41 
42 #define WINDOW_ID_MASK 0xffff
43 
44 /*!
45  \ingroup winman
46  \brief
47  */
48 class CGUIWindowManager : public KODI::MESSAGING::IMessageTarget
49 {
50   friend CGUIDialog;
51   friend CGUIMediaWindow;
52 public:
53   CGUIWindowManager();
54   ~CGUIWindowManager() override;
55   bool SendMessage(CGUIMessage& message);
56   bool SendMessage(int message, int senderID, int destID, int param1 = 0, int param2 = 0);
57   bool SendMessage(CGUIMessage& message, int window);
58   void Initialize();
59   void Add(CGUIWindow* pWindow);
60   void AddUniqueInstance(CGUIWindow *window);
61   void AddCustomWindow(CGUIWindow* pWindow);
62   void Remove(int id);
63   void Delete(int id);
64   void ActivateWindow(int iWindowID, const std::string &strPath = "");
65   void ForceActivateWindow(int iWindowID, const std::string &strPath = "");
66   void ChangeActiveWindow(int iNewID, const std::string &strPath = "");
67   void ActivateWindow(int iWindowID, const std::vector<std::string>& params, bool swappingWindows = false, bool force = false);
68   void PreviousWindow();
69 
70   void CloseDialogs(bool forceClose = false) const;
71   void CloseInternalModalDialogs(bool forceClose = false) const;
72 
73   void OnApplicationMessage(KODI::MESSAGING::ThreadMessage* pMsg) override;
74   int GetMessageMask() override;
75 
76   // OnAction() runs through our active dialogs and windows and sends the message
77   // off to the callbacks (application, python, playlist player) and to the
78   // currently focused window(s).  Returns true only if the message is handled.
79   bool OnAction(const CAction &action) const;
80 
81   /*! \brief Process active controls allowing them to animate before rendering.
82    */
83   void Process(unsigned int currentTime);
84 
85   /*! \brief Mark the screen as dirty, forcing a redraw at the next Render()
86    */
87   void MarkDirty();
88 
89   /*! \brief Mark a region as dirty, forcing a redraw at the next Render()
90    */
91   void MarkDirty(const CRect& rect);
92 
93   /*! \brief Rendering of the current window and any dialogs
94    Render is called every frame to draw the current window and any dialogs.
95    It should only be called from the application thread.
96    Returns true only if it has rendered something.
97    */
98   bool Render();
99 
100   void RenderEx() const;
101 
102   /*! \brief Do any post render activities.
103    */
104   void AfterRender();
105 
106   /*! \brief Per-frame updating of the current window and any dialogs
107    FrameMove is called every frame to update the current window and any dialogs
108    on screen. It should only be called from the application thread.
109    */
110   void FrameMove();
111 
112   /*! \brief Return whether the window manager is initialized.
113    The window manager is initialized on skin load - if the skin isn't yet loaded,
114    no windows should be able to be initialized.
115    \return true if the window manager is initialized, false otherwise.
116    */
Initialized()117   bool Initialized() const { return m_initialized; };
118 
119   /*! \brief Create and initialize all windows and dialogs
120    */
121   void CreateWindows();
122 
123   /*! \brief Destroy and remove all windows and dialogs
124   *
125   * \return true on success, false if destruction fails for any window
126   */
127   bool DestroyWindows();
128 
129   /*! \brief Destroy and remove the window or dialog with the given id
130    *
131    *\param id the window id
132    */
133   void DestroyWindow(int id);
134 
135   /*! \brief Return the window of type \code{T} with the given id or
136    * null if no window exists with the given id.
137    *
138    * \tparam T the window class type
139    * \param id the window id
140    * \return the window with for the given type \code{T} or null
141    */
142   template<typename T, typename std::enable_if<std::is_base_of<CGUIWindow,T>::value>::type* = nullptr>
GetWindow(int id)143   T* GetWindow(int id) const { return dynamic_cast<T *>(GetWindow(id)); };
144 
145   /*! \brief Return the window with the given id or null.
146    *
147    * \param id the window id
148    * \return the window with the given id or null
149    */
150   CGUIWindow* GetWindow(int id) const;
151 
152   /*! \brief Return the dialog window with the given id or null.
153    *
154    * \param id the dialog window id
155    * \return the dialog window with the given id or null
156    */
157   CGUIDialog* GetDialog(int id) const;
158 
159   void SetCallback(IWindowManagerCallback& callback);
160   void DeInitialize();
161 
162   /*! \brief Register a dialog as active dialog
163    *
164    * \param dialog The dialog to register as active dialog
165    */
166   void RegisterDialog(CGUIWindow* dialog);
167   void RemoveDialog(int id);
168 
169   /*! \brief Get the ID of the topmost dialog
170    *
171    * \param ignoreClosing ignore dialog is closing
172    * \return the ID of the topmost dialog or WINDOW_INVALID if no dialog is active
173    */
174   int GetTopmostDialog(bool ignoreClosing = false) const;
175 
176   /*! \brief Get the ID of the topmost modal dialog
177    *
178    * \param ignoreClosing ignore dialog is closing
179    * \return the ID of the topmost modal dialog or WINDOW_INVALID if no modal dialog is active
180    */
181   int GetTopmostModalDialog(bool ignoreClosing = false) const;
182 
183   void SendThreadMessage(CGUIMessage& message, int window = 0);
184   void DispatchThreadMessages();
185   // method to removed queued messages with message id in the requested message id list.
186   // pMessageIDList: point to first integer of a 0 ends integer array.
187   int RemoveThreadMessageByMessageIds(int *pMessageIDList);
188   void AddMsgTarget( IMsgTargetCallback* pMsgTarget );
189   int GetActiveWindow() const;
190   int GetActiveWindowOrDialog() const;
191   bool HasModalDialog(bool ignoreClosing) const;
192   bool HasVisibleModalDialog() const;
193   bool IsDialogTopmost(int id, bool modal = false) const;
194   bool IsDialogTopmost(const std::string &xmlFile, bool modal = false) const;
195   bool IsModalDialogTopmost(int id) const;
196   bool IsModalDialogTopmost(const std::string &xmlFile) const;
197   bool IsWindowActive(int id, bool ignoreClosing = true) const;
198   bool IsWindowVisible(int id) const;
199   bool IsWindowActive(const std::string &xmlFile, bool ignoreClosing = true) const;
200   bool IsWindowVisible(const std::string &xmlFile) const;
201   /*! \brief Checks if the given window is an addon window.
202    *
203    * \return true if the given window is an addon window, otherwise false.
204    */
IsAddonWindow(int id)205   bool IsAddonWindow(int id) const { return (id >= WINDOW_ADDON_START && id <= WINDOW_ADDON_END); };
206   /*! \brief Checks if the given window is a python window.
207    *
208    * \return true if the given window is a python window, otherwise false.
209    */
IsPythonWindow(int id)210   bool IsPythonWindow(int id) const { return (id >= WINDOW_PYTHON_START && id <= WINDOW_PYTHON_END); };
211 
212   bool HasVisibleControls();
213 
214 #ifdef _DEBUG
215   void DumpTextureUse();
216 #endif
217 private:
218   void RenderPass() const;
219 
220   void LoadNotOnDemandWindows();
221   void UnloadNotOnDemandWindows();
222   void AddToWindowHistory(int newWindowID);
223 
224   /*!
225    \brief Check if the given window id is in the window history, and if so, remove this
226     window and all overlying windows from the history so that we always have a predictable
227     "Back" behaviour for each window.
228 
229    \param windowID the window id to remove from the window history
230    */
231   void RemoveFromWindowHistory(int windowID);
232   void ClearWindowHistory();
233   void CloseWindowSync(CGUIWindow *window, int nextWindowID = 0);
234   int GetTopmostDialog(bool modal, bool ignoreClosing) const;
235 
236   friend class KODI::MESSAGING::CApplicationMessenger;
237 
238   /*! \brief Activate the given window.
239    *
240    * \param windowID The window ID to activate.
241    * \param params Parameter
242    * \param swappingWindows True if the window should be swapped with the previous window instead of put it in the window history, otherwise false
243    * \param force True to ignore checks which refuses opening the window, otherwise false
244    */
245   void ActivateWindow_Internal(int windowID, const std::vector<std::string> &params, bool swappingWindows, bool force = false);
246 
247   bool ProcessRenderLoop(bool renderOnly);
248 
249   bool HandleAction(const CAction &action) const;
250 
251   std::unordered_map<int, CGUIWindow*> m_mapWindows;
252   std::vector<CGUIWindow*> m_vecCustomWindows;
253   std::vector<CGUIWindow*> m_activeDialogs;
254   std::vector<CGUIWindow*> m_deleteWindows;
255 
256   std::deque<int> m_windowHistory;
257 
258   IWindowManagerCallback* m_pCallback;
259   std::list< std::pair<CGUIMessage*,int> > m_vecThreadMessages;
260   CCriticalSection m_critSection;
261   std::vector<IMsgTargetCallback*> m_vecMsgTargets;
262 
263   int  m_iNested;
264   bool m_initialized;
265   mutable bool m_touchGestureActive{false};
266   mutable bool m_inhibitTouchGestureEvents{false};
267 
268   CDirtyRegionList m_dirtyregions;
269   CDirtyRegionTracker m_tracker;
270 };
271