2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
11 #include "guilib/WindowIDs.h"
12 #include "messaging/ThreadMessage.h"
13 #include "threads/Thread.h"
22 #define TMSG_MASK_MESSAGE 0xFFFF0000 // only keep the high bits to route messages
23 #define TMSG_MASK_APPLICATION (1<<30) //Don't use bit 31 as it'll fail to build, using unsigned variable to hold the message.
24 #define TMSG_MASK_PLAYLISTPLAYER (1<<29)
25 #define TMSG_MASK_GUIINFOMANAGER (1<<28)
26 #define TMSG_MASK_WINDOWMANAGER (1<<27)
27 #define TMSG_MASK_PERIPHERALS (1<<26)
30 #define TMSG_PLAYLISTPLAYER_PLAY TMSG_MASK_PLAYLISTPLAYER + 0
31 #define TMSG_PLAYLISTPLAYER_NEXT TMSG_MASK_PLAYLISTPLAYER + 1
32 #define TMSG_PLAYLISTPLAYER_PREV TMSG_MASK_PLAYLISTPLAYER + 2
33 #define TMSG_PLAYLISTPLAYER_ADD TMSG_MASK_PLAYLISTPLAYER + 3
34 #define TMSG_PLAYLISTPLAYER_CLEAR TMSG_MASK_PLAYLISTPLAYER + 4
35 #define TMSG_PLAYLISTPLAYER_SHUFFLE TMSG_MASK_PLAYLISTPLAYER + 5
36 #define TMSG_PLAYLISTPLAYER_GET_ITEMS TMSG_MASK_PLAYLISTPLAYER + 6
37 #define TMSG_PLAYLISTPLAYER_PLAY_ITEM_ID TMSG_MASK_PLAYLISTPLAYER + 7
38 #define TMSG_PLAYLISTPLAYER_INSERT TMSG_MASK_PLAYLISTPLAYER + 8
39 #define TMSG_PLAYLISTPLAYER_REMOVE TMSG_MASK_PLAYLISTPLAYER + 9
40 #define TMSG_PLAYLISTPLAYER_SWAP TMSG_MASK_PLAYLISTPLAYER + 10
41 #define TMSG_PLAYLISTPLAYER_REPEAT TMSG_MASK_PLAYLISTPLAYER + 11
42 #define TMSG_MEDIA_PLAY TMSG_MASK_PLAYLISTPLAYER + 12
43 #define TMSG_MEDIA_STOP TMSG_MASK_PLAYLISTPLAYER + 13
44 // the PAUSE is indeed a PLAYPAUSE
45 #define TMSG_MEDIA_PAUSE TMSG_MASK_PLAYLISTPLAYER + 14
46 #define TMSG_MEDIA_RESTART TMSG_MASK_PLAYLISTPLAYER + 15
47 #define TMSG_MEDIA_UNPAUSE TMSG_MASK_PLAYLISTPLAYER + 16
48 #define TMSG_MEDIA_PAUSE_IF_PLAYING TMSG_MASK_PLAYLISTPLAYER + 17
49 #define TMSG_MEDIA_SEEK_TIME TMSG_MASK_PLAYLISTPLAYER + 18
51 #define TMSG_SHUTDOWN TMSG_MASK_APPLICATION + 0
52 #define TMSG_POWERDOWN TMSG_MASK_APPLICATION + 1
53 #define TMSG_QUIT TMSG_MASK_APPLICATION + 2
54 #define TMSG_HIBERNATE TMSG_MASK_APPLICATION + 3
55 #define TMSG_SUSPEND TMSG_MASK_APPLICATION + 4
56 #define TMSG_RESTART TMSG_MASK_APPLICATION + 5
57 #define TMSG_RESET TMSG_MASK_APPLICATION + 6
58 #define TMSG_RESTARTAPP TMSG_MASK_APPLICATION + 7
59 #define TMSG_ACTIVATESCREENSAVER TMSG_MASK_APPLICATION + 8
60 #define TMSG_NETWORKMESSAGE TMSG_MASK_APPLICATION + 9
61 #define TMSG_RESETSCREENSAVER TMSG_MASK_APPLICATION + 10
62 #define TMSG_VOLUME_SHOW TMSG_MASK_APPLICATION + 11
63 #define TMSG_DISPLAY_SETUP TMSG_MASK_APPLICATION + 12
64 #define TMSG_DISPLAY_DESTROY TMSG_MASK_APPLICATION + 13
65 #define TMSG_SETVIDEORESOLUTION TMSG_MASK_APPLICATION + 14
66 #define TMSG_SWITCHTOFULLSCREEN TMSG_MASK_APPLICATION + 15
67 #define TMSG_MINIMIZE TMSG_MASK_APPLICATION + 16
68 #define TMSG_TOGGLEFULLSCREEN TMSG_MASK_APPLICATION + 17
69 #define TMSG_SETLANGUAGE TMSG_MASK_APPLICATION + 18
70 #define TMSG_RENDERER_FLUSH TMSG_MASK_APPLICATION + 19
71 #define TMSG_INHIBITIDLESHUTDOWN TMSG_MASK_APPLICATION + 20
72 #define TMSG_START_ANDROID_ACTIVITY TMSG_MASK_APPLICATION + 21
73 #define TMSG_EXECUTE_SCRIPT TMSG_MASK_APPLICATION + 22
74 #define TMSG_EXECUTE_BUILT_IN TMSG_MASK_APPLICATION + 23
75 #define TMSG_EXECUTE_OS TMSG_MASK_APPLICATION + 24
76 #define TMSG_PICTURE_SHOW TMSG_MASK_APPLICATION + 25
77 #define TMSG_PICTURE_SLIDESHOW TMSG_MASK_APPLICATION + 26
78 #define TMSG_LOADPROFILE TMSG_MASK_APPLICATION + 27
79 #define TMSG_VIDEORESIZE TMSG_MASK_APPLICATION + 28
80 #define TMSG_INHIBITSCREENSAVER TMSG_MASK_APPLICATION + 29
82 #define TMSG_SYSTEM_POWERDOWN TMSG_MASK_APPLICATION + 30
83 #define TMSG_RENDERER_PREINIT TMSG_MASK_APPLICATION + 31
84 #define TMSG_RENDERER_UNINIT TMSG_MASK_APPLICATION + 32
85 #define TMSG_EVENT TMSG_MASK_APPLICATION + 33
86 #define TMSG_MOVETOSCREEN TMSG_MASK_APPLICATION + 34
88 /// @brief Called from the player when its current item is updated
89 #define TMSG_UPDATE_PLAYER_ITEM TMSG_MASK_APPLICATION + 35
91 #define TMSG_SET_VOLUME TMSG_MASK_APPLICATION + 36
92 #define TMSG_SET_MUTE TMSG_MASK_APPLICATION + 37
94 #define TMSG_GUI_INFOLABEL TMSG_MASK_GUIINFOMANAGER + 0
95 #define TMSG_GUI_INFOBOOL TMSG_MASK_GUIINFOMANAGER + 1
96 #define TMSG_UPDATE_CURRENT_ITEM TMSG_MASK_GUIINFOMANAGER + 2
98 #define TMSG_CECTOGGLESTATE TMSG_MASK_PERIPHERALS + 1
99 #define TMSG_CECACTIVATESOURCE TMSG_MASK_PERIPHERALS + 2
100 #define TMSG_CECSTANDBY TMSG_MASK_PERIPHERALS + 3
102 #define TMSG_GUI_DIALOG_OPEN TMSG_MASK_WINDOWMANAGER + 1
103 #define TMSG_GUI_ACTIVATE_WINDOW TMSG_MASK_WINDOWMANAGER + 2
104 #define TMSG_GUI_PYTHON_DIALOG TMSG_MASK_WINDOWMANAGER + 3
105 #define TMSG_GUI_WINDOW_CLOSE TMSG_MASK_WINDOWMANAGER + 4
106 #define TMSG_GUI_ACTION TMSG_MASK_WINDOWMANAGER + 5
107 #define TMSG_GUI_ADDON_DIALOG TMSG_MASK_WINDOWMANAGER + 6
108 #define TMSG_GUI_MESSAGE TMSG_MASK_WINDOWMANAGER + 7
111 \def TMSG_GUI_DIALOG_YESNO
112 \brief Message sent through CApplicationMessenger to open a yes/no dialog box
114 There's two ways to send this message, a short and concise way and a more
115 flexible way allowing more customization.
118 CApplicationMessenger::Get().SendMsg(TMSG_GUI_DIALOG_YESNO, 123, 456);
119 123: This is the string id for the heading
120 456: This is the string id for the text
123 \a HELPERS::DialogYesNoMessage options.
125 CApplicationMessenger::Get().SendMsg(TMSG_GUI_DIALOG_YESNO, -1, -1, static_cast<void*>(&options));
127 \returns -1 for cancelled, 0 for No and 1 for Yes
128 \sa HELPERS::DialogYesNoMessage
130 #define TMSG_GUI_DIALOG_YESNO TMSG_MASK_WINDOWMANAGER + 8
131 #define TMSG_GUI_DIALOG_OK TMSG_MASK_WINDOWMANAGER + 9
134 \def TMSG_GUI_PREVIOUS_WINDOW
135 \brief Message sent through CApplicationMessenger to go back to the previous window
137 This is an alternative to TMSG_GUI_ACTIVATE_WINDOW, but it keeps
138 all configured parameters, like startup directory.
140 #define TMSG_GUI_PREVIOUS_WINDOW TMSG_MASK_WINDOWMANAGER + 10
143 #define TMSG_CALLBACK 800
152 class IMessageTarget
;
154 struct ThreadMessageCallback
156 void (*callback
)(void *userptr
);
161 * \class CApplicationMessenger ApplicationMessenger.h "messaging/ApplicationMessenger.h"
162 * \brief This implements a simple message dispatcher/router for Kodi
164 * For most users that wants to send message go to the documentation for these
165 * \sa CApplicationMessenger::SendMsg
166 * \sa CApplicationMessenger::PostMsg
168 * For anyone wanting to implement a message receiver, go to the documentation for
171 * IMPLEMENTATION SPECIFIC NOTES - DOCUMENTED HERE FOR THE SOLE PURPOSE OF IMPLEMENTERS OF THIS CLASS
172 * On a high level this implements two methods for dispatching messages, SendMsg and PostMsg.
173 * These are roughly modeled on the implementation of SendMessage and PostMessage in Windows.
175 * PostMsg is the preferred method to use as it's non-blocking and does not wait for any response before
176 * returning to the caller. Messages will be stored in a queue and processed in order.
178 * SendMsg is a blocking version and has a bit more subtleties to it regarding how inter-process
179 * dispatching is handled.
181 * Calling SendMsg with a message type that doesn't require marshalling will bypass the message queue
182 * and call the receiver directly
184 * Calling SendMsg with a message type that require marshalling to a specific thread when not on that thread
185 * will add a message to the queue with a an event, it will then block the calling thread waiting on this event
187 * The message will be processed by the correct thread in it's message pump and the event will be signaled, unblocking
190 * Calling SendMsg with a message type that require marshalling to a specific thread when already on that thread
191 * will behave as scenario one, it will bypass the queue and call the receiver directly.
193 * Currently there is a hack implemented in the message dispatcher that releases the graphicslock before dispatching
194 * a message. This was here before the redesign and removing it will require careful inspection of every call site.
195 * TODO: add logging if the graphicslock is held during message dispatch
197 * Current design has three different message types
198 * 1. Normal messages that can be processed on any thread
199 * 2. GUI messages that require marshalling to the UI thread
200 * 3. A thread message that will spin up a background thread and wait a specified amount of time before posting the message
201 * This should probably be removed, it's left for compatibility
203 * Heavy emphasis on current design, the idea is that we can easily add more message types to route messages
204 * to more threads or other scenarios.
206 * \sa CApplicationMessenger::ProcessMessages()
207 * handles regular messages that require no marshalling, this can be called from any thread to drive the message
210 * \sa CApplicationMessenger::ProcessWindowMessages()
211 * handles GUI messages and currently should only be called on the UI thread
213 * If/When this is expanded upon ProcessMessage() and ProcessWindowMessages() should be combined into a single method
214 * taking an enum or similar to indicate which message it's interested in.
216 * The above methods are backed by two messages queues, one for each type of message. If more types are added
217 * this might need to be redesigned to simplify the lookup of the correct message queue but currently they're implemented
218 * as two member variables
220 * The design is meant to be very encapsulated and easy to extend without altering the public interface.
221 * e.g. If GUI messages should be handled on another thread, call \sa CApplicationMessenger::ProcessWindowMessage() on that
222 * thread and nothing else has to change. The callers have no knowledge of how this is implemented.
224 * The design is also meant to be very dependency free to work as a bridge between lower layer functionality without
225 * having to have knowledge of the GUI or having a dependency on the GUI in any way. This is not the reality currently as
226 * this depends on \sa CApplication and the graphicslock but should be fixed soon enough.
228 * To keep things simple the current implementation routes messages based on a mask that the receiver provides.
229 * Any message fitting that mask will be routed to that specific receiver.
230 * This will likely need to change if many different receivers are added but it should be possible to do it without
231 * any of the callers being changed.
233 class CApplicationMessenger
236 CApplicationMessenger();
237 ~CApplicationMessenger();
240 // if a message has to be send to the gui, use MSG_TYPE_WINDOW instead
242 * \brief Send a blocking message and wait for a response
244 * If and what the response is depends entirely on the message being sent and
245 * should be documented on the message.
247 * Under no circumestances shall the caller hold a lock when calling SendMsg as there's
248 * no guarantee what the receiver will do to answer the request.
250 * \param [in] messageId defined further up in this file
251 * \return meaning of the return varies based on the message
253 int SendMsg(uint32_t messageId
);
256 * \brief Send a blocking message and wait for a response
258 * If and what the response is depends entirely on the message being sent and
259 * should be documented on the message.
261 * Under no circumestances shall the caller hold a lock when calling SendMsg as there's
262 * no guarantee what the receiver will do to answer the request.
264 * \param [in] messageId defined further up in this file
265 * \param [in] param1 value depends on the message being sent
266 * \param [in] param2 value depends on the message being sent, defaults to -1
267 * \param [in] payload this is a void pointer that is meant to send larger objects to the receiver
268 * what to send depends on the message
269 * \return meaning of the return varies based on the message
271 int SendMsg(uint32_t messageId
, int param1
, int param2
= -1, void* payload
= nullptr);
274 * \brief Send a blocking message and wait for a response
276 * If and what the response is depends entirely on the message being sent and
277 * should be documented on the message.
279 * Under no circumestances shall the caller hold a lock when calling SendMsg as there's
280 * no guarantee what the receiver will do to answer the request.
282 * \param [in] messageId defined further up in this file
283 * \param [in] param1 value depends on the message being sent
284 * \param [in] param2 value depends on the message being sent
285 * \param [in,out] payload this is a void pointer that is meant to send larger objects to the receiver
286 * what to send depends on the message
287 * \param [in] strParam value depends on the message being sent, remains for backward compat
288 * \return meaning of the return varies based on the message
290 int SendMsg(uint32_t messageId
, int param1
, int param2
, void* payload
, std::string strParam
);
293 * \brief Send a blocking message and wait for a response
295 * If and what the response is depends entirely on the message being sent and
296 * should be documented on the message.
298 * Under no circumestances shall the caller hold a lock when calling SendMsg as there's
299 * no guarantee what the receiver will do to answer the request.
301 * \param [in] messageId defined further up in this file
302 * \param [in] param1 value depends on the message being sent
303 * \param [in] param2 value depends on the message being sent
304 * \param [in,out] payload this is a void pointer that is meant to send larger objects to the receiver
305 * what to send depends on the message
306 * \param [in] strParam value depends on the message being sent, remains for backward compat
307 * \param [in] params value depends on the message being sent, kept for backward compatibility
308 * \return meaning of the return varies based on the message
310 int SendMsg(uint32_t messageId
, int param1
, int param2
, void* payload
, std::string strParam
, std::vector
<std::string
> params
);
313 * \brief Send a non-blocking message and return immediately
315 * If and what the response is depends entirely on the message being sent and
316 * should be documented on the message.
318 * \param [in] messageId defined further up in this file
320 void PostMsg(uint32_t messageId
);
323 * \brief Send a non-blocking message and return immediately
325 * If and what the response is depends entirely on the message being sent and
326 * should be documented on the message.
328 * \param [in] messageId defined further up in this file
329 * \param [in] param3 value depends on the message being sent
331 void PostMsg(uint32_t messageId
, int64_t param3
);
334 * \brief Send a non-blocking message and return immediately
336 * If and what the response is depends entirely on the message being sent and
337 * should be documented on the message.
339 * \param [in] messageId defined further up in this file
340 * \param [in] param1 value depends on the message being sent
341 * \param [in] param2 value depends on the message being sent
342 * \param [in,out] payload this is a void pointer that is meant to send larger objects to the receiver
343 * what to send depends on the message
345 void PostMsg(uint32_t messageId
, int param1
, int param2
= -1, void* payload
= nullptr);
348 * \brief Send a non-blocking message and return immediately
350 * If and what the response is depends entirely on the message being sent and
351 * should be documented on the message.
353 * \param [in] messageId defined further up in this file
354 * \param [in] param1 value depends on the message being sent
355 * \param [in] param2 value depends on the message being sent
356 * \param [in,out] payload this is a void pointer that is meant to send larger objects to the receiver
357 * what to send depends on the message
358 * \param [in] strParam value depends on the message being sent, remains for backward compat
360 void PostMsg(uint32_t messageId
, int param1
, int param2
, void* payload
, std::string strParam
);
362 * \brief Send a non-blocking message and return immediately
364 * If and what the response is depends entirely on the message being sent and
365 * should be documented on the message.
367 * \param [in] messageId defined further up in this file
368 * \param [in] param1 value depends on the message being sent
369 * \param [in] param2 value depends on the message being sent
370 * \param [in,out] payload this is a void pointer that is meant to send larger objects to the receiver
371 * what to send depends on the message
372 * \param [in] strParam value depends on the message being sent, remains for backward compat
373 * \param [in] params value depends on the message being sent, kept for backward compatibility
375 void PostMsg(uint32_t messageId
, int param1
, int param2
, void* payload
, std::string strParam
, std::vector
<std::string
> params
);
378 * \brief Called from any thread to dispatch messages
380 void ProcessMessages();
383 * \brief Called from the UI thread to dispatch UI messages
384 * This is only of value to implementers of the message pump, do not rely on a specific thread
385 * being used other than that it's appropriate for UI messages
387 void ProcessWindowMessages();
389 /*! \brief Send a GUIMessage, optionally waiting before it's processed to return.
390 * This is kept for backward compat and is just a convenience wrapper for for SendMsg and PostMsg
391 * specifically for UI messages
392 * \param msg the GUIMessage to send.
393 * \param windowID optional window to send the message to (defaults to no specified window).
394 * \param waitResult whether to wait for the result (defaults to false).
396 void SendGUIMessage(const CGUIMessage
&msg
, int windowID
= WINDOW_INVALID
, bool waitResult
=false);
399 * \brief This should be called any class implementing \sa IMessageTarget before it
400 * can receive any messages
402 void RegisterReceiver(IMessageTarget
* target
);
405 * \brief Set the UI thread id to avoid messenger being dependent on
406 * CApplication to determine if marshaling is required
407 * \param thread The UI thread ID
409 void SetGUIThread(const std::thread::id thread
) { m_guiThreadId
= thread
; }
412 * \brief Set the processing thread id to avoid messenger being dependent on
413 * CApplication to determine if marshaling is required
414 * \param thread The processing thread ID
416 void SetProcessThread(const std::thread::id thread
) { m_processThreadId
= thread
; }
419 * \brief Signals the shutdown of the application and message processing
421 void Stop() { m_bStop
= true; }
423 //! \brief Returns true if this is the process / app loop thread.
424 bool IsProcessThread() const;
427 CApplicationMessenger(const CApplicationMessenger
&) = delete;
428 CApplicationMessenger
const& operator=(CApplicationMessenger
const&) = delete;
430 int SendMsg(ThreadMessage
&& msg
, bool wait
);
431 void ProcessMessage(ThreadMessage
*pMsg
);
433 std::queue
<ThreadMessage
*> m_vecMessages
; /*!< queue for regular messages */
434 std::queue
<ThreadMessage
*> m_vecWindowMessages
; /*!< queue for UI messages */
435 std::map
<int, IMessageTarget
*> m_mapTargets
; /*!< a map of registered receivers indexed on the message mask*/
436 CCriticalSection m_critSection
;
437 std::thread::id m_guiThreadId
;
438 std::thread::id m_processThreadId
;
439 bool m_bStop
{ false };