Merged fix for bug #240 from SDL 1.2 SDL-1.3
authorSam Lantinga <slouken@libsdl.org>
Sat, 24 Jun 2006 17:31:46 +0000
branchSDL-1.3
changeset 1708 cd14138a8703
parent 1707 57ce47f033a5
child 1709 2047ec9bb665
Merged fix for bug #240 from SDL 1.2
src/video/quartz/SDL_QuartzWM.m
test/Makefile.in
test/testcursor.c
--- a/src/video/quartz/SDL_QuartzWM.m	Sat Jun 24 17:01:29 2006 +0000
+++ b/src/video/quartz/SDL_QuartzWM.m	Sat Jun 24 17:31:46 2006 +0000
@@ -26,57 +26,82 @@
 
 struct WMcursor
 {
-    Cursor curs;
+    NSCursor *nscursor;
 };
 
 void
-QZ_FreeWMCursor (_THIS, WMcursor * cursor)
+QZ_FreeWMCursor(_THIS, WMcursor * cursor)
 {
 
-    if (cursor != NULL)
-        free (cursor);
+    if (cursor != NULL) {
+        [cursor->nscursor release];
+        free(cursor);
+    }
 }
 
-/* Use the Carbon cursor routines for now */
 WMcursor *
-QZ_CreateWMCursor (_THIS, Uint8 * data, Uint8 * mask,
-                   int w, int h, int hot_x, int hot_y)
+QZ_CreateWMCursor(_THIS, Uint8 * data, Uint8 * mask,
+                  int w, int h, int hot_x, int hot_y)
 {
     WMcursor *cursor;
-    int row, bytes;
+    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) {
-        SDL_OutOfMemory ();
-        return (NULL);
+    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: NSDeviceBlackColorSpace 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];
+        planes[1][i] = mask[i] | data[i];
     }
-    SDL_memset (cursor, 0, sizeof (*cursor));
-
-    if (w > 16)
-        w = 16;
-
-    if (h > 16)
-        h = 16;
 
