Global keyboard shortcuts for Cocoa.

Requires "Enable access for assistive devices" set in Universal Access control panel.
This commit is contained in:
John Maguire 2010-06-17 20:31:34 +00:00
parent 4dbd1f2a2f
commit f9e29fccf4
7 changed files with 205 additions and 33 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 2.6)
set(CMAKE_C_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "-Wnon-virtual-dtor -Woverloaded-virtual -Wall")
set(CMAKE_CXX_FLAGS "-Woverloaded-virtual -Wall")
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${SPARKLE})
@ -356,6 +356,8 @@ endif(ENABLE_VISUALISATIONS)
# OSDs
if(APPLE)
list(APPEND SOURCES widgets/osd_mac.mm)
list(APPEND SOURCES core/macglobalshortcutbackend.mm)
list(APPEND HEADERS core/macglobalshortcutbackend.h)
include_directories(${GROWL}/Headers)
else(APPLE)
if(WIN32)

View File

@ -16,6 +16,7 @@
#include "globalshortcuts.h"
#include "gnomeglobalshortcutbackend.h"
#include "macglobalshortcutbackend.h"
#include "qxtglobalshortcutbackend.h"
#include "mac_startup.h"
@ -39,11 +40,11 @@ GlobalShortcuts::GlobalShortcuts(QObject *parent)
// Create actions
AddShortcut("play", tr("Play"), SIGNAL(Play()));
AddShortcut("pause", tr("Pause"), SIGNAL(Pause()));
AddShortcut("play_pause", tr("Play/Pause"), SIGNAL(PlayPause()), QKeySequence(Qt::Key_MediaPlay));
AddShortcut("stop", tr("Stop"), SIGNAL(Stop()), QKeySequence(Qt::Key_MediaStop));
AddShortcut("play_pause", tr("Play/Pause"), SIGNAL(PlayPause())); //QKeySequence(Qt::Key_MediaPlay));
AddShortcut("stop", tr("Stop"), SIGNAL(Stop()));// QKeySequence(Qt::Key_MediaStop));
AddShortcut("stop_after", tr("Stop playing after current track"), SIGNAL(StopAfter()));
AddShortcut("next_track", tr("Next track"), SIGNAL(Next()), QKeySequence(Qt::Key_MediaNext));
AddShortcut("prev_track", tr("Previous track"), SIGNAL(Previous()), QKeySequence(Qt::Key_MediaPrevious));
AddShortcut("next_track", tr("Next track"), SIGNAL(Next()));// QKeySequence(Qt::Key_MediaNext));
AddShortcut("prev_track", tr("Previous track"), SIGNAL(Previous()));// QKeySequence(Qt::Key_MediaPrevious));
AddShortcut("inc_volume", tr("Increase volume"), SIGNAL(IncVolume()));
AddShortcut("dec_volume", tr("Decrease volume"), SIGNAL(DecVolume()));
AddShortcut("mute", tr("Mute"), SIGNAL(Mute()));
@ -57,8 +58,7 @@ GlobalShortcuts::GlobalShortcuts(QObject *parent)
#ifndef Q_OS_DARWIN
system_backend_ = new QxtGlobalShortcutBackend(this);
#else
// Setup global media key shortcuts for mac.
mac::SetShortcutHandler(this);
system_backend_ = new MacGlobalShortcutBackend(this);
#endif
ReloadSettings();

View File

