juce_mac_MouseCursor and retina display

Hi Jules,


It seems that mouse cursors like Resize ones don't have any retina display support.

WebKit do not seems to provides @2x for those and you don't check for those either.


Still it seems that some are using WebKit resources while a standard mouse cursor exists like UpDownResizeCursor.

What is the reason behind this choice ?




The resize cursors look ok on my retina display (?)

The reason for using the Webkit stuff was because the cursors weren't supported in older versions of OSX, but they may have been added since then. Would still need to be careful not to use the newer ones when backwards compatibility is important.

resize cursor like UpDownResizeCursor are here since 10.3 so don't think backwards compatibility is an issue here :)




The retina stuff is a report from one of our beta tester. Will need to check myself with someone with a retina screen :)

Seems there is a newer way than webkit as well to get resizenortheastsouthwest stuff and retina proof


Here is an implementation of this with retina support


static void* fromHIServices (const char* filename)



      NSString *cursorName = [[NSString alloc] initWithUTF8String:filename];

      NSString *cursorPath = [@"/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors" stringByAppendingPathComponent:cursorName];

      NSImage *image = [[NSImage alloc] initByReferencingFile:[cursorPath stringByAppendingPathComponent:@"cursor.pdf"]];

      NSImage *resultImage = [[NSImage alloc] initWithSize:[image size]];

      for (int scale = 1; scale <= 4; scale++) 


        NSAffineTransform *xform = [[NSAffineTransform alloc] init];

        [xform scaleBy:scale];

        id hints = @{ NSImageHintCTM: xform };

        CGImageRef rasterCGImage = [image CGImageForProposedRect:NULL context:nil hints:hints];

        NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithCGImage:rasterCGImage];

        [rep setSize:[image size]];

        [resultImage addRepresentation:rep];


      NSDictionary *info = [NSDictionary dictionaryWithContentsOfFile:[cursorPath stringByAppendingPathComponent:@"info.plist"]];

      NSCursor *cursor = [[NSCursor alloc] initWithImage:resultImage hotSpot:NSMakePoint([[info valueForKey:@"hotx"] doubleValue], [[info valueForKey:@"hoty"] doubleValue])];

      return cursor;



Beware that the call are a bit changed as well.


void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type)




        NSCursor* c = nil;

        switch (type)


            case NormalCursor:

            case ParentCursor:          c = [NSCursor arrowCursor]; break;

            case NoCursor:              return CustomMouseCursorInfo (Image (Image::ARGB, 8, 8, true), 0, 0).create();

            case DraggingHandCursor:    c = [NSCursor openHandCursor]; break;

            case WaitCursor:            c = [NSCursor arrowCursor]; break; // avoid this on the mac, let the OS provide the beachball

            case IBeamCursor:           c = [NSCursor IBeamCursor]; break;

            case PointingHandCursor:    c = [NSCursor pointingHandCursor]; break;

            case LeftEdgeResizeCursor:  c = [NSCursor resizeLeftCursor]; break;

            case RightEdgeResizeCursor: c = [NSCursor resizeRightCursor]; break;

            case CrosshairCursor:       c = [NSCursor crosshairCursor]; break;

            case CopyingCursor:



                if (void* m = MouseCursorHelpers::fromHIServices ("copy"))

                    return m;


                c = [NSCursor dragCopyCursor]; // added in 10.6



            case UpDownResizeCursor:

            case TopEdgeResizeCursor:

            case BottomEdgeResizeCursor:

                return MouseCursorHelpers::fromHIServices ("resizenorthsouth");

            case LeftRightResizeCursor:

                if (void* m = MouseCursorHelpers::fromHIServices ("resizeeastwest"))

                    return m;

                c = [NSCursor resizeLeftRightCursor];


            case TopLeftCornerResizeCursor:

            case BottomRightCornerResizeCursor:

                return MouseCursorHelpers::fromHIServices ("resizenorthwestsoutheast");

            case TopRightCornerResizeCursor:

            case BottomLeftCornerResizeCursor:

                return MouseCursorHelpers::fromHIServices ("resizenortheastsouthwest");

            case UpDownLeftRightResizeCursor:

                return MouseCursorHelpers::fromHIServices ("move");





        [c retain];

        return c;




Please let me know what do you think.


Thanks !



I know Jules is in SF, but Timur or Fabian are not allowed to check OSX thingy ? :D

Please be patient, we'll have a look as soon as we have the time... these are quite busy times right now ;-)

ah ah. Thanks Timur :)

Here are some thoughts:

Yes, I can confirm the problem. If you programmatically create a MouseCursor::UpDownResizeCursor in JUCE, it looks wrong on Retina displays.

Yes, I can also confirm that your code fixes the problem.

One thing I am concerned about in your code is that you actually compute the scaling of the image for 4 different scales, every time a MouseCursor is created?

Also, are you sure that this file in HIServices.framework will be present on all relevant older OSX versions?

Yes, this is computed each time.

AFAIK, cursor are shared so this doesn't happen a lot though, This is probably more CPU heavy than the current code but probably not that much as it only happen one time.

Not sure about the OSX version for HIServices  but at least 10.8 AFAIK, but this can be implemented like it is done LeftRightResizeCursor in the current code with a check that it exists. My code probably miss some check if it fails.

Older OSX version won't need retina assets and this doesn't require a recent version of the Base SDK to compile so we don't lose anything with this kind of code added.


This is more a proof of concept that a direct patch to commit to Juce though.


I'll take a look at this.

The code you posted has a lot of problems the way it's written, but it seems to be a sensible approach - I'll refactor it and post a fix shortly, thanks!

Thanks a lot !