-    bytes = (w + 7) / 8;
-
-    for (row = 0; row < h; ++row) {
-        SDL_memcpy (&cursor->curs.data[row], data, bytes);
-        data += bytes;
+    /* 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)];
     }
-    for (row = 0; row < h; ++row) {
-        SDL_memcpy (&cursor->curs.mask[row], mask, bytes);
-        mask += bytes;
-    }
-    cursor->curs.hotSpot.h = hot_x;
-    cursor->curs.hotSpot.v = hot_y;
+  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_ShowMouse (_THIS)
+QZ_ShowMouse(_THIS)
 {
     if (!cursor_visible) {
         [NSCursor unhide];
@@ -85,42 +110,42 @@
 }
 
 void
-QZ_HideMouse (_THIS)
+QZ_HideMouse(_THIS)
 {
-    if ((SDL_GetAppState () & SDL_APPMOUSEFOCUS) && cursor_visible) {
+    if ((SDL_GetAppState() & SDL_APPMOUSEFOCUS) && cursor_visible) {
         [NSCursor hide];
         cursor_visible = NO;
     }
 }
 
 BOOL
-QZ_IsMouseInWindow (_THIS)
+QZ_IsMouseInWindow(_THIS)
 {
     if (qz_window == nil)
         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]);
+        return NSPointInRect(p,[window_view frame]);
     }
 }
 
 int
-QZ_ShowWMCursor (_THIS, WMcursor * cursor)
+QZ_ShowWMCursor(_THIS, WMcursor * cursor)
 {
 
     if (cursor == NULL) {
         if (cursor_should_be_visible) {
-            QZ_HideMouse (this);
+            QZ_HideMouse(this);
             cursor_should_be_visible = NO;
-            QZ_ChangeGrabState (this, QZ_HIDECURSOR);
+            QZ_ChangeGrabState(this, QZ_HIDECURSOR);
         }
     } else {
-        SetCursor (&cursor->curs);
+        [cursor->nscursor set];
         if (!cursor_should_be_visible) {
-            QZ_ShowMouse (this);
+            QZ_ShowMouse(this);
             cursor_should_be_visible = YES;
-            QZ_ChangeGrabState (this, QZ_SHOWCURSOR);
+            QZ_ChangeGrabState(this, QZ_SHOWCURSOR);
         }
     }
 
@@ -137,7 +162,7 @@
 
 /* Convert Cocoa screen coordinate to Cocoa window coordinate */
 void
-QZ_PrivateGlobalToLocal (_THIS, NSPoint * p)
+QZ_PrivateGlobalToLocal(_THIS, NSPoint * p)
 {
 
   *p =[qz_window convertScreenToBase:*p];
@@ -146,7 +171,7 @@
 
 /* Convert Cocoa window coordinate to Cocoa screen coordinate */
 void
-QZ_PrivateLocalToGlobal (_THIS, NSPoint * p)
+QZ_PrivateLocalToGlobal(_THIS, NSPoint * p)
 {
 
   *p =[qz_window convertBaseToScreen:*p];
@@ -154,18 +179,18 @@
 
 /* Convert SDL coordinate to Cocoa coordinate */
 void
-QZ_PrivateSDLToCocoa (_THIS, NSPoint * p)
+QZ_PrivateSDLToCocoa(_THIS, NSPoint * p)
 {
 
-    if (CGDisplayIsCaptured (display_id)) {     /* capture signals fullscreen */
+    if (CGDisplayIsCaptured(display_id)) {      /* capture signals fullscreen */
 
-        p->y = CGDisplayPixelsHigh (display_id) - p->y;
+        p->y = CGDisplayPixelsHigh(display_id) - p->y;
     } else {
 
       *p =[window_view convertPoint: *p toView:nil];
 
         /* We need a workaround in OpenGL mode */
-        if (SDL_VideoSurface->flags & SDL_INTERNALOPENGL) {
+        if (SDL_VideoSurface->flags & SDL_OPENGL) {
             p->y =[window_view frame].size.height - p->y;
         }
     }
@@ -173,19 +198,19 @@
 
 /* Convert Cocoa coordinate to SDL coordinate */
 void
-QZ_PrivateCocoaToSDL (_THIS, NSPoint * p)
+QZ_PrivateCocoaToSDL(_THIS, NSPoint * p)
 {
 
-    if (CGDisplayIsCaptured (display_id)) {     /* capture signals fullscreen */
+    if (CGDisplayIsCaptured(display_id)) {      /* capture signals fullscreen */
 
-        p->y = CGDisplayPixelsHigh (display_id) - p->y;
+        p->y = CGDisplayPixelsHigh(display_id) - p->y;
     } else {
 
       *p =[window_view convertPoint: *p fromView:nil];
 
         /* We need a workaround in OpenGL mode */
         if (SDL_VideoSurface != NULL
-            && (SDL_VideoSurface->flags & SDL_INTERNALOPENGL)) {
+            && (SDL_VideoSurface->flags & SDL_OPENGL)) {
             p->y =[window_view frame].size.height - p->y;
         }
     }
@@ -193,19 +218,19 @@
 
 /* Convert SDL coordinate to window server (CoreGraphics) coordinate */
 CGPoint
-QZ_PrivateSDLToCG (_THIS, NSPoint * p)
+QZ_PrivateSDLToCG(_THIS, NSPoint * p)
 {
 
     CGPoint cgp;
 
-    if (!CGDisplayIsCaptured (display_id)) {    /* not captured => not fullscreen => local coord */
+    if (!CGDisplayIsCaptured(display_id)) {     /* not captured => not fullscreen => local coord */
 
         int height;
 
-        QZ_PrivateSDLToCocoa (this, p);
-        QZ_PrivateLocalToGlobal (this, p);
+        QZ_PrivateSDLToCocoa(this, p);
+        QZ_PrivateLocalToGlobal(this, p);
 
-        height = CGDisplayPixelsHigh (display_id);
+        height = CGDisplayPixelsHigh(display_id);
         p->y = height - p->y;
     }
 
@@ -218,40 +243,40 @@
 #if 0                           /* Dead code */
 /* Convert window server (CoreGraphics) coordinate to SDL coordinate */
 void
-QZ_PrivateCGToSDL (_THIS, NSPoint * p)
+QZ_PrivateCGToSDL(_THIS, NSPoint * p)
 {
 
-    if (!CGDisplayIsCaptured (display_id)) {    /* not captured => not fullscreen => local coord */
+    if (!CGDisplayIsCaptured(display_id)) {     /* not captured => not fullscreen => local coord */
 
         int height;
 
         /* Convert CG Global to Cocoa Global */
-        height = CGDisplayPixelsHigh (display_id);
+        height = CGDisplayPixelsHigh(display_id);
         p->y = height - p->y;
 
-        QZ_PrivateGlobalToLocal (this, p);
-        QZ_PrivateCocoaToSDL (this, p);
+        QZ_PrivateGlobalToLocal(this, p);
+        QZ_PrivateCocoaToSDL(this, p);
     }
 }
 #endif /* Dead code */
 
 void
-QZ_PrivateWarpCursor (_THIS, int x, int y)
+QZ_PrivateWarpCursor(_THIS, int x, int y)
 {
 
     NSPoint p;
     CGPoint cgp;
 
-    p = NSMakePoint (x, y);
-    cgp = QZ_PrivateSDLToCG (this, &p);
+    p = NSMakePoint(x, y);
+    cgp = QZ_PrivateSDLToCG(this, &p);
 
     /* this is the magic call that fixes cursor "freezing" after warp */
-    CGSetLocalEventsSuppressionInterval (0.0);
-    CGWarpMouseCursorPosition (cgp);
+    CGSetLocalEventsSuppressionInterval(0.0);
+    CGWarpMouseCursorPosition(cgp);
 }
 
 void
-QZ_WarpWMCursor (_THIS, Uint16 x, Uint16 y)
+QZ_WarpWMCursor(_THIS, Uint16 x, Uint16 y)
 {
 
     /* Only allow warping when in foreground */
@@ -260,23 +285,23 @@
 
     /* Do the actual warp */
     if (grab_state != QZ_INVISIBLE_GRAB)
-        QZ_PrivateWarpCursor (this, x, y);
+        QZ_PrivateWarpCursor(this, x, y);
 
     /* Generate the mouse moved event */
-    SDL_PrivateMouseMotion (0, 0, x, y);
+    SDL_PrivateMouseMotion(0, 0, x, y);
 }
 
 void
-QZ_MoveWMCursor (_THIS, int x, int y)
+QZ_MoveWMCursor(_THIS, int x, int y)
 {
 }
 void
-QZ_CheckMouseMode (_THIS)
+QZ_CheckMouseMode(_THIS)
 {
 }
 
 void
-QZ_SetCaption (_THIS, const char *title, const char *icon)
+QZ_SetCaption(_THIS, const char *title, const char *icon)
 {
 
     if (qz_window != nil) {
@@ -295,7 +320,7 @@
 }
 
 void
-QZ_SetIcon (_THIS, SDL_Surface * icon, Uint8 * mask)
+QZ_SetIcon(_THIS, SDL_Surface * icon, Uint8 * mask)
 {
     NSBitmapImageRep *imgrep;
     NSImage *img;
@@ -312,7 +337,7 @@
     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 */
+    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
@@ -320,20 +345,20 @@
 #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);
+        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);
+    SDL_SetAlpha(icon, 0, 255);
+    SDL_BlitSurface(icon, NULL, mergedSurface, NULL);
     if (iconSrcAlpha)
-        SDL_SetAlpha (icon, SDL_SRCALPHA, iconAlphaValue);
+        SDL_SetAlpha(icon, SDL_SRCALPHA, iconAlphaValue);
 
-    SDL_FreeSurface (mergedSurface);
+    SDL_FreeSurface(mergedSurface);
 
     /* apply mask, source alpha, and premultiply color values by alpha */
     maskPitch = (icon->w + 7) / 8;
@@ -361,8 +386,8 @@
         }
     }
 
-  img =[[[NSImage alloc] initWithSize:NSMakeSize (icon->w,
-                icon->h)] autorelease];
+  img =[[[NSImage alloc] initWithSize:NSMakeSize(icon->w,
+               icon->h)] autorelease];
     if (img == nil)
         goto freePool;
   [img addRepresentation:imgrep];
@@ -373,14 +398,14 @@
 }
 
 int
-QZ_IconifyWindow (_THIS)
+QZ_IconifyWindow(_THIS)
 {
 
     if (![qz_window isMiniaturized]) {
       [qz_window miniaturize:nil];
         return 1;
     } else {
-        SDL_SetError ("window already iconified");
+        SDL_SetError("window already iconified");
         return 0;
     }
 }
@@ -392,7 +417,7 @@
 }*/
 
 void
-QZ_ChangeGrabState (_THIS, int action)
+QZ_ChangeGrabState(_THIS, int action)
 {
 
     /* 
@@ -412,7 +437,7 @@
         else if (action == QZ_HIDECURSOR)
             grab_state = QZ_INVISIBLE_GRAB;
     } else {
-        assert (grab_state == QZ_INVISIBLE_GRAB);
+        assert(grab_state == QZ_INVISIBLE_GRAB);
 
         if (action == QZ_DISABLE_GRAB)
             grab_state = QZ_UNGRABBED;
@@ -423,28 +448,28 @@
     /* now apply the new state */
     if (grab_state == QZ_UNGRABBED) {
 
-        CGAssociateMouseAndMouseCursorPosition (1);
+        CGAssociateMouseAndMouseCursorPosition(1);
     } else if (grab_state == QZ_VISIBLE_GRAB) {
 
-        CGAssociateMouseAndMouseCursorPosition (1);
+        CGAssociateMouseAndMouseCursorPosition(1);
     } else {
-        assert (grab_state == QZ_INVISIBLE_GRAB);
+        assert(grab_state == QZ_INVISIBLE_GRAB);
 
-        QZ_PrivateWarpCursor (this, SDL_VideoSurface->w / 2,
-                              SDL_VideoSurface->h / 2);
-        CGAssociateMouseAndMouseCursorPosition (0);
+        QZ_PrivateWarpCursor(this, SDL_VideoSurface->w / 2,
+                             SDL_VideoSurface->h / 2);
+        CGAssociateMouseAndMouseCursorPosition(0);
     }
 }
 
 SDL_GrabMode
-QZ_GrabInput (_THIS, SDL_GrabMode grab_mode)
+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");
+        SDL_SetError("QZ_GrabInput: screen is NULL");
         return SDL_GRAB_OFF;
     }
 
@@ -456,9 +481,9 @@
 
     if (grab_mode != SDL_GRAB_QUERY) {
         if (doGrab)
-            QZ_ChangeGrabState (this, QZ_ENABLE_GRAB);
+            QZ_ChangeGrabState(this, QZ_ENABLE_GRAB);
         else
-            QZ_ChangeGrabState (this, QZ_DISABLE_GRAB);
+            QZ_ChangeGrabState(this, QZ_DISABLE_GRAB);
 
         current_grab_mode = doGrab ? SDL_GRAB_ON : SDL_GRAB_OFF;
     }
--- a/test/Makefile.in	Sat Jun 24 17:01:29 2006 +0000
+++ b/test/Makefile.in	Sat Jun 24 17:31:46 2006 +0000
@@ -7,7 +7,7 @@
 CFLAGS  = @CFLAGS@
 LIBS	= @LIBS@
 
-TARGETS = checkkeys$(EXE) graywin$(EXE) loopwave$(EXE) testalpha$(EXE) testbitmap$(EXE) testblitspeed$(EXE) testcdrom$(EXE) testdyngl$(EXE) testerror$(EXE) testfile$(EXE) testgamma$(EXE) testgl$(EXE) testhread$(EXE) testiconv$(EXE) testjoystick$(EXE) testkeys$(EXE) testlock$(EXE) testoverlay2$(EXE) testoverlay$(EXE) testpalette$(EXE) testplatform$(EXE) testsem$(EXE) testsprite$(EXE) testsprite2$(EXE) testtimer$(EXE) testver$(EXE) testvidinfo$(EXE) testwin$(EXE) testwm$(EXE) threadwin$(EXE) torturethread$(EXE)
+TARGETS = checkkeys$(EXE) graywin$(EXE) loopwave$(EXE) testalpha$(EXE) testbitmap$(EXE) testblitspeed$(EXE) testcdrom$(EXE) testcursor$(EXE) testdyngl$(EXE) testerror$(EXE) testfile$(EXE) testgamma$(EXE) testgl$(EXE) testhread$(EXE) testiconv$(EXE) testjoystick$(EXE) testkeys$(EXE) testlock$(EXE) testoverlay2$(EXE) testoverlay$(EXE) testpalette$(EXE) testplatform$(EXE) testsem$(EXE) testsprite$(EXE) testsprite2$(EXE) testtimer$(EXE) testver$(EXE) testvidinfo$(EXE) testwin$(EXE) testwm$(EXE) threadwin$(EXE) torturethread$(EXE)
 
 all: $(TARGETS)
 
@@ -32,6 +32,9 @@
 testcdrom$(EXE): $(srcdir)/testcdrom.c
 	$(CC) -o $@ $? $(CFLAGS) $(LIBS)
 
+testcursor$(EXE): $(srcdir)/testcursor.c
+	$(CC) -o $@ $? $(CFLAGS) $(LIBS)
+
 testdyngl$(EXE): $(srcdir)/testdyngl.c
 	$(CC) -o $@ $? $(CFLAGS) $(LIBS)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testcursor.c	Sat Jun 24 17:31:46 2006 +0000
@@ -0,0 +1,223 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "SDL.h"
+
+/* This is an example 16x16 cursor
+	top left :	black
+	top right : inverted color or black
+	bottom left: white
+	bottom right: transparent
+	(swap left and right for different endianness)
+*/
+
+Uint16 cursor_data[16] = {
+    0xffff,
+    0xffff,
+    0xffff,
+    0xffff,
+
+    0xffff,
+    0xffff,
+    0xffff,
+    0xffff,
+
+    0x0000,
+    0x0000,
+    0x0000,
+    0x0000,
+
+    0x0000,
+    0x0000,
+    0x0000,
+    0x0000
+};
+
+Uint16 cursor_mask[16] = {
+    0xff00,
+    0xff00,
+    0xff00,
+    0xff00,
+
+    0xff00,
+    0xff00,
+    0xff00,
+    0xff00,
+
+    0xff00,
+    0xff00,
+    0xff00,
+    0xff00,
+
+    0xff00,
+    0xff00,
+    0xff00,
+    0xff00
+};
+
+/* another test cursor: smaller than 16x16, and with an odd height */
+
+Uint8 small_cursor_data[11] =
+    { 0x00, 0x18, 0x08, 0x38, 0x44, 0x54, 0x44, 0x38, 0x20, 0x20, 0x00 };
+Uint8 small_cursor_mask[11] =
+    { 0x3C, 0x3C, 0x3C, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0x78, 0x70, 0x70 };
+
+/* XPM */
+static const char *arrow[] = {
+    /* width height num_colors chars_per_pixel */
+    "    32    32        3            1",
+    /* colors */
+    "X c #000000",
+    ". c #ffffff",
+    "  c None",
+    /* pixels */
+    "X                               ",
+    "XX                              ",
+    "X.X                             ",
+    "X..X                            ",
+    "X...X                           ",
+    "X....X                          ",
+    "X.....X                         ",
+    "X......X                        ",
+    "X.......X                       ",
+    "X........X                      ",
+    "X.....XXXXX                     ",
+    "X..X..X                         ",
+    "X.X X..X                        ",
+    "XX  X..X                        ",
+    "X    X..X                       ",
+    "     X..X                       ",
+    "      X..X                      ",
+    "      X..X                      ",
+    "       XX                       ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "                                ",
+    "0,0"
+};
+
+static SDL_Cursor *
+create_arrow_cursor()
+{
+    int i, row, col;
+    Uint8 data[4 * 32];
+    Uint8 mask[4 * 32];
+    int hot_x, hot_y;
+
+    i = -1;
+    for (row = 0; row < 32; ++row) {
+        for (col = 0; col < 32; ++col) {
+            if (col % 8) {
+                data[i] <<= 1;
+                mask[i] <<= 1;
+            } else {
+                ++i;
+                data[i] = mask[i] = 0;
+            }
+            switch (arrow[4 + row][col]) {
+            case 'X':
+                data[i] |= 0x01;
+                mask[i] |= 0x01;
+                break;
+            case '.':
+                mask[i] |= 0x01;
+                break;
+            case ' ':
+                break;
+            }
+        }
+    }
+    sscanf(arrow[4 + row], "%d,%d", &hot_x, &hot_y);
+    return SDL_CreateCursor(data, mask, 32, 32, hot_x, hot_y);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+    SDL_Surface *screen;
+    SDL_bool quit = SDL_FALSE, first_time = SDL_TRUE;
+    SDL_Cursor *cursor[3];
+    int current;
+
+    /* Load the SDL library */
+    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+        fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
+        return (1);
+    }
+
+    screen = SDL_SetVideoMode(320, 200, 8, SDL_ANYFORMAT);
+    if (screen == NULL) {
+        fprintf(stderr, "Couldn't initialize video mode: %s\n",
+                SDL_GetError());
+        return (1);
+    }
+
+    SDL_FillRect(screen, NULL, 0x664422);
+
+    cursor[0] = SDL_CreateCursor((Uint8 *) cursor_data, (Uint8 *) cursor_mask,
+                                 16, 16, 8, 8);
+    if (cursor[0] == NULL) {
+        fprintf(stderr, "Couldn't initialize test cursor: %s\n",
+                SDL_GetError());
+        SDL_Quit();
+        return (1);
+    }
+    cursor[1] = create_arrow_cursor();
+    if (cursor[1] == NULL) {
+        fprintf(stderr, "Couldn't initialize arrow cursor: %s\n",
+                SDL_GetError());
+        SDL_FreeCursor(cursor[0]);
+        SDL_Quit();
+        return (1);
+    }
+    cursor[2] = SDL_CreateCursor(small_cursor_data, small_cursor_mask,
+                                 8, 11, 3, 5);
+    if (cursor[2] == NULL) {
+        fprintf(stderr, "Couldn't initialize test cursor: %s\n",
+                SDL_GetError());
+        SDL_Quit();
+        return (1);
+    }
+
+    current = 0;
+    SDL_SetCursor(cursor[current]);
+
+    while (!quit) {
+        SDL_Event event;
+        while (SDL_PollEvent(&event)) {
+            switch (event.type) {
+            case SDL_MOUSEBUTTONDOWN:
+                current = (current + 1) % 3;
+                SDL_SetCursor(cursor[current]);
+                break;
+            case SDL_KEYDOWN:
+                if (event.key.keysym.sym == SDLK_ESCAPE) {
+                    quit = SDL_TRUE;
+                }
+                break;
+            case SDL_QUIT:
+                quit = SDL_TRUE;
+                break;
+            }
+        }
+        SDL_Flip(screen);
+        SDL_Delay(1);
+    }
+
+    SDL_FreeCursor(cursor[0]);
+    SDL_FreeCursor(cursor[1]);
+
+    SDL_Quit();
+    return (0);
+}