@ -1,7 +1,7 @@
#ifndef MAC_STARTUP_H
#define MAC_STARTUP_H
class GlobalShortcuts;
class MacGlobalShortcutBackend;
class QObject;
class PlatformInterface {
@ -16,7 +16,7 @@ class PlatformInterface {
namespace mac {
void MacMain();
void SetShortcutHandler(GlobalShortcuts* handler);
void SetShortcutHandler(MacGlobalShortcutBackend* handler);
void SetApplicationHandler(PlatformInterface* handler);
void CheckForUpdates();

View File

@ -10,8 +10,11 @@
#import <AppKit/NSNibDeclarations.h>
#import <Sparkle/SUUpdater.h>
#import <Kernel/AvailabilityMacros.h>
#include "globalshortcuts.h"
#include "mac_startup.h"
#include "macglobalshortcutbackend.h"
#include <QCoreApplication>
#include <QEvent>
@ -22,12 +25,12 @@
// See: http://www.rogueamoeba.com/utm/2007/09/29/apple-keyboard-media-key-event-handling/
@interface MacApplication :NSApplication {
GlobalShortcuts* shortcut_handler_;
MacGlobalShortcutBackend* shortcut_handler_;
PlatformInterface* application_handler_;
}
- (GlobalShortcuts*) shortcut_handler;
- (void) SetShortcutHandler: (GlobalShortcuts*)handler;
- (MacGlobalShortcutBackend*) shortcut_handler;
- (void) SetShortcutHandler: (MacGlobalShortcutBackend*)handler;
- (PlatformInterface*) application_handler;
- (void) SetApplicationHandler: (PlatformInterface*)handler;
@ -35,7 +38,11 @@
- (void) mediaKeyEvent: (int)key state: (BOOL)state repeat: (BOOL)repeat;
@end
@interface AppDelegate :NSObject { //<NSApplicationDelegate> {
#ifdef MAC_OS_X_VERSION_10_6
@interface AppDelegate :NSObject <NSApplicationDelegate> {
#else
@interface AppDelegate :NSObject {
#endif
PlatformInterface* application_handler_;
}
@ -86,11 +93,11 @@
return self;
}
- (GlobalShortcuts*) shortcut_handler {
- (MacGlobalShortcutBackend*) shortcut_handler {
return shortcut_handler_;
}
- (void) SetShortcutHandler: (GlobalShortcuts*)handler {
- (void) SetShortcutHandler: (MacGlobalShortcutBackend*)handler {
shortcut_handler_ = handler;
}
@ -121,21 +128,7 @@
return;
}
if (state == 0) {
switch (key) {
case NX_KEYTYPE_PLAY:
// Play pressed.
shortcut_handler_->MacMediaKeyPressed("Play");
break;
case NX_KEYTYPE_FAST:
// Next pressed.
shortcut_handler_->MacMediaKeyPressed("Next");
break;
case NX_KEYTYPE_REWIND:
shortcut_handler_->MacMediaKeyPressed("Previous");
break;
default:
break;
}
shortcut_handler_->MacMediaKeyPressed(key);
}
}
@ -151,7 +144,7 @@ void MacMain() {
[[SUUpdater sharedUpdater] setDelegate: NSApp];
}
void SetShortcutHandler(GlobalShortcuts* handler) {
void SetShortcutHandler(MacGlobalShortcutBackend* handler) {
[NSApp SetShortcutHandler: handler];
}

View File

@ -0,0 +1,51 @@
/* This file is part of Clementine.
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MACGLOBALSHORTCUTBACKEND_H
#define MACGLOBALSHORTCUTBACKEND_H
#include "globalshortcutbackend.h"
#include <QKeySequence>
#include <QMap>
class MacGlobalShortcutBackendPrivate;
class QAction;
class MacGlobalShortcutBackend : public GlobalShortcutBackend {
Q_OBJECT
public:
MacGlobalShortcutBackend(GlobalShortcuts* parent);
virtual ~MacGlobalShortcutBackend();
void MacMediaKeyPressed(int key);
protected:
bool DoRegister();
void DoUnregister();
private:
void KeyPressed(const QKeySequence& sequence);
QMap<QKeySequence, QAction*> shortcuts_;
friend class MacGlobalShortcutBackendPrivate;
MacGlobalShortcutBackendPrivate* p_;
};
#endif // MACGLOBALSHORTCUTBACKEND_H

View File

@ -0,0 +1,128 @@
/* This file is part of Clementine.
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "macglobalshortcutbackend.h"
#include "globalshortcuts.h"
#include "mac_startup.h"
#include <boost/noncopyable.hpp>
#include <QAction>
#include <QList>
#include <QtDebug>
#include <AppKit/NSEvent.h>
#include <Foundation/NSString.h>
#include <IOKit/hidsystem/ev_keymap.h>
class MacGlobalShortcutBackendPrivate : boost::noncopyable {
public:
explicit MacGlobalShortcutBackendPrivate(MacGlobalShortcutBackend* backend)
: monitor_(nil),
backend_(backend) {
}
bool Register() {
#ifdef NS_BLOCKS_AVAILABLE
monitor_ = [NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask
handler:^(NSEvent* event) {
qDebug() << __PRETTY_FUNCTION__;
HandleKeyEvent(event);
}];
return true;
#else
return false;
#endif
}
void Unregister() {
[NSEvent removeMonitor:monitor_];
}
private:
static QKeySequence GetSequence(NSEvent* event) {
NSString* str = [event charactersIgnoringModifiers];
NSString* lower = [str lowercaseString];
const char* chars = [lower UTF8String];
NSUInteger modifiers = [event modifierFlags];
int key = Qt::Key_A + chars[0] - 'a'; // >.>
if (modifiers & NSShiftKeyMask) {
key += Qt::SHIFT;
}
if (modifiers & NSControlKeyMask) {
key += Qt::META;
}
if (modifiers & NSAlternateKeyMask) {
key += Qt::ALT;
}
if (modifiers & NSCommandKeyMask) {
key += Qt::CTRL;
}
return QKeySequence(key);
}
void HandleKeyEvent(NSEvent* event) {
QKeySequence sequence = GetSequence(event);
backend_->KeyPressed(sequence);
}
id monitor_;
MacGlobalShortcutBackend* backend_;
};
MacGlobalShortcutBackend::MacGlobalShortcutBackend(GlobalShortcuts* parent)
: GlobalShortcutBackend(parent),
p_(new MacGlobalShortcutBackendPrivate(this)) {
}
MacGlobalShortcutBackend::~MacGlobalShortcutBackend() {
delete p_;
}
bool MacGlobalShortcutBackend::DoRegister() {
mac::SetShortcutHandler(this);
foreach (const GlobalShortcuts::Shortcut& shortcut, manager_->shortcuts().values()) {
shortcuts_[shortcut.action->shortcut()] = shortcut.action;
}
return p_->Register();
}
void MacGlobalShortcutBackend::DoUnregister() {
p_->Unregister();
}
void MacGlobalShortcutBackend::MacMediaKeyPressed(int key) {
switch (key) {
case NX_KEYTYPE_PLAY:
manager_->shortcuts()["play_pause"].action->trigger();
break;
case NX_KEYTYPE_FAST:
manager_->shortcuts()["next_track"].action->trigger();
break;
case NX_KEYTYPE_REWIND:
manager_->shortcuts()["prev_track"].action->trigger();
break;
}
}
void MacGlobalShortcutBackend::KeyPressed(const QKeySequence& sequence) {
QAction* action = shortcuts_[sequence];
if (action) {
action->trigger();
}
}

View File

@ -28,11 +28,9 @@ QxtGlobalShortcutBackend::QxtGlobalShortcutBackend(GlobalShortcuts *parent)
bool QxtGlobalShortcutBackend::DoRegister() {
qDebug() << __PRETTY_FUNCTION__;
#ifndef Q_OS_DARWIN
foreach (const GlobalShortcuts::Shortcut& shortcut, manager_->shortcuts().values()) {
AddShortcut(shortcut.action);
}
#endif
return true;
}