1/* ______ ___ ___ 2 * /\ _ \ /\_ \ /\_ \ 3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ 4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ 5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ 6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ 7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ 8 * /\____/ 9 * \_/__/ 10 * 11 * MacOS X mouse driver. 12 * 13 * By Angelo Mottola. 14 * 15 * See readme.txt for copyright information. 16 */ 17 18 19#include "allegro.h" 20#include "allegro/internal/aintern.h" 21#include "allegro/platform/aintosx.h" 22 23#ifndef ALLEGRO_MACOSX 24#error Something is wrong with the makefile 25#endif 26 27 28static int osx_mouse_init(void); 29static void osx_mouse_exit(void); 30static void osx_mouse_position(int, int); 31static void osx_mouse_set_range(int, int, int, int); 32static void osx_mouse_get_mickeys(int *, int *); 33static void osx_enable_hardware_cursor(AL_CONST int mode); 34static int osx_select_system_cursor(AL_CONST int cursor); 35 36 37MOUSE_DRIVER mouse_macosx = { 38 MOUSE_MACOSX, 39 empty_string, 40 empty_string, 41 "MacOS X mouse", 42 osx_mouse_init, 43 osx_mouse_exit, 44 NULL, // AL_METHOD(void, poll, (void)); 45 NULL, // AL_METHOD(void, timer_poll, (void)); 46 osx_mouse_position, 47 osx_mouse_set_range, 48 NULL, // AL_METHOD(void, set_speed, (int xspeed, int yspeed)); 49 osx_mouse_get_mickeys, 50 NULL, // AL_METHOD(int, analyse_data, (AL_CONST char *buffer, int size)); 51 osx_enable_hardware_cursor, 52 osx_select_system_cursor 53}; 54 55 56/* global variable */ 57int osx_mouse_warped = FALSE; 58int osx_skip_mouse_move = FALSE; 59NSTrackingRectTag osx_mouse_tracking_rect = -1; 60 61 62static NSCursor *cursor = NULL, *current_cursor = NULL; 63static NSCursor *requested_cursor = NULL; 64static unsigned char *cursor_data = NULL; 65static NSBitmapImageRep *cursor_rep = NULL; 66static NSImage *cursor_image = NULL; 67 68static int mouse_minx = 0; 69static int mouse_miny = 0; 70static int mouse_maxx = 319; 71static int mouse_maxy = 199; 72 73static int mymickey_x = 0; 74static int mymickey_y = 0; 75 76static char driver_desc[256]; 77 78 79/* osx_change_cursor: 80 * Actually change the current cursor. This can be called fom any thread 81 * but ensures that the change is only called from the main thread. 82 */ 83static void osx_change_cursor(NSCursor* cursor) 84{ 85 _unix_lock_mutex(osx_event_mutex); 86 osx_cursor = cursor; 87 _unix_unlock_mutex(osx_event_mutex); 88 [cursor performSelectorOnMainThread: @selector(set) withObject: nil waitUntilDone: NO]; 89} 90 91 92 93/* osx_mouse_handler: 94 * Mouse "interrupt" handler for mickey-mode driver. 95 */ 96void osx_mouse_handler(int ax, int ay, int x, int y, int z, int buttons) 97{ 98 if (!_mouse_on) 99 mymickey_x = mymickey_y = 0; 100 101 if ((!_mouse_installed) || (!_mouse_on) || (osx_gfx_mode == OSX_GFX_NONE)) 102 return; 103 104 if (osx_cursor != current_cursor) { 105 if (osx_window) { 106 NSView* vw = [osx_window contentView]; 107 [osx_window invalidateCursorRectsForView: vw]; 108 } 109 else { 110 [osx_cursor set]; 111 } 112 current_cursor = osx_cursor; 113 } 114 115 if (osx_mouse_warped) { 116 osx_mouse_warped = FALSE; 117 return; 118 } 119 120 _mouse_b = buttons; 121 122 mymickey_x += x; 123 mymickey_y += y; 124 _mouse_x = ax; 125 _mouse_y = ay; 126 _mouse_z += z; 127 128 _mouse_x = CLAMP(mouse_minx, _mouse_x, mouse_maxx); 129 _mouse_y = CLAMP(mouse_miny, _mouse_y, mouse_maxy); 130 131 _handle_mouse_input(); 132} 133 134 135 136/* osx_mouse_init: 137 * Initializes the mickey-mode driver. 138 */ 139static int osx_mouse_init(void) 140{ 141 HID_DEVICE_COLLECTION devices={0,0,NULL}; 142 int i, j; 143 int buttons, max_buttons = -1; 144 HID_DEVICE* device; 145 146 if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) { 147 /* On 10.1.x mice and keyboards aren't available from the HID Manager, 148 * so we can't autodetect. We assume an 1-button mouse to always be 149 * present. 150 */ 151 max_buttons = 1; 152 } 153 else { 154 osx_hid_scan(HID_MOUSE, &devices); 155 for (i = 0; i < devices.count; i++) { 156 device=&devices.devices[i]; 157 buttons = 0; 158 for (j = 0; j < device->num_elements; j++) { 159 if (device->element[j].type == HID_ELEMENT_BUTTON) 160 buttons++; 161 } 162 if (buttons > max_buttons) { 163 max_buttons = buttons; 164 _al_sane_strncpy(driver_desc, "", sizeof(driver_desc)); 165 if (device->manufacturer) { 166 strncat(driver_desc, device->manufacturer, sizeof(driver_desc)-1); 167 strncat(driver_desc, " ", sizeof(driver_desc)-1); 168 } 169 if (device->product) 170 strncat(driver_desc, device->product, sizeof(driver_desc)-1); 171 mouse_macosx.desc = driver_desc; 172 } 173 } 174 osx_hid_free(&devices); 175 } 176 177 _unix_lock_mutex(osx_event_mutex); 178 osx_emulate_mouse_buttons = (max_buttons == 1) ? TRUE : FALSE; 179 _unix_unlock_mutex(osx_event_mutex); 180 181 return max_buttons; 182} 183 184 185 186/* osx_mouse_exit: 187 * Shuts down the mickey-mode driver. 188 */ 189static void osx_mouse_exit(void) 190{ 191 osx_cursor = osx_blank_cursor; 192 if (cursor) 193 [cursor release]; 194 if (cursor_image) 195 [cursor_image release]; 196 if (cursor_rep) 197 [cursor_rep release]; 198 if (cursor_data) 199 free(cursor_data); 200 cursor = NULL; 201 cursor_image = NULL; 202 cursor_rep = NULL; 203 cursor_data = NULL; 204} 205 206 207 208/* osx_mouse_position: 209 * Sets the position of the mickey-mode mouse. 210 */ 211static void osx_mouse_position(int x, int y) 212{ 213 CGPoint point; 214 NSRect frame; 215 int screen_height; 216 217 _unix_lock_mutex(osx_event_mutex); 218 219 _mouse_x = point.x = x; 220 _mouse_y = point.y = y; 221 222 if (osx_window) { 223 CFNumberGetValue(CFDictionaryGetValue(CGDisplayCurrentMode(kCGDirectMainDisplay), kCGDisplayHeight), kCFNumberSInt32Type, &screen_height); 224 frame = [osx_window frame]; 225 point.x += frame.origin.x; 226 point.y += (screen_height - (frame.origin.y + gfx_driver->h)); 227 } 228 229 CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, point); 230 231 mymickey_x = mymickey_y = 0; 232 osx_mouse_warped = TRUE; 233 234 _unix_unlock_mutex(osx_event_mutex); 235} 236 237 238 239/* osx_mouse_set_range: 240 * Sets the range of the mickey-mode mouse. 241 */ 242static void osx_mouse_set_range(int x1, int y1, int x2, int y2) 243{ 244 mouse_minx = x1; 245 mouse_miny = y1; 246 mouse_maxx = x2; 247 mouse_maxy = y2; 248 249 osx_mouse_position(CLAMP(mouse_minx, _mouse_x, mouse_maxx), CLAMP(mouse_miny, _mouse_y, mouse_maxy)); 250} 251 252 253 254/* osx_mouse_get_mickeys: 255 * Reads the mickey-mode count. 256 */ 257static void osx_mouse_get_mickeys(int *mickeyx, int *mickeyy) 258{ 259 _unix_lock_mutex(osx_event_mutex); 260 261 *mickeyx = mymickey_x; 262 *mickeyy = mymickey_y; 263 mymickey_x = mymickey_y = 0; 264 265 _unix_unlock_mutex(osx_event_mutex); 266} 267 268 269 270/* osx_mouse_set_sprite: 271 * Sets the hardware cursor sprite. 272 */ 273int osx_mouse_set_sprite(BITMAP *sprite, int x, int y) 274{ 275 int ix, iy; 276 int sw, sh; 277 278 if (!sprite) 279 return -1; 280 sw = sprite->w; 281 sh = sprite->h; 282 if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_2) { 283 // Before MacOS X 10.3, NSCursor can handle only 16x16 cursor sprites 284 // Pad to 16x16 or fail if the sprite is already larger. 285 if (sw>16 || sh>16) 286 return -1; 287 sh = sw = 16; 288 } 289 290 // Delete the old cursor (OK to send a message to nil) 291 [cursor release]; 292 293 NSBitmapImageRep* cursor_rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL 294 pixelsWide: sw 295 pixelsHigh: sh 296 bitsPerSample: 8 297 samplesPerPixel: 4 298 hasAlpha: YES 299 isPlanar: NO 300 colorSpaceName: NSDeviceRGBColorSpace 301 bytesPerRow: 0 302 bitsPerPixel: 0]; 303 int bpp = bitmap_color_depth(sprite); 304 int mask = bitmap_mask_color(sprite); 305 for (iy = 0; iy< sh; ++iy) { 306 unsigned char* ptr = [cursor_rep bitmapData] + (iy * [cursor_rep bytesPerRow]); 307 for (ix = 0; ix< sw; ++ix) { 308 int color = is_inside_bitmap(sprite, ix, iy, 1) 309 ? getpixel(sprite, ix, iy) : mask; 310 // Disable the possibility of mouse sprites with alpha for now, because 311 // this causes the built-in cursors to be invisible in 32 bit mode. 312 // int alpha = (color == mask) ? 0 : ((bpp == 32) ? geta_depth(bpp, color) : 255); 313 int alpha = (color == mask) ? 0 : 255; 314 // BitmapImageReps use premultiplied alpha 315 ptr[0] = getb_depth(bpp, color) * alpha / 255; 316 ptr[1] = getg_depth(bpp, color) * alpha / 255; 317 ptr[2] = getr_depth(bpp, color) * alpha / 255; 318 ptr[3] = alpha; 319 ptr += 4; 320 } 321 } 322 NSImage* cursor_image = [[NSImage alloc] initWithSize: NSMakeSize(sw, sh)]; 323 [cursor_image addRepresentation: cursor_rep]; 324 [cursor_rep release]; 325 cursor = [[NSCursor alloc] initWithImage: cursor_image 326 hotSpot: NSMakePoint(x, y)]; 327 [cursor_image release]; 328 osx_change_cursor(requested_cursor = cursor); 329 return 0; 330} 331 332 333 334/* osx_mouse_show: 335 * Show the hardware cursor. 336 */ 337int osx_mouse_show(BITMAP *bmp, int x, int y) 338{ 339 /* Only draw on screen */ 340 if (!is_same_bitmap(bmp, screen)) 341 return -1; 342 343 if (!requested_cursor) 344 return -1; 345 346 osx_change_cursor(requested_cursor); 347 348 return 0; 349} 350 351 352 353/* osx_mouse_hide: 354 * Hide the hardware cursor. 355 */ 356void osx_mouse_hide(void) 357{ 358 osx_change_cursor(osx_blank_cursor); 359} 360 361 362 363/* osx_mouse_move: 364 * Get mouse move notification. Not that we need it... 365 */ 366void osx_mouse_move(int x, int y) 367{ 368} 369 370 371 372/* osx_enable_hardware_cursor: 373 * Enable hardware cursor - on OSX it's always enabled. 374 */ 375void osx_enable_hardware_cursor(AL_CONST int mode) 376{ 377 (void)mode; 378} 379 380 381 382/* osx_select_system_cursor: 383 * Select a system cursor - on this platform, only the I-beam and the Arrow 384 * are available as system cursors. 385 */ 386static int osx_select_system_cursor(AL_CONST int cursor) 387{ 388 switch (cursor) { 389 case MOUSE_CURSOR_ARROW: 390 requested_cursor = [NSCursor arrowCursor]; 391 break; 392 case MOUSE_CURSOR_EDIT: 393 requested_cursor = [NSCursor IBeamCursor]; 394 break; 395 default: 396 return 0; 397 } 398 osx_change_cursor(requested_cursor); 399 return cursor; 400} 401 402 403 404/* Local variables: */ 405/* c-basic-offset: 3 */ 406/* indent-tabs-mode: nil */ 407/* End: */ 408