src/video/quartz/SDL_QuartzWM.m
author Sam Lantinga <slouken@libsdl.org>
Sun, 20 Apr 2003 05:41:16 +0000
changeset 615 7ec821f3cbd0
parent 592 1970e458070d
child 683 5d2f027b3349
permissions -rw-r--r--
Date: Thu, 17 Apr 2003 23:27:34 -0400
From: Darrell Walisser
Subject: Yet another OS X cursor bug

The synopsis:

1. Call SDL_ShowCursor(0);
2. Call SDL_SetVideoMode();
3. Call SDL_GetEvent();
3. Call SDL_ShowCursor(1);

The result: Sometimes the cursor doesn't come back! Ack! Oddly enough,
it does come back when mousing over the dock or clicking in the menu
bar. But that's besides the point.

The reason why this is happening is a flaw in the handling of
activation/deactivation events. The short explanation is that the
HideCursor() and ShowCursor() calls must be balanced, but if the cursor
was initially hidden, HideCursor() was called again on the activate
event - so now the next ShowCursor() fails (as does the next, and the
next, for some reason).

So, here's the patch. All it does is keep track of the
HideCursor()/ShowCursor() calls so that they will always be balanced.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 static void QZ_ChangeGrabState (_THIS, int action);
    24 
    25 struct WMcursor {
    26     Cursor curs;
    27 };
    28 
    29 static void QZ_FreeWMCursor     (_THIS, WMcursor *cursor) { 
    30 
    31     if ( cursor != NULL )
    32         free (cursor);
    33 }
    34 
    35 /* Use the Carbon cursor routines for now */
    36 static WMcursor*    QZ_CreateWMCursor   (_THIS, Uint8 *data, Uint8 *mask, 
    37                                          int w, int h, int hot_x, int hot_y) { 
    38     WMcursor *cursor;
    39     int row, bytes;
    40         
    41     /* Allocate the cursor memory */
    42     cursor = (WMcursor *)malloc(sizeof(WMcursor));
    43     if ( cursor == NULL ) {
    44         SDL_OutOfMemory();
    45         return(NULL);
    46     }
    47     memset(cursor, 0, sizeof(*cursor));
    48     
    49     if (w > 16)
    50         w = 16;
    51     
    52     if (h > 16)
    53         h = 16;
    54     
    55     bytes = (w+7)/8;
    56 
    57     for ( row=0; row<h; ++row ) {
    58         memcpy(&cursor->curs.data[row], data, bytes);
    59         data += bytes;
    60     }
    61     for ( row=0; row<h; ++row ) {
    62         memcpy(&cursor->curs.mask[row], mask, bytes);
    63         mask += bytes;
    64     }
    65     cursor->curs.hotSpot.h = hot_x;
    66     cursor->curs.hotSpot.v = hot_y;
    67     
    68     return(cursor);
    69 }
    70 
    71 static int QZ_ShowWMCursor (_THIS, WMcursor *cursor) { 
    72 
    73     if ( cursor == NULL) {
    74         if ( cursor_visible ) {
    75             if (!cursor_hidden) {
    76                 HideCursor ();
    77                 cursor_hidden = YES;
    78             }
    79             cursor_visible = NO;
    80             QZ_ChangeGrabState (this, QZ_HIDECURSOR);
    81         }
    82     }
    83     else {
    84         SetCursor(&cursor->curs);
    85         if ( ! cursor_visible ) {
    86             if (cursor_hidden) {
    87                 ShowCursor ();
    88                 cursor_hidden = NO;
    89             }
    90             cursor_visible = YES;
    91             QZ_ChangeGrabState (this, QZ_SHOWCURSOR);
    92         }
    93     }
    94 
    95     return 1;
    96 }
    97 
    98 /*
    99     Coordinate conversion functions, for convenience
   100     Cocoa sets the origin at the lower left corner of the window/screen
   101     SDL, CoreGraphics/WindowServer, and QuickDraw use the origin at the upper left corner
   102     The routines were written so they could be called before SetVideoMode() has finished;
   103     this might have limited usefulness at the moment, but the extra cost is trivial.
   104 */
   105 
   106 /* Convert Cocoa screen coordinate to Cocoa window coordinate */
   107 static void QZ_PrivateGlobalToLocal (_THIS, NSPoint *p) {
   108 
   109     *p = [ qz_window convertScreenToBase:*p ];
   110 }
   111 
   112 
   113 /* Convert Cocoa window coordinate to Cocoa screen coordinate */
   114 static void QZ_PrivateLocalToGlobal (_THIS, NSPoint *p) {
   115 
   116     *p = [ qz_window convertBaseToScreen:*p ];
   117 }
   118 
   119 /* Convert SDL coordinate to Cocoa coordinate */
   120 static void QZ_PrivateSDLToCocoa (_THIS, NSPoint *p) {
   121 
   122     int height;
   123     
   124     if ( CGDisplayIsCaptured (display_id) ) { /* capture signals fullscreen */
   125     
   126         height = CGDisplayPixelsHigh (display_id);
   127     }
   128     else {
   129         
   130         height = NSHeight ( [ qz_window frame ] );
   131         if ( [ qz_window styleMask ] & NSTitledWindowMask ) {
   132         
   133             height -= 22;
   134         }
   135     }
   136     
   137     p->y = height - p->y - 1;
   138 }
   139 
   140 /* Convert Cocoa coordinate to SDL coordinate */
   141 static void QZ_PrivateCocoaToSDL (_THIS, NSPoint *p) {
   142 
   143     QZ_PrivateSDLToCocoa (this, p);
   144 }
   145 
   146 /* Convert SDL coordinate to window server (CoreGraphics) coordinate */
   147 static CGPoint QZ_PrivateSDLToCG (_THIS, NSPoint *p) {
   148     
   149     CGPoint cgp;
   150     
   151     if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */
   152     
   153         int height;
   154         
   155         QZ_PrivateSDLToCocoa (this, p);
   156         QZ_PrivateLocalToGlobal (this, p);
   157         
   158         height = CGDisplayPixelsHigh (display_id);
   159         p->y = height - p->y;
   160     }
   161     
   162     cgp.x = p->x;
   163     cgp.y = p->y;
   164     
   165     return cgp;
   166 }
   167 
   168 /* Convert window server (CoreGraphics) coordinate to SDL coordinate */
   169 static void QZ_PrivateCGToSDL (_THIS, NSPoint *p) {
   170             
   171     if ( ! CGDisplayIsCaptured (display_id) ) { /* not captured => not fullscreen => local coord */
   172     
   173         int height;
   174 
   175         /* Convert CG Global to Cocoa Global */
   176         height = CGDisplayPixelsHigh (display_id);
   177         p->y = height - p->y;
   178 
   179         QZ_PrivateGlobalToLocal (this, p);
   180         QZ_PrivateCocoaToSDL (this, p);
   181     }
   182 }
   183 
   184 static void  QZ_PrivateWarpCursor (_THIS, int x, int y) {
   185     
   186     NSPoint p;
   187     CGPoint cgp;
   188     
   189     p = NSMakePoint (x, y);
   190     cgp = QZ_PrivateSDLToCG (this, &p);
   191     QZ_PrivateCGToSDL (this, &p);
   192     
   193     /* this is the magic call that fixes cursor "freezing" after warp */
   194     CGSetLocalEventsSuppressionInterval (0.0);
   195     CGWarpMouseCursorPosition (cgp);
   196 }
   197 
   198 static void QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y) {
   199 
   200     /* Only allow warping when in foreground */
   201     if ( ! in_foreground )
   202         return;
   203             
   204     /* Do the actual warp */
   205     QZ_PrivateWarpCursor (this, x, y);
   206 
   207     /* Generate the mouse moved event */
   208     SDL_PrivateMouseMotion (0, 0, x, y);
   209 }
   210 
   211 static void QZ_MoveWMCursor     (_THIS, int x, int y) { }
   212 static void QZ_CheckMouseMode   (_THIS) { }
   213 
   214 static void QZ_SetCaption    (_THIS, const char *title, const char *icon) {
   215 
   216     if ( qz_window != nil ) {
   217         NSString *string;
   218         if ( title != NULL ) {
   219             string = [ [ NSString alloc ] initWithCString:title ];
   220             [ qz_window setTitle:string ];
   221             [ string release ];
   222         }
   223         if ( icon != NULL ) {
   224             string = [ [ NSString alloc ] initWithCString:icon ];
   225             [ qz_window setMiniwindowTitle:string ];
   226             [ string release ];
   227         }
   228     }
   229 }
   230 
   231 static void QZ_SetIcon       (_THIS, SDL_Surface *icon, Uint8 *mask)
   232 {
   233     NSBitmapImageRep *imgrep;
   234     NSImage *img;
   235     SDL_Surface *mergedSurface;
   236     int i,j;
   237     NSAutoreleasePool *pool;
   238     SDL_Rect rrect;
   239     NSSize imgSize = {icon->w, icon->h};
   240     
   241     pool = [ [ NSAutoreleasePool alloc ] init ];
   242     SDL_GetClipRect(icon, &rrect);
   243     
   244     /* create a big endian RGBA surface */
   245     mergedSurface = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA, 
   246                     icon->w, icon->h, 32, 0xff<<24, 0xff<<16, 0xff<<8, 0xff<<0);
   247     if (mergedSurface==NULL) {
   248         NSLog(@"Error creating surface for merge");
   249         goto freePool;
   250     }
   251     
   252     if (mergedSurface->pitch != 
   253         mergedSurface->format->BytesPerPixel * mergedSurface->w) {
   254         SDL_SetError ("merged surface has wrong format");
   255         SDL_FreeSurface (mergedSurface);
   256         goto freePool;
   257     }
   258     
   259     if (SDL_BlitSurface(icon,&rrect,mergedSurface,&rrect)) {
   260         NSLog(@"Error blitting to mergedSurface");
   261         goto freePool;
   262     }
   263     
   264     if (mask) {
   265 
   266         Uint32 *pixels = mergedSurface->pixels;
   267         for (i = 0; i < mergedSurface->h; i++) {
   268             for (j = 0; j < mergedSurface->w; j++) {
   269                 
   270                 int index = i * mergedSurface->w + j;
   271                 int mindex = index >> 3;
   272                 int bindex = 7 - (index & 0x7);
   273                 
   274                 if (mask[mindex] & (1 << bindex))
   275                     pixels[index] |= 0x000000FF;
   276                 else
   277                     pixels[index] &= 0xFFFFFF00;
   278             }
   279         }
   280     }
   281     
   282     imgrep = [ [ NSBitmapImageRep alloc] 
   283                     initWithBitmapDataPlanes:(unsigned char **)&mergedSurface->pixels 
   284                         pixelsWide:icon->w pixelsHigh:icon->h bitsPerSample:8 samplesPerPixel:4 
   285                         hasAlpha:YES isPlanar:NO colorSpaceName:NSDeviceRGBColorSpace 
   286                         bytesPerRow:icon->w<<2 bitsPerPixel:32 ];
   287     
   288     img = [ [ NSImage alloc ] initWithSize:imgSize ];
   289     
   290     [ img addRepresentation: imgrep ];
   291     [ NSApp setApplicationIconImage:img ];
   292     
   293     [ img release ];
   294     [ imgrep release ];
   295     SDL_FreeSurface(mergedSurface);
   296 freePool:
   297     [pool release];
   298 }
   299 
   300 static int  QZ_IconifyWindow (_THIS) { 
   301 
   302     if ( ! [ qz_window isMiniaturized ] ) {
   303         [ qz_window miniaturize:nil ];
   304         return 1;
   305     }
   306     else {
   307         SDL_SetError ("window already iconified");
   308         return 0;
   309     }
   310 }
   311 
   312 /*
   313 static int  QZ_GetWMInfo  (_THIS, SDL_SysWMinfo *info) { 
   314     info->nsWindowPtr = qz_window;
   315     return 0; 
   316 }*/
   317 
   318 static void QZ_ChangeGrabState (_THIS, int action) {
   319 
   320     /* 
   321         Figure out what the next state should be based on the action.
   322         Ignore actions that can't change the current state.
   323     */
   324     if ( grab_state == QZ_UNGRABBED ) {
   325         if ( action == QZ_ENABLE_GRAB ) {
   326             if ( cursor_visible )
   327                 grab_state = QZ_VISIBLE_GRAB;
   328             else
   329                 grab_state = QZ_INVISIBLE_GRAB;
   330         }
   331     }
   332     else if ( grab_state == QZ_VISIBLE_GRAB ) {
   333         if ( action == QZ_DISABLE_GRAB )
   334             grab_state = QZ_UNGRABBED;
   335         else if ( action == QZ_HIDECURSOR )
   336             grab_state = QZ_INVISIBLE_GRAB;
   337     }
   338     else {
   339         assert( grab_state == QZ_INVISIBLE_GRAB );
   340         
   341         if ( action == QZ_DISABLE_GRAB )
   342             grab_state = QZ_UNGRABBED;
   343         else if ( action == QZ_SHOWCURSOR )
   344             grab_state = QZ_VISIBLE_GRAB;
   345     }
   346     
   347     /* now apply the new state */
   348     if (grab_state == QZ_UNGRABBED) {
   349     
   350         CGAssociateMouseAndMouseCursorPosition (1);
   351     }
   352     else if (grab_state == QZ_VISIBLE_GRAB) {
   353     
   354         CGAssociateMouseAndMouseCursorPosition (1);
   355     }
   356     else {
   357         assert( grab_state == QZ_INVISIBLE_GRAB );
   358 
   359         QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2, SDL_VideoSurface->h / 2);
   360         CGAssociateMouseAndMouseCursorPosition (0);
   361     }
   362 }
   363 
   364 static SDL_GrabMode QZ_GrabInput (_THIS, SDL_GrabMode grab_mode) {
   365 
   366     int doGrab = grab_mode & SDL_GRAB_ON;
   367     /*int fullscreen = grab_mode & SDL_GRAB_FULLSCREEN;*/
   368 
   369     if ( this->screen == NULL ) {
   370         SDL_SetError ("QZ_GrabInput: screen is NULL");
   371         return SDL_GRAB_OFF;
   372     }
   373         
   374     if ( ! video_set ) {
   375         /*SDL_SetError ("QZ_GrabInput: video is not set, grab will take effect on mode switch"); */
   376         current_grab_mode = grab_mode;
   377         return grab_mode;       /* Will be set later on mode switch */
   378     }
   379 
   380     if ( grab_mode != SDL_GRAB_QUERY ) {
   381         if ( doGrab )
   382             QZ_ChangeGrabState (this, QZ_ENABLE_GRAB);
   383         else
   384             QZ_ChangeGrabState (this, QZ_DISABLE_GRAB);
   385         
   386         current_grab_mode = doGrab ? SDL_GRAB_ON : SDL_GRAB_OFF;
   387     }
   388 
   389     return current_grab_mode;
   390 }
   391 
   392 /* Resize icon, BMP format */
   393 static unsigned char QZ_ResizeIcon[] = {
   394     0x42,0x4d,0x31,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
   395     0x00,0x00,0x0d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
   396     0x00,0x00,0xfb,0x01,0x00,0x00,0x13,0x0b,0x00,0x00,0x13,0x0b,0x00,0x00,0x00,0x00,
   397     0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   398     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   399     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
   400     0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,
   401     0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
   402     0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xda,0xda,0xda,0x87,
   403     0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,
   404     0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
   405     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xd5,0xd5,0x87,0x87,0x87,0xe8,0xe8,0xe8,
   406     0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,
   407     0xda,0xda,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   408     0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,
   409     0xda,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
   410     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,
   411     0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xda,0xda,0xda,0x87,0x87,0x87,0xe8,
   412     0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   413     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd7,0xd7,0xd7,0x87,0x87,0x87,0xe8,0xe8,
   414     0xe8,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
   415     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   416     0xff,0xff,0xff,0xd9,0xd9,0xd9,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xdc,
   417     0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   418     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,
   419     0xdb,0x87,0x87,0x87,0xe8,0xe8,0xe8,0xff,0xff,0xff,0xff,0xff,0xff,0x0b,0xff,0xff,
   420     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   421     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdb,0xdb,0xdb,0x87,0x87,0x87,0xe8,
   422     0xe8,0xe8,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   423     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   424     0xff,0xff,0xff,0xff,0xdc,0xdc,0xdc,0x87,0x87,0x87,0xff,0xff,0xff,0x0b,0xff,0xff,
   425     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   426     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xdc,
   427     0xdc,0xdc,0xff,0xff,0xff,0x0b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   428     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
   429     0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0b
   430 };
   431 
   432 static void QZ_DrawResizeIcon (_THIS, RgnHandle dirtyRegion) {
   433 
   434     /* Check if we should draw the resize icon */
   435     if (SDL_VideoSurface->flags & SDL_RESIZABLE) {
   436     
   437         Rect	icon;
   438         SetRect (&icon, SDL_VideoSurface->w - 13, SDL_VideoSurface->h - 13, 
   439                     SDL_VideoSurface->w, SDL_VideoSurface->h);
   440                     
   441         if (RectInRgn (&icon, dirtyRegion)) {
   442         
   443             SDL_Rect icon_rect;
   444             
   445             /* Create the icon image */
   446             if (resize_icon == NULL) {
   447             
   448                 SDL_RWops *rw;
   449                 SDL_Surface *tmp;
   450                 
   451                 rw = SDL_RWFromMem (QZ_ResizeIcon, sizeof(QZ_ResizeIcon));
   452                 tmp = SDL_LoadBMP_RW (rw, SDL_TRUE);
   453                                                                 
   454                 resize_icon = SDL_ConvertSurface (tmp, SDL_VideoSurface->format, SDL_SRCCOLORKEY);								
   455                 SDL_SetColorKey (resize_icon, SDL_SRCCOLORKEY, 0xFFFFFF);
   456                 
   457                 SDL_FreeSurface (tmp);
   458             }
   459             
   460             icon_rect.x = SDL_VideoSurface->w - 13;
   461             icon_rect.y = SDL_VideoSurface->h - 13;
   462             icon_rect.w = 13;
   463             icon_rect.h = 13;
   464             
   465             SDL_BlitSurface (resize_icon, NULL, SDL_VideoSurface, &icon_rect);
   466         }
   467     }
   468 }