summaryrefslogtreecommitdiffstats
path: root/xbmc/messaging/ApplicationMessenger.h
blob: 8f15ef9f1674dc07300c1fb23b3ea8b8efe0ea65 (plain)
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
436
437
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 };
};
}
}