A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using osu.Framework.Platform.MacOS.Native;
6using osuTK;
7
8namespace osu.Framework.Platform.MacOS
9{
10 /// <summary>
11 /// macOS-specific subclass of <see cref="SDL2DesktopWindow"/>.
12 /// </summary>
13 public class MacOSWindow : SDL2DesktopWindow
14 {
15 private static readonly IntPtr sel_hasprecisescrollingdeltas = Selector.Get("hasPreciseScrollingDeltas");
16 private static readonly IntPtr sel_scrollingdeltax = Selector.Get("scrollingDeltaX");
17 private static readonly IntPtr sel_scrollingdeltay = Selector.Get("scrollingDeltaY");
18 private static readonly IntPtr sel_respondstoselector_ = Selector.Get("respondsToSelector:");
19
20 private delegate void ScrollWheelDelegate(IntPtr handle, IntPtr selector, IntPtr theEvent); // v@:@
21
22 private IntPtr originalScrollWheel;
23 private ScrollWheelDelegate scrollWheelHandler;
24
25 public override void Create()
26 {
27 base.Create();
28
29 // replace [SDLView scrollWheel:(NSEvent *)] with our own version
30 var viewClass = Class.Get("SDLView");
31 scrollWheelHandler = scrollWheel;
32 originalScrollWheel = Class.SwizzleMethod(viewClass, "scrollWheel:", "v@:@", scrollWheelHandler);
33 }
34
35 /// <summary>
36 /// Swizzled replacement of [SDLView scrollWheel:(NSEvent *)] that checks for precise scrolling deltas.
37 /// </summary>
38 private void scrollWheel(IntPtr receiver, IntPtr selector, IntPtr theEvent)
39 {
40 var hasPrecise = Cocoa.SendBool(theEvent, sel_respondstoselector_, sel_hasprecisescrollingdeltas) &&
41 Cocoa.SendBool(theEvent, sel_hasprecisescrollingdeltas);
42
43 if (!hasPrecise)
44 {
45 // calls the unswizzled [SDLView scrollWheel:(NSEvent *)] method if this is a regular scroll wheel event
46 // the receiver may sometimes not be SDLView, ensure it has a scroll wheel selector implemented before attempting to call.
47 if (Cocoa.SendBool(receiver, sel_respondstoselector_, originalScrollWheel))
48 Cocoa.SendVoid(receiver, originalScrollWheel, theEvent);
49
50 return;
51 }
52
53 // according to osuTK, 0.1f is the scaling factor expected to be returned by CGEventSourceGetPixelsPerLine
54 const float scale_factor = 0.1f;
55
56 float scrollingDeltaX = Cocoa.SendFloat(theEvent, sel_scrollingdeltax);
57 float scrollingDeltaY = Cocoa.SendFloat(theEvent, sel_scrollingdeltay);
58
59 ScheduleEvent(() => TriggerMouseWheel(new Vector2(scrollingDeltaX * scale_factor, scrollingDeltaY * scale_factor), true));
60 }
61 }
62}