Commit 6bae7cf7 authored by Ken Thomases's avatar Ken Thomases Committed by Alexandre Julliard

winemac: Implement support for global hot keys.

Partially based on a patch for winex11 previously submitted by Vincent Povirk.
parent 5a859655
......@@ -34,6 +34,9 @@
int kq; /* kqueue for waiting in OnMainThread(). */
macdrv_event_handler event_handler;
NSMutableDictionary* hotKeysByMacID;
NSMutableDictionary* hotKeysByWinID;
}
- (void) postEvent:(macdrv_event*)inEvent;
......
......@@ -22,6 +22,7 @@
#include <sys/event.h>
#include <sys/time.h>
#include <libkern/OSAtomic.h>
#import <Carbon/Carbon.h>
#include "macdrv_cocoa.h"
#import "cocoa_event.h"
......@@ -31,6 +32,13 @@
static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThreadDictionaryKey";
static NSString* const WineHotKeyMacIDKey = @"macID";
static NSString* const WineHotKeyVkeyKey = @"vkey";
static NSString* const WineHotKeyModFlagsKey = @"modFlags";
static NSString* const WineHotKeyKeyCodeKey = @"keyCode";
static NSString* const WineHotKeyCarbonRefKey = @"hotKeyRef";
static const OSType WineHotKeySignature = 'Wine';
@interface MacDrvEvent : NSObject
{
......@@ -127,6 +135,16 @@ static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThrea
- (void) dealloc
{
NSNumber* hotKeyMacID;
for (hotKeyMacID in hotKeysByMacID)
{
NSDictionary* hotKeyDict = [hotKeysByMacID objectForKey:hotKeyMacID];
EventHotKeyRef hotKeyRef = [[hotKeyDict objectForKey:WineHotKeyCarbonRefKey] pointerValue];
UnregisterEventHotKey(hotKeyRef);
}
[hotKeysByMacID release];
[hotKeysByWinID release];
[events release];
[eventsLock release];
......@@ -304,6 +322,125 @@ static NSString* const WineEventQueueThreadDictionaryKey = @"WineEventQueueThrea
[eventsLock unlock];
}
- (BOOL) postHotKeyEvent:(UInt32)hotKeyNumber time:(double)time
{
NSDictionary* hotKeyDict = [hotKeysByMacID objectForKey:[NSNumber numberWithUnsignedInt:hotKeyNumber]];
if (hotKeyDict)
{
macdrv_event* event;
event = macdrv_create_event(HOTKEY_PRESS, nil);
event->hotkey_press.vkey = [[hotKeyDict objectForKey:WineHotKeyVkeyKey] unsignedIntValue];
event->hotkey_press.mod_flags = [[hotKeyDict objectForKey:WineHotKeyModFlagsKey] unsignedIntValue];
event->hotkey_press.keycode = [[hotKeyDict objectForKey:WineHotKeyKeyCodeKey] unsignedIntValue];
event->hotkey_press.time_ms = [[WineApplicationController sharedController] ticksForEventTime:time];
[self postEvent:event];
macdrv_release_event(event);
}
return hotKeyDict != nil;
}
static OSStatus HotKeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
WineEventQueue* self = userData;
OSStatus status;
EventHotKeyID hotKeyID;
status = GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, NULL,
sizeof(hotKeyID), NULL, &hotKeyID);
if (status == noErr)
{
if (hotKeyID.signature != WineHotKeySignature ||
![self postHotKeyEvent:hotKeyID.id time:GetEventTime(theEvent)])
status = eventNotHandledErr;
}
return status;
}
- (void) unregisterHotKey:(unsigned int)vkey modFlags:(unsigned int)modFlags
{
NSNumber* vkeyNumber = [NSNumber numberWithUnsignedInt:vkey];
NSNumber* modFlagsNumber = [NSNumber numberWithUnsignedInt:modFlags];
NSArray* winIDPair = [NSArray arrayWithObjects:vkeyNumber, modFlagsNumber, nil];
NSDictionary* hotKeyDict = [hotKeysByWinID objectForKey:winIDPair];
if (hotKeyDict)
{
EventHotKeyRef hotKeyRef = [[hotKeyDict objectForKey:WineHotKeyCarbonRefKey] pointerValue];
NSNumber* macID = [hotKeyDict objectForKey:WineHotKeyMacIDKey];
UnregisterEventHotKey(hotKeyRef);
[hotKeysByMacID removeObjectForKey:macID];
[hotKeysByWinID removeObjectForKey:winIDPair];
}
}
- (int) registerHotKey:(UInt32)keyCode modifiers:(UInt32)modifiers vkey:(unsigned int)vkey modFlags:(unsigned int)modFlags
{
static EventHandlerRef handler;
static UInt32 hotKeyNumber;
OSStatus status;
NSNumber* vkeyNumber;
NSNumber* modFlagsNumber;
NSArray* winIDPair;
EventHotKeyID hotKeyID;
EventHotKeyRef hotKeyRef;
NSNumber* macIDNumber;
NSDictionary* hotKeyDict;
if (!handler)
{
EventTypeSpec eventType = { kEventClassKeyboard, kEventHotKeyPressed };
status = InstallApplicationEventHandler(HotKeyHandler, 1, &eventType, self, &handler);
if (status != noErr)
{
ERR(@"InstallApplicationEventHandler() failed: %d\n", status);
handler = NULL;
return MACDRV_HOTKEY_FAILURE;
}
}
if (!hotKeysByMacID && !(hotKeysByMacID = [[NSMutableDictionary alloc] init]))
return MACDRV_HOTKEY_FAILURE;
if (!hotKeysByWinID && !(hotKeysByWinID = [[NSMutableDictionary alloc] init]))
return MACDRV_HOTKEY_FAILURE;
vkeyNumber = [NSNumber numberWithUnsignedInt:vkey];
modFlagsNumber = [NSNumber numberWithUnsignedInt:modFlags];
winIDPair = [NSArray arrayWithObjects:vkeyNumber, modFlagsNumber, nil];
if ([hotKeysByWinID objectForKey:winIDPair])
return MACDRV_HOTKEY_ALREADY_REGISTERED;
hotKeyID.signature = WineHotKeySignature;
hotKeyID.id = hotKeyNumber++;
status = RegisterEventHotKey(keyCode, modifiers, hotKeyID, GetApplicationEventTarget(),
kEventHotKeyExclusive, &hotKeyRef);
if (status == eventHotKeyExistsErr)
return MACDRV_HOTKEY_ALREADY_REGISTERED;
if (status != noErr)
{
ERR(@"RegisterEventHotKey() failed: %d\n", status);
return MACDRV_HOTKEY_FAILURE;
}
macIDNumber = [NSNumber numberWithUnsignedInt:hotKeyID.id];
hotKeyDict = [NSDictionary dictionaryWithObjectsAndKeys:
macIDNumber, WineHotKeyMacIDKey,
vkeyNumber, WineHotKeyVkeyKey,
modFlagsNumber, WineHotKeyModFlagsKey,
[NSNumber numberWithUnsignedInt:keyCode], WineHotKeyKeyCodeKey,
[NSValue valueWithPointer:hotKeyRef], WineHotKeyCarbonRefKey,
nil];
[hotKeysByMacID setObject:hotKeyDict forKey:macIDNumber];
[hotKeysByWinID setObject:hotKeyDict forKey:winIDPair];
return MACDRV_HOTKEY_SUCCESS;
}
/***********************************************************************
* OnMainThread
......@@ -574,3 +711,33 @@ void macdrv_set_query_done(macdrv_query *query)
}
@end
/***********************************************************************
* macdrv_register_hot_key
*/
int macdrv_register_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags,
unsigned int keycode, unsigned int modifiers)
{
WineEventQueue* queue = (WineEventQueue*)q;
__block int ret;
OnMainThread(^{
ret = [queue registerHotKey:keycode modifiers:modifiers vkey:vkey modFlags:mod_flags];
});
return ret;
}
/***********************************************************************
* macdrv_unregister_hot_key
*/
void macdrv_unregister_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags)
{
WineEventQueue* queue = (WineEventQueue*)q;
OnMainThreadAsync(^{
[queue unregisterHotKey:vkey modFlags:mod_flags];
});
}
......@@ -35,6 +35,7 @@ static const char *dbgstr_event(int type)
"APP_DEACTIVATED",
"APP_QUIT_REQUESTED",
"DISPLAYS_CHANGED",
"HOTKEY_PRESS",
"IM_SET_TEXT",
"KEY_PRESS",
"KEY_RELEASE",
......@@ -69,6 +70,9 @@ static macdrv_event_mask get_event_mask(DWORD mask)
if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return -1;
if (mask & QS_HOTKEY)
event_mask |= event_mask_for_type(HOTKEY_PRESS);
if (mask & QS_KEY)
{
event_mask |= event_mask_for_type(KEY_PRESS);
......@@ -191,6 +195,9 @@ void macdrv_handle_event(const macdrv_event *event)
case DISPLAYS_CHANGED:
macdrv_displays_changed(event);
break;
case HOTKEY_PRESS:
macdrv_hotkey_press(event);
break;
case IM_SET_TEXT:
macdrv_im_set_text(event);
break;
......
......@@ -29,6 +29,7 @@
#include "macdrv.h"
#include "winuser.h"
#include "wine/unicode.h"
#include "wine/server.h"
WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
WINE_DECLARE_DEBUG_CHANNEL(key);
......@@ -786,6 +787,67 @@ static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, DWORD fl
/***********************************************************************
* get_async_key_state
*/
static BOOL get_async_key_state(BYTE state[256])
{
BOOL ret;
SERVER_START_REQ(get_key_state)
{
req->tid = 0;
req->key = -1;
wine_server_set_reply(req, state, 256);
ret = !wine_server_call(req);
}
SERVER_END_REQ;
return ret;
}
/***********************************************************************
* update_modifier_state
*/
static void update_modifier_state(unsigned int modifier, unsigned int modifiers, const BYTE *keystate,
WORD vkey, WORD alt_vkey, WORD scan, WORD alt_scan,
DWORD event_time, BOOL restore)
{
int key_pressed = (modifiers & modifier) != 0;
int vkey_pressed = (keystate[vkey] & 0x80) || (keystate[alt_vkey] & 0x80);
DWORD flags;
if (key_pressed != vkey_pressed)
{
if (key_pressed)
{
flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
if (restore)
flags |= KEYEVENTF_KEYUP;
macdrv_send_keyboard_input(NULL, vkey, scan & 0xff, flags, event_time);
}
else
{
flags = restore ? 0 : KEYEVENTF_KEYUP;
if (keystate[vkey] & 0x80)
{
macdrv_send_keyboard_input(NULL, vkey, scan & 0xff,
flags | ((scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
event_time);
}
if (keystate[alt_vkey] & 0x80)
{
macdrv_send_keyboard_input(NULL, alt_vkey, alt_scan & 0xff,
flags | ((alt_scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0),
event_time);
}
}
}
}
/***********************************************************************
* macdrv_key_event
*
* Handler for KEY_PRESS and KEY_RELEASE events.
......@@ -847,6 +909,59 @@ void macdrv_keyboard_changed(const macdrv_event *event)
/***********************************************************************
* macdrv_hotkey_press
*
* Handler for HOTKEY_PRESS events.
*/
void macdrv_hotkey_press(const macdrv_event *event)
{
struct macdrv_thread_data *thread_data = macdrv_thread_data();
TRACE_(key)("vkey 0x%04x mod_flags 0x%04x keycode 0x%04x time %lu\n",
event->hotkey_press.vkey, event->hotkey_press.mod_flags, event->hotkey_press.keycode,
event->hotkey_press.time_ms);
if (event->hotkey_press.keycode < sizeof(thread_data->keyc2vkey) / sizeof(thread_data->keyc2vkey[0]))
{
WORD scan = thread_data->keyc2scan[event->hotkey_press.keycode];
BYTE keystate[256];
BOOL got_keystate;
DWORD flags;
if ((got_keystate = get_async_key_state(keystate)))
{
update_modifier_state(MOD_ALT, event->hotkey_press.mod_flags, keystate, VK_LMENU, VK_RMENU,
0x38, 0x138, event->hotkey_press.time_ms, FALSE);
update_modifier_state(MOD_CONTROL, event->hotkey_press.mod_flags, keystate, VK_LCONTROL, VK_RCONTROL,
0x1D, 0x11D, event->hotkey_press.time_ms, FALSE);
update_modifier_state(MOD_SHIFT, event->hotkey_press.mod_flags, keystate, VK_LSHIFT, VK_RSHIFT,
0x2A, 0x36, event->hotkey_press.time_ms, FALSE);
update_modifier_state(MOD_WIN, event->hotkey_press.mod_flags, keystate, VK_LWIN, VK_RWIN,
0x15B, 0x15C, event->hotkey_press.time_ms, FALSE);
}
flags = (scan & 0x100) ? KEYEVENTF_EXTENDEDKEY : 0;
macdrv_send_keyboard_input(NULL, event->hotkey_press.vkey, scan & 0xff,
flags, event->key.time_ms);
macdrv_send_keyboard_input(NULL, event->hotkey_press.vkey, scan & 0xff,
flags | KEYEVENTF_KEYUP, event->key.time_ms);
if (got_keystate)
{
update_modifier_state(MOD_ALT, event->hotkey_press.mod_flags, keystate, VK_LMENU, VK_RMENU,
0x38, 0x138, event->hotkey_press.time_ms, TRUE);
update_modifier_state(MOD_CONTROL, event->hotkey_press.mod_flags, keystate, VK_LCONTROL, VK_RCONTROL,
0x1D, 0x11D, event->hotkey_press.time_ms, TRUE);
update_modifier_state(MOD_SHIFT, event->hotkey_press.mod_flags, keystate, VK_LSHIFT, VK_RSHIFT,
0x2A, 0x36, event->hotkey_press.time_ms, TRUE);
update_modifier_state(MOD_WIN, event->hotkey_press.mod_flags, keystate, VK_LWIN, VK_RWIN,
0x15B, 0x15C, event->hotkey_press.time_ms, TRUE);
}
}
}
/***********************************************************************
* get_locale_keyboard_layout
*/
static HKL get_locale_keyboard_layout(void)
......@@ -1244,6 +1359,48 @@ UINT CDECL macdrv_MapVirtualKeyEx(UINT wCode, UINT wMapType, HKL hkl)
/***********************************************************************
* RegisterHotKey (MACDRV.@)
*/
BOOL CDECL macdrv_RegisterHotKey(HWND hwnd, UINT mod_flags, UINT vkey)
{
struct macdrv_thread_data *thread_data = macdrv_init_thread_data();
unsigned int keyc, modifiers = 0;
int ret;
TRACE_(key)("hwnd %p mod_flags 0x%04x vkey 0x%04x\n", hwnd, mod_flags, vkey);
/* Find the Mac keycode corresponding to the vkey */
for (keyc = 0; keyc < sizeof(thread_data->keyc2vkey) / sizeof(thread_data->keyc2vkey[0]); keyc++)
if (thread_data->keyc2vkey[keyc] == vkey) break;
if (keyc >= sizeof(thread_data->keyc2vkey) / sizeof(thread_data->keyc2vkey[0]))
{
WARN_(key)("ignoring unknown virtual key 0x%04x\n", vkey);
return TRUE;
}
if (mod_flags & MOD_ALT) modifiers |= cmdKey;
if (mod_flags & MOD_CONTROL) modifiers |= controlKey;
if (mod_flags & MOD_SHIFT) modifiers |= shiftKey;
if (mod_flags & MOD_WIN)
{
WARN_(key)("MOD_WIN not supported; ignoring\n");
return TRUE;
}
ret = macdrv_register_hot_key(thread_data->queue, vkey, mod_flags, keyc, modifiers);
TRACE_(key)("keyc 0x%04x modifiers 0x%08x -> %d\n", keyc, modifiers, ret);
if (ret == MACDRV_HOTKEY_ALREADY_REGISTERED)
SetLastError(ERROR_HOTKEY_ALREADY_REGISTERED);
else if (ret != MACDRV_HOTKEY_SUCCESS)
SetLastError(ERROR_GEN_FAILURE);
return ret == MACDRV_HOTKEY_SUCCESS;
}
/***********************************************************************
* ToUnicodeEx (MACDRV.@)
*
* The ToUnicode function translates the specified virtual-key code and keyboard
......@@ -1446,6 +1603,20 @@ done:
/***********************************************************************
* UnregisterHotKey (MACDRV.@)
*/
void CDECL macdrv_UnregisterHotKey(HWND hwnd, UINT modifiers, UINT vkey)
{
struct macdrv_thread_data *thread_data = macdrv_thread_data();
TRACE_(key)("hwnd %p modifiers 0x%04x vkey 0x%04x\n", hwnd, modifiers, vkey);
if (thread_data)
macdrv_unregister_hot_key(thread_data->queue, vkey, modifiers);
}
/***********************************************************************
* VkKeyScanEx (MACDRV.@)
*
* Note: Windows ignores HKL parameter and uses current active layout instead
......
......@@ -172,6 +172,7 @@ extern void macdrv_release_capture(HWND hwnd, const macdrv_event *event) DECLSPE
extern void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data) DECLSPEC_HIDDEN;
extern void macdrv_keyboard_changed(const macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_key_event(HWND hwnd, const macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_hotkey_press(const macdrv_event *event) DECLSPEC_HIDDEN;
extern void macdrv_displays_changed(const macdrv_event *event) DECLSPEC_HIDDEN;
......
......@@ -117,6 +117,12 @@ enum {
TOPMOST_FLOAT_INACTIVE_ALL,
};
enum {
MACDRV_HOTKEY_SUCCESS,
MACDRV_HOTKEY_ALREADY_REGISTERED,
MACDRV_HOTKEY_FAILURE,
};
typedef struct macdrv_opaque_window* macdrv_window;
typedef struct macdrv_opaque_event_queue* macdrv_event_queue;
......@@ -166,6 +172,7 @@ enum {
APP_DEACTIVATED,
APP_QUIT_REQUESTED,
DISPLAYS_CHANGED,
HOTKEY_PRESS,
IM_SET_TEXT,
KEY_PRESS,
KEY_RELEASE,
......@@ -209,6 +216,12 @@ typedef struct macdrv_event {
int activating;
} displays_changed;
struct {
unsigned int vkey;
unsigned int mod_flags;
unsigned int keycode;
unsigned long time_ms;
} hotkey_press;
struct {
void *data;
CFStringRef text; /* new text or NULL if just completing existing text */
unsigned int cursor_pos;
......@@ -327,6 +340,9 @@ extern macdrv_query* macdrv_create_query(void) DECLSPEC_HIDDEN;
extern macdrv_query* macdrv_retain_query(macdrv_query *query) DECLSPEC_HIDDEN;
extern void macdrv_release_query(macdrv_query *query) DECLSPEC_HIDDEN;
extern void macdrv_set_query_done(macdrv_query *query) DECLSPEC_HIDDEN;
extern int macdrv_register_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags,
unsigned int keycode, unsigned int modifiers) DECLSPEC_HIDDEN;
extern void macdrv_unregister_hot_key(macdrv_event_queue q, unsigned int vkey, unsigned int mod_flags) DECLSPEC_HIDDEN;
/* window */
......
......@@ -28,6 +28,7 @@
@ cdecl IsClipboardFormatAvailable(long) macdrv_IsClipboardFormatAvailable
@ cdecl MapVirtualKeyEx(long long long) macdrv_MapVirtualKeyEx
@ cdecl MsgWaitForMultipleObjectsEx(long ptr long long long) macdrv_MsgWaitForMultipleObjectsEx
@ cdecl RegisterHotKey(long long long) macdrv_RegisterHotKey
@ cdecl SetCapture(long long) macdrv_SetCapture
@ cdecl SetClipboardData(long long long) macdrv_SetClipboardData
@ cdecl SetCursor(long) macdrv_SetCursor
......@@ -42,6 +43,7 @@
@ cdecl SysCommand(long long long) macdrv_SysCommand
@ cdecl SystemParametersInfo(long long ptr long) macdrv_SystemParametersInfo
@ cdecl ToUnicodeEx(long long ptr ptr long long long) macdrv_ToUnicodeEx
@ cdecl UnregisterHotKey(long long long) macdrv_UnregisterHotKey
@ cdecl UpdateLayeredWindow(long ptr ptr) macdrv_UpdateLayeredWindow
@ cdecl VkKeyScanEx(long long) macdrv_VkKeyScanEx
@ cdecl WindowMessage(long long long long) macdrv_WindowMessage
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment