diff options
Diffstat (limited to '3rdparty/SDL/src/video/quartz/SDL_QuartzWM.m')
-rw-r--r-- | 3rdparty/SDL/src/video/quartz/SDL_QuartzWM.m | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/3rdparty/SDL/src/video/quartz/SDL_QuartzWM.m b/3rdparty/SDL/src/video/quartz/SDL_QuartzWM.m new file mode 100644 index 0000000..d526424 --- /dev/null +++ b/3rdparty/SDL/src/video/quartz/SDL_QuartzWM.m @@ -0,0 +1,444 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_QuartzVideo.h" +#include "SDL_QuartzWM.h" + + +void QZ_FreeWMCursor (_THIS, WMcursor *cursor) { + + if ( cursor != NULL ) { + [ cursor->nscursor release ]; + free (cursor); + } +} + +WMcursor* QZ_CreateWMCursor (_THIS, Uint8 *data, Uint8 *mask, + int w, int h, int hot_x, int hot_y) { + WMcursor *cursor; + NSBitmapImageRep *imgrep; + NSImage *img; + unsigned char *planes[5]; + int i; + NSAutoreleasePool *pool; + + pool = [ [ NSAutoreleasePool alloc ] init ]; + + /* Allocate the cursor memory */ + cursor = (WMcursor *)SDL_malloc(sizeof(WMcursor)); + if (cursor == NULL) goto outOfMemory; + + /* create the image representation and get the pointers to its storage */ + imgrep = [ [ [ NSBitmapImageRep alloc ] initWithBitmapDataPlanes: NULL pixelsWide: w pixelsHigh: h bitsPerSample: 1 samplesPerPixel: 2 hasAlpha: YES isPlanar: YES colorSpaceName: NSDeviceWhiteColorSpace bytesPerRow: (w+7)/8 bitsPerPixel: 0 ] autorelease ]; + if (imgrep == nil) goto outOfMemory; + [ imgrep getBitmapDataPlanes: planes ]; + + /* copy data and mask, extending the mask to all black pixels because the inversion effect doesn't work with Cocoa's alpha-blended cursors */ + for (i = 0; i < (w+7)/8*h; i++) { + planes[0][i] = data[i] ^ 0xFF; + planes[1][i] = mask[i] | data[i]; + } + + /* create image and cursor */ + img = [ [ [ NSImage alloc ] initWithSize: NSMakeSize(w, h) ] autorelease ]; + if (img == nil) goto outOfMemory; + [ img addRepresentation: imgrep ]; + if (system_version < 0x1030) { /* on 10.2, cursors must be 16*16 */ + if (w > 16 || h > 16) { /* too big: scale it down */ + [ img setScalesWhenResized: YES ]; + hot_x = hot_x*16/w; + hot_y = hot_y*16/h; + } + else { /* too small (or just right): extend it (from the bottom left corner, so hot_y must be adjusted) */ + hot_y += 16 - h; + } + [ img setSize: NSMakeSize(16, 16) ]; + } + cursor->nscursor = [ [ NSCursor alloc ] initWithImage: img hotSpot: NSMakePoint(hot_x, hot_y) ]; + if (cursor->nscursor == nil) goto outOfMemory; + + [ pool release ]; + return(cursor); + +outOfMemory: + [ pool release ]; + if (cursor != NULL) SDL_free(cursor); + SDL_OutOfMemory(); + return(NULL); +} + +void QZ_UpdateCursor (_THIS) { + BOOL state; + + if (cursor_should_be_visible || !(SDL_GetAppState() & SDL_APPMOUSEFOCUS)) { + state = YES; + } else { + state = NO; + } + if (state != cursor_visible) { + if (state) { + [ NSCursor unhide ]; + } else { + [ NSCursor hide ]; + } + cursor_visible = state; + } +} + +BOOL QZ_IsMouseInWindow (_THIS) { + if (qz_window == nil || (mode_flags & SDL_FULLSCREEN)) return YES; /*fullscreen*/ + else { + NSPoint p = [ qz_window mouseLocationOutsideOfEventStream ]; + p.y -= 1.0f; /* Apparently y goes from 1 to h, not from 0 to h-1 (i.e. the "location of the mouse" seems to be defined as "the location of the top left corner of the mouse pointer's hot pixel" */ + return NSPointInRect(p, [ window_view frame ]); + } +} + +int QZ_ShowWMCursor (_THIS, WMcursor *cursor) { + + if ( cursor == NULL) { + if ( cursor_should_be_visible ) { + cursor_should_be_visible = NO; + QZ_ChangeGrabState (this, QZ_HIDECURSOR); + } + QZ_UpdateCursor(this); + } + else { + if ( qz_window != nil && !(mode_flags & SDL_FULLSCREEN) ) { + [ qz_window invalidateCursorRectsForView: [ qz_window contentView ] ]; + } + if ( ! cursor_should_be_visible ) { + cursor_should_be_visible = YES; + QZ_ChangeGrabState (this, QZ_SHOWCURSOR); + } + [ cursor->nscursor performSelectorOnMainThread:@selector(set) withObject:nil waitUntilDone:NO ]; + QZ_UpdateCursor(this); + } + + return 1; +} + +/* + Coordinate conversion functions, for convenience + Cocoa sets the origin at the lower left corner of the window/screen + SDL, CoreGraphics/WindowServer, and QuickDraw use the origin at the upper left corner + The routines were written so they could be called before SetVideoMode() has finished; + this might have limited usefulness at the moment, but the extra cost is trivial. +*/ + +/* Convert Cocoa screen coordinate to Cocoa window coordinate */ +void QZ_PrivateGlobalToLocal (_THIS, NSPoint *p) { + + if ( ! CGDisplayIsCaptured (display_id) ) + *p = [ qz_window convertScreenToBase:*p ]; +} + + +/* Convert Cocoa window coordinate to Cocoa screen coordinate */ +void QZ_PrivateLocalToGlobal (_THIS, NSPoint *p) { + + if ( ! CGDisplayIsCaptured (display_id) ) + *p = [ qz_window convertBaseToScreen:*p ]; +} + +/* Convert SDL coordinate to Cocoa coordinate */ +void QZ_PrivateSDLToCocoa (_THIS, NSPoint *p) { + + if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */ + + p->y = CGDisplayPixelsHigh (display_id) - p->y; + } + else { + + *p = [ window_view convertPoint:*p toView: nil ]; + p->y = [window_view frame].size.height - p->y; + } +} + +/* Convert Cocoa coordinate to SDL coordinate */ +void QZ_PrivateCocoaToSDL (_THIS, NSPoint *p) { + + if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */ + + p->y = CGDisplayPixelsHigh (display_id) - p->y; + } + else { + + *p = [ window_view convertPoint:*p fromView: nil ]; + p->y = [window_view frame].size.height - p->y; + } +} + +/* Convert SDL coordinate to window server (CoreGraphics) coordinate */ +CGPoint QZ_PrivateSDLToCG (_THIS, NSPoint *p) { + + CGPoint cgp; + + if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */ + + int height; + + QZ_PrivateSDLToCocoa (this, p); + QZ_PrivateLocalToGlobal (this, p); + + height = CGDisplayPixelsHigh (display_id); + p->y = height - p->y; + } + + cgp.x = p->x; + cgp.y = p->y; + + return cgp; +} + +#if 0 /* Dead code */ +/* Convert window server (CoreGraphics) coordinate to SDL coordinate */ +void QZ_PrivateCGToSDL (_THIS, NSPoint *p) { + + if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */ + + int height; + + /* Convert CG Global to Cocoa Global */ + height = CGDisplayPixelsHigh (display_id); + p->y = height - p->y; + + QZ_PrivateGlobalToLocal (this, p); + QZ_PrivateCocoaToSDL (this, p); + } +} +#endif /* Dead code */ + +void QZ_PrivateWarpCursor (_THIS, int x, int y) { + NSPoint p; + CGPoint cgp; + + p = NSMakePoint (x, y); + cgp = QZ_PrivateSDLToCG (this, &p); + + /* this is the magic call that fixes cursor "freezing" after warp */ + CGAssociateMouseAndMouseCursorPosition (0); + CGWarpMouseCursorPosition (cgp); + if (grab_state != QZ_INVISIBLE_GRAB) { /* can't leave it disassociated? */ + CGAssociateMouseAndMouseCursorPosition (1); + } + SDL_PrivateAppActive (QZ_IsMouseInWindow (this), SDL_APPMOUSEFOCUS); +} + +void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) { + + /* Only allow warping when in foreground */ + if ( ! [ NSApp isActive ] ) + return; + + /* Do the actual warp */ + if (grab_state != QZ_INVISIBLE_GRAB) QZ_PrivateWarpCursor (this, x, y); + + /* Generate the mouse moved event */ + SDL_PrivateMouseMotion (0, 0, x, y); +} + +void QZ_MoveWMCursor (_THIS, int x, int y) { } +void QZ_CheckMouseMode (_THIS) { } + +void QZ_SetCaption (_THIS, const char *title, const char *icon) { + + if ( qz_window != nil ) { + NSString *string; + if ( title != NULL ) { + string = [ [ NSString alloc ] initWithUTF8String:title ]; + [ qz_window setTitle:string ]; + [ string release ]; + } + if ( icon != NULL ) { + string = [ [ NSString alloc ] initWithUTF8String:icon ]; + [ qz_window setMiniwindowTitle:string ]; + [ string release ]; + } + } +} + +void QZ_SetIcon (_THIS, SDL_Surface *icon, Uint8 *mask) +{ + NSBitmapImageRep *imgrep; + NSImage *img; + SDL_Surface *mergedSurface; + NSAutoreleasePool *pool; + Uint8 *pixels; + SDL_bool iconSrcAlpha; + Uint8 iconAlphaValue; + int i, j, maskPitch, index; + + pool = [ [ NSAutoreleasePool alloc ] init ]; + + imgrep = [ [ [ NSBitmapImageRep alloc ] initWithBitmapDataPlanes: NULL pixelsWide: icon->w pixelsHigh: icon->h bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES isPlanar: NO colorSpaceName: NSDeviceRGBColorSpace bytesPerRow: 4*icon->w bitsPerPixel: 32 ] autorelease ]; + if (imgrep == nil) goto freePool; + pixels = [ imgrep bitmapData ]; + SDL_memset(pixels, 0, 4*icon->w*icon->h); /* make the background, which will survive in colorkeyed areas, completely transparent */ + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define BYTEORDER_DEPENDENT_RGBA_MASKS 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF +#else +#define BYTEORDER_DEPENDENT_RGBA_MASKS 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 +#endif + mergedSurface = SDL_CreateRGBSurfaceFrom(pixels, icon->w, icon->h, 32, 4*icon->w, BYTEORDER_DEPENDENT_RGBA_MASKS); + if (mergedSurface == NULL) goto freePool; + + /* blit, with temporarily cleared SRCALPHA flag because we want to copy, not alpha-blend */ + iconSrcAlpha = ((icon->flags & SDL_SRCALPHA) != 0); + iconAlphaValue = icon->format->alpha; + SDL_SetAlpha(icon, 0, 255); + SDL_BlitSurface(icon, NULL, mergedSurface, NULL); + if (iconSrcAlpha) SDL_SetAlpha(icon, SDL_SRCALPHA, iconAlphaValue); + + SDL_FreeSurface(mergedSurface); + + /* apply mask, source alpha, and premultiply color values by alpha */ + maskPitch = (icon->w+7)/8; + for (i = 0; i < icon->h; i++) { + for (j = 0; j < icon->w; j++) { + index = i*4*icon->w + j*4; + if (!(mask[i*maskPitch + j/8] & (128 >> j%8))) { + pixels[index + 3] = 0; + } + else { + if (iconSrcAlpha) { + if (icon->format->Amask == 0) pixels[index + 3] = icon->format->alpha; + } + else { + pixels[index + 3] = 255; + } + } + if (pixels[index + 3] < 255) { + pixels[index + 0] = (Uint16)pixels[index + 0]*pixels[index + 3]/255; + pixels[index + 1] = (Uint16)pixels[index + 1]*pixels[index + 3]/255; + pixels[index + 2] = (Uint16)pixels[index + 2]*pixels[index + 3]/255; + } + } + } + + img = [ [ [ NSImage alloc ] initWithSize: NSMakeSize(icon->w, icon->h) ] autorelease ]; + if (img == nil) goto freePool; + [ img addRepresentation: imgrep ]; + [ NSApp setApplicationIconImage:img ]; + +freePool: + [ pool release ]; +} + +int QZ_IconifyWindow (_THIS) { + + if ( ! [ qz_window isMiniaturized ] ) { + [ qz_window miniaturize:nil ]; + if ( ! [ qz_window isMiniaturized ] ) { + SDL_SetError ("window iconification failed"); + return 0; + } + return 1; + } + else { + SDL_SetError ("window already iconified"); + return 0; + } +} + +/* +int QZ_GetWMInfo (_THIS, SDL_SysWMinfo *info) { + info->nsWindowPtr = qz_window; + return 0; +}*/ + +void QZ_ChangeGrabState (_THIS, int action) { + + /* + Figure out what the next state should be based on the action. + Ignore actions that can't change the current state. + */ + if ( grab_state == QZ_UNGRABBED ) { + if ( action == QZ_ENABLE_GRAB ) { + if ( cursor_should_be_visible ) + grab_state = QZ_VISIBLE_GRAB; + else + grab_state = QZ_INVISIBLE_GRAB; + } + } + else if ( grab_state == QZ_VISIBLE_GRAB ) { + if ( action == QZ_DISABLE_GRAB ) + grab_state = QZ_UNGRABBED; + else if ( action == QZ_HIDECURSOR ) + grab_state = QZ_INVISIBLE_GRAB; + } + else { + assert( grab_state == QZ_INVISIBLE_GRAB ); + + if ( action == QZ_DISABLE_GRAB ) + grab_state = QZ_UNGRABBED; + else if ( action == QZ_SHOWCURSOR ) + grab_state = QZ_VISIBLE_GRAB; + } + + /* now apply the new state */ + if (grab_state == QZ_UNGRABBED) { + + CGAssociateMouseAndMouseCursorPosition (1); + } + else if (grab_state == QZ_VISIBLE_GRAB) { + + CGAssociateMouseAndMouseCursorPosition (1); + } + else { + assert( grab_state == QZ_INVISIBLE_GRAB ); + + QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2); + CGAssociateMouseAndMouseCursorPosition (0); + } +} + +SDL_GrabMode QZ_GrabInput (_THIS, SDL_GrabMode grab_mode) { + + int doGrab = grab_mode & SDL_GRAB_ON; + /*int fullscreen = grab_mode & SDL_GRAB_FULLSCREEN;*/ + + if ( this->screen == NULL ) { + SDL_SetError ("QZ_GrabInput: screen is NULL"); + return SDL_GRAB_OFF; + } + + if ( ! video_set ) { + /*SDL_SetError ("QZ_GrabInput: video is not set, grab will take effect on mode switch"); */ + current_grab_mode = grab_mode; + return grab_mode; /* Will be set later on mode switch */ + } + + if ( grab_mode != SDL_GRAB_QUERY ) { + if ( doGrab ) + QZ_ChangeGrabState (this, QZ_ENABLE_GRAB); + else + QZ_ChangeGrabState (this, QZ_DISABLE_GRAB); + + current_grab_mode = doGrab ? SDL_GRAB_ON : SDL_GRAB_OFF; + QZ_UpdateCursor(this); + } + + return current_grab_mode; +} |