Skip to content

Commit

Permalink
Clean up permission handling
Browse files Browse the repository at this point in the history
  • Loading branch information
thealpa committed Oct 23, 2022
1 parent f3c0073 commit c3f7a27
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 116 deletions.
4 changes: 2 additions & 2 deletions SaneSideButtons.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@
CODE_SIGN_ENTITLEMENTS = SaneSideButtons/SaneSideButtons.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 12;
CURRENT_PROJECT_VERSION = 16;
DEVELOPMENT_TEAM = B7HSFEB698;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand Down Expand Up @@ -345,7 +345,7 @@
CODE_SIGN_ENTITLEMENTS = SaneSideButtons/SaneSideButtons.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 12;
CURRENT_PROJECT_VERSION = 16;
DEVELOPMENT_TEAM = B7HSFEB698;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
Expand Down
57 changes: 29 additions & 28 deletions SaneSideButtons/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// MARK: - NSApplicationDelegate

func applicationDidFinishLaunching(_ aNotification: Notification) {
self.setupPermissions()
self.setupTapWithPermissions()
self.setupMenuBarExtra()
}

Expand Down Expand Up @@ -123,56 +123,58 @@ private extension AppDelegate {
SwipeSimulator.shared.removeIgnoredApplication(bundleID: currentFrontAppBundleID)
}

// MARK: - Permissions
// MARK: - Setup & Permissions

