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