winemac: Generate KEY_PRESS/RELEASE events from Cocoa key events.

This commit is contained in:
Ken Thomases 2013-02-03 17:20:18 -06:00 committed by Alexandre Julliard
parent d0e1a02515
commit 77de57683f
8 changed files with 259 additions and 1 deletions

View file

@ -42,9 +42,11 @@ @interface WineApplication : NSApplication <NSApplicationDelegate>
unsigned long windowFocusSerial;
CGEventSourceKeyboardType keyboardType;
NSEvent* lastFlagsChanged;
}
@property (nonatomic) CGEventSourceKeyboardType keyboardType;
@property (readonly, copy, nonatomic) NSEvent* lastFlagsChanged;
- (void) transformProcessToForeground;
@ -56,6 +58,8 @@ - (double) ticksForEventTime:(NSTimeInterval)eventTime;
- (void) windowGotFocus:(WineWindow*)window;
- (void) keyboardSelectionDidChange;
@end
void OnMainThread(dispatch_block_t block);

View file

@ -28,9 +28,16 @@
int macdrv_err_on;
@interface WineApplication ()
@property (readwrite, copy, nonatomic) NSEvent* lastFlagsChanged;
@end
@implementation WineApplication
@synthesize keyboardType;
@synthesize keyboardType, lastFlagsChanged;
- (id) init
{
@ -219,6 +226,18 @@ - (void) keyboardSelectionDidChange
}
/*
* ---------- NSApplication method overrides ----------
*/
- (void) sendEvent:(NSEvent*)anEvent
{
if ([anEvent type] == NSFlagsChanged)
self.lastFlagsChanged = anEvent;
[super sendEvent:anEvent];
}
/*
* ---------- NSApplicationDelegate methods ----------
*/

View file

@ -46,6 +46,8 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
BOOL usePerPixelAlpha;
NSUInteger lastModifierFlags;
BOOL causing_becomeKeyWindow;
BOOL ignore_windowMiniaturize;
BOOL ignore_windowDeminiaturize;

View file

