diff options
Diffstat (limited to '')
-rw-r--r-- | xbmc/messaging/ApplicationMessenger.h | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/xbmc/messaging/ApplicationMessenger.h b/xbmc/messaging/ApplicationMessenger.h new file mode 100644 index 0000000..8f15ef9 --- /dev/null +++ b/xbmc/messaging/ApplicationMessenger.h @@ -0,0 +1,438 @@ +/* + * 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 + +/// @brief Called from the player when its current item is updated +#define TMSG_UPDATE_PLAYER_ITEM TMSG_MASK_APPLICATION + 35 + +#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 }; +}; +} +} |