diff --git a/AeroSpace.xcodeproj/project.pbxproj b/AeroSpace.xcodeproj/project.pbxproj index fc95cd3f..ddbe849e 100644 --- a/AeroSpace.xcodeproj/project.pbxproj +++ b/AeroSpace.xcodeproj/project.pbxproj @@ -54,6 +54,7 @@ 77FA83225024151CD556E1ED /* CloseAllWindowsButCurrentCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99853C505D93E41F6531C324 /* CloseAllWindowsButCurrentCommand.swift */; }; 78622D72C0CCE8D69B080D29 /* moveWithMouse.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0938DE74E687A8959E3F9C /* moveWithMouse.swift */; }; 78C38FAF21E574DBE3B88806 /* CloseCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8FDC079C530D7B3C568E35F /* CloseCommand.swift */; }; + 7C0ACF53062631B667424927 /* MacosNativeMinimizeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7862747CF7F654B8BF8C9CF9 /* MacosNativeMinimizeCommand.swift */; }; 7ED8C2A66DD6F903796F090C /* TestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2431BEAFFF1AC12D3001317A /* TestApp.swift */; }; 8086A22EDCDC4C906C337D0B /* resizeWithMouse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9745E5FC11701AE35712EC18 /* resizeWithMouse.swift */; }; 852F88894A3B9FC385563665 /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = 42BC1E757EF69233C2262FF4 /* HotKey */; }; @@ -157,6 +158,7 @@ 6F1905935B0C61590A96EFEF /* TestWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestWindow.swift; sourceTree = ""; }; 71908440CD1ADBE13AD58E26 /* DebugWindowsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugWindowsCommand.swift; sourceTree = ""; }; 739F4FE1DB101DF4E508F3FE /* Monitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Monitor.swift; sourceTree = ""; }; + 7862747CF7F654B8BF8C9CF9 /* MacosNativeMinimizeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacosNativeMinimizeCommand.swift; sourceTree = ""; }; 796713A1B3AEEBF4D0D180C7 /* server.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = server.swift; sourceTree = ""; }; 7ACD4E9C6C0C08F1B0622C57 /* ResizeCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeCommandTest.swift; sourceTree = ""; }; 7DAEA4B9015FBF8711E532D0 /* MonitorEx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonitorEx.swift; sourceTree = ""; }; @@ -419,6 +421,7 @@ D27A7AF7E9A049700BDE276B /* JoinWithCommand.swift */, 1A2B673C67B00DBFCC27FFE7 /* LayoutCommand.swift */, BF3D87FF4D8238F07870F2F6 /* MacosNativeFullscreenCommand.swift */, + 7862747CF7F654B8BF8C9CF9 /* MacosNativeMinimizeCommand.swift */, 4B0CD3C2A0E86CDB9DF312AB /* ModeCommand.swift */, 1376845BAB666880919B9AA2 /* MoveCommand.swift */, 25AC44D0E9450867215FCBEC /* MoveNodeToWorkspaceCommand.swift */, @@ -621,6 +624,7 @@ BD6301B2CFC16FDE4223ACB8 /* MacApp.swift in Sources */, EECC59858691B99A95542D72 /* MacWindow.swift in Sources */, DEE74487CFB70CEC10D9D3D7 /* MacosNativeFullscreenCommand.swift in Sources */, + 7C0ACF53062631B667424927 /* MacosNativeMinimizeCommand.swift in Sources */, 6E4E235FDA41307B19F16182 /* ModeCommand.swift in Sources */, C76E421FB0D0F0EB1C93A06C /* Monitor.swift in Sources */, B04363E8C28CEC76F39977F2 /* MonitorDescription.swift in Sources */, diff --git a/LocalPackage/Sources/Cli/subcommandDescriptionsGenerated.swift b/LocalPackage/Sources/Cli/subcommandDescriptionsGenerated.swift index c49432a7..4d704d8f 100644 --- a/LocalPackage/Sources/Cli/subcommandDescriptionsGenerated.swift +++ b/LocalPackage/Sources/Cli/subcommandDescriptionsGenerated.swift @@ -14,6 +14,7 @@ let subcommandDescriptions = [ [" list-windows", "Prints windows that satisfy conditions"], [" list-workspaces", "Prints workspaces that satisfy conditions"], [" macos-native-fullscreen", "Toggle macOS fullscreen for the currently focused window"], + [" macos-native-minimize", "Toggle macOS minimize for the currently focused window"], [" mode", "Activates the specified binding modes"], [" move-node-to-workspace", "Moves currently focused window to the specified workspace"], [" move-workspace-to-monitor", "Moves currently focused workspace to the next or previous monitor"], diff --git a/LocalPackage/Sources/Common/cmdArgs/MacosNativeMinimize.swift b/LocalPackage/Sources/Common/cmdArgs/MacosNativeMinimize.swift new file mode 100644 index 00000000..166c9812 --- /dev/null +++ b/LocalPackage/Sources/Common/cmdArgs/MacosNativeMinimize.swift @@ -0,0 +1,4 @@ +public struct MacosNativeMinimizeCmdArgs: RawCmdArgs, CmdArgs { + public init() {} + public static let parser: CmdParser = noArgsParser(.macosNativeMinimize, allowInConfig: true) +} diff --git a/LocalPackage/Sources/Common/cmdArgs/other/CmdKind.swift b/LocalPackage/Sources/Common/cmdArgs/other/CmdKind.swift index 7a98b896..bf2a8fd3 100644 --- a/LocalPackage/Sources/Common/cmdArgs/other/CmdKind.swift +++ b/LocalPackage/Sources/Common/cmdArgs/other/CmdKind.swift @@ -14,6 +14,7 @@ public enum CmdKind: String, CaseIterable, Equatable { case listWindows = "list-windows" case listWorkspaces = "list-workspaces" case macosNativeFullscreen = "macos-native-fullscreen" + case macosNativeMinimize = "macos-native-minimize" case mode case move = "move" case moveNodeToWorkspace = "move-node-to-workspace" diff --git a/LocalPackage/Sources/Common/cmdArgs/other/parseCmdArgs.swift b/LocalPackage/Sources/Common/cmdArgs/other/parseCmdArgs.swift index bca02ce9..1a67098a 100644 --- a/LocalPackage/Sources/Common/cmdArgs/other/parseCmdArgs.swift +++ b/LocalPackage/Sources/Common/cmdArgs/other/parseCmdArgs.swift @@ -48,6 +48,8 @@ private func initSubcommands() -> [String: any SubCommandParserProtocol] { result[kind.rawValue] = SubCommandParser(parseListWorkspacesCmdArgs) case .macosNativeFullscreen: result[kind.rawValue] = defaultSubCommandParser(MacosNativeFullscreenCmdArgs()) + case .macosNativeMinimize: + result[kind.rawValue] = defaultSubCommandParser(MacosNativeMinimizeCmdArgs()) case .mode: result[kind.rawValue] = defaultSubCommandParser(ModeCmdArgs()) case .moveNodeToWorkspace: diff --git a/docs/aerospace-macos-native-minimize.adoc b/docs/aerospace-macos-native-minimize.adoc new file mode 100644 index 00000000..b5412fc8 --- /dev/null +++ b/docs/aerospace-macos-native-minimize.adoc @@ -0,0 +1,24 @@ += aerospace-macos-native-minimize(1) +include::util/man-attributes.adoc[] +:manname: aerospace-macos-native-minimize +// tag::purpose[] +:manpurpose: Toggle macOS minimize for the currently focused window +// end::purpose[] + +== Synopsis +// tag::synopsis[] +macos-native-minimize [-h|--help] +// end::synopsis[] + +== Description + +// tag::body[] +{manpurpose} + +// end::body[] + +include::util/conditional-options-header.adoc[] + +-h, --help:: Print help + +include::util/man-footer.adoc[] diff --git a/docs/commands.adoc b/docs/commands.adoc index 09ee4211..8535740b 100644 --- a/docs/commands.adoc +++ b/docs/commands.adoc @@ -102,6 +102,13 @@ include::aerospace-macos-native-fullscreen.adoc[tags=synopsis] include::aerospace-macos-native-fullscreen.adoc[tags=purpose] include::aerospace-macos-native-fullscreen.adoc[tags=body] +== macos-native-minimize +---- +include::aerospace-macos-native-minimize.adoc[tags=synopsis] +---- +include::aerospace-macos-native-minimize.adoc[tags=purpose] +include::aerospace-macos-native-minimize.adoc[tags=body] + == mode ---- include::aerospace-mode.adoc[tags=synopsis] diff --git a/src/command/MacosNativeMinimizeCommand.swift b/src/command/MacosNativeMinimizeCommand.swift new file mode 100644 index 00000000..7076c3e4 --- /dev/null +++ b/src/command/MacosNativeMinimizeCommand.swift @@ -0,0 +1,17 @@ +import Common + +struct MacosNativeMinimizeCommand: Command { + let args: MacosNativeMinimizeCmdArgs + + func _run(_ state: CommandMutableState, stdin: String) -> Bool { + check(Thread.current.isMainThread) + guard let window = state.subject.windowOrNil else { + state.stderr.append("No window in focus") + return false + } + let axWindow = window.asMacWindow().axWindow + let success = axWindow.set(Ax.minimizedAttr, !window.isMacosMinimized) + if !success { state.stderr.append("Failed") } + return success + } +} diff --git a/src/command/other/parseCommand.swift b/src/command/other/parseCommand.swift index fb48960b..3681d86c 100644 --- a/src/command/other/parseCommand.swift +++ b/src/command/other/parseCommand.swift @@ -39,6 +39,8 @@ extension CmdArgs { command = ListWorkspacesCommand(args: self as! ListWorkspacesCmdArgs) case .macosNativeFullscreen: command = MacosNativeFullscreenCommand(args: self as! MacosNativeFullscreenCmdArgs) + case .macosNativeMinimize: + command = MacosNativeMinimizeCommand(args: self as! MacosNativeMinimizeCmdArgs) case .mode: command = ModeCommand(args: self as! ModeCmdArgs) case .moveNodeToWorkspace: diff --git a/src/tree/MacWindow.swift b/src/tree/MacWindow.swift index 4b8c5a22..4da71747 100644 --- a/src/tree/MacWindow.swift +++ b/src/tree/MacWindow.swift @@ -85,6 +85,7 @@ final class MacWindow: Window, CustomStringConvertible { override var title: String? { axWindow.get(Ax.titleAttr) } override var isMacosFullscreen: Bool { axWindow.get(Ax.isFullscreenAttr) == true } + override var isMacosMinimized: Bool { axWindow.get(Ax.minimizedAttr) == true } @discardableResult override func nativeFocus() -> Bool { // todo make focus reliable: make async + active waiting diff --git a/src/tree/Window.swift b/src/tree/Window.swift index 269d740c..d8eaef18 100644 --- a/src/tree/Window.swift +++ b/src/tree/Window.swift @@ -28,6 +28,7 @@ class Window: TreeNode, Hashable { func getSize() -> CGSize? { error("Not implemented") } var title: String? { error("Not implemented") } var isMacosFullscreen: Bool { false } + var isMacosMinimized: Bool { false } var isHiddenViaEmulation: Bool { error("Not implemented") } func setSize(_ size: CGSize) { error("Not implemented") } diff --git a/src/util/accessibility.swift b/src/util/accessibility.swift index e13d6ba7..95840e72 100644 --- a/src/util/accessibility.swift +++ b/src/util/accessibility.swift @@ -207,9 +207,10 @@ enum Ax { getter: { $0 as? Bool }, setter: { $0 as CFTypeRef } ) - static let minimizedAttr = ReadableAttrImpl( + static let minimizedAttr = WritableAttrImpl( key: kAXMinimizedAttribute, - getter: { $0 as? Bool } + getter: { $0 as? Bool }, + setter: { $0 as CFTypeRef } ) //static let minimizedAttr = ReadableAttrImpl( // key: kAXMinimizedAttribute,