/* ______ ___ ___
* /\ _ \ /\_ \ /\_ \
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
* /\____/
* \_/__/
*
* MacOS X mouse driver.
*
* By Angelo Mottola.
*
* Modified for 4.9 mouse API by Peter Hull.
*
* See readme.txt for copyright information.
*/
#include "allegro5/allegro.h"
#include "allegro5/allegro_opengl.h"
#include "allegro5/internal/aintern.h"
#include "allegro5/internal/aintern_driver.h"
#include "allegro5/internal/aintern_mouse.h"
#include "allegro5/internal/aintern_keyboard.h"
#include "allegro5/platform/aintosx.h"
#include "./osxgl.h"
#ifndef ALLEGRO_MACOSX
#error Something is wrong with the makefile
#endif
ALLEGRO_DEBUG_CHANNEL("MacOSX");
typedef struct ALLEGRO_MOUSE AL_MOUSE;
typedef struct ALLEGRO_MOUSE_STATE AL_MOUSE_STATE;
static bool osx_init_mouse(void);
static unsigned int osx_get_mouse_num_buttons(void);
static unsigned int osx_get_mouse_num_axes(void);
static bool osx_set_mouse_axis(int axis, int value);
static ALLEGRO_MOUSE* osx_get_mouse(void);
/* Mouse info - includes extra info for OS X */
static struct {
ALLEGRO_MOUSE parent;
unsigned int button_count;
unsigned int axis_count;
int minx, miny, maxx, maxy;
ALLEGRO_MOUSE_STATE state;
float z_axis, w_axis;
BOOL warped;
int warped_x, warped_y;
NSCursor* cursor;
} osx_mouse;
void _al_osx_clear_mouse_state(void)
{
memset(&osx_mouse.state, 0, sizeof(ALLEGRO_MOUSE_STATE));
}
/* _al_osx_switch_keyboard_focus:
* Handle a focus switch event.
*/
void _al_osx_switch_mouse_focus(ALLEGRO_DISPLAY *dpy, bool switch_in)
{
_al_event_source_lock(&osx_mouse.parent.es);
if (switch_in)
osx_mouse.state.display = dpy;
else
osx_mouse.state.display = NULL;
_al_event_source_unlock(&osx_mouse.parent.es);
}
/* osx_get_mouse:
* Return the Allegro mouse structure
*/
static ALLEGRO_MOUSE* osx_get_mouse(void)
{
return (ALLEGRO_MOUSE*) &osx_mouse.parent;
}
/* _al_osx_mouse_generate_event:
* Convert an OS X mouse event to an Allegro event
* and push it into a queue.
* First check that the event is wanted.
*/
void _al_osx_mouse_generate_event(NSEvent* evt, ALLEGRO_DISPLAY* dpy)
{
NSPoint pos;
int type, b_change = 0, b = 0;
float pressure = 0.0;
float dx = 0, dy = 0, dz = 0, dw = 0;
switch ([evt type])
{
case NSMouseMoved:
type = ALLEGRO_EVENT_MOUSE_AXES;
dx = [evt deltaX];
dy = [evt deltaY];
pressure = [evt pressure];
break;
case NSLeftMouseDragged:
case NSRightMouseDragged:
case NSOtherMouseDragged:
type = ALLEGRO_EVENT_MOUSE_AXES;
b = [evt buttonNumber]+1;
dx = [evt deltaX];
dy = [evt deltaY];
pressure = [evt pressure];
break;
case NSLeftMouseDown:
case NSRightMouseDown:
case NSOtherMouseDown:
type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
b = [evt buttonNumber]+1;
b_change = 1;
osx_mouse.state.buttons |= (1 << (b-1));
pressure = [evt pressure];
break;
case NSLeftMouseUp:
case NSRightMouseUp:
case NSOtherMouseUp:
type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
b = [evt buttonNumber]+1;
b_change = 1;
osx_mouse.state.buttons &= ~(1 << (b-1));
pressure = [evt pressure];
break;
case NSScrollWheel:
type = ALLEGRO_EVENT_MOUSE_AXES;
dx = 0;
dy = 0;
osx_mouse.w_axis += al_get_mouse_wheel_precision() * [evt deltaX];
osx_mouse.z_axis += al_get_mouse_wheel_precision() * [evt deltaY];
dw = osx_mouse.w_axis - osx_mouse.state.w;
dz = osx_mouse.z_axis - osx_mouse.state.z;
break;
case NSMouseEntered:
type = ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY;
b = [evt buttonNumber]+1;
dx = [evt deltaX];
dy = [evt deltaY];
break;
case NSMouseExited:
type = ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY;
b = [evt buttonNumber]+1;
dx = [evt deltaX];
dy = [evt deltaY];
break;
default:
return;
}
pos = [evt locationInWindow];
BOOL within = true;
float scaling_factor = 1.0;
if ([evt window])
{
NSRect frm = [[[evt window] contentView] frame];
within = NSMouseInRect(pos, frm, NO);
// Y-coordinates in OS X start from the bottom.
pos.y = NSHeight(frm) - pos.y;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
scaling_factor = [[evt window] backingScaleFactor];
#endif
}
else
{
pos.y = [[NSScreen mainScreen] frame].size.height - pos.y;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
scaling_factor = [[NSScreen mainScreen] backingScaleFactor];
#endif
}
dx *= scaling_factor;
dy *= scaling_factor;
if (osx_mouse.warped && type == ALLEGRO_EVENT_MOUSE_AXES) {
dx -= osx_mouse.warped_x;
dy -= osx_mouse.warped_y;
osx_mouse.warped = FALSE;
}
_al_event_source_lock(&osx_mouse.parent.es);
if ((within || b_change || type == ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY)
&& _al_event_source_needs_to_generate_event(&osx_mouse.parent.es))
{
ALLEGRO_EVENT new_event;
ALLEGRO_MOUSE_EVENT* mouse_event = &new_event.mouse;
mouse_event->type = type;
// Note: we use 'allegro time' rather than the time stamp
// from the event
mouse_event->timestamp = al_get_time();
mouse_event->display = dpy;
mouse_event->button = b;
mouse_event->x = pos.x * scaling_factor;
mouse_event->y = pos.y * scaling_factor;
mouse_event->z = osx_mouse.z_axis;
mouse_event->w = osx_mouse.w_axis;
mouse_event->dx = dx;
mouse_event->dy = dy;
mouse_event->dz = dz;
mouse_event->dw = dw;
mouse_event->pressure = pressure;
_al_event_source_emit_event(&osx_mouse.parent.es, &new_event);
}
// Record current state
osx_mouse.state.x = pos.x * scaling_factor;
osx_mouse.state.y = pos.y * scaling_factor;
osx_mouse.state.w = osx_mouse.w_axis;
osx_mouse.state.z = osx_mouse.z_axis;
osx_mouse.state.pressure = pressure;
_al_event_source_unlock(&osx_mouse.parent.es);
}
/* osx_init_mouse:
* Initializes the driver.
*/
static bool osx_init_mouse(void)
{
/* NOTE: This function is deprecated, however until we have a better fix
* or it is removed, we can used it. The problem without calling this
* function is that after synthesizing events (as with al_set_mouse_xy)
* there is an unacceptably long delay in receiving new events (mouse
* locks up for about 0.5 seconds.)
*/
CGSetLocalEventsSuppressionInterval(0.0);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
HID_DEVICE_COLLECTION devices={0,0,NULL};
int i = 0, j = 0;
int axes = 0, buttons = 0;
int max_axes = 0, max_buttons = 0;
HID_DEVICE* device = nil;
NSString* desc = nil;
_al_osx_hid_scan(HID_MOUSE, &devices);
ALLEGRO_INFO("Detected %d pointing devices\n", devices.count);
for (i=0; i<devices.count; i++) {
device=&devices.devices[i];
buttons = 0;
axes = 0;
desc = [NSString stringWithFormat: @"%s %s",
device->manufacturer ? device->manufacturer : "",
device->product ? device->product : ""];
ALLEGRO_INFO("Device %d is a \"%s\"\n", i, [desc UTF8String]);
for (j = 0; j < device->num_elements; j++) {
switch (device->element[j].type) {
case HID_ELEMENT_BUTTON:
buttons++;
break;
case HID_ELEMENT_AXIS:
case HID_ELEMENT_AXIS_PRIMARY_X:
case HID_ELEMENT_AXIS_PRIMARY_Y:
case HID_ELEMENT_STANDALONE_AXIS:
axes ++;
break;
}
}
ALLEGRO_INFO("Detected %d axes and %d buttons\n", axes, buttons);
/* When mouse events reach the application, it is not clear which
* device generated them, so effectively the largest number of
* buttons and axes reported corresponds to the device that the
* application "sees"
*/
if (axes > max_axes) max_axes = axes;
if (buttons > max_buttons) max_buttons = buttons;
}
_al_osx_hid_free(&devices);
ALLEGRO_INFO("Device effectively has %d axes and %d buttons\n", axes, buttons);
if (max_buttons <= 0) return false;
_al_event_source_init(&osx_mouse.parent.es);
osx_mouse.button_count = max_buttons;
osx_mouse.axis_count = max_axes;
osx_mouse.warped = FALSE;
memset(&osx_mouse.state, 0, sizeof(ALLEGRO_MOUSE_STATE));
_al_osx_mouse_was_installed(YES);
[pool drain];
return true;
}
/* osx_exit_mouse:
* Shut down the mouse driver
*/
static void osx_exit_mouse(void)
{
_al_osx_mouse_was_installed(NO);
}
/* osx_get_mouse_num_buttons:
* Return the number of buttons on the mouse
*/
static unsigned int osx_get_mouse_num_buttons(void)
{
return osx_mouse.button_count;
}
/* osx_get_mouse_num_buttons:
* Return the number of buttons on the mouse
*/
static unsigned int osx_get_mouse_num_axes(void)
{
return osx_mouse.axis_count;
}
static void osx_get_mouse_state(ALLEGRO_MOUSE_STATE *ret_state)
{
_al_event_source_lock(&osx_mouse.parent.es);
memcpy(ret_state, &osx_mouse.state, sizeof(ALLEGRO_MOUSE_STATE));
_al_event_source_unlock(&osx_mouse.parent.es);
}
/* osx_set_mouse_xy:
* Set the current mouse position
*/
static bool osx_set_mouse_xy(ALLEGRO_DISPLAY *dpy_, int x, int y)
{
CGPoint pos;
CGDirectDisplayID display = 0;
ALLEGRO_DISPLAY_OSX_WIN *dpy = (ALLEGRO_DISPLAY_OSX_WIN *)dpy_;
float scaling_factor = 1.0;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
scaling_factor = [[NSScreen mainScreen] backingScaleFactor];
#endif
x /= scaling_factor;
y /= scaling_factor;
if ((dpy) && !(dpy->parent.flags & ALLEGRO_FULLSCREEN) &&
!(dpy->parent.flags & ALLEGRO_FULLSCREEN_WINDOW) && (dpy->win)) {
NSWindow *window = dpy->win;
NSRect content = [window contentRectForFrameRect: [window frame]];
NSRect frame = [[window screen] frame];
CGRect rect = { { NSMinX(frame), NSMinY(frame) }, { NSWidth(frame), NSHeight(frame) } };
CGDirectDisplayID displays[16];
CGDisplayCount displayCount;
if ((CGGetDisplaysWithRect(rect, 16, displays, &displayCount) == 0) && (displayCount >= 1))
display = displays[0];
CGPoint point_pos = CGPointMake(x, y);
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
point_pos = NSPointToCGPoint([[window contentView] convertPointFromBacking: NSPointFromCGPoint(point_pos)]);
#endif
pos.x = content.origin.x + point_pos.x;
pos.y = rect.size.height - content.origin.y - content.size.height + point_pos.y;
}
else {
if (dpy)
display = dpy->display_id;
pos.x = x;
pos.y = y;
}
_al_event_source_lock(&osx_mouse.parent.es);
if (_al_event_source_needs_to_generate_event(&osx_mouse.parent.es)) {
ALLEGRO_EVENT new_event;
ALLEGRO_MOUSE_EVENT* mouse_event = &new_event.mouse;
mouse_event->type = ALLEGRO_EVENT_MOUSE_WARPED;
// Note: we use 'allegro time' rather than the time stamp
// from the event
mouse_event->timestamp = al_get_time();
mouse_event->display = (ALLEGRO_DISPLAY *)dpy;
mouse_event->button = 0;
mouse_event->x = x;
mouse_event->y = y;
mouse_event->z = osx_mouse.z_axis;
mouse_event->dx = x - osx_mouse.state.x;
mouse_event->dy = y - osx_mouse.state.y;
mouse_event->dz = 0;
mouse_event->pressure = osx_mouse.state.pressure;
if (mouse_event->dx || mouse_event->dy) {
osx_mouse.warped = TRUE;
osx_mouse.warped_x = mouse_event->dx;
osx_mouse.warped_y = mouse_event->dy;
_al_event_source_emit_event(&osx_mouse.parent.es, &new_event);
}
}
CGDisplayMoveCursorToPoint(display, pos);
osx_mouse.state.x = x;
osx_mouse.state.y = y;
_al_event_source_unlock(&osx_mouse.parent.es);
return true;
}
/* osx_set_mouse_axis:
* Set the axis value of the mouse
*/
static bool osx_set_mouse_axis(int axis, int value)
{
bool result = false;
_al_event_source_lock(&osx_mouse.parent.es);
switch (axis)
{
case 0:
case 1:
// By design, this doesn't apply to (x, y)
break;
case 2:
osx_mouse.z_axis = value;
result = true;
break;
case 3:
osx_mouse.w_axis = value;
result = true;
break;
}
/* XXX generate an event if the axis value changed */
_al_event_source_unlock(&osx_mouse.parent.es);
return result;
}
/* Mouse driver */
static ALLEGRO_MOUSE_DRIVER osx_mouse_driver =
{
0, //int msedrv_id;
"OSXMouse", //const char *msedrv_name;
"Driver for Mac OS X",// const char *msedrv_desc;
"OSX Mouse", //const char *msedrv_ascii_name;
osx_init_mouse, //AL_METHOD(bool, init_mouse, (void));
osx_exit_mouse, //AL_METHOD(void, exit_mouse, (void));
osx_get_mouse, //AL_METHOD(ALLEGRO_MOUSE*, get_mouse, (void));
osx_get_mouse_num_buttons, //AL_METHOD(unsigned int, get_mouse_num_buttons, (void));
osx_get_mouse_num_axes, //AL_METHOD(unsigned int, get_mouse_num_axes, (void));
osx_set_mouse_xy, //AL_METHOD(bool, set_mouse_xy, (int x, int y));
osx_set_mouse_axis, //AL_METHOD(bool, set_mouse_axis, (int which, int value));
osx_get_mouse_state, //AL_METHOD(void, get_mouse_state, (ALLEGRO_MOUSE_STATE *ret_state));
};
ALLEGRO_MOUSE_DRIVER* _al_osx_get_mouse_driver(void)
{
return &osx_mouse_driver;
}
/* list the available drivers */
_AL_DRIVER_INFO _al_mouse_driver_list[] =
{
{ 1, &osx_mouse_driver, 1 },
{ 0, NULL, 0 }
};
/* Local variables: */
/* c-basic-offset: 3 */
/* indent-tabs-mode: nil */
/* End: */