InputAppleMediaKeys.mm 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. //
  2. // Created by Tobias Hieta on 21/08/15.
  3. //
  4. #include "InputAppleMediaKeys.h"
  5. #include "SPMediaKeyTap.h"
  6. #include "QsLog.h"
  7. #import <dlfcn.h>
  8. #import <MediaPlayer/MediaPlayer.h>
  9. @interface MediaKeysDelegate : NSObject
  10. {
  11. SPMediaKeyTap* keyTap;
  12. InputAppleMediaKeys* input;
  13. }
  14. -(instancetype)initWithInput:(InputAppleMediaKeys*)input;
  15. @end
  16. @implementation MediaKeysDelegate
  17. - (instancetype)initWithInput:(InputAppleMediaKeys*)input_
  18. {
  19. self = [super init];
  20. if (self) {
  21. input = input_;
  22. if (NSClassFromString(@"MPRemoteCommandCenter")) {
  23. MPRemoteCommandCenter* center = [MPRemoteCommandCenter sharedCommandCenter];
  24. #define CONFIG_CMD(name) \
  25. [center.name ## Command addTarget:self action:@selector(gotCommand:)]
  26. CONFIG_CMD(play);
  27. CONFIG_CMD(pause);
  28. CONFIG_CMD(togglePlayPause);
  29. CONFIG_CMD(stop);
  30. CONFIG_CMD(nextTrack);
  31. CONFIG_CMD(previousTrack);
  32. CONFIG_CMD(seekForward);
  33. CONFIG_CMD(seekBackward);
  34. CONFIG_CMD(skipForward);
  35. CONFIG_CMD(skipBackward);
  36. [center.changePlaybackPositionCommand addTarget:self action:@selector(gotPlaybackPosition:)];
  37. } else {
  38. keyTap = [[SPMediaKeyTap alloc] initWithDelegate:self];
  39. if ([SPMediaKeyTap usesGlobalMediaKeyTap])
  40. [keyTap startWatchingMediaKeys];
  41. else
  42. QLOG_WARN() << "Could not grab global media keys";
  43. }
  44. }
  45. return self;
  46. }
  47. - (void)dealloc
  48. {
  49. [super dealloc];
  50. }
  51. -(MPRemoteCommandHandlerStatus)gotCommand:(MPRemoteCommandEvent *)event
  52. {
  53. QString keyPressed;
  54. MPRemoteCommand* command = [event command];
  55. #define CMD(name) [MPRemoteCommandCenter sharedCommandCenter].name ## Command
  56. if (command == CMD(play)) {
  57. keyPressed = INPUT_KEY_PLAY;
  58. } else if (command == CMD(pause)) {
  59. keyPressed = INPUT_KEY_PAUSE;
  60. } else if (command == CMD(togglePlayPause)) {
  61. keyPressed = INPUT_KEY_PLAY_PAUSE;
  62. } else if (command == CMD(stop)) {
  63. keyPressed = INPUT_KEY_STOP;
  64. } else if (command == CMD(nextTrack)) {
  65. keyPressed = INPUT_KEY_NEXT;
  66. } else if (command == CMD(previousTrack)) {
  67. keyPressed = INPUT_KEY_PREV;
  68. } else {
  69. return MPRemoteCommandHandlerStatusCommandFailed;
  70. }
  71. emit input->receivedInput("AppleMediaKeys", keyPressed, InputBase::KeyPressed);
  72. return MPRemoteCommandHandlerStatusSuccess;
  73. }
  74. -(MPRemoteCommandHandlerStatus)gotPlaybackPosition:(MPChangePlaybackPositionCommandEvent *)event
  75. {
  76. PlayerComponent::Get().seekTo(event.positionTime * 1000);
  77. return MPRemoteCommandHandlerStatusSuccess;
  78. }
  79. -(void)mediaKeyTap:(SPMediaKeyTap *)keyTap receivedMediaKeyEvent:(NSEvent *)event
  80. {
  81. int keyCode = (([event data1] & 0xFFFF0000) >> 16);
  82. int keyFlags = ([event data1] & 0x0000FFFF);
  83. BOOL keyIsPressed = (((keyFlags & 0xFF00) >> 8)) == 0xA;
  84. QString keyPressed;
  85. switch (keyCode) {
  86. case NX_KEYTYPE_PLAY:
  87. keyPressed = INPUT_KEY_PLAY_PAUSE;
  88. break;
  89. case NX_KEYTYPE_FAST:
  90. keyPressed = "KEY_FAST";
  91. break;
  92. case NX_KEYTYPE_REWIND:
  93. keyPressed = "KEY_REWIND";
  94. break;
  95. case NX_KEYTYPE_NEXT:
  96. keyPressed = INPUT_KEY_NEXT;
  97. break;
  98. case NX_KEYTYPE_PREVIOUS:
  99. keyPressed = INPUT_KEY_PREV;
  100. break;
  101. default:
  102. break;
  103. // More cases defined in hidsystem/ev_keymap.h
  104. }
  105. emit input->receivedInput("AppleMediaKeys", keyPressed, keyIsPressed ? InputBase::KeyDown : InputBase::KeyUp);
  106. }
  107. @end
  108. // macOS private enum
  109. enum {
  110. MRNowPlayingClientVisibilityUndefined = 0,
  111. MRNowPlayingClientVisibilityAlwaysVisible,
  112. MRNowPlayingClientVisibilityVisibleWhenBackgrounded,
  113. MRNowPlayingClientVisibilityNeverVisible
  114. };
  115. ///////////////////////////////////////////////////////////////////////////////////////////////////
  116. bool InputAppleMediaKeys::initInput()
  117. {
  118. m_currentTime = 0;
  119. m_pendingUpdate = false;
  120. m_delegate = [[MediaKeysDelegate alloc] initWithInput:this];
  121. if (NSClassFromString(@"MPNowPlayingInfoCenter")) {
  122. connect(&PlayerComponent::Get(), &PlayerComponent::stateChanged, this, &InputAppleMediaKeys::handleStateChanged);
  123. connect(&PlayerComponent::Get(), &PlayerComponent::positionUpdate, this, &InputAppleMediaKeys::handlePositionUpdate);
  124. connect(&PlayerComponent::Get(), &PlayerComponent::updateDuration, this, &InputAppleMediaKeys::handleUpdateDuration);
  125. void* lib = dlopen("/System/Library/PrivateFrameworks/MediaRemote.framework/MediaRemote", RTLD_NOW);
  126. if (lib) {
  127. #define LOAD_FUNC(name) \
  128. name = (name ## Func)dlsym(lib, "MRMediaRemote" #name)
  129. LOAD_FUNC(SetNowPlayingVisibility);
  130. LOAD_FUNC(GetLocalOrigin);
  131. LOAD_FUNC(SetCanBeNowPlayingApplication);
  132. if (SetCanBeNowPlayingApplication)
  133. SetCanBeNowPlayingApplication(1);
  134. }
  135. }
  136. return true;
  137. }
  138. ///////////////////////////////////////////////////////////////////////////////////////////////////
  139. static MPNowPlayingPlaybackState convertState(PlayerComponent::State newState)
  140. {
  141. switch (newState) {
  142. case PlayerComponent::State::finished:
  143. return MPNowPlayingPlaybackStateStopped;
  144. case PlayerComponent::State::canceled:
  145. case PlayerComponent::State::error:
  146. return MPNowPlayingPlaybackStateInterrupted;
  147. case PlayerComponent::State::buffering:
  148. case PlayerComponent::State::paused:
  149. return MPNowPlayingPlaybackStatePaused;
  150. case PlayerComponent::State::playing:
  151. return MPNowPlayingPlaybackStatePlaying;
  152. default:
  153. return MPNowPlayingPlaybackStateUnknown;
  154. }
  155. }
  156. ///////////////////////////////////////////////////////////////////////////////////////////////////
  157. void InputAppleMediaKeys::handleStateChanged(PlayerComponent::State newState, PlayerComponent::State oldState)
  158. {
  159. MPNowPlayingPlaybackState newMPState = convertState(newState);
  160. MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
  161. NSMutableDictionary *playingInfo = [NSMutableDictionary dictionaryWithDictionary:center.nowPlayingInfo];
  162. [playingInfo setObject:[NSNumber numberWithDouble:(double)m_currentTime / 1000] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
  163. center.nowPlayingInfo = playingInfo;
  164. [MPNowPlayingInfoCenter defaultCenter].playbackState = newMPState;
  165. if (SetNowPlayingVisibility && GetLocalOrigin) {
  166. if (newState == PlayerComponent::State::finished || newState == PlayerComponent::State::canceled || newState == PlayerComponent::State::error)
  167. SetNowPlayingVisibility(GetLocalOrigin(), MRNowPlayingClientVisibilityNeverVisible);
  168. else if (newState == PlayerComponent::State::paused || newState == PlayerComponent::State::playing || newState == PlayerComponent::State::buffering)
  169. SetNowPlayingVisibility(GetLocalOrigin(), MRNowPlayingClientVisibilityAlwaysVisible);
  170. }
  171. m_pendingUpdate = true;
  172. }
  173. ///////////////////////////////////////////////////////////////////////////////////////////////////
  174. void InputAppleMediaKeys::handlePositionUpdate(quint64 position)
  175. {
  176. m_currentTime = position;
  177. if (m_pendingUpdate) {
  178. MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
  179. NSMutableDictionary *playingInfo = [NSMutableDictionary dictionaryWithDictionary:center.nowPlayingInfo];
  180. [playingInfo setObject:[NSNumber numberWithDouble:(double)position / 1000] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];
  181. center.nowPlayingInfo = playingInfo;
  182. [MPNowPlayingInfoCenter defaultCenter].playbackState = [MPNowPlayingInfoCenter defaultCenter].playbackState;
  183. m_pendingUpdate = false;
  184. }
  185. }
  186. ///////////////////////////////////////////////////////////////////////////////////////////////////
  187. void InputAppleMediaKeys::handleUpdateDuration(qint64 duration)
  188. {
  189. MPNowPlayingInfoCenter *center = [MPNowPlayingInfoCenter defaultCenter];
  190. NSMutableDictionary *playingInfo = [NSMutableDictionary dictionaryWithDictionary:center.nowPlayingInfo];
  191. [playingInfo setObject:[NSNumber numberWithDouble:(double)duration / 1000] forKey:MPMediaItemPropertyPlaybackDuration];
  192. center.nowPlayingInfo = playingInfo;
  193. m_pendingUpdate = true;
  194. }