Skip to content

Commit

Permalink
πŸ”€ Merge pull request #660 from MrKai77/fix-bugs
Browse files Browse the repository at this point in the history
🐞 Bug fixes
  • Loading branch information
MrKai77 authored Jan 5, 2025
2 parents 7231287 + b1e90fa commit 9107745
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 47 deletions.
3 changes: 0 additions & 3 deletions Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
4C6B93E82C1DCF6E00AFF832 /* Updater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C6B93E22C1DCF6E00AFF832 /* Updater.swift */; };
4C6B93E92C1DCF6E00AFF832 /* UpdateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C6B93E32C1DCF6E00AFF832 /* UpdateView.swift */; };
4CD883A42C30F0D7009A132A /* WallpaperColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CD883A32C30F0D7009A132A /* WallpaperColors.swift */; };
A80554C22D234E5D009238C7 /* Luminare in Frameworks */ = {isa = PBXBuildFile; productRef = A80554C12D234E5D009238C7 /* Luminare */; };
A8055EC22AFEDE0B00459D13 /* Keycorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8055EC12AFEDE0B00459D13 /* Keycorder.swift */; };
A80900D52AA3F9F30085C63B /* VisualEffectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80900D32AA3F9F20085C63B /* VisualEffectView.swift */; };
A80D49BB2BAE479900493B67 /* Migrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D49BA2BAE479900493B67 /* Migrator.swift */; };
Expand Down Expand Up @@ -484,7 +483,6 @@
name = Loop;
packageProductDependencies = (
A8DCC97A2980D5F500D41065 /* Defaults */,
A80554C42D235233009238C7 /* Luminare */,
);
productName = WindowManager;
productReference = A8E59C35297F5E9A0064D4BA /* Loop.app */;
Expand Down Expand Up @@ -913,7 +911,6 @@
/* Begin XCSwiftPackageProductDependency section */
A860324E2CB34A6C005742EB /* Luminare */ = {
isa = XCSwiftPackageProductDependency;
package = A860324D2CB34A6C005742EB /* XCRemoteSwiftPackageReference "Luminare" */;
productName = Luminare;
};
A8DCC97A2980D5F500D41065 /* Defaults */ = {
Expand Down
86 changes: 53 additions & 33 deletions Loop/Luminare/Settings/Keybindings/Keybind Recorder/Keycorder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import SwiftUI

struct Keycorder: View {
@EnvironmentObject private var model: KeybindsConfigurationModel
@Environment(\.appearsActive) private var appearsActive

let keyLimit: Int = 6

Expand Down Expand Up @@ -49,7 +50,14 @@ struct Keycorder: View {
.modifier(LuminareBordered())
} else {
HStack(spacing: 5) {
ForEach(selectionKeybind.sorted(), id: \.self) { key in
// First show modifiers in order
let sortedKeys = selectionKeybind.sorted { (a: CGKeyCode, b: CGKeyCode) in
if a.isModifier, !b.isModifier { return true }
if !a.isModifier, b.isModifier { return false }
return a < b
}

ForEach(sortedKeys, id: \.self) { key in
if let systemImage = key.systemImage {
Text("\(Image(systemName: systemImage))")
} else if let humanReadable = key.humanReadable {
Expand Down Expand Up @@ -77,56 +85,39 @@ struct Keycorder: View {
finishedObservingKeys(wasForced: true)
}
}
.onChange(of: appearsActive) { _ in
if appearsActive {
finishedObservingKeys(wasForced: true)
}
}
.onChange(of: validCurrentKeybind) { _ in
if selectionKeybind != validCurrentKeybind {
selectionKeybind = validCurrentKeybind
}
}
.buttonStyle(PlainButtonStyle())
.buttonStyle(.plain)
// Don't allow the button to be pressed if more than one keybind is selected in the list
.allowsHitTesting(model.selectedKeybinds.count <= 1)
}

func startObservingKeys() {
selectionKeybind = []
isActive = true
eventMonitor = NSEventMonitor(scope: .local, eventMask: [.keyDown, .keyUp, .flagsChanged]) { event in
if event.type == .flagsChanged {
if !Defaults[.triggerKey].contains(where: { $0.baseModifier == event.keyCode.baseModifier }) {
shouldError = false
selectionKeybind.insert(event.keyCode.baseModifier)
} else {
if let systemImage = event.keyCode.baseModifier.systemImage {
errorMessage = "\(Image(systemName: systemImage)) is already used as your trigger key."
} else {
errorMessage = "That key is already used as your trigger key."
}

shouldShake.toggle()
shouldError = true
}
}

if event.type == .keyUp ||
(event.type == .flagsChanged && !selectionKeybind.isEmpty && event.modifierFlags.rawValue == 256) {
finishedObservingKeys()
return nil
}
eventMonitor = NSEventMonitor(scope: .local, eventMask: [.keyDown, .keyUp]) { event in

// Handle regular key presses first
if event.type == .keyDown, !event.isARepeat {
if event.keyCode == CGKeyCode.kVK_Escape {
if event.keyCode == .kVK_Escape {
finishedObservingKeys(wasForced: true)
return nil
}

if (selectionKeybind.count + triggerKey.count) >= keyLimit {
errorMessage = "You can only use up to \(keyLimit) keys in a keybind, including the trigger key."
shouldShake.toggle()
shouldError = true
} else {
shouldError = false
selectionKeybind.insert(event.keyCode)
}
handleKeyDown(with: event)
}

if event.type == .keyUp {
finishedObservingKeys()
return nil
}

return nil
Expand All @@ -136,6 +127,35 @@ struct Keycorder: View {
model.currentEventMonitor = eventMonitor
}

/// Handles key presses and updates the current keybind
func handleKeyDown(with event: NSEvent) {
/// Get current selected keys that aren't modifiers
let currentKeys = selectionKeybind + [event.keyCode]
.filter { !$0.isModifier }

/// Get current modifiers that aren't trigger keys
let currentModifiers = event.modifierFlags
.convertToCGKeyCode()
.filter {
!Defaults[.triggerKey]
.map(\.baseModifier)
.contains($0)
}

let newSelection = Set(currentKeys + currentModifiers)

/// Make sure we don't go over the key limit
guard newSelection.count < keyLimit else {
errorMessage = "You can only use up to \(keyLimit) keys in a keybind, including the trigger key."
shouldShake.toggle()
shouldError = true
return
}

shouldError = false
selectionKeybind = newSelection
}

func finishedObservingKeys(wasForced: Bool = false) {
isActive = false
var willSet = !wasForced
Expand Down
2 changes: 1 addition & 1 deletion Loop/Managers/LoopManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class LoopManager: ObservableObject {
}

// This is called when setting the trigger key, so that there aren't conflicting event monitors
func setFlagsObservers(scope: NSEventMonitor.Scope = .all) {
func setFlagsObservers(scope: NSEventMonitor.Scope) {
flagsChangedEventMonitor?.stop()

flagsChangedEventMonitor = NSEventMonitor(
Expand Down
38 changes: 30 additions & 8 deletions Loop/Utilities/EventMonitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ class NSEventMonitor: EventMonitor, Identifiable, Equatable {
}

deinit {
stop()
if isEnabled {
stop()
}

// Clear references
localEventMonitor = nil
globalEventMonitor = nil
}

init(scope: Scope, eventMask: NSEvent.EventTypeMask, handler: @escaping (NSEvent) -> (NSEvent?)) {
Expand Down Expand Up @@ -62,12 +68,16 @@ class NSEventMonitor: EventMonitor, Identifiable, Equatable {
}

func stop() {
guard isEnabled else { return }

if let localEventMonitor {
NSEvent.removeMonitor(localEventMonitor)
self.localEventMonitor = nil
}

if let globalEventMonitor {
NSEvent.removeMonitor(globalEventMonitor)
self.globalEventMonitor = nil
}
isEnabled = false
}
Expand Down Expand Up @@ -110,24 +120,36 @@ class CGEventMonitor: EventMonitor, Identifiable, Equatable {
}

deinit {
stop()
if isEnabled {
stop()
}

// Clean up run loop source and event tap
if let runLoopSource {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes)
self.runLoopSource = nil
}

if eventTap != nil {
self.eventTap = nil
}
}

private func handleEvent(event: CGEvent) -> Unmanaged<CGEvent>? {
eventCallback(event)
}

func start() {
if let eventTap {
CGEvent.tapEnable(tap: eventTap, enable: true)
}
guard let eventTap else { return }
CGEvent.tapEnable(tap: eventTap, enable: true)
isEnabled = true
}

func stop() {
if let eventTap {
CGEvent.tapEnable(tap: eventTap, enable: false)
}
guard isEnabled else { return }

guard let eventTap else { return }
CGEvent.tapEnable(tap: eventTap, enable: false)
isEnabled = false
}

Expand Down
9 changes: 7 additions & 2 deletions Loop/Window Management/Window.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,14 @@ class Window {
/// Activate the window. This will bring it to the front and focus it if possible
func activate() {
do {
try self.axWindow.setValue(.main, value: true)
// First activate the application to ensure proper window management context
if let runningApplication = self.nsRunningApplication {
runningApplication.activate()
runningApplication.activate(options: .activateIgnoringOtherApps)
}

// Then set the window as main after a brief delay to ensure proper ordering
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
try? self.axWindow.setValue(.main, value: true)
}
} catch {
print("Failed to activate window: \(error.localizedDescription)")
Expand Down

0 comments on commit 9107745

Please sign in to comment.