private func setupPermissions() {
if self.hasPermissions() {
SwipeSimulator.shared.setupEventTap()
} else {
if self.getEventPermission() {
SwipeSimulator.shared.setupEventTap()
}
Task {
await self.promptPermissions()
private func setupTapWithPermissions() {
self.getEventPermission()
do {
try SwipeSimulator.shared.setupEventTap()
} catch {
if window == nil {
Task {
await self.promptPermissions()
}
}
}
}

private func hasPermissions() -> Bool {
if getEventPermission() && getAccessibilityPermission() {
return true
}
return false
}
// private func hasPermissions() -> Bool {
// if getEventPermission() && hasAccessibilityPermission() {
// return true
// }
// return false
// }

private func getEventPermission() -> Bool {
@discardableResult private func getEventPermission() -> Bool {
if !CGPreflightListenEventAccess() {
CGRequestListenEventAccess()
return false
}
return true
}

private func getAccessibilityPermission() -> Bool {
let prompt = kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String
let options = [prompt: true] as CFDictionary
return AXIsProcessTrustedWithOptions(options)
}
// private func hasAccessibilityPermission() -> Bool {
// let prompt = kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String
// let options = [prompt: true] as CFDictionary
// return AXIsProcessTrustedWithOptions(options)
// }

@MainActor @objc private func promptPermissions() {
NSApplication.shared.activate(ignoringOtherApps: true)
self.window = NSWindow(
contentRect: NSRect(),
styleMask: [.closable, .titled],
styleMask: [.closable, .titled, .fullSizeContentView],
backing: .buffered, defer: false)
self.window?.isReleasedWhenClosed = false
self.window?.titlebarAppearsTransparent = true
self.window?.titleVisibility = .hidden
self.window?.toolbar = NSToolbar()
self.window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
self.window?.standardWindowButton(.zoomButton)?.isHidden = true
let permissionView = PermissionView(closeWindow: self.closePermissionsPrompt,
hasPermissions: self.hasPermissions)
let permissionView = PermissionView(closeWindow: self.closePermissionsPrompt)
self.window?.contentView = NSHostingView(rootView: permissionView)
self.window?.isOpaque = false
self.window?.backgroundColor = NSColor(white: 1, alpha: 0)
self.window?.center()
self.window?.makeKeyAndOrderFront(nil)
self.window?.delegate = self
Expand All @@ -181,7 +183,6 @@ private extension AppDelegate {
func closePermissionsPrompt() {
self.window?.close()
self.window = nil
self.setupPermissions()
}
}

Expand All @@ -190,7 +191,7 @@ private extension AppDelegate {
extension AppDelegate: NSMenuDelegate {
func menuWillOpen(_ menu: NSMenu) {
// Permission Detection
if !self.hasPermissions() {
if !SwipeSimulator.shared.eventTapIsRunning {
NSApplication.shared.activate(ignoringOtherApps: true)
if self.window == nil {
self.promptPermissions()
Expand Down
164 changes: 81 additions & 83 deletions SaneSideButtons/PermissionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,107 +10,105 @@ import SwiftUI
struct PermissionView: View {
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
let closeWindow: () -> Void
let hasPermissions: () -> Bool

var body: some View {
VStack {
VStack {
Text("Authorize SaneSideButtons")
.font(.title)
.fontWeight(.medium)
Text("We'll have you up and running in just a minute!")
.font(.subheadline)
.fontWeight(.medium)
}.padding(.vertical, 10)
ZStack {
Rectangle()
.frame(width: 500, height: 100)
.foregroundColor(Color("SecondaryColor"))
VStack {
Text("Authorize SaneSideButtons")
.font(.title)
.fontWeight(.medium)
Text("We'll have you up and running in just a minute!")
.font(.subheadline)
}
}.offset(y: -30)
ZStack {
Rectangle()
.frame(width: 500, height: 40)
RoundedRectangle(cornerRadius: 5, style: .continuous)
.frame(width: 600, height: 200)
.foregroundColor(Color("BackgroundColor"))
.offset(y: -120)
VStack {
// swiftlint:disable line_length
Text("SaneSideButtons needs your permission to detect mouse events and trigger actions in applications. Follow these steps to authorize it:")
// swiftlint:enable line_length
.font(.subheadline)
.multilineTextAlignment(.center)
.padding(.horizontal, 40)
.padding(.bottom, 30)
HStack(alignment: .top) {
VStack {
Image(systemName: "1.circle.fill")
.font(.title)
.foregroundColor(Color.gray)
Text("Go to System Settings")
.font(.callout)
.padding(7)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(.blue, lineWidth: 1)
)
.padding(.top, 30)
}.padding(.horizontal, 5)

VStack {
Image(systemName: "2.circle.fill")
.font(.title)
.foregroundColor(Color.gray)
Text("Privacy & Security")
.font(.callout)
.padding(7)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(.blue, lineWidth: 1)
)
.padding(.top, 30)
}.padding(.horizontal, 5)

VStack {
Image(systemName: "3.circle.fill")
.font(.title)
.foregroundColor(Color.gray)
Text("Add SaneSideButtons to Accessibility & Input Monitoring")
.font(.callout)
.multilineTextAlignment(.center)
.padding(7)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(.blue, lineWidth: 1)
)
.padding(.top, 15)
}.padding(.horizontal, 5)
}
}
.offset(y: -40)
.padding(.horizontal, 20)
.padding(10)
.padding(.bottom, 20)
PermissionContentView()
}
.onReceive(timer) { _ in
self.pollPermissions()
}
.frame(width: 500, height: 200)
.background(Color("BackgroundColor"))

}
.background(.thinMaterial)
.onReceive(timer) { _ in
self.pollPermissions()
}
}

private func pollPermissions() {
if self.hasPermissions() {
do {
try SwipeSimulator.shared.setupEventTap()
self.closeWindow()
} catch {
return
}
}
}

struct PermissionContentView: View {
var body: some View {
VStack {
// swiftlint:disable line_length
Text("SaneSideButtons needs your permission to detect mouse events and trigger actions in applications. Follow these steps to authorize it:")
// swiftlint:enable line_length
.font(.subheadline)
.multilineTextAlignment(.center)
.padding(.horizontal, 40)
.padding(.bottom, 30)
HStack(alignment: .top) {
VStack {
Image(systemName: "1.circle.fill")
.font(.title)
.foregroundColor(Color.gray)
Text("Go to System Settings")
.font(.callout)
.padding(7)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(.blue, lineWidth: 1)
)
.padding(.top, 30)
}.padding(.horizontal, 5)

VStack {
Image(systemName: "2.circle.fill")
.font(.title)
.foregroundColor(Color.gray)
Text("Privacy & Security")
.font(.callout)
.padding(7)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(.blue, lineWidth: 1)
)
.padding(.top, 30)
}.padding(.horizontal, 5)

VStack {
Image(systemName: "3.circle.fill")
.font(.title)
.foregroundColor(Color.gray)
Text("Add SaneSideButtons to Accessibility & Input Monitoring")
.font(.callout)
.multilineTextAlignment(.center)
.padding(7)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(.blue, lineWidth: 1)
)
.padding(.top, 15)
}.padding(.horizontal, 5)
}
}
}
}

struct PermissionView_Previews: PreviewProvider {
static var previews: some View {
PermissionView(closeWindow: self.closeWindow,
hasPermissions: self.hasPermissions)
PermissionView(closeWindow: self.closeWindow)
}

static func closeWindow() { }

static func hasPermissions() -> Bool {
return false
}
}
13 changes: 10 additions & 3 deletions SaneSideButtons/SwipeSimulator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import AppKit

final class SwipeSimulator {
static let shared = SwipeSimulator()
private(set) var eventTapIsRunning: Bool = false

private let swipeBegin = [
kTLInfoKeyGestureSubtype: kTLInfoSubtypeSwipe,
Expand All @@ -29,6 +30,10 @@ final class SwipeSimulator {

var ignoredApplications: [String] = UserDefaults.standard.stringArray(forKey: "ignoredApplications") ?? []

enum EventTap: Error {
case failedSetup
}

private init() { }

fileprivate func SBFFakeSwipe(direction: TLInfoSwipeDirection) {
Expand Down Expand Up @@ -66,20 +71,22 @@ final class SwipeSimulator {
UserDefaults.standard.set(self.ignoredApplications, forKey: "ignoredApplications")
}

func setupEventTap() {
func setupEventTap() throws {
guard !eventTapIsRunning else { return }
let eventMask = CGEventMask(1 << CGEventType.otherMouseDown.rawValue | 1 << CGEventType.otherMouseUp.rawValue)
guard let eventTap = CGEvent.tapCreate(tap: .cghidEventTap,
place: .headInsertEventTap,
options: .defaultTap,
eventsOfInterest: eventMask,
callback: mouseEventCallBack,
userInfo: nil) else {
print("Failed to create eventTap")
return
self.eventTapIsRunning = false
throw EventTap.failedSetup
}
let runLoopSource = CFMachPortCreateRunLoopSource(nil, eventTap, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes)
CGEvent.tapEnable(tap: eventTap, enable: true)
self.eventTapIsRunning = true
}
}

Expand Down

0 comments on commit c3f7a27

Please sign in to comment.