Skip to content

Commit

Permalink
Add universal back and forward support (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
lujjjh committed Aug 5, 2021
1 parent 92672e6 commit 23c2da5
Show file tree
Hide file tree
Showing 22 changed files with 1,186 additions and 121 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [Unreleased]

- Tweak the preferences UI.
- Add universal back and forward support for side buttons (#12).

## [0.2.6] - 2021-07-30

Expand Down
54 changes: 50 additions & 4 deletions LinearMouse.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

/* Begin PBXBuildFile section */
B71392F426731645004C0173 /* LoginServiceKit in Frameworks */ = {isa = PBXBuildFile; productRef = B71392F326731645004C0173 /* LoginServiceKit */; };
B716F15E26BC10300052F551 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = B716F15D26BC10300052F551 /* LICENSE */; };
B716F16026BC10B80052F551 /* SideButtonsFixer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B716F15F26BC10B80052F551 /* SideButtonsFixer.swift */; };
B75A061C2671E9D800BEAF77 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B75A061B2671E9D800BEAF77 /* AppDelegate.swift */; };
B75A06202671E9DA00BEAF77 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B75A061F2671E9DA00BEAF77 /* Assets.xcassets */; };
B75A06232671E9DA00BEAF77 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B75A06222671E9DA00BEAF77 /* Preview Assets.xcassets */; };
Expand All @@ -19,18 +21,26 @@
B77A16122674C21600E99A5D /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77A16112674C21600E99A5D /* PreferencesView.swift */; };
B77A16172674D88E00E99A5D /* PreferencesWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77A16162674D88E00E99A5D /* PreferencesWindow.swift */; };
B77A161B2674EF0E00E99A5D /* HyperLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = B77A161A2674EF0E00E99A5D /* HyperLink.swift */; };
B794BFE026BC0A73003B11EF /* TouchEvents.c in Sources */ = {isa = PBXBuildFile; fileRef = B794BFDC26BC0A73003B11EF /* TouchEvents.c */; };
B794BFE426BC0E5D003B11EF /* TouchSimulate.m in Sources */ = {isa = PBXBuildFile; fileRef = B794BFE326BC0E5D003B11EF /* TouchSimulate.m */; };
B7A9397526B6D75600CFFFB8 /* MouseWheelEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7A9397426B6D75600CFFFB8 /* MouseWheelEvent.swift */; };
B7B1D08D26B2FD6C00F48176 /* AppStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B1D08C26B2FD6C00F48176 /* AppStorage.swift */; };
B7B1D08F26B3EC4F00F48176 /* GeneralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B1D08E26B3EC4F00F48176 /* GeneralView.swift */; };
B7B1D09126B3ECC500F48176 /* ModifierKeysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B1D09026B3ECC500F48176 /* ModifierKeysView.swift */; };
B7B6D83C26B2726B00B0D247 /* ModifierKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B6D83B26B2726B00B0D247 /* ModifierKeys.swift */; };
B7B6D83E26B2745300B0D247 /* ModifierKeyActionPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7B6D83D26B2745300B0D247 /* ModifierKeyActionPicker.swift */; };
B7BD51812672FEB5001FE742 /* ScrollWheelEventTap.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7BD51802672FEB5001FE742 /* ScrollWheelEventTap.swift */; };
B7BD51812672FEB5001FE742 /* EventTap.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7BD51802672FEB5001FE742 /* EventTap.swift */; };
B7BD5183267300DC001FE742 /* AutoStartManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7BD5182267300DC001FE742 /* AutoStartManager.swift */; };
B7DD796426730A800067F1AD /* LoginServiceKit in Frameworks */ = {isa = PBXBuildFile; productRef = B7DD796326730A800067F1AD /* LoginServiceKit */; };
B7E897E42675EBB0003C7058 /* MouseAcceleration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7E897E32675EBB0003C7058 /* MouseAcceleration.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
B716F15D26BC10300052F551 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
B716F15F26BC10B80052F551 /* SideButtonsFixer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideButtonsFixer.swift; sourceTree = "<group>"; };
B716F16126BC18920052F551 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = "<group>"; };
B716F16226BC18AB0052F551 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
B716F16326BC19350052F551 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
B75A06182671E9D800BEAF77 /* LinearMouse.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LinearMouse.app; sourceTree = BUILT_PRODUCTS_DIR; };
B75A061B2671E9D800BEAF77 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B75A061F2671E9DA00BEAF77 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand All @@ -45,12 +55,20 @@
B77A16112674C21600E99A5D /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
B77A16162674D88E00E99A5D /* PreferencesWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindow.swift; sourceTree = "<group>"; };
B77A161A2674EF0E00E99A5D /* HyperLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HyperLink.swift; sourceTree = "<group>"; };
B794BFDC26BC0A73003B11EF /* TouchEvents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TouchEvents.c; sourceTree = "<group>"; };
B794BFDD26BC0A73003B11EF /* TouchEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TouchEvents.h; sourceTree = "<group>"; };
B794BFDE26BC0A73003B11EF /* IOHIDEventData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOHIDEventData.h; sourceTree = "<group>"; };
B794BFDF26BC0A73003B11EF /* IOHIDEventTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IOHIDEventTypes.h; sourceTree = "<group>"; };
B794BFE126BC0B2F003B11EF /* LinearMouse-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "LinearMouse-Bridging-Header.h"; sourceTree = "<group>"; };
B794BFE226BC0E45003B11EF /* TouchSimulate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchSimulate.h; sourceTree = "<group>"; };
B794BFE326BC0E5D003B11EF /* TouchSimulate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TouchSimulate.m; sourceTree = "<group>"; };
B7A9397426B6D75600CFFFB8 /* MouseWheelEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MouseWheelEvent.swift; sourceTree = "<group>"; };
B7B1D08C26B2FD6C00F48176 /* AppStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorage.swift; sourceTree = "<group>"; };
B7B1D08E26B3EC4F00F48176 /* GeneralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralView.swift; sourceTree = "<group>"; };
B7B1D09026B3ECC500F48176 /* ModifierKeysView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifierKeysView.swift; sourceTree = "<group>"; };
B7B6D83B26B2726B00B0D247 /* ModifierKeys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifierKeys.swift; sourceTree = "<group>"; };
B7B6D83D26B2745300B0D247 /* ModifierKeyActionPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifierKeyActionPicker.swift; sourceTree = "<group>"; };
B7BD51802672FEB5001FE742 /* ScrollWheelEventTap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollWheelEventTap.swift; sourceTree = "<group>"; };
B7BD51802672FEB5001FE742 /* EventTap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTap.swift; sourceTree = "<group>"; };
B7BD5182267300DC001FE742 /* AutoStartManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoStartManager.swift; sourceTree = "<group>"; };
B7E897E22675B1D9003C7058 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
B7E897E32675EBB0003C7058 /* MouseAcceleration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MouseAcceleration.swift; sourceTree = "<group>"; };
Expand All @@ -73,6 +91,10 @@
isa = PBXGroup;
children = (
B75A061A2671E9D800BEAF77 /* LinearMouse */,
B794BFDB26BC0A73003B11EF /* Touch */,
B716F16126BC18920052F551 /* CHANGELOG.md */,
B716F16326BC19350052F551 /* LICENSE */,
B716F16226BC18AB0052F551 /* README.md */,
B75A06192671E9D800BEAF77 /* Products */,
);
sourceTree = "<group>";
Expand All @@ -88,14 +110,16 @@
B75A061A2671E9D800BEAF77 /* LinearMouse */ = {
isa = PBXGroup;
children = (
B794BFE126BC0B2F003B11EF /* LinearMouse-Bridging-Header.h */,
B77A161A2674EF0E00E99A5D /* HyperLink.swift */,
B75A061B2671E9D800BEAF77 /* AppDelegate.swift */,
B75A061F2671E9DA00BEAF77 /* Assets.xcassets */,
B75A06242671E9DA00BEAF77 /* Main.storyboard */,
B75A06272671E9DA00BEAF77 /* Info.plist */,
B75A06282671E9DA00BEAF77 /* LinearMouse.entitlements */,
B75A06212671E9DA00BEAF77 /* Preview Content */,
B7BD51802672FEB5001FE742 /* ScrollWheelEventTap.swift */,
B7BD51802672FEB5001FE742 /* EventTap.swift */,
B7A9397426B6D75600CFFFB8 /* MouseWheelEvent.swift */,
B7BD5182267300DC001FE742 /* AutoStartManager.swift */,
B77A160B26744DB400E99A5D /* StatusItem.swift */,
B77A160D2674B3DD00E99A5D /* LinearMouse.swift */,
Expand All @@ -109,6 +133,7 @@
B7B1D08C26B2FD6C00F48176 /* AppStorage.swift */,
B7B1D08E26B3EC4F00F48176 /* GeneralView.swift */,
B7B1D09026B3ECC500F48176 /* ModifierKeysView.swift */,
B716F15F26BC10B80052F551 /* SideButtonsFixer.swift */,
);
path = LinearMouse;
sourceTree = "<group>";
Expand All @@ -121,6 +146,20 @@
path = "Preview Content";
sourceTree = "<group>";
};
B794BFDB26BC0A73003B11EF /* Touch */ = {
isa = PBXGroup;
children = (
B794BFDC26BC0A73003B11EF /* TouchEvents.c */,
B794BFDD26BC0A73003B11EF /* TouchEvents.h */,
B794BFDE26BC0A73003B11EF /* IOHIDEventData.h */,
B794BFDF26BC0A73003B11EF /* IOHIDEventTypes.h */,
B794BFE226BC0E45003B11EF /* TouchSimulate.h */,
B794BFE326BC0E5D003B11EF /* TouchSimulate.m */,
B716F15D26BC10300052F551 /* LICENSE */,
);
path = Touch;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -191,6 +230,7 @@
B76173442675AD8700CC9412 /* Localizable.strings in Resources */,
B75A06232671E9DA00BEAF77 /* Preview Assets.xcassets in Resources */,
B75A06202671E9DA00BEAF77 /* Assets.xcassets in Resources */,
B716F15E26BC10300052F551 /* LICENSE in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -201,20 +241,24 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B7A9397526B6D75600CFFFB8 /* MouseWheelEvent.swift in Sources */,
B77A16102674B85300E99A5D /* AppDefaults.swift in Sources */,
B7E897E42675EBB0003C7058 /* MouseAcceleration.swift in Sources */,
B77A160C26744DB400E99A5D /* StatusItem.swift in Sources */,
B7B1D08D26B2FD6C00F48176 /* AppStorage.swift in Sources */,
B794BFE426BC0E5D003B11EF /* TouchSimulate.m in Sources */,
B77A161B2674EF0E00E99A5D /* HyperLink.swift in Sources */,
B794BFE026BC0A73003B11EF /* TouchEvents.c in Sources */,
B7B6D83E26B2745300B0D247 /* ModifierKeyActionPicker.swift in Sources */,
B7BD5183267300DC001FE742 /* AutoStartManager.swift in Sources */,
B7B1D08F26B3EC4F00F48176 /* GeneralView.swift in Sources */,
B7B6D83C26B2726B00B0D247 /* ModifierKeys.swift in Sources */,
B77A160E2674B3DD00E99A5D /* LinearMouse.swift in Sources */,
B716F16026BC10B80052F551 /* SideButtonsFixer.swift in Sources */,
B77A16172674D88E00E99A5D /* PreferencesWindow.swift in Sources */,
B7B1D09126B3ECC500F48176 /* ModifierKeysView.swift in Sources */,
B75A061C2671E9D800BEAF77 /* AppDelegate.swift in Sources */,
B7BD51812672FEB5001FE742 /* ScrollWheelEventTap.swift in Sources */,
B7BD51812672FEB5001FE742 /* EventTap.swift in Sources */,
B77A16122674C21600E99A5D /* PreferencesView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -379,6 +423,7 @@
MARKETING_VERSION = 0.2.6;
PRODUCT_BUNDLE_IDENTIFIER = plus.programming.LinearMouse;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "LinearMouse/LinearMouse-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
name = Debug;
Expand All @@ -402,6 +447,7 @@
MARKETING_VERSION = 0.2.6;
PRODUCT_BUNDLE_IDENTIFIER = plus.programming.LinearMouse;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "LinearMouse/LinearMouse-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
name = Release;
Expand Down
6 changes: 6 additions & 0 deletions LinearMouse/AppDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ class AppDefaults: ObservableObject {
}
}

@AppStorageCompat(wrappedValue: true, "universalBackForwardOn") var universalBackForwardOn: Bool {
willSet {
objectWillChange.send()
}
}

@AppStorageCompat(wrappedValue: true, "showInMenuBar") var showInMenuBar: Bool {
willSet {
objectWillChange.send()
Expand Down
2 changes: 1 addition & 1 deletion LinearMouse/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
AutoStartManager.enable()

// scrolling functionalities
ScrollWheelEventTap().enable()
EventTap().enable()

// subscribe to the user settings
let defaults = AppDefaults.shared
Expand Down
58 changes: 58 additions & 0 deletions LinearMouse/EventTap.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// ScrollWheelEventTap.swift
// LinearMouse
//
// Created by lujjjh on 2021/6/11.
//

import Foundation

class EventTap {
var eventTap: CFMachPort?
var runLoopSource: CFRunLoopSource?

let eventTapCallback: CGEventTapCallBack = { (proxy, type, event, refcon) in
if let transformed: CGEvent = {
switch type {
case .scrollWheel:
return MouseWheelEvent(event).transformed
case .otherMouseUp:
return fixSideButtonsEvent(event)
default:
return event
}
}() {
return Unmanaged.passUnretained(transformed)
}
return nil
}

init() {
let eventsOfInterest: CGEventMask =
1 << CGEventType.scrollWheel.rawValue
| 1 << CGEventType.otherMouseUp.rawValue
eventTap = CGEvent.tapCreate(
tap: .cghidEventTap,
place: .tailAppendEventTap,
options: .defaultTap,
eventsOfInterest: eventsOfInterest,
callback: eventTapCallback,
userInfo: nil
)
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes)
CFRunLoopRun()
}

func enable() {
if let eventTap = eventTap {
CGEvent.tapEnable(tap: eventTap, enable: true)
}
}

func disable() {
if let eventTap = eventTap {
CGEvent.tapEnable(tap: eventTap, enable: false)
}
}
}
14 changes: 14 additions & 0 deletions LinearMouse/GeneralView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ struct GeneralView: View {
}
}

Toggle(isOn: $defaults.universalBackForwardOn) {
VStack(alignment: .leading) {
Text("Enable universal back and forward")
Text("""
Convert the back and forward side buttons to \
swiping gestures to allow universal back and \
forward functionality.
""")
.controlSize(.small)
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}

Toggle(isOn: $defaults.showInMenuBar) {
VStack(alignment: .leading) {
Text("Show in menu bar")
Expand Down
8 changes: 8 additions & 0 deletions LinearMouse/LinearMouse-Bridging-Header.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// Touch-Bridging-Header.h
// LinearMouse
//
// Created by lujjjh on 2021/8/5.
//

#include "../Touch/TouchSimulate.h"
100 changes: 100 additions & 0 deletions LinearMouse/MouseWheelEvent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// MouseWheelEvent.swift
// LinearMouse
//
// Created by lujjjh on 2021/8/1.
//

import Foundation

class MouseWheelEvent {
let event: CGEvent

let defaults = AppDefaults.shared

var continuous: Bool {
get { event.getIntegerValueField(.scrollWheelEventIsContinuous) != 0 }
set { event.setIntegerValueField(.scrollWheelEventIsContinuous, value: newValue ? 1 : 0) }
}

var deltaX: Int64 {
get { event.getIntegerValueField(.scrollWheelEventDeltaAxis2) }
set { event.setIntegerValueField(.scrollWheelEventDeltaAxis2, value: newValue) }
}

var deltaY: Int64 {
get { event.getIntegerValueField(.scrollWheelEventDeltaAxis1) }
set { event.setIntegerValueField(.scrollWheelEventDeltaAxis1, value: newValue) }
}

init(_ event: CGEvent) {
self.event = event
}

lazy var transformed: CGEvent = {
guard !continuous else {
return event
}

if defaults.reverseScrollingOn {
negateDelta()
}

if defaults.linearScrollingOn {
let scrollLines = Int64(defaults.scrollLines)
deltaX = deltaX.signum() * scrollLines
deltaY = deltaY.signum() * scrollLines
}

modifierActions()

return event
}()

func negateDelta() {
deltaX = -deltaX
deltaY = -deltaY
}

func modifierActions() {
let actions: [(CGEventFlags.Element, ModifierKeyAction)] = [
(.maskCommand, defaults.modifiersCommandAction),
(.maskShift, defaults.modifiersShiftAction),
(.maskAlternate, defaults.modifiersAlternateAction),
(.maskControl, defaults.modifiersControlAction),
]
for case (let flag, let action) in actions {
if event.flags.contains(flag) {
if handleModifierKeyAction(for: event, action: action) {
event.flags.remove(flag)
}
}
}
}

func handleModifierKeyAction(for event: CGEvent, action: ModifierKeyAction) -> Bool {
guard action.type != .noAction else {
return false
}

// fix orientation on Catalina
// TODO: is there a better way?
if deltaY == 0 {
(deltaX, deltaY) = (deltaY, deltaX)
}

switch action.type {
case .noAction: // make the compiler happy
break
case .alterOrientation:
(deltaX, deltaY) = (deltaY, deltaX)
case .changeSpeed:
let factor = action.speedFactor
let scale = { (delta: Int64, factor: Double) in Int64((Double(delta) * factor).rounded()) }
deltaX = deltaX.signum() * max(1, abs(scale(deltaX, factor)))
deltaY = deltaY.signum() * max(1, abs(scale(deltaY, factor)))
}

return true
}
}
2 changes: 1 addition & 1 deletion LinearMouse/PreferencesWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import SwiftUI
class PreferencesWindow: NSWindow {
init() {
super.init(
contentRect: .init(x: 0, y: 0, width: 500, height: 450),
contentRect: .init(x: 0, y: 0, width: 500, height: 550),
styleMask: [.titled, .closable, .fullSizeContentView],
backing: .buffered,
defer: false)
Expand Down
Loading

0 comments on commit 23c2da5

Please sign in to comment.