/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * MacOS X mouse driver. * * By Angelo Mottola. * * See readme.txt for copyright information. */ #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/platform/aintosx.h" #ifndef ALLEGRO_MACOSX #error Something is wrong with the makefile #endif static int osx_mouse_init(void); static void osx_mouse_exit(void); static void osx_mouse_position(int, int); static void osx_mouse_set_range(int, int, int, int); static void osx_mouse_get_mickeys(int *, int *); static void osx_enable_hardware_cursor(AL_CONST int mode); static int osx_select_system_cursor(AL_CONST int cursor); MOUSE_DRIVER mouse_macosx = { MOUSE_MACOSX, empty_string, empty_string, "MacOS X mouse", osx_mouse_init, osx_mouse_exit, NULL, // AL_METHOD(void, poll, (void)); NULL, // AL_METHOD(void, timer_poll, (void)); osx_mouse_position, osx_mouse_set_range, NULL, // AL_METHOD(void, set_speed, (int xspeed, int yspeed)); osx_mouse_get_mickeys, NULL, // AL_METHOD(int, analyse_data, (AL_CONST char *buffer, int size)); osx_enable_hardware_cursor, osx_select_system_cursor }; /* global variable */ int osx_mouse_warped = FALSE; int osx_skip_mouse_move = FALSE; NSTrackingRectTag osx_mouse_tracking_rect = -1; static NSCursor *cursor = NULL, *current_cursor = NULL; static NSCursor *requested_cursor = NULL; static unsigned char *cursor_data = NULL; static NSBitmapImageRep *cursor_rep = NULL; static NSImage *cursor_image = NULL; static int mouse_minx = 0; static int mouse_miny = 0; static int mouse_maxx = 319; static int mouse_maxy = 199; static int mymickey_x = 0; static int mymickey_y = 0; static char driver_desc[256]; /* osx_change_cursor: * Actually change the current cursor. This can be called fom any thread * but ensures that the change is only called from the main thread. */ static void osx_change_cursor(NSCursor* cursor) { _unix_lock_mutex(osx_event_mutex); osx_cursor = cursor; _unix_unlock_mutex(osx_event_mutex); [cursor performSelectorOnMainThread: @selector(set) withObject: nil waitUntilDone: NO]; } /* osx_mouse_handler: * Mouse "interrupt" handler for mickey-mode driver. */ void osx_mouse_handler(int ax, int ay, int x, int y, int z, int buttons) { if (!_mouse_on) mymickey_x = mymickey_y = 0; if ((!_mouse_installed) || (!_mouse_on) || (osx_gfx_mode == OSX_GFX_NONE)) return; if (osx_cursor != current_cursor) { if (osx_window) { NSView* vw = [osx_window contentView]; [osx_window invalidateCursorRectsForView: vw]; } else { [osx_cursor set]; } current_cursor = osx_cursor; } if (osx_mouse_warped) { osx_mouse_warped = FALSE; return; } _mouse_b = buttons; mymickey_x += x; mymickey_y += y; _mouse_x = ax; _mouse_y = ay; _mouse_z += z; _mouse_x = MID(mouse_minx, _mouse_x, mouse_maxx); _mouse_y = MID(mouse_miny, _mouse_y, mouse_maxy); _handle_mouse_input(); } /* osx_mouse_init: * Initializes the mickey-mode driver. */ static int osx_mouse_init(void) { HID_DEVICE_COLLECTION devices={0,0,NULL}; int i, j; int buttons, max_buttons = -1; HID_DEVICE* device; if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) { /* On 10.1.x mice and keyboards aren't available from the HID Manager, * so we can't autodetect. We assume an 1-button mouse to always be * present. */ max_buttons = 1; } else { osx_hid_scan(HID_MOUSE, &devices); for (i = 0; i < devices.count; i++) { device=&devices.devices[i]; buttons = 0; for (j = 0; j < device->num_elements; j++) { if (device->element[j].type == HID_ELEMENT_BUTTON) buttons++; } if (buttons > max_buttons) { max_buttons = buttons; _al_sane_strncpy(driver_desc, "", sizeof(driver_desc)); if (device->manufacturer) { strncat(driver_desc, device->manufacturer, sizeof(driver_desc)-1); strncat(driver_desc, " ", sizeof(driver_desc)-1); } if (device->product) strncat(driver_desc, device->product, sizeof(driver_desc)-1); mouse_macosx.desc = driver_desc; } } osx_hid_free(&devices); } _unix_lock_mutex(osx_event_mutex); osx_emulate_mouse_buttons = (max_buttons == 1) ? TRUE : FALSE; _unix_unlock_mutex(osx_event_mutex); return max_buttons; } /* osx_mouse_exit: * Shuts down the mickey-mode driver. */ static void osx_mouse_exit(void) { osx_cursor = osx_blank_cursor; if (cursor) [cursor release]; if (cursor_image) [cursor_image release]; if (cursor_rep) [cursor_rep release]; if (cursor_data) free(cursor_data); cursor = NULL; cursor_image = NULL; cursor_rep = NULL; cursor_data = NULL; } /* osx_mouse_position: * Sets the position of the mickey-mode mouse. */ static void osx_mouse_position(int x, int y) { CGPoint point; NSRect frame; int screen_height; _unix_lock_mutex(osx_event_mutex); _mouse_x = point.x = x; _mouse_y = point.y = y; if (osx_window) { CFNumberGetValue(CFDictionaryGetValue(CGDisplayCurrentMode(kCGDirectMainDisplay), kCGDisplayHeight), kCFNumberSInt32Type, &screen_height); frame = [osx_window frame]; point.x += frame.origin.x; point.y += (screen_height - (frame.origin.y + gfx_driver->h)); } CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, point); mymickey_x = mymickey_y = 0; osx_mouse_warped = TRUE; _unix_unlock_mutex(osx_event_mutex); } /* osx_mouse_set_range: * Sets the range of the mickey-mode mouse. */ static void osx_mouse_set_range(int x1, int y1, int x2, int y2) { mouse_minx = x1; mouse_miny = y1; mouse_maxx = x2; mouse_maxy = y2; osx_mouse_position(MID(mouse_minx, _mouse_x, mouse_maxx), MID(mouse_miny, _mouse_y, mouse_maxy)); } /* osx_mouse_get_mickeys: * Reads the mickey-mode count. */ static void osx_mouse_get_mickeys(int *mickeyx, int *mickeyy) { _unix_lock_mutex(osx_event_mutex); *mickeyx = mymickey_x; *mickeyy = mymickey_y; mymickey_x = mymickey_y = 0; _unix_unlock_mutex(osx_event_mutex); } /* osx_mouse_set_sprite: * Sets the hardware cursor sprite. */ int osx_mouse_set_sprite(BITMAP *sprite, int x, int y) { int ix, iy; int sw, sh; if (!sprite) return -1; sw = sprite->w; sh = sprite->h; if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_2) { // Before MacOS X 10.3, NSCursor can handle only 16x16 cursor sprites // Pad to 16x16 or fail if the sprite is already larger. if (sw>16 || sh>16) return -1; sh = sw = 16; } // Delete the old cursor (OK to send a message to nil) [cursor release]; NSBitmapImageRep* cursor_rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL pixelsWide: sw pixelsHigh: sh bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES isPlanar: NO colorSpaceName: NSDeviceRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0]; int bpp = bitmap_color_depth(sprite); int mask = bitmap_mask_color(sprite); for (iy = 0; iy< sh; ++iy) { unsigned char* ptr = [cursor_rep bitmapData] + (iy * [cursor_rep bytesPerRow]); for (ix = 0; ix< sw; ++ix) { int color = is_inside_bitmap(sprite, ix, iy, 1) ? getpixel(sprite, ix, iy) : mask; // Disable the possibility of mouse sprites with alpha for now, because // this causes the built-in cursors to be invisible in 32 bit mode. // int alpha = (color == mask) ? 0 : ((bpp == 32) ? geta_depth(bpp, color) : 255); int alpha = (color == mask) ? 0 : 255; // BitmapImageReps use premultiplied alpha ptr[0] = getb_depth(bpp, color) * alpha / 255; ptr[1] = getg_depth(bpp, color) * alpha / 255; ptr[2] = getr_depth(bpp, color) * alpha / 255; ptr[3] = alpha; ptr += 4; } } NSImage* cursor_image = [[NSImage alloc] initWithSize: NSMakeSize(sw, sh)]; [cursor_image addRepresentation: cursor_rep]; [cursor_rep release]; cursor = [[NSCursor alloc] initWithImage: cursor_image hotSpot: NSMakePoint(x, y)]; [cursor_image release]; osx_change_cursor(requested_cursor = cursor); return 0; } /* osx_mouse_show: * Show the hardware cursor. */ int osx_mouse_show(BITMAP *bmp, int x, int y) { /* Only draw on screen */ if (!is_same_bitmap(bmp, screen)) return -1; if (!requested_cursor) return -1; osx_change_cursor(requested_cursor); return 0; } /* osx_mouse_hide: * Hide the hardware cursor. */ void osx_mouse_hide(void) { osx_change_cursor(osx_blank_cursor); } /* osx_mouse_move: * Get mouse move notification. Not that we need it... */ void osx_mouse_move(int x, int y) { } /* osx_enable_hardware_cursor: * Enable hardware cursor - on OSX it's always enabled. */ void osx_enable_hardware_cursor(AL_CONST int mode) { (void)mode; } /* osx_select_system_cursor: * Select a system cursor - on this platform, only the I-beam and the Arrow * are available as system cursors. */ static int osx_select_system_cursor(AL_CONST int cursor) { switch (cursor) { case MOUSE_CURSOR_ARROW: requested_cursor = [NSCursor arrowCursor]; break; case MOUSE_CURSOR_EDIT: requested_cursor = [NSCursor IBeamCursor]; break; default: return 0; } osx_change_cursor(requested_cursor); return cursor; } /* Local variables: */ /* c-basic-offset: 3 */ /* indent-tabs-mode: nil */ /* End: */