@ -18,6 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#import <Carbon/Carbon.h>
#import "cocoa_window.h"
#include "macdrv_cocoa.h"
@ -25,6 +27,12 @@
#import "cocoa_event.h"
/* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
enum {
kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
};
static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
{
NSUInteger style_mask;
@ -55,6 +63,49 @@ static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
}
/* We rely on the supposedly device-dependent modifier flags to distinguish the
keys on the left side of the keyboard from those on the right. Some event
sources don't set those device-depdendent flags. If we see a device-independent
flag for a modifier without either corresponding device-dependent flag, assume
the left one. */
static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
{
if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
*modifiers |= NX_DEVICELCMDKEYMASK;
if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
*modifiers |= NX_DEVICELSHIFTKEYMASK;
if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
*modifiers |= NX_DEVICELCTLKEYMASK;
if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
*modifiers |= NX_DEVICELALTKEYMASK;
}
/* As we manipulate individual bits of a modifier mask, we can end up with
inconsistent sets of flags. In particular, we might set or clear one of the
left/right-specific bits, but not the corresponding non-side-specific bit.
Fix that. If either side-specific bit is set, set the non-side-specific bit,
otherwise clear it. */
static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
{
if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
*modifiers |= NX_COMMANDMASK;
else
*modifiers &= ~NX_COMMANDMASK;
if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
*modifiers |= NX_SHIFTMASK;
else
*modifiers &= ~NX_SHIFTMASK;
if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
*modifiers |= NX_CONTROLMASK;
else
*modifiers &= ~NX_CONTROLMASK;
if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
*modifiers |= NX_ALTERNATEMASK;
else
*modifiers &= ~NX_ALTERNATEMASK;
}
@interface WineContentView : NSView
@end
@ -493,6 +544,44 @@ - (void) makeFocused
[self windowDidResize:nil];
}
- (void) postKey:(uint16_t)keyCode
pressed:(BOOL)pressed
modifiers:(NSUInteger)modifiers
event:(NSEvent*)theEvent
{
macdrv_event event;
CGEventRef cgevent;
WineApplication* app = (WineApplication*)NSApp;
event.type = pressed ? KEY_PRESS : KEY_RELEASE;
event.window = (macdrv_window)[self retain];
event.key.keycode = keyCode;
event.key.modifiers = modifiers;
event.key.time_ms = [app ticksForEventTime:[theEvent timestamp]];
if ((cgevent = [theEvent CGEvent]))
{
CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
kCGKeyboardEventKeyboardType);
if (keyboardType != app.keyboardType)
{
app.keyboardType = keyboardType;
[app keyboardSelectionDidChange];
}
}
[queue postEvent:&event];
}
- (void) postKeyEvent:(NSEvent *)theEvent
{
[self flagsChanged:theEvent];
[self postKey:[theEvent keyCode]
pressed:[theEvent type] == NSKeyDown
modifiers:[theEvent modifierFlags]
event:theEvent];
}
/*
* ---------- NSWindow method overrides ----------
@ -556,12 +645,81 @@ - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent press
- (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
- (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
- (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
- (void) keyUp:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
- (void) flagsChanged:(NSEvent *)theEvent
{
static const struct {
NSUInteger mask;
uint16_t keycode;
} modifiers[] = {
{ NX_ALPHASHIFTMASK, kVK_CapsLock },
{ NX_DEVICELSHIFTKEYMASK, kVK_Shift },
{ NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
{ NX_DEVICELCTLKEYMASK, kVK_Control },
{ NX_DEVICERCTLKEYMASK, kVK_RightControl },
{ NX_DEVICELALTKEYMASK, kVK_Option },
{ NX_DEVICERALTKEYMASK, kVK_RightOption },
{ NX_DEVICELCMDKEYMASK, kVK_Command },
{ NX_DEVICERCMDKEYMASK, kVK_RightCommand },
};
NSUInteger modifierFlags = [theEvent modifierFlags];
NSUInteger changed;
int i, last_changed;
fix_device_modifiers_by_generic(&modifierFlags);
changed = modifierFlags ^ lastModifierFlags;
last_changed = -1;
for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
if (changed & modifiers[i].mask)
last_changed = i;
for (i = 0; i <= last_changed; i++)
{
if (changed & modifiers[i].mask)
{
BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
if (i == last_changed)
lastModifierFlags = modifierFlags;
else
{
lastModifierFlags ^= modifiers[i].mask;
fix_generic_modifiers_by_device(&lastModifierFlags);
}
// Caps lock generates one event for each press-release action.
// We need to simulate a pair of events for each actual event.
if (modifiers[i].mask == NX_ALPHASHIFTMASK)
{
[self postKey:modifiers[i].keycode
pressed:TRUE
modifiers:lastModifierFlags
event:(NSEvent*)theEvent];
pressed = FALSE;
}
[self postKey:modifiers[i].keycode
pressed:pressed
modifiers:lastModifierFlags
event:(NSEvent*)theEvent];
}
}
}
/*
* ---------- NSWindowDelegate methods ----------
*/
- (void)windowDidBecomeKey:(NSNotification *)notification
{
NSEvent* event = [NSApp lastFlagsChanged];
if (event)
[self flagsChanged:event];
if (causing_becomeKeyWindow) return;
[NSApp windowGotFocus:self];

View file

@ -33,6 +33,8 @@ static const char *dbgstr_event(int type)
{
static const char * const event_names[] = {
"APP_DEACTIVATED",
"KEY_PRESS",
"KEY_RELEASE",
"KEYBOARD_CHANGED",
"MOUSE_BUTTON",
"WINDOW_CLOSE_REQUESTED",
@ -58,7 +60,11 @@ static macdrv_event_mask get_event_mask(DWORD mask)
if ((mask & QS_ALLINPUT) == QS_ALLINPUT) return -1;
if (mask & QS_KEY)
{
event_mask |= event_mask_for_type(KEY_PRESS);
event_mask |= event_mask_for_type(KEY_RELEASE);
event_mask |= event_mask_for_type(KEYBOARD_CHANGED);
}
if (mask & QS_MOUSEBUTTON)
event_mask |= event_mask_for_type(MOUSE_BUTTON);
@ -98,6 +104,10 @@ void macdrv_handle_event(macdrv_event *event)
case APP_DEACTIVATED:
macdrv_app_deactivated();
break;
case KEY_PRESS:
case KEY_RELEASE:
macdrv_key_event(hwnd, event);
break;
case KEYBOARD_CHANGED:
macdrv_keyboard_changed(event);
break;

View file

@ -31,6 +31,7 @@
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(keyboard);
WINE_DECLARE_DEBUG_CHANNEL(key);
/* Carbon-style modifier mask definitions from <Carbon/HIToolbox/Events.h>. */
@ -663,6 +664,62 @@ void macdrv_compute_keyboard_layout(struct macdrv_thread_data *thread_data)
}
/***********************************************************************
* macdrv_send_keyboard_input
*/
static void macdrv_send_keyboard_input(HWND hwnd, WORD vkey, WORD scan, DWORD flags, DWORD time)
{
INPUT input;
TRACE_(key)("hwnd %p vkey=%04x scan=%04x flags=%04x\n", hwnd, vkey, scan, flags);
input.type = INPUT_KEYBOARD;
input.ki.wVk = vkey;
input.ki.wScan = scan;
input.ki.dwFlags = flags;
input.ki.time = time;
input.ki.dwExtraInfo = 0;
__wine_send_input(hwnd, &input);
}
/***********************************************************************
* macdrv_key_event
*
* Handler for KEY_PRESS and KEY_RELEASE events.
*/
void macdrv_key_event(HWND hwnd, const macdrv_event *event)
{
struct macdrv_thread_data *thread_data = macdrv_thread_data();
WORD vkey, scan;
DWORD flags;
TRACE_(key)("win %p/%p key %s keycode %hu modifiers 0x%08llx\n",
hwnd, event->window, (event->type == KEY_PRESS ? "press" : "release"),
event->key.keycode, event->key.modifiers);
if (event->key.keycode < sizeof(thread_data->keyc2vkey)/sizeof(thread_data->keyc2vkey[0]))
{
vkey = thread_data->keyc2vkey[event->key.keycode];
scan = thread_data->keyc2scan[event->key.keycode];
}
else
vkey = scan = 0;
TRACE_(key)("keycode %hu converted to vkey 0x%X scan 0x%02x\n",
event->key.keycode, vkey, scan);
if (!vkey) return;
flags = 0;
if (event->type == KEY_RELEASE) flags |= KEYEVENTF_KEYUP;
if (scan & 0x100) flags |= KEYEVENTF_EXTENDEDKEY;
macdrv_send_keyboard_input(hwnd, vkey, scan & 0xff, flags, event->key.time_ms);
}
/***********************************************************************
* macdrv_keyboard_changed
*

View file

@ -135,5 +135,6 @@ static inline RECT rect_from_cgrect(CGRect cgrect)
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;
#endif /* __WINE_MACDRV_H */

View file

@ -125,6 +125,8 @@
/* event */
enum {
APP_DEACTIVATED,
KEY_PRESS,
KEY_RELEASE,
KEYBOARD_CHANGED,
MOUSE_BUTTON,
WINDOW_CLOSE_REQUESTED,
@ -142,6 +144,11 @@
int type;
macdrv_window window;
union {
struct {
CGKeyCode keycode;
CGEventFlags modifiers;
unsigned long time_ms;
} key;
struct {
CFDataRef uchr;
CGEventSourceKeyboardType keyboard_type;