Skip to content

Commit

Permalink
Implement move-mouse command
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitabobko committed Jul 21, 2024
1 parent 879cb61 commit 2fa4be0
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 8 deletions.
51 changes: 51 additions & 0 deletions Sources/AppBundle/command/MoveMouseCommand.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import AppKit
import Common

struct MoveMouseCommand: Command {
let args: MoveMouseCmdArgs

func _run(_ state: CommandMutableState, stdin: String) -> Bool {
check(Thread.current.isMainThread)
// todo bug it's bad that we operate on the "ax physical" state directly. command seq won't work correctly
// focus <direction> command has the similar problem
let mouse = mouseLocation
let point: Result<CGPoint, String> = switch args.mouseTarget.val {
case .windowLazyCenter:
windowSubjectRect(state)
.flatMap { $0.takeIf { !$0.contains(mouse) }.orFailure("The mouse already belongs to the window") }
.map(\.center)
case .windowForceCenter:
windowSubjectRect(state).map(\.center)
case .monitorLazyCenter:
Result.success(state.subject.workspace.workspaceMonitor.rect)
.flatMap { $0.takeIf { !$0.contains(mouse) }.orFailure("The mouse already belongs to the monitor") }
.map(\.center)
case .monitorForceCenter:
.success(state.subject.workspace.workspaceMonitor.rect.center)
}
switch point {
case .success(let point):
CGEvent(
mouseEventSource: nil,
mouseType: CGEventType.mouseMoved,
mouseCursorPosition: point,
mouseButton: CGMouseButton.left
)?.post(tap: CGEventTapLocation.cghidEventTap)
return true
case .failure(let msg):
return state.failCmd(msg: msg)
}
}
}

