Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F621422
globalshortcutmanager_mac.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None
globalshortcutmanager_mac.cpp
View Options
/*
* globalshortcutmanager_mac.cpp - Mac OS X implementation of global shortcuts
* Copyright (C) 2003-2007 Eric Smith, Michail Pishchagin
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include
"globalshortcutmanager.h"
#include
"globalshortcuttrigger.h"
// TODO:
// - don't invoke hotkey if there is a modal dialog?
// - do multi-mapping, like the x11 version
#include
<QCoreApplication>
#include
<Carbon/Carbon.h>
class
MacKeyTrigger
{
public
:
virtual
~
MacKeyTrigger
()
{}
virtual
void
activate
()
=
0
;
virtual
bool
isAccepted
(
int
id
)
const
=
0
;
};
class
MacKeyTriggerManager
:
public
QObject
{
public
:
static
MacKeyTriggerManager
*
instance
()
{
if
(
!
instance_
)
instance_
=
new
MacKeyTriggerManager
();
return
instance_
;
}
void
addTrigger
(
MacKeyTrigger
*
trigger
)
{
triggers_
<<
trigger
;
}
void
removeTrigger
(
MacKeyTrigger
*
trigger
)
{
triggers_
.
removeAll
(
trigger
);
}
private
:
MacKeyTriggerManager
()
:
QObject
(
QCoreApplication
::
instance
())
{
initAscii2KeyCodeTable
(
&
key_codes_
);
hot_key_function_
=
NewEventHandlerUPP
(
hotKeyHandler
);
EventTypeSpec
type
;
type
.
eventClass
=
kEventClassKeyboard
;
type
.
eventKind
=
kEventHotKeyPressed
;
InstallApplicationEventHandler
(
hot_key_function_
,
1
,
&
type
,
this
,
NULL
);
}
/**
* Callback function invoked when the user hits a hot-key.
*/
static
pascal
OSStatus
hotKeyHandler
(
EventHandlerCallRef
/*nextHandler*/
,
EventRef
theEvent
,
void
*
userData
)
{
EventHotKeyID
hkID
;
GetEventParameter
(
theEvent
,
kEventParamDirectObject
,
typeEventHotKeyID
,
NULL
,
sizeof
(
EventHotKeyID
),
NULL
,
&
hkID
);
static_cast
<
MacKeyTriggerManager
*>
(
userData
)
->
activated
(
hkID
.
id
);
return
noErr
;
}
void
activated
(
int
id
)
{
foreach
(
MacKeyTrigger
*
trigger
,
triggers_
)
{
if
(
trigger
->
isAccepted
(
id
))
{
trigger
->
activate
();
break
;
}
}
}
static
MacKeyTriggerManager
*
instance_
;
QList
<
MacKeyTrigger
*>
triggers_
;
typedef
struct
{
short
kchrID
;
Str255
KCHRname
;
short
transtable
[
256
];
}
Ascii2KeyCodeTable
;
enum
{
kTableCountOffset
=
256
+
2
,
kFirstTableOffset
=
256
+
4
,
kTableSize
=
128
};
static
EventHandlerUPP
hot_key_function_
;
static
Ascii2KeyCodeTable
key_codes_
;
private
:
/**
* initAscii2KeyCodeTable initializes the ascii to key code
* look up table using the currently active KCHR resource. This
* routine calls GetResource so it cannot be called at interrupt time.
*/
static
OSStatus
initAscii2KeyCodeTable
(
Ascii2KeyCodeTable
*
ttable
)
{
unsigned
char
*
theCurrentKCHR
,
*
ithKeyTable
;
short
count
,
i
,
j
,
resID
;
Handle
theKCHRRsrc
;
ResType
rType
;
// set up our table to all minus ones
for
(
i
=
0
;
i
<
256
;
i
++
)
ttable
->
transtable
[
i
]
=
-1
;
// find the current kchr resource ID
ttable
->
kchrID
=
(
short
)
GetScriptVariable
(
smCurrentScript
,
smScriptKeys
);
// get the current KCHR resource
theKCHRRsrc
=
GetResource
(
'
KCHR
'
,
ttable
->
kchrID
);
if
(
theKCHRRsrc
==
NULL
)
return
resNotFound
;
GetResInfo
(
theKCHRRsrc
,
&
resID
,
&
rType
,
ttable
->
KCHRname
);
// dereference the resource
theCurrentKCHR
=
(
unsigned
char
*
)(
*
theKCHRRsrc
);
// get the count from the resource
count
=
*
(
short
*
)(
theCurrentKCHR
+
kTableCountOffset
);
// build inverse table by merging all key tables
for
(
i
=
0
;
i
<
count
;
i
++
)
{
ithKeyTable
=
theCurrentKCHR
+
kFirstTableOffset
+
(
i
*
kTableSize
);
for
(
j
=
0
;
j
<
kTableSize
;
j
++
)
{
if
(
ttable
->
transtable
[
ithKeyTable
[
j
]
]
==
-1
)
ttable
->
transtable
[
ithKeyTable
[
j
]
]
=
j
;
}
}
return
noErr
;
}
/**
* validateAscii2KeyCodeTable verifies that the ascii to key code
* lookup table is synchronized with the current KCHR resource. If
* it is not synchronized, then the table is re-built. This routine calls
* GetResource so it cannot be called at interrupt time.
*
* Should probably call this at some point, in case the user has switched keyboard
* layouts while we were running.
*/
static
OSStatus
validateAscii2KeyCodeTable
(
Ascii2KeyCodeTable
*
ttable
,
Boolean
*
wasChanged
)
{
short
theID
;
theID
=
(
short
)
GetScriptVariable
(
smCurrentScript
,
smScriptKeys
);
if
(
theID
!=
ttable
->
kchrID
)
{
*
wasChanged
=
true
;
return
initAscii2KeyCodeTable
(
ttable
);
}
else
{
*
wasChanged
=
false
;
return
noErr
;
}
}
/**
* asciiToKeyCode looks up the ascii character in the key
* code look up table and returns the virtual key code for that
* letter. If there is no virtual key code for that letter, then
* the value -1 will be returned.
*/
static
short
asciiToKeyCode
(
Ascii2KeyCodeTable
*
ttable
,
short
asciiCode
)
{
if
(
asciiCode
>=
0
&&
asciiCode
<=
255
)
return
ttable
->
transtable
[
asciiCode
];
else
return
false
;
}
/**
* Not used.
*/
static
char
keyCodeToAscii
(
short
virtualKeyCode
)
{
unsigned
long
state
;
long
keyTrans
;
char
charCode
;
Ptr
kchr
;
state
=
0
;
kchr
=
(
Ptr
)
GetScriptVariable
(
smCurrentScript
,
smKCHRCache
);
keyTrans
=
KeyTranslate
(
kchr
,
virtualKeyCode
,
&
state
);
charCode
=
keyTrans
;
if
(
!
charCode
)
charCode
=
(
keyTrans
>>
16
);
return
charCode
;
}
private
:
struct
Qt_Mac_Keymap
{
int
qt_key
;
int
mac_key
;
};
static
Qt_Mac_Keymap
qt_keymap
[];
public
:
static
bool
convertKeySequence
(
const
QKeySequence
&
ks
,
quint32
*
_key
,
quint32
*
_mod
)
{
int
code
=
ks
[
0
];
quint32
mod
=
0
;
if
(
code
&
Qt
::
META
)
mod
|=
controlKey
;
if
(
code
&
Qt
::
SHIFT
)
mod
|=
shiftKey
;
if
(
code
&
Qt
::
CTRL
)
mod
|=
cmdKey
;
if
(
code
&
Qt
::
ALT
)
mod
|=
optionKey
;
code
&=
~
Qt
::
KeyboardModifierMask
;
quint32
key
=
0
;
for
(
int
n
=
0
;
qt_keymap
[
n
].
qt_key
!=
Qt
::
Key_unknown
;
++
n
)
{
if
(
qt_keymap
[
n
].
qt_key
==
code
)
{
key
=
qt_keymap
[
n
].
mac_key
;
break
;
}
}
if
(
key
==
0
)
{
key
=
asciiToKeyCode
(
&
key_codes_
,
code
&
0xffff
);
}
if
(
_mod
)
*
_mod
=
mod
;
if
(
_key
)
*
_key
=
key
;
return
true
;
}
};
MacKeyTriggerManager
*
MacKeyTriggerManager
::
instance_
=
NULL
;
EventHandlerUPP
MacKeyTriggerManager
::
hot_key_function_
=
NULL
;
MacKeyTriggerManager
::
Ascii2KeyCodeTable
MacKeyTriggerManager
::
key_codes_
;
class
GlobalShortcutManager
::
KeyTrigger
::
Impl
:
public
MacKeyTrigger
{
private
:
KeyTrigger
*
trigger_
;
EventHotKeyRef
hotKey_
;
int
id_
;
static
int
nextId
;
public
:
/**
* Constructor registers the hotkey.
*/
Impl
(
GlobalShortcutManager
::
KeyTrigger
*
t
,
const
QKeySequence
&
ks
)
:
trigger_
(
t
)
,
id_
(
0
)
{
MacKeyTriggerManager
::
instance
()
->
addTrigger
(
this
);
quint32
key
,
mod
;
if
(
MacKeyTriggerManager
::
convertKeySequence
(
ks
,
&
key
,
&
mod
))
{
EventHotKeyID
hotKeyID
;
hotKeyID
.
signature
=
'
QtHK
'
;
hotKeyID
.
id
=
nextId
;
OSStatus
ret
=
RegisterEventHotKey
(
key
,
mod
,
hotKeyID
,
GetApplicationEventTarget
(),
0
,
&
hotKey_
);
if
(
ret
!=
0
)
{
qWarning
(
"RegisterEventHotKey(%d, %d): %d"
,
key
,
mod
,
(
int
)
ret
);
return
;
}
id_
=
nextId
++
;
}
}
/**
* Destructor unregisters the hotkey.
*/
~
Impl
()
{
MacKeyTriggerManager
::
instance
()
->
removeTrigger
(
this
);
if
(
id_
)
UnregisterEventHotKey
(
hotKey_
);
}
void
activate
()
{
emit
trigger_
->
activated
();
}
bool
isAccepted
(
int
id
)
const
{
return
id_
==
id
;
}
};
/*
* The following table is from Apple sample-code.
* Apple's headers don't appear to define any constants for the virtual key
* codes of special keys, but these constants are somewhat documented in the chart at
* <http://developer.apple.com/techpubs/mac/Text/Text-571.html#MARKER-9-18>
*
* The constants on the chartappear to be the same values as are used in Apple's iGetKeys
* sample.
* <http://developer.apple.com/samplecode/Sample_Code/Human_Interface_Toolbox/Mac_OS_High_Level_Toolbox/iGetKeys.htm>.
*
* See also <http://www.mactech.com/articles/mactech/Vol.04/04.12/Macinkeys/>.
*/
MacKeyTriggerManager
::
Qt_Mac_Keymap
MacKeyTriggerManager
::
qt_keymap
[]
=
{
{
Qt
::
Key_Escape
,
0x35
},
{
Qt
::
Key_Tab
,
0x30
},
{
Qt
::
Key_Backtab
,
0
},
{
Qt
::
Key_Backspace
,
0x33
},
{
Qt
::
Key_Return
,
0x24
},
{
Qt
::
Key_Enter
,
0x4c
},
// Return & Enter are different on the Mac
{
Qt
::
Key_Insert
,
0
},
{
Qt
::
Key_Delete
,
0x75
},
{
Qt
::
Key_Pause
,
0
},
{
Qt
::
Key_Print
,
0
},
{
Qt
::
Key_SysReq
,
0
},
{
Qt
::
Key_Clear
,
0x47
},
{
Qt
::
Key_Home
,
0x73
},
{
Qt
::
Key_End
,
0x77
},
{
Qt
::
Key_Left
,
0x7b
},
{
Qt
::
Key_Up
,
0x7e
},
{
Qt
::
Key_Right
,
0x7c
},
{
Qt
::
Key_Down
,
0x7d
},
{
Qt
::
Key_PageUp
,
0x74
},
// Page Up
{
Qt
::
Key_PageDown
,
0x79
},
// Page Down
{
Qt
::
Key_Shift
,
0x38
},
{
Qt
::
Key_Control
,
0x3b
},
{
Qt
::
Key_Meta
,
0x37
},
// Command
{
Qt
::
Key_Alt
,
0x3a
},
// Option
{
Qt
::
Key_CapsLock
,
57
},
{
Qt
::
Key_NumLock
,
0
},
{
Qt
::
Key_ScrollLock
,
0
},
{
Qt
::
Key_F1
,
0x7a
},
{
Qt
::
Key_F2
,
0x78
},
{
Qt
::
Key_F3
,
0x63
},
{
Qt
::
Key_F4
,
0x76
},
{
Qt
::
Key_F5
,
0x60
},
{
Qt
::
Key_F6
,
0x61
},
{
Qt
::
Key_F7
,
0x62
},
{
Qt
::
Key_F8
,
0x64
},
{
Qt
::
Key_F9
,
0x65
},
{
Qt
::
Key_F10
,
0x6d
},
{
Qt
::
Key_F11
,
0x67
},
{
Qt
::
Key_F12
,
0x6f
},
{
Qt
::
Key_F13
,
0x69
},
{
Qt
::
Key_F14
,
0x6b
},
{
Qt
::
Key_F15
,
0x71
},
{
Qt
::
Key_F16
,
0
},
{
Qt
::
Key_F17
,
0
},
{
Qt
::
Key_F18
,
0
},
{
Qt
::
Key_F19
,
0
},
{
Qt
::
Key_F20
,
0
},
{
Qt
::
Key_F21
,
0
},
{
Qt
::
Key_F22
,
0
},
{
Qt
::
Key_F23
,
0
},
{
Qt
::
Key_F24
,
0
},
{
Qt
::
Key_F25
,
0
},
{
Qt
::
Key_F26
,
0
},
{
Qt
::
Key_F27
,
0
},
{
Qt
::
Key_F28
,
0
},
{
Qt
::
Key_F29
,
0
},
{
Qt
::
Key_F30
,
0
},
{
Qt
::
Key_F31
,
0
},
{
Qt
::
Key_F32
,
0
},
{
Qt
::
Key_F33
,
0
},
{
Qt
::
Key_F34
,
0
},
{
Qt
::
Key_F35
,
0
},
{
Qt
::
Key_Super_L
,
0
},
{
Qt
::
Key_Super_R
,
0
},
{
Qt
::
Key_Menu
,
0
},
{
Qt
::
Key_Hyper_L
,
0
},
{
Qt
::
Key_Hyper_R
,
0
},
{
Qt
::
Key_Help
,
0x72
},
{
Qt
::
Key_Direction_L
,
0
},
{
Qt
::
Key_Direction_R
,
0
},
{
Qt
::
Key_unknown
,
0
}
};
int
GlobalShortcutManager
::
KeyTrigger
::
Impl
::
nextId
=
1
;
GlobalShortcutManager
::
KeyTrigger
::
KeyTrigger
(
const
QKeySequence
&
key
)
{
d
=
new
Impl
(
this
,
key
);
}
GlobalShortcutManager
::
KeyTrigger
::~
KeyTrigger
()
{
delete
d
;
d
=
0
;
}
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Sat, Nov 14, 7:06 PM (17 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
136953
Default Alt Text
globalshortcutmanager_mac.cpp (10 KB)
Attached To
Mode
R63 darkscreen
Attached
Detach File
Event Timeline