mirror of
https://github.com/Ryujinx/Opentk.git
synced 2025-09-17 01:47:07 +00:00
[Mac] Implement NSCursor rectangles
This commit is contained in:
parent
d013ef1868
commit
7d8f14baa7
|
@ -57,6 +57,15 @@ namespace OpenTK.Platform.MacOS
|
||||||
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr intPtr1, IntPtr intPtr2, IntPtr intPtr3);
|
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr intPtr1, IntPtr intPtr2, IntPtr intPtr3);
|
||||||
|
|
||||||
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
|
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr p1, PointF p2);
|
||||||
|
|
||||||
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
|
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, SizeF p1);
|
||||||
|
|
||||||
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
|
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, RectangleF rectangle1);
|
||||||
|
|
||||||
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, RectangleF rectangle1, int int1, int int2, bool bool1);
|
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, RectangleF rectangle1, int int1, int int2, bool bool1);
|
||||||
|
|
||||||
|
@ -66,6 +75,9 @@ namespace OpenTK.Platform.MacOS
|
||||||
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, RectangleF rectangle1, int int1, IntPtr intPtr1, IntPtr intPtr2);
|
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, RectangleF rectangle1, int int1, IntPtr intPtr1, IntPtr intPtr2);
|
||||||
|
|
||||||
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
|
public extern static IntPtr SendIntPtr(IntPtr receiver, IntPtr selector, IntPtr p1, int p2, int p3, int p4, int p5, int p6, int p7, IntPtr p8, int p9, int p10);
|
||||||
|
|
||||||
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
public extern static bool SendBool(IntPtr receiver, IntPtr selector);
|
public extern static bool SendBool(IntPtr receiver, IntPtr selector);
|
||||||
|
|
||||||
|
@ -99,6 +111,9 @@ namespace OpenTK.Platform.MacOS
|
||||||
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
public extern static void SendVoid(IntPtr receiver, IntPtr selector, RectangleF rect1, bool bool1);
|
public extern static void SendVoid(IntPtr receiver, IntPtr selector, RectangleF rect1, bool bool1);
|
||||||
|
|
||||||
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
|
public extern static void SendVoid(IntPtr receiver, IntPtr selector, RectangleF rect1, IntPtr intPtr1);
|
||||||
|
|
||||||
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
[DllImport(LibObjC, EntryPoint="objc_msgSend")]
|
||||||
public extern static int SendInt(IntPtr receiver, IntPtr selector);
|
public extern static int SendInt(IntPtr receiver, IntPtr selector);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using OpenTK.Input;
|
using OpenTK.Input;
|
||||||
|
@ -87,6 +88,7 @@ namespace OpenTK.Platform.MacOS
|
||||||
static readonly IntPtr selAddTrackingArea = Selector.Get("addTrackingArea:");
|
static readonly IntPtr selAddTrackingArea = Selector.Get("addTrackingArea:");
|
||||||
static readonly IntPtr selRemoveTrackingArea = Selector.Get("removeTrackingArea:");
|
static readonly IntPtr selRemoveTrackingArea = Selector.Get("removeTrackingArea:");
|
||||||
static readonly IntPtr selTrackingArea = Selector.Get("trackingArea");
|
static readonly IntPtr selTrackingArea = Selector.Get("trackingArea");
|
||||||
|
static readonly IntPtr selInitWithSize = Selector.Get("initWithSize:");
|
||||||
static readonly IntPtr selInitWithRect = Selector.Get("initWithRect:options:owner:userInfo:");
|
static readonly IntPtr selInitWithRect = Selector.Get("initWithRect:options:owner:userInfo:");
|
||||||
static readonly IntPtr selOwner = Selector.Get("owner");
|
static readonly IntPtr selOwner = Selector.Get("owner");
|
||||||
static readonly IntPtr selLocationInWindowOwner = Selector.Get("locationInWindow");
|
static readonly IntPtr selLocationInWindowOwner = Selector.Get("locationInWindow");
|
||||||
|
@ -112,9 +114,19 @@ namespace OpenTK.Platform.MacOS
|
||||||
//static readonly IntPtr selExitFullScreenModeWithOptions = Selector.Get("exitFullScreenModeWithOptions:");
|
//static readonly IntPtr selExitFullScreenModeWithOptions = Selector.Get("exitFullScreenModeWithOptions:");
|
||||||
//static readonly IntPtr selEnterFullScreenModeWithOptions = Selector.Get("enterFullScreenMode:withOptions:");
|
//static readonly IntPtr selEnterFullScreenModeWithOptions = Selector.Get("enterFullScreenMode:withOptions:");
|
||||||
static readonly IntPtr selArrowCursor = Selector.Get("arrowCursor");
|
static readonly IntPtr selArrowCursor = Selector.Get("arrowCursor");
|
||||||
|
static readonly IntPtr selAddCursorRect = Selector.Get("addCursorRect:cursor:");
|
||||||
|
static readonly IntPtr selInvalidateCursorRectsForView = Selector.Get("invalidateCursorRectsForView:");
|
||||||
|
static readonly IntPtr selInitWithBitmapDataPlanes =
|
||||||
|
Selector.Get("initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bytesPerRow:bitsPerPixel:");
|
||||||
|
static readonly IntPtr selBitmapData = Selector.Get("bitmapData");
|
||||||
|
static readonly IntPtr selAddRepresentation = Selector.Get("addRepresentation:");
|
||||||
|
static readonly IntPtr selInitWithImageHotSpot = Selector.Get("initWithImage:hotSpot:");
|
||||||
|
|
||||||
static readonly IntPtr NSDefaultRunLoopMode;
|
static readonly IntPtr NSDefaultRunLoopMode;
|
||||||
static readonly IntPtr NSCursor;
|
static readonly IntPtr NSCursor;
|
||||||
|
static readonly IntPtr NSImage;
|
||||||
|
static readonly IntPtr NSBitmapImageRep;
|
||||||
|
static readonly IntPtr NSDeviceRGBColorSpace = Cocoa.ToNSString("NSDeviceRGBColorSpace");
|
||||||
|
|
||||||
static CocoaNativeWindow()
|
static CocoaNativeWindow()
|
||||||
{
|
{
|
||||||
|
@ -122,6 +134,8 @@ namespace OpenTK.Platform.MacOS
|
||||||
NSApplication.Initialize(); // Problem: This does not allow creating a separate app and using CocoaNativeWindow.
|
NSApplication.Initialize(); // Problem: This does not allow creating a separate app and using CocoaNativeWindow.
|
||||||
NSDefaultRunLoopMode = Cocoa.GetStringConstant(Cocoa.FoundationLibrary, "NSDefaultRunLoopMode");
|
NSDefaultRunLoopMode = Cocoa.GetStringConstant(Cocoa.FoundationLibrary, "NSDefaultRunLoopMode");
|
||||||
NSCursor = Class.Get("NSCursor");
|
NSCursor = Class.Get("NSCursor");
|
||||||
|
NSImage = Class.Get("NSImage");
|
||||||
|
NSBitmapImageRep = Class.Get("NSBitmapImageRep");
|
||||||
}
|
}
|
||||||
|
|
||||||
private CocoaWindowInfo windowInfo;
|
private CocoaWindowInfo windowInfo;
|
||||||
|
@ -167,9 +181,12 @@ namespace OpenTK.Platform.MacOS
|
||||||
Class.RegisterMethod(windowClass, new AcceptsFirstResponderDelegate(AcceptsFirstResponder), "acceptsFirstResponder", "b@:");
|
Class.RegisterMethod(windowClass, new AcceptsFirstResponderDelegate(AcceptsFirstResponder), "acceptsFirstResponder", "b@:");
|
||||||
Class.RegisterMethod(windowClass, new CanBecomeKeyWindowDelegate(CanBecomeKeyWindow), "canBecomeKeyWindow", "b@:");
|
Class.RegisterMethod(windowClass, new CanBecomeKeyWindowDelegate(CanBecomeKeyWindow), "canBecomeKeyWindow", "b@:");
|
||||||
Class.RegisterMethod(windowClass, new CanBecomeMainWindowDelegate(CanBecomeMainWindow), "canBecomeMainWindow", "b@:");
|
Class.RegisterMethod(windowClass, new CanBecomeMainWindowDelegate(CanBecomeMainWindow), "canBecomeMainWindow", "b@:");
|
||||||
|
|
||||||
Class.RegisterClass(windowClass);
|
Class.RegisterClass(windowClass);
|
||||||
|
|
||||||
|
IntPtr viewClass = Class.AllocateClass("OpenTK_NSView" + UniqueId, "NSView");
|
||||||
|
Class.RegisterMethod(viewClass, new ResetCursorRectsDelegate(ResetCursorRects), "resetCursorRects", "v@:");
|
||||||
|
Class.RegisterClass(viewClass);
|
||||||
|
|
||||||
// Create window instance
|
// Create window instance
|
||||||
var contentRect = new System.Drawing.RectangleF(x, y, width, height);
|
var contentRect = new System.Drawing.RectangleF(x, y, width, height);
|
||||||
var style = GetStyleMask(windowBorder);
|
var style = GetStyleMask(windowBorder);
|
||||||
|
@ -178,6 +195,22 @@ namespace OpenTK.Platform.MacOS
|
||||||
IntPtr windowPtr;
|
IntPtr windowPtr;
|
||||||
windowPtr = Cocoa.SendIntPtr(windowClass, Selector.Alloc);
|
windowPtr = Cocoa.SendIntPtr(windowClass, Selector.Alloc);
|
||||||
windowPtr = Cocoa.SendIntPtr(windowPtr, Selector.Get("initWithContentRect:styleMask:backing:defer:"), contentRect, (int)style, (int)bufferingType, false);
|
windowPtr = Cocoa.SendIntPtr(windowPtr, Selector.Get("initWithContentRect:styleMask:backing:defer:"), contentRect, (int)style, (int)bufferingType, false);
|
||||||
|
|
||||||
|
// Replace view with our custom implementation
|
||||||
|
// that overrides resetCursorRects (maybe there is
|
||||||
|
// a better way to implement this override?)
|
||||||
|
// Existing view:
|
||||||
|
IntPtr viewPtr = Cocoa.SendIntPtr(windowPtr, Selector.Get("contentView"));
|
||||||
|
// Our custom view with the same bounds:
|
||||||
|
viewPtr = Cocoa.SendIntPtr(
|
||||||
|
Cocoa.SendIntPtr(viewClass, Selector.Alloc),
|
||||||
|
Selector.Get("initWithFrame:"),
|
||||||
|
Cocoa.SendRect(viewPtr, selBounds));
|
||||||
|
if (viewPtr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Cocoa.SendVoid(windowPtr, Selector.Get("setContentView:"), viewPtr);
|
||||||
|
}
|
||||||
|
|
||||||
windowInfo = new CocoaWindowInfo(windowPtr);
|
windowInfo = new CocoaWindowInfo(windowPtr);
|
||||||
|
|
||||||
// Set up behavior
|
// Set up behavior
|
||||||
|
@ -205,6 +238,7 @@ namespace OpenTK.Platform.MacOS
|
||||||
delegate bool AcceptsFirstResponderDelegate(IntPtr self, IntPtr cmd);
|
delegate bool AcceptsFirstResponderDelegate(IntPtr self, IntPtr cmd);
|
||||||
delegate bool CanBecomeKeyWindowDelegate(IntPtr self, IntPtr cmd);
|
delegate bool CanBecomeKeyWindowDelegate(IntPtr self, IntPtr cmd);
|
||||||
delegate bool CanBecomeMainWindowDelegate(IntPtr self, IntPtr cmd);
|
delegate bool CanBecomeMainWindowDelegate(IntPtr self, IntPtr cmd);
|
||||||
|
delegate void ResetCursorRectsDelegate(IntPtr self, IntPtr cmd);
|
||||||
|
|
||||||
private void WindowKeyDown(IntPtr self, IntPtr cmd, IntPtr notification)
|
private void WindowKeyDown(IntPtr self, IntPtr cmd, IntPtr notification)
|
||||||
{
|
{
|
||||||
|
@ -331,7 +365,11 @@ namespace OpenTK.Platform.MacOS
|
||||||
}
|
}
|
||||||
|
|
||||||
var ownerBounds = Cocoa.SendRect(owner, selBounds);
|
var ownerBounds = Cocoa.SendRect(owner, selBounds);
|
||||||
var options = (int)(NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.ActiveInKeyWindow | NSTrackingAreaOptions.MouseMoved);
|
var options = (int)(
|
||||||
|
NSTrackingAreaOptions.MouseEnteredAndExited |
|
||||||
|
NSTrackingAreaOptions.ActiveInKeyWindow |
|
||||||
|
NSTrackingAreaOptions.MouseMoved |
|
||||||
|
NSTrackingAreaOptions.CursorUpdate);
|
||||||
|
|
||||||
trackingArea = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSTrackingArea"), Selector.Alloc),
|
trackingArea = Cocoa.SendIntPtr(Cocoa.SendIntPtr(Class.Get("NSTrackingArea"), Selector.Alloc),
|
||||||
selInitWithRect, ownerBounds, options, owner, IntPtr.Zero);
|
selInitWithRect, ownerBounds, options, owner, IntPtr.Zero);
|
||||||
|
@ -431,7 +469,7 @@ namespace OpenTK.Platform.MacOS
|
||||||
{
|
{
|
||||||
if (selectedCursor != MouseCursor.Default)
|
if (selectedCursor != MouseCursor.Default)
|
||||||
{
|
{
|
||||||
SetCursor(selectedCursor);
|
//SetCursor(selectedCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
cursorInsideWindow = true;
|
cursorInsideWindow = true;
|
||||||
|
@ -473,19 +511,13 @@ namespace OpenTK.Platform.MacOS
|
||||||
MathHelper.Clamp((int)Math.Round(rf.X), 0, Width),
|
MathHelper.Clamp((int)Math.Round(rf.X), 0, Width),
|
||||||
MathHelper.Clamp((int)Math.Round(Height - rf.Y), 0, Height));
|
MathHelper.Clamp((int)Math.Round(Height - rf.Y), 0, Height));
|
||||||
|
|
||||||
if (p.X < 0)
|
|
||||||
p.X = 0;
|
|
||||||
if (p.Y < 0)
|
|
||||||
p.Y = 0;
|
|
||||||
if (p.X > Width)
|
|
||||||
p.X = Width;
|
|
||||||
if (p.Y > Height)
|
|
||||||
p.Y = Height;
|
|
||||||
|
|
||||||
InputDriver.Mouse[0].Position = p;
|
InputDriver.Mouse[0].Position = p;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NSEventType.CursorUpdate:
|
||||||
|
break;
|
||||||
|
|
||||||
case NSEventType.ScrollWheel:
|
case NSEventType.ScrollWheel:
|
||||||
{
|
{
|
||||||
var scrollingDelta = Cocoa.SendFloat(e, selScrollingDeltaY);
|
var scrollingDelta = Cocoa.SendFloat(e, selScrollingDeltaY);
|
||||||
|
@ -912,19 +944,116 @@ namespace OpenTK.Platform.MacOS
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// We only modify the cursor when it is
|
|
||||||
// inside the window and visible.
|
|
||||||
// If it is outside the window or invisible,
|
|
||||||
// we store the selected cursor and change it
|
|
||||||
// in the MouseEnter event.
|
|
||||||
if (CursorVisible && cursorInsideWindow)
|
|
||||||
{
|
|
||||||
SetCursor(value);
|
|
||||||
}
|
|
||||||
selectedCursor = value;
|
selectedCursor = value;
|
||||||
|
InvalidateCursorRects();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IntPtr ToNSCursor(MouseCursor cursor)
|
||||||
|
{
|
||||||
|
// We need to allocate a NSBitmapImageRep, fill it with pixels
|
||||||
|
// and then convert it to a NSImage.
|
||||||
|
// According to the documentation, alpha-enabled formats should
|
||||||
|
// premultiply alpha, even though that "generally has negligible
|
||||||
|
// effect on output quality."
|
||||||
|
IntPtr imgdata =
|
||||||
|
Cocoa.SendIntPtr(
|
||||||
|
Cocoa.SendIntPtr(
|
||||||
|
Cocoa.SendIntPtr(NSBitmapImageRep, Selector.Alloc),
|
||||||
|
selInitWithBitmapDataPlanes,
|
||||||
|
IntPtr.Zero,
|
||||||
|
cursor.Width,
|
||||||
|
cursor.Height,
|
||||||
|
8,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
NSDeviceRGBColorSpace,
|
||||||
|
4 * cursor.Width,
|
||||||
|
32),
|
||||||
|
Selector.Autorelease);
|
||||||
|
if (imgdata == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Debug.Print("Failed to create NSBitmapImageRep with size ({0},{1]})",
|
||||||
|
cursor.Width, cursor.Height);
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Premultiply and copy the cursor data
|
||||||
|
int i = 0;
|
||||||
|
IntPtr data = Cocoa.SendIntPtr(imgdata, selBitmapData);
|
||||||
|
for (int y = 0; y < cursor.Height; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < cursor.Width; x++)
|
||||||
|
{
|
||||||
|
byte a = cursor.Argb[i];
|
||||||
|
byte r = (byte)((cursor.Argb[i + 1] * a) / 255);
|
||||||
|
byte g = (byte)((cursor.Argb[i + 2] * a) / 255);
|
||||||
|
byte b = (byte)((cursor.Argb[i + 3] * a) / 255);
|
||||||
|
Marshal.WriteByte(data, i++, a);
|
||||||
|
Marshal.WriteByte(data, i++, r);
|
||||||
|
Marshal.WriteByte(data, i++, g);
|
||||||
|
Marshal.WriteByte(data, i++, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the actual NSImage
|
||||||
|
IntPtr img =
|
||||||
|
Cocoa.SendIntPtr(
|
||||||
|
Cocoa.SendIntPtr(
|
||||||
|
Cocoa.SendIntPtr(NSImage, Selector.Alloc),
|
||||||
|
selInitWithSize,
|
||||||
|
new SizeF(cursor.Width, cursor.Height)),
|
||||||
|
Selector.Autorelease);
|
||||||
|
if (img == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Debug.Print("Failed to construct NSImage from NSBitmapImageRep");
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
Cocoa.SendVoid(img, selAddRepresentation, imgdata);
|
||||||
|
|
||||||
|
// Convert the NSImage to a NSCursor
|
||||||
|
IntPtr nscursor =
|
||||||
|
Cocoa.SendIntPtr(
|
||||||
|
Cocoa.SendIntPtr(
|
||||||
|
Cocoa.SendIntPtr(NSCursor, Selector.Alloc),
|
||||||
|
selInitWithImageHotSpot,
|
||||||
|
img,
|
||||||
|
new PointF(cursor.X, cursor.Y)
|
||||||
|
),
|
||||||
|
Selector.Autorelease);
|
||||||
|
|
||||||
|
return nscursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetCursorRects(IntPtr sender, IntPtr cmd)
|
||||||
|
{
|
||||||
|
// We will add a new cursor rectangle that covers the complete view
|
||||||
|
var rect = Cocoa.SendRect(windowInfo.ViewHandle, selBounds);
|
||||||
|
|
||||||
|
// Inside this rectangle, the following NSCursor will be used
|
||||||
|
var cursor = IntPtr.Zero;
|
||||||
|
if (selectedCursor == MouseCursor.Default)
|
||||||
|
{
|
||||||
|
cursor = Cocoa.SendIntPtr(NSCursor, selArrowCursor);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cursor = ToNSCursor(selectedCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the cursor rectangle
|
||||||
|
if (cursor != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Cocoa.SendVoid(sender, selAddCursorRect, rect, cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvalidateCursorRects()
|
||||||
|
{
|
||||||
|
Cocoa.SendVoid(windowInfo.Handle, selInvalidateCursorRectsForView, windowInfo.ViewHandle);
|
||||||
|
}
|
||||||
|
|
||||||
public bool CursorVisible
|
public bool CursorVisible
|
||||||
{
|
{
|
||||||
get { return cursorVisible; }
|
get { return cursorVisible; }
|
||||||
|
@ -1018,7 +1147,6 @@ namespace OpenTK.Platform.MacOS
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue