Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macOS: Missing bindings for CGEvent & NSEvent APIs #12650

Closed
tipa opened this issue Sep 7, 2021 · 5 comments · Fixed by #20424
Closed

macOS: Missing bindings for CGEvent & NSEvent APIs #12650

tipa opened this issue Sep 7, 2021 · 5 comments · Fixed by #20424
Assignees
Labels
feature A feature to be implemented missing-api-bindings
Milestone

Comments

@tipa
Copy link

tipa commented Sep 7, 2021

I am trying to do what is described here in Xamarin:
https://isapozhnik.com/articles/how-to-swap-horizontal-and-vertical-scroll-in-nsscrollview/
In my app, I want to make a horizontal scrollview to respond to mouse wheel interaction.
Unfortunately, I am hitting a few obstacles while "translating" the code to Xamarin & C#:

  • if event.subtype == .mouseEvent
    subtype is of type short and NSEventSubtype doesn't have "mouse" value

  • if let cgEvent = event.cgEvent?.copy() {
    cgEvent is of type IntPtr and not CGEvent - I need to call the constructor of CGEvent myself

  • cgEvent.setDoubleValueField(.scrollWheelEventDeltaAxis2, value: Double(event.scrollingDeltaY))
    setDoubleValueField does not exist in the Xamarin API

  • if let event = NSEvent(cgEvent: cgEvent) {
    I am unable to create a NSEvent from a CGEvent. The constructor does not exist

Environment

=== Visual Studio Community 2019 for Mac ===

Version 8.10.8 (build 0)
Installation UUID: 292045fa-1cd3-4098-b3c4-f3f10b09297a
	GTK+ 2.24.23 (Raleigh theme)
	Xamarin.Mac 6.18.0.23 (d16-6 / 088c73638)

	Package version: 612000140

=== Mono Framework MDK ===

Runtime:
	Mono 6.12.0.140 (2020-02/51d876a041e) (64-bit)
	Package version: 612000140

=== Roslyn (Language Service) ===

3.10.0-4.21269.26+029847714208ebe49668667c60ea5b0a294e0fcb

=== NuGet ===

Version: 5.9.0.7134

=== .NET Core SDK ===

SDK: /usr/local/share/dotnet/sdk/5.0.400/Sdks
SDK Versions:
	5.0.400
	5.0.302
	5.0.301
	5.0.203
	5.0.202
	5.0.201
	3.1.412
	3.1.411
	3.1.410
	3.1.409
	3.1.408
	3.1.407
MSBuild SDKs: /Applications/Visual Studio.app/Contents/Resources/lib/monodevelop/bin/MSBuild/Current/bin/Sdks

=== .NET Core Runtime ===

Runtime: /usr/local/share/dotnet/dotnet
Runtime Versions:
	5.0.9
	5.0.8
	5.0.7
	5.0.6
	5.0.5
	5.0.4
	3.1.18
	3.1.17
	3.1.16
	3.1.15
	3.1.14
	3.1.13

=== .NET Core 3.1 SDK ===

SDK: 3.1.412

=== Xamarin.Profiler ===

Version: 1.6.15.68
Location: /Applications/Xamarin Profiler.app/Contents/MacOS/Xamarin Profiler

=== Updater ===

Version: 11

=== Apple Developer Tools ===

Xcode 12.5.1 (18212)
Build 12E507

=== Xamarin.Mac ===

Version: 7.14.0.24 (Visual Studio Community)
Hash: c4b89cddb
Branch: d16-10
Build date: 2021-06-15 22:03:00-0400

=== Xamarin.Android ===

Not Installed

=== Microsoft OpenJDK for Mobile ===

Java SDK: Not Found

Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

=== Android SDK Manager ===

Version: 16.10.0.13
Hash: 1b81df5
Branch: remotes/origin/d16-10
Build date: 2021-08-12 20:43:52 UTC

=== Android Device Manager ===

Version: 16.10.0.15
Hash: 89dcc0b
Branch: remotes/origin/d16-10
Build date: 2021-08-12 20:44:10 UTC

=== Xamarin.iOS ===

Version: 14.20.0.24 (Visual Studio Community)
Hash: c4b89cddb
Branch: d16-10
Build date: 2021-06-15 22:03:01-0400

=== Xamarin Designer ===

Version: 16.10.0.119
Hash: 36a2d986f
Branch: remotes/origin/d16-10
Build date: 2021-06-02 19:41:34 UTC

=== Build Information ===

Release ID: 810080000
Git revision: 0bc91c25997e4c374dc6eb96809c7bf52f6a7c33
Build date: 2021-08-18 14:50:30-04
Build branch: release-8.10

=== Operating System ===

Mac OS X 10.16.0
Darwin 20.6.0 Darwin Kernel Version 20.6.0
    Wed Jun 23 00:26:27 PDT 2021
    root:xnu-7195.141.2~5/RELEASE_ARM64_T8101 x86_64


@filipnavara
Copy link
Contributor

if event.subtype == .mouseEvent

It's NSEventMouseSubtype.Mouse.

if let cgEvent = event.cgEvent?.copy() {
cgEvent is of type IntPtr and not CGEvent - I need to call the constructor of CGEvent myself

CGEvent is defined in the documentation as opaque type. Essentially this uses the CGEventCreateCopy C API which actually happens to be implemented in the bindings.

cgEvent.setDoubleValueField(.scrollWheelEventDeltaAxis2, value: Double(event.scrollingDeltaY))
setDoubleValueField does not exist in the Xamarin API

That would be CGEventSetDoubleValueField which is indeed missing in the bindings.

if let event = NSEvent(cgEvent: cgEvent) {
I am unable to create a NSEvent from a CGEvent. The constructor does not exist

There's static method NSEvent.EventWithCGEvent(...).

@tipa
Copy link
Author

tipa commented Sep 8, 2021

Thanks for the quick response.

It's NSEventMouseSubtype.Mouse.

Maybe it is possible to adjust the type of the Subtype property to NSEventMouseSubtype then?

CGEvent is defined in the documentation as opaque type. Essentially this uses the CGEventCreateCopy C API which actually happens to be implemented in the bindings.

Here it would be desired if the CGEvent property on NSEvent was of the type CGEvent and not IntPtr

That would be CGEventSetDoubleValueField which is indeed missing in the bindings.

Would be very much appreciated if it could be added in a future release!

There's static method NSEvent.EventWithCGEvent(...).

Thanks - I must have missed that one. But again, it uses a IntPtr instead of a CGEvent. Perhaps there's some reason for it. But if not, maybe the type can be adjusted

@chamons chamons added the feature A feature to be implemented label Sep 8, 2021
@chamons chamons added this to the Future milestone Sep 8, 2021
@chamons chamons added the help wanted This is an issue or pull request where we request help from the community to fix or complete label Sep 8, 2021
@mandel-macaque mandel-macaque self-assigned this Jan 28, 2022
@tipa
Copy link
Author

tipa commented Mar 21, 2024

Is there any hope the CGEventSetDoubleValueField method will be bound in the future?

!missing-pinvoke! CGEventSetDoubleValueField is not bound

@rolfbjarne
Copy link
Member

Is there any hope the CGEventSetDoubleValueField method will be bound in the future?

!missing-pinvoke! CGEventSetDoubleValueField is not bound

I'll have a look.

@tipa
Copy link
Author

tipa commented Mar 21, 2024

Thanks! I just managed to workaround the missing bindings myself somehow:

var cgEvent = ObjCRuntime.Runtime.GetINativeObject<CGEvent>(theEvent.CGEvent, false).Copy();
// 11 = https://developer.apple.com/documentation/coregraphics/cgeventfield/kcgscrollwheeleventdeltaaxis1
// 12 = https://developer.apple.com/documentation/coregraphics/cgeventfield/kcgscrollwheeleventdeltaaxis2
CGEventMonkeyPatch.CGEventSetDoubleValueField(cgEvent.Handle, 11, 0);
CGEventMonkeyPatch.CGEventSetDoubleValueField(cgEvent.Handle, 12, theEvent.ScrollingDeltaY);
base.ScrollWheel(NSEvent.EventWithCGEvent(cgEvent.Handle));

static partial class CGEventMonkeyPatch
{
    [System.Runtime.InteropServices.LibraryImport(ObjCRuntime.Constants.AppKitLibrary)]
    internal static unsafe partial void CGEventSetDoubleValueField(IntPtr _event, int field, double value);
}

Ideally, I could transform this code to the following:

var cgEvent = theEvent.CGEvent.Copy();
cgEvent.SetDoubleValueField(CGEventField.ScrollWheelEventDeltaAxis1, 0);
cgEvent.SetDoubleValueField(CGEventField.ScrollWheelEventDeltaAxis2, theEvent.ScrollingDeltaY);
base.ScrollWheel(NSEvent.EventWithCGEvent(cgEvent));

Besides the missing bindings, the CGEvent property would have to return CGEvent instead of IntPtr, EventWithCGEvent would have to accept CGEvent instead of IntPtr & CGEventField would need to be public, it's currently internal:

internal enum CGEventField : int {

@rolfbjarne rolfbjarne removed the help wanted This is an issue or pull request where we request help from the community to fix or complete label Mar 21, 2024
rolfbjarne added a commit to rolfbjarne/xamarin-macios that referenced this issue Mar 22, 2024
Create a better binding for `[NSEvent eventWithGCEvent:]` by binding it as
`NSEvent.Create(CGEvent)` instead of `NSEvent.EventWithGCEvent(IntPtr)`.

Partial fix for xamarin#12650.
rolfbjarne added a commit that referenced this issue Mar 25, 2024
…0362)

Create a better binding for `[NSEvent eventWithGCEvent:]` by binding it as
`NSEvent.Create(CGEvent)` instead of `NSEvent.EventWithGCEvent(IntPtr)`.

Partial fix for #12650.
rolfbjarne added a commit to rolfbjarne/xamarin-macios that referenced this issue Apr 10, 2024
Provide an NSEvent.GetCGEventObject method that returns the CGEvent object for
the NSEvent.CGEvent field (which returns a NativeHandle).

Contributes towards xamarin#12650.
rolfbjarne added a commit to rolfbjarne/xamarin-macios that referenced this issue Apr 10, 2024
…alueField. Fixes xamarin#12650.

Also expose both getter and setter methods using the enum itself, so we don't
have to make getter and setter properties for each of the enum fields.

This required making CGEventField a public enum, so do that.

And finally document all the new APIs, and some of the old ones.

Fixes xamarin#12650.
rolfbjarne added a commit to rolfbjarne/xamarin-macios that referenced this issue Apr 11, 2024
…alueField. Fixes xamarin#12650. (xamarin#20424)

Also expose both getter and setter methods using the enum itself, so we don't
have to make getter and setter properties for each of the enum fields.

This required making CGEventField a public enum, so do that.

And finally document all the new APIs, and some of the old ones.

Fixes xamarin#12650.
rolfbjarne added a commit that referenced this issue Apr 12, 2024
Provide an NSEvent.GetCGEventObject method that returns the CGEvent object for
the NSEvent.CGEvent field (which returns a NativeHandle).

Contributes towards #12650.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature A feature to be implemented missing-api-bindings
Projects
None yet
5 participants