private func windowSubjectRect(_ state: CommandMutableState) -> Result<Rect, String> {
if let window: Window = state.subject.windowOrNil {
if let rect = window.getRect() {
return .success(rect)
} else {
return .failure("Failed to get rect of window '\(window.windowId)'")
}
} else {
return .failure(noWindowIsFocused)
}
}
6 changes: 4 additions & 2 deletions Sources/AppBundle/command/other/parseCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ extension CmdArgs {
command = MacosNativeMinimizeCommand(args: self as! MacosNativeMinimizeCmdArgs)
case .mode:
command = ModeCommand(args: self as! ModeCmdArgs)
case .move:
command = MoveCommand(args: self as! MoveCmdArgs)
case .moveMouse:
command = MoveMouseCommand(args: self as! MoveMouseCmdArgs)
case .moveNodeToMonitor:
command = MoveNodeToMonitorCommand(args: self as! MoveNodeToMonitorCmdArgs)
case .moveNodeToWorkspace:
command = MoveNodeToWorkspaceCommand(args: self as! MoveNodeToWorkspaceCmdArgs)
case .move:
command = MoveCommand(args: self as! MoveCmdArgs)
case .moveWorkspaceToMonitor:
command = MoveWorkspaceToMonitorCommand(args: self as! MoveWorkspaceToMonitorCmdArgs)
case .reloadConfig:
Expand Down
1 change: 1 addition & 0 deletions Sources/Cli/subcommandDescriptionsGenerated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ let subcommandDescriptions = [
[" macos-native-fullscreen", "Toggle macOS fullscreen for the focused window"],
[" macos-native-minimize", "Toggle macOS minimize for the focused window"],
[" mode", "Activate the specified binding mode"],
[" move-mouse", "Move mouse to the requested position"],
[" move-node-to-monitor", "Move window to monitor targeted by relative direction, by order, or by pattern"],
[" move-node-to-workspace", "Move the focused window to the specified workspace"],
[" move-workspace-to-monitor", "Move the focused workspace to the next or previous monitor"],
Expand Down
33 changes: 33 additions & 0 deletions Sources/Common/cmdArgs/MoveMouseCmdArgs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
public struct MoveMouseCmdArgs: CmdArgs, RawCmdArgs {
public let rawArgs: EquatableNoop<[String]>
init(rawArgs: [String]) { self.rawArgs = .init(rawArgs) }
public static let parser: CmdParser<Self> = cmdParser(
kind: .moveMouse,
allowInConfig: true,
help: """
USAGE: move-mouse [-h|--help] <mouse-position>
OPTIONS:
-h, --help Print help
ARGUMENTS:
<mouse-position> Position to move mouse to. See the man page for the possible values.
""",
options: [:],
arguments: [newArgParser(\.mouseTarget, parseMouseTarget, mandatoryArgPlaceholder: "<mouse-position>")]
)

public var mouseTarget: Lateinit<MouseTarget> = .uninitialized
}

func parseMouseTarget(arg: String, nextArgs: inout [String]) -> Parsed<MouseTarget> {
parseEnum(arg, MouseTarget.self)
}

public enum MouseTarget: String, CaseIterable {
case monitorLazyCenter = "monitor-lazy-center"
case monitorForceCenter = "monitor-force-center"

case windowLazyCenter = "window-lazy-center"
case windowForceCenter = "window-force-center"
}
2 changes: 2 additions & 0 deletions Sources/Common/cmdArgs/other/CmdKind.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
public enum CmdKind: String, CaseIterable, Equatable {
// Sorted

case balanceSizes = "balance-sizes"
case close
case closeAllWindowsButCurrent = "close-all-windows-but-current"
Expand All @@ -23,6 +24,7 @@ public enum CmdKind: String, CaseIterable, Equatable {
case macosNativeMinimize = "macos-native-minimize"
case mode
case move = "move"
case moveMouse = "move-mouse"
case moveNodeToMonitor = "move-node-to-monitor"
case moveNodeToWorkspace = "move-node-to-workspace"
case moveWorkspaceToMonitor = "move-workspace-to-monitor"
Expand Down
10 changes: 6 additions & 4 deletions Sources/Common/cmdArgs/other/parseCmdArgs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,16 @@ private func initSubcommands() -> [String: any SubCommandParserProtocol] {
result[kind.rawValue] = defaultSubCommandParser(MacosNativeMinimizeCmdArgs.init)
case .mode:
result[kind.rawValue] = defaultSubCommandParser(ModeCmdArgs.init)
case .moveNodeToMonitor:
result[kind.rawValue] = SubCommandParser(parseMoveNodeToMonitorCmdArgs)
case .moveNodeToWorkspace:
result[kind.rawValue] = SubCommandParser(parseMoveNodeToWorkspaceCmdArgs)
case .move:
result[kind.rawValue] = SubCommandParser(parseMoveCmdArgs)
// deprecated
result["move-through"] = SubCommandParser(parseMoveCmdArgs)
case .moveMouse:
result[kind.rawValue] = defaultSubCommandParser(MoveMouseCmdArgs.init)
case .moveNodeToMonitor:
result[kind.rawValue] = SubCommandParser(parseMoveNodeToMonitorCmdArgs)
case .moveNodeToWorkspace:
result[kind.rawValue] = SubCommandParser(parseMoveNodeToWorkspaceCmdArgs)
case .moveWorkspaceToMonitor:
result[kind.rawValue] = defaultSubCommandParser(MoveWorkspaceToMonitorCmdArgs.init)
// deprecated
Expand Down
59 changes: 59 additions & 0 deletions docs/aerospace-move-mouse.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
= aerospace-move-mouse(1)
include::./util/man-attributes.adoc[]
// tag::purpose[]
:manpurpose: Move mouse to the requested position
// end::purpose[]
:manname: aerospace-move-mouse

// =========================================================== Synopsis
== Synopsis
// tag::synopsis[]
aerospace move-mouse [-h|--help] <mouse-position>
// end::synopsis[]

// =========================================================== Description
== Description

// tag::body[]
{manpurpose}

// =========================================================== Options
include::./util/conditional-options-header.adoc[]

-h, --help:: Print help

// =========================================================== Arguments
include::./util/conditional-arguments-header.adoc[]

<mouse-position>::
Position to move mouse to.
Possible values:
+
[cols="1,3"]
|===
|Value |Description

|`monitor-lazy-center`
|Move mouse to the center of the focused monitor, *unless* it is already within the monitor boundaries

|`monitor-force-center`
|Move mouse to the center of the focused monitor

|`window-lazy-center`
|Move mouse to the center of the focused window, *unless* it is already within the window boundaries

|`window-force-center`
|Move mouse to the center of the focused window

|===

// =========================================================== Examples
include::util/conditional-examples-header.adoc[]

* Try to move mouse to the center of the window. If there is no window in focus, move mouse to the center of the monitor: +
`aerospace move-mouse window-lazy-center || aerospace move-mouse monitor-lazy-center`

// end::body[]

// =========================================================== Footer
include::./util/man-footer.adoc[]
4 changes: 2 additions & 2 deletions docs/aerospace-trigger-binding.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ include::util/conditional-arguments-header.adoc[]
// =========================================================== Examples
include::util/conditional-examples-header.adoc[]

* Run alphabetically first binding from config (useless and synthetic example) +
* Run alphabetically first binding from config (useless and synthetic example): +
`aerospace trigger-binding --mode main "$(aerospace config --get mode.main.binding --keys | head -1)"`
* Trigger `alt-tab` binding +
* Trigger `alt-tab` binding: +
`aerospace trigger-binding --mode main alt-tab`

// end::body[]
Expand Down
7 changes: 7 additions & 0 deletions docs/commands.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ include::aerospace-move.adoc[tags=synopsis]
include::aerospace-move.adoc[tags=purpose]
include::aerospace-move.adoc[tags=body]

== move-mouse
----
include::./aerospace-move-mouse.adoc[tags=synopsis]
----
include::./aerospace-move-mouse.adoc[tags=purpose]
include::./aerospace-move-mouse.adoc[tags=body]

== move-node-to-monitor
----
include::aerospace-move-node-to-monitor.adoc[tags=synopsis]
Expand Down
2 changes: 2 additions & 0 deletions grammar/commands-bnf-grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ aerospace -h;

| move (left|down|up|right)

| move-mouse (monitor-lazy-center|monitor-force-center|window-lazy-center|window-force-center)

| move-node-to-monitor [--wrap-around] (left|down|up|right) [--wrap-around]
| move-node-to-monitor [--wrap-around] (next|prev) [--wrap-around]
| move-node-to-monitor <monitor_pattern>...
Expand Down

0 comments on commit 2fa4be0

Please sign in to comment.