mutter/clutter/osx/clutter-event-osx.c

497 lines
15 KiB
C
Raw Normal View History

/* Clutter - An OpenGL based 'interactive canvas' library.
* OSX backend - event loops integration
*
* Copyright (C) 2007-2008 Tommi Komulainen <tommi.komulainen@iki.fi>
* Copyright (C) 2007 OpenedHand Ltd.
* Copyright (C) 2011 Crystalnix <vgachkaylo@crystalnix.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#include "config.h"
#include "clutter-osx.h"
#include "clutter-device-manager-osx.h"
#include "clutter-stage-osx.h"
#import <AppKit/AppKit.h>
#include <glib.h>
#include "clutter-debug.h"
#include "clutter-device-manager.h"
#include "clutter-event-private.h"
#include "clutter-keysyms.h"
#include "clutter-private.h"
#include "clutter-stage-private.h"
#define WHEEL_DELTA 1
/*************************************************************************/
@interface NSEvent (Clutter)
- (gint)clutterTime;
- (gint)clutterButton;
- (void)clutterX:(gfloat*)ptrX y:(gfloat*)ptrY;
- (gint)clutterModifierState;
- (guint)clutterKeyVal;
@end
@implementation NSEvent (Clutter)
- (gint)clutterTime
{
return [self timestamp] * 1000;
}
- (gint)clutterButton
{
switch ([self buttonNumber])
{
case 0: return 1; /* left */
case 1: return 3; /* right */
case 2: return 2; /* middle */
default: return 1 + [self buttonNumber];
}
}
- (void)clutterX:(gfloat*)ptrX y:(gfloat*)ptrY
{
NSView *view = [[self window] contentView];
NSPoint pt = [view convertPoint:[self locationInWindow] fromView:nil];
*ptrX = pt.x;
*ptrY = pt.y;
}
- (gint)clutterModifierState
{
guint mods = [self modifierFlags];
guint type = [self type];
gint rv = 0;
/* add key masks */
if (mods & NSAlphaShiftKeyMask)
rv |= CLUTTER_LOCK_MASK;
if (mods & NSShiftKeyMask)
rv |= CLUTTER_SHIFT_MASK;
if (mods & NSControlKeyMask)
rv |= CLUTTER_CONTROL_MASK;
if (mods & NSAlternateKeyMask)
rv |= CLUTTER_MOD1_MASK;
if (mods & NSCommandKeyMask)
rv |= CLUTTER_MOD2_MASK;
/* add button mask */
if ((type == NSLeftMouseDragged) ||
(type == NSRightMouseDragged) ||
(type == NSOtherMouseDragged))
rv |= CLUTTER_BUTTON1_MASK << [self buttonNumber];
return rv;
}
- (guint)clutterKeyVal
{
unichar c;
/* FIXME: doing this right is a lot of work, see gdkkeys-quartz.c in gtk+
* For now handle some common/simple keys only. Might not work with other
* hardware than mine (MacBook Pro, finnish layout). Sorry.
*
* charactersIgnoringModifiers ignores most modifiers, not Shift though.
* So, for all Shift-modified keys we'll end up reporting 'keyval' identical
* to 'unicode_value' Instead of <Shift>a or <Shift>3 you'd get <Shift>A
* and <Shift>#
*/
if ([self type] == NSFlagsChanged)
{
switch ([self keyCode])
{
case 54: // Right Command
return CLUTTER_KEY_Meta_R;
case 55: // Left Command
return CLUTTER_KEY_Meta_L;
case 57: // Capslock
return CLUTTER_KEY_Caps_Lock;
case 56: // Left Shift
return CLUTTER_KEY_Shift_L;
case 60: // Right Shift
return CLUTTER_KEY_Shift_R;
case 58: // Left Alt
return CLUTTER_KEY_Alt_L;
case 61: // Right Alt
return CLUTTER_KEY_Alt_R;
case 59: // Left Ctrl
return CLUTTER_KEY_Control_L;
case 62: // Right Ctrl
return CLUTTER_KEY_Control_R;
case 63: // Function
return CLUTTER_KEY_function;
default: // No such key??!??
CLUTTER_NOTE (EVENT, "Got NSFlagsChanged event with keyCode not a known modifier key: %d",
[self keyCode]);
return CLUTTER_KEY_VoidSymbol;
}
}
c = [[self charactersIgnoringModifiers] characterAtIndex:0];
/* Latin-1 characters, 1:1 mapping - this ought to be reliable */
if ((c >= 0x0020 && c <= 0x007e) ||
(c >= 0x00a0 && c <= 0x00ff))
return c;
switch (c)
{
/* these should be fairly standard */
/* (maybe add 0x0008 (Ctrl+H) for backspace too) */
case 0x000d:
return CLUTTER_KEY_Return;
case 0x001b:
return CLUTTER_KEY_Escape;
case 0x007f:
return CLUTTER_KEY_BackSpace;
/* Defined in NSEvent.h */
case NSUpArrowFunctionKey:
return CLUTTER_KEY_Up;
case NSDownArrowFunctionKey:
return CLUTTER_KEY_Down;
case NSLeftArrowFunctionKey:
return CLUTTER_KEY_Left;
case NSRightArrowFunctionKey:
return CLUTTER_KEY_Right;
case NSF1FunctionKey:
return CLUTTER_KEY_F1;
case NSF2FunctionKey:
return CLUTTER_KEY_F2;
case NSF3FunctionKey:
return CLUTTER_KEY_F3;
case NSF4FunctionKey:
return CLUTTER_KEY_F4;
case NSF5FunctionKey:
return CLUTTER_KEY_F5;
case NSF6FunctionKey:
return CLUTTER_KEY_F6;
case NSF7FunctionKey:
return CLUTTER_KEY_F7;
case NSF8FunctionKey:
return CLUTTER_KEY_F8;
case NSF9FunctionKey:
return CLUTTER_KEY_F9;
case NSF10FunctionKey:
return CLUTTER_KEY_F10;
case NSF11FunctionKey:
return CLUTTER_KEY_F11;
case NSF12FunctionKey:
return CLUTTER_KEY_F12;
case NSInsertFunctionKey:
return CLUTTER_KEY_Insert;
case NSDeleteFunctionKey:
return CLUTTER_KEY_Delete;
case NSHomeFunctionKey:
return CLUTTER_KEY_Home;
case NSEndFunctionKey:
return CLUTTER_KEY_End;
case NSPageUpFunctionKey:
return CLUTTER_KEY_Page_Up;
case NSPageDownFunctionKey:
return CLUTTER_KEY_Page_Down;
}
CLUTTER_NOTE (BACKEND, "unhandled unicode key 0x%x (%d)", c, c);
/* hardware dependent, worksforme(tm) Redundant due to above, left around as
* example.
*/
switch ([self keyCode])
{
case 115:
return CLUTTER_KEY_Home;
case 116:
return CLUTTER_KEY_Page_Up;
case 117:
return CLUTTER_KEY_Delete;
case 119:
return CLUTTER_KEY_End;
case 121:
return CLUTTER_KEY_Page_Down;
case 123:
return CLUTTER_KEY_Left;
case 124:
return CLUTTER_KEY_Right;
case 125:
return CLUTTER_KEY_Down;
case 126:
return CLUTTER_KEY_Up;
}
return 0;
}
@end
/*************************************************************************/
static void
take_and_queue_event (ClutterEvent *event)
{
_clutter_event_push (event, FALSE);
}
static void
process_scroll_event (ClutterEvent *event,
gboolean isVertical)
{
2011-02-15 11:34:21 +00:00
ClutterStageWindow *impl;
ClutterStageOSX *stage_osx;
2011-02-15 11:34:21 +00:00
gfloat *scroll_pos;
impl = _clutter_stage_get_window (event->any.stage);
stage_osx = CLUTTER_STAGE_OSX (impl);
2011-02-15 11:34:21 +00:00
scroll_pos = isVertical
? &(stage_osx->scroll_pos_y)
: &(stage_osx->scroll_pos_x);
while (abs (*scroll_pos) >= WHEEL_DELTA)
{
ClutterEvent *event_gen = clutter_event_new (CLUTTER_SCROLL);
event_gen->scroll.time = event->any.time;
event_gen->scroll.modifier_state = event->scroll.modifier_state;
event_gen->any.stage = event->any.stage;
event_gen->scroll.x = event->scroll.x;
event_gen->scroll.y = event->scroll.y;
if (*scroll_pos > 0)
{
event_gen->scroll.direction = isVertical ? CLUTTER_SCROLL_UP : CLUTTER_SCROLL_RIGHT;
*scroll_pos -= WHEEL_DELTA;
}
else
{
event_gen->scroll.direction = isVertical ? CLUTTER_SCROLL_DOWN : CLUTTER_SCROLL_LEFT;
*scroll_pos += WHEEL_DELTA;
}
clutter_event_set_device (event_gen, clutter_event_get_device (event));
take_and_queue_event (event_gen);
CLUTTER_NOTE (EVENT, "scroll %s at %f,%f",
(event_gen->scroll.direction == CLUTTER_SCROLL_UP) ? "UP" :
(
(event_gen->scroll.direction == CLUTTER_SCROLL_DOWN) ? "DOWN" :
(
(event_gen->scroll.direction == CLUTTER_SCROLL_RIGHT) ? "RIGHT" : "LEFT")),
event->scroll.x, event->scroll.y);
}
}
static gboolean
clutter_event_osx_translate (NSEvent *nsevent,
ClutterEvent *event)
{
ClutterDeviceManagerOSX *manager_osx;
ClutterDeviceManager *manager;
ClutterStageOSX *stage_osx;
ClutterStageWindow *impl;
ClutterStage *stage;
manager = clutter_device_manager_get_default ();
if (manager == NULL)
return FALSE;
stage = event->any.stage;
impl = _clutter_stage_get_window (event->any.stage);
stage_osx = CLUTTER_STAGE_OSX (impl);
manager_osx = CLUTTER_DEVICE_MANAGER_OSX (manager);
event->any.time = [nsevent clutterTime];
switch ([nsevent type])
{
case NSLeftMouseDown:
case NSRightMouseDown:
case NSOtherMouseDown:
event->type = CLUTTER_BUTTON_PRESS;
/* fall through */
case NSLeftMouseUp:
case NSRightMouseUp:
case NSOtherMouseUp:
if (event->type != CLUTTER_BUTTON_PRESS)
event->type = CLUTTER_BUTTON_RELEASE;
event->button.button = [nsevent clutterButton];
event->button.click_count = [nsevent clickCount];
event->motion.modifier_state = [nsevent clutterModifierState];
[nsevent clutterX:&(event->button.x) y:&(event->button.y)];
clutter_event_set_device (event, manager_osx->core_pointer);
CLUTTER_NOTE (EVENT, "button %d %s at %f,%f clicks=%d",
(int)[nsevent buttonNumber],
event->type == CLUTTER_BUTTON_PRESS ? "press" : "release",
event->button.x, event->button.y,
event->button.click_count);
return TRUE;
case NSMouseMoved:
case NSLeftMouseDragged:
case NSRightMouseDragged:
case NSOtherMouseDragged:
event->type = CLUTTER_MOTION;
[nsevent clutterX:&(event->motion.x) y:&(event->motion.y)];
event->motion.modifier_state = [nsevent clutterModifierState];
clutter_event_set_device (event, manager_osx->core_pointer);
CLUTTER_NOTE (EVENT, "motion %d at %f,%f",
(int)[nsevent buttonNumber],
event->button.x, event->button.y);
return TRUE;
case NSMouseEntered:
event->type = CLUTTER_ENTER;
[nsevent clutterX:&(event->crossing.x) y:&(event->crossing.y)];
event->crossing.related = NULL;
event->crossing.source = CLUTTER_ACTOR (stage);
clutter_event_set_device (event, manager_osx->core_pointer);
_clutter_stage_add_device (stage, manager_osx->core_pointer);
CLUTTER_NOTE (EVENT, "enter at %f,%f",
event->crossing.x, event->crossing.y);
return TRUE;
case NSMouseExited:
event->type = CLUTTER_LEAVE;
[nsevent clutterX:&(event->crossing.x) y:&(event->crossing.y)];
event->crossing.related = NULL;
event->crossing.source = CLUTTER_ACTOR (stage);
clutter_event_set_device (event, manager_osx->core_pointer);
_clutter_stage_remove_device (stage, manager_osx->core_pointer);
CLUTTER_NOTE (EVENT, "exit at %f,%f",
event->crossing.x, event->crossing.y);
return TRUE;
case NSScrollWheel:
stage_osx->scroll_pos_x += [nsevent deltaX];
stage_osx->scroll_pos_y += [nsevent deltaY];
[nsevent clutterX:&(event->scroll.x) y:&(event->scroll.y)];
event->scroll.modifier_state = [nsevent clutterModifierState];
clutter_event_set_device (event, manager_osx->core_pointer);
process_scroll_event (event, TRUE);
process_scroll_event (event, FALSE);
break;
case NSFlagsChanged:
// FIXME: This logic fails if the user presses both Shift keys at once, for example:
// we treat releasing one of them as keyDown.
switch ([nsevent keyCode])
{
case 54: // Right Command
case 55: // Left Command
if ([nsevent modifierFlags] & NSCommandKeyMask)
event->type = CLUTTER_KEY_PRESS;
break;
case 57: // Capslock
if ([nsevent modifierFlags] & NSAlphaShiftKeyMask)
event->type = CLUTTER_KEY_PRESS;
break;
case 56: // Left Shift
case 60: // Right Shift
if ([nsevent modifierFlags] & NSShiftKeyMask)
event->type = CLUTTER_KEY_PRESS;
break;
case 58: // Left Alt
case 61: // Right Alt
if ([nsevent modifierFlags] & NSAlternateKeyMask)
event->type = CLUTTER_KEY_PRESS;
break;
case 59: // Left Ctrl
case 62: // Right Ctrl
if ([nsevent modifierFlags] & NSControlKeyMask)
event->type = CLUTTER_KEY_PRESS;
break;
case 63: // Function
if ([nsevent modifierFlags] & NSFunctionKeyMask)
event->type = CLUTTER_KEY_PRESS;
break;
}
/* fall through */
case NSKeyDown:
if ([nsevent type] == NSKeyDown)
event->type = CLUTTER_KEY_PRESS;
/* fall through */
case NSKeyUp:
if (event->type != CLUTTER_KEY_PRESS)
event->type = CLUTTER_KEY_RELEASE;
event->key.hardware_keycode = [nsevent keyCode];
event->key.modifier_state = [nsevent clutterModifierState];
event->key.keyval = [nsevent clutterKeyVal];
event->key.unicode_value = ([nsevent type] == NSFlagsChanged)
? (gunichar)'\0'
: [[nsevent characters] characterAtIndex:0];
clutter_event_set_device (event, manager_osx->core_keyboard);
CLUTTER_NOTE (EVENT, "key %d (%s) (%s) %s, keyval %d",
[nsevent keyCode],
([nsevent type] == NSFlagsChanged) ? "NULL" : [[nsevent characters] UTF8String],
([nsevent type] == NSFlagsChanged) ? "NULL" : [[nsevent charactersIgnoringModifiers] UTF8String],
(event->type == CLUTTER_KEY_PRESS) ? "press" : "release",
event->key.keyval);
return TRUE;
default:
CLUTTER_NOTE (EVENT, "unhandled event %d", (int)[nsevent type]);
break;
}
return FALSE;
}
void
_clutter_event_osx_put (NSEvent *nsevent,
ClutterStage *wrapper)
{
ClutterEvent *event = clutter_event_new (CLUTTER_NOTHING);
/* common fields */
event->any.stage = wrapper;
event->any.time = [nsevent clutterTime];
if (clutter_event_osx_translate (nsevent, event))
{
g_assert (event->type != CLUTTER_NOTHING);
_clutter_event_push (event, FALSE);
}
else
clutter_event_free (event);
}