|
|
@@ -571,17 +571,17 @@ open_window() {
|
|
|
}
|
|
|
|
|
|
if (_properties.has_cursor_filename()) {
|
|
|
- NSImage *image = load_image(_properties.get_cursor_filename());
|
|
|
- NSCursor *cursor = nil;
|
|
|
- // TODO: allow setting the hotspot, read it from file when loading .cur.
|
|
|
- if (image != nil) {
|
|
|
- cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint(0, 0)];
|
|
|
- }
|
|
|
+ NSCursor *cursor = load_cursor(_properties.get_cursor_filename());
|
|
|
+
|
|
|
if (cursor != nil) {
|
|
|
+ if (_cursor != nil) {
|
|
|
+ [_cursor release];
|
|
|
+ }
|
|
|
_cursor = cursor;
|
|
|
} else {
|
|
|
_properties.clear_cursor_filename();
|
|
|
}
|
|
|
+
|
|
|
// This will ensure that NSView's resetCursorRects gets called, which sets
|
|
|
// the appropriate cursor rects.
|
|
|
[[_view window] invalidateCursorRectsForView:_view];
|
|
|
@@ -1045,19 +1045,15 @@ set_properties_now(WindowProperties &properties) {
|
|
|
properties.set_cursor_filename(cursor_filename);
|
|
|
properties.clear_cursor_filename();
|
|
|
} else {
|
|
|
- NSImage *image = load_image(cursor_filename);
|
|
|
- if (image != nil) {
|
|
|
- NSCursor *cursor;
|
|
|
- cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint(0, 0)];
|
|
|
- if (cursor != nil) {
|
|
|
- // Replace the existing cursor.
|
|
|
- if (_cursor != nil) {
|
|
|
- [_cursor release];
|
|
|
- }
|
|
|
- _cursor = cursor;
|
|
|
- _properties.set_cursor_filename(cursor_filename);
|
|
|
- properties.clear_cursor_filename();
|
|
|
+ NSCursor *cursor = load_cursor(cursor_filename);
|
|
|
+ if (cursor != nil) {
|
|
|
+ // Replace the existing cursor.
|
|
|
+ if (_cursor != nil) {
|
|
|
+ [_cursor release];
|
|
|
}
|
|
|
+ _cursor = cursor;
|
|
|
+ _properties.set_cursor_filename(cursor_filename);
|
|
|
+ properties.clear_cursor_filename();
|
|
|
}
|
|
|
}
|
|
|
// This will ensure that NSView's resetCursorRects gets called, which sets
|
|
|
@@ -1321,11 +1317,12 @@ do_switch_fullscreen(CFDictionaryRef mode) {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Loads the indicated filename and returns an NSImage pointer, or NULL on
|
|
|
- * failure. Must be called from the window thread.
|
|
|
+ * Loads the indicated filename and returns an NSData pointer (which can then
|
|
|
+ * be used to create a CGImageSource or NSImage), or NULL on failure. Must be
|
|
|
+ * called from the window thread. May return nil.
|
|
|
*/
|
|
|
-NSImage *CocoaGraphicsWindow::
|
|
|
-load_image(const Filename &filename) {
|
|
|
+NSData *CocoaGraphicsWindow::
|
|
|
+load_image_data(const Filename &filename) {
|
|
|
if (filename.empty()) {
|
|
|
return nil;
|
|
|
}
|
|
|
@@ -1345,7 +1342,6 @@ load_image(const Filename &filename) {
|
|
|
}
|
|
|
|
|
|
// Look in our index.
|
|
|
- NSImage *image = nil;
|
|
|
IconImages::const_iterator it = _images.find(resolved);
|
|
|
if (it != _images.end()) {
|
|
|
// Found it.
|
|
|
@@ -1373,21 +1369,81 @@ load_image(const Filename &filename) {
|
|
|
|
|
|
NSData *data = [NSData dataWithBytesNoCopy:buffer length:size];
|
|
|
if (data == nil) {
|
|
|
+ cocoadisplay_cat.error()
|
|
|
+ << "Could not load image data from file " << filename << "\n";
|
|
|
return nil;
|
|
|
}
|
|
|
|
|
|
- image = [[NSImage alloc] initWithData:data];
|
|
|
- [data release];
|
|
|
+ _images[resolved] = data;
|
|
|
+ return data;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Wraps image data loaded by load_image_data with an NSImage. The returned
|
|
|
+ * pointer is autoreleased. May return nil.
|
|
|
+ */
|
|
|
+NSImage *CocoaGraphicsWindow::
|
|
|
+load_image(const Filename &filename) {
|
|
|
+ NSData *image_data = load_image_data(filename);
|
|
|
+ NSImage *image = [[[NSImage alloc] initWithData:image_data] autorelease];
|
|
|
if (image == nil) {
|
|
|
cocoadisplay_cat.error()
|
|
|
<< "Could not load image from file " << filename << "\n";
|
|
|
return nil;
|
|
|
}
|
|
|
-
|
|
|
- _images[resolved] = image;
|
|
|
return image;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Returns a cursor with the proper hotspot if a .cur filename is passed in.
|
|
|
+ * You must release the returned pointer. May return nil.
|
|
|
+ */
|
|
|
+NSCursor *CocoaGraphicsWindow::
|
|
|
+load_cursor(const Filename &filename) {
|
|
|
+ NSData *image_data = load_image_data(cursor_filename);
|
|
|
+ if (image_data == nil) {
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Read the metadata from the image, which should contain hotspotX and
|
|
|
+ // hotspotY properties.
|
|
|
+ CGImageSourceRef cg_image = CGImageSourceCreateWithData((CFDataRef)image_data, nullptr);
|
|
|
+ if (cg_image == NULL) {
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+
|
|
|
+ NSDictionary *image_props = (NSDictionary *)CGImageSourceCopyPropertiesAtIndex(cg_image, 0, nil);
|
|
|
+ CFRelease(cg_image);
|
|
|
+
|
|
|
+ if (image_props == nil) {
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+
|
|
|
+ CGFloat hotspot_x = 0.0f;
|
|
|
+ CGFloat hotspot_y = 0.0f;
|
|
|
+ if (image_props[@"hotspotX"] != nil) {
|
|
|
+ hotspot_x = [(NSNumber *)image_props[@"hotspotX"] floatValue];
|
|
|
+ }
|
|
|
+ if (image_props[@"hotspotY"] != nil) {
|
|
|
+ hotspot_y = [(NSNumber *)image_props[@"hotspotY"] floatValue];
|
|
|
+ }
|
|
|
+ [image_props release];
|
|
|
+
|
|
|
+ NSImage *image = [[NSImage alloc] initWithData:image_data];
|
|
|
+
|
|
|
+ NSCursor *cursor;
|
|
|
+ if (image != nil) {
|
|
|
+ // Apple recognizes that hotspots are usually specified from a .cur
|
|
|
+ // file, whose origin is in the top-left, so there's no need to flip
|
|
|
+ // it like most other Cocoa coordinates.
|
|
|
+ cursor = [[NSCursor alloc] initWithImage:image
|
|
|
+ hotSpot:NSMakePoint(hotspot_x, hotspot_y)];
|
|
|
+ [image release];
|
|
|
+ }
|
|
|
+
|
|
|
+ return cursor;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Called by CocoaPandaView or the window delegate when the frame rect
|
|
|
* changes.
|