~habibalamin/Commandeer

Fix hotkey w/ command treated as command alone

This literally goes against the entire purpose of the app; crazy, what a
regression! I assume 5abc0eb3b6cf276f06bba649b34c75a3d5263246 introduced
this.

This should fix lots of other problems, as I simplified the callback, as
promised, though not in the exact way I had the idea to in 5abc0eb.

As far as I can tell, it's impossible to know the state of all keys on a
given event, so I cannot test that only command is held down, or that no
other keys are pressed when it's released, or the like.

I did find a way to check when command is pressed (as an only modifier),
not just merely checking that modifier flags were changed and command is
down, which can just be a remnant of pressing command + another modifier
key, then releasing the other key before releasing command; this was one
of the many cases we were trying to code for with the messy conditionals
in the older solution, but all these silly hacks are unnecessary now.

We now simply check if the event keycode is command key, save it only if
it's exactly that and command is down, and on the next event, if there's
a saved event, we know it's exactly command press, so if the current one
is a command key event as well and command is up, that's a release, then
we can send an escape; since we always clear the saved event on the next
one, whether it's a command release or not, we know the previous event's
always a command press if it's saved.

Since this solution doesn't check the other keys which are down, you can
hold a letter key, for example, and quickly press command and release in
less than the time it takes the letter to repeat, and it would act as an
escape. This may or may not be what some people want, but I don't really
like it. It's not a huge deal, but it's probably the main issue with how
it works now. I wanted to even save the down/up state of all keys in the
callback's `userInfo`, but that's too much extra effort for now.
1 files changed, 27 insertions(+), 25 deletions(-)

M Commandeer/CommandPressToEscapePressTransformer.swift
M Commandeer/CommandPressToEscapePressTransformer.swift => Commandeer/CommandPressToEscapePressTransformer.swift +27 -25
@@ 9,18 9,30 @@
import Foundation
import AppKit

func keyEventCmdPressedAlone(_ event: CGEvent) -> Bool
func keyEventCmdDown(_ event: CGEvent) -> Bool
{
  let keycode = event.getIntegerValueField(CGEventField.keyboardEventKeycode)

  // 55 = L Command
  // 54 = R Command
  return
    Int32(event.flags.rawValue) ==
      NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK ||
    Int32(event.flags.rawValue) ==
      NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK
    (keycode == 55 &&
      Int32(event.flags.rawValue) ==
        NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICELCMDKEYMASK) ||
    (keycode == 54 &&
      Int32(event.flags.rawValue) ==
        NX_NONCOALSESCEDMASK | NX_COMMANDMASK | NX_DEVICERCMDKEYMASK)
}

func keyEventNoFlagsPressed(_ event: CGEvent) -> Bool
func keyEventCmdUp(_ event: CGEvent) -> Bool
{
  return Int32(event.flags.rawValue) == NX_NONCOALSESCEDMASK // no flags
  let keycode = event.getIntegerValueField(CGEventField.keyboardEventKeycode)

  // 55 = L Command
  // 54 = R Command
  return
    (keycode == 55 && Int32(event.flags.rawValue) == NX_NONCOALSESCEDMASK) ||
    (keycode == 54 && Int32(event.flags.rawValue) == NX_NONCOALSESCEDMASK)
}

func commandPressToEscapePressTransformer(proxy: CGEventTapProxy,


@@ 32,17 44,14 @@ func commandPressToEscapePressTransformer(proxy: CGEventTapProxy,
  // `UnsafeMutableRawPointer` -> `UnsafeMutablePointer`
  let refcon = refcon?.bindMemory(to: CGEvent?.self, capacity: 1)

  if let currentKeyDown = refcon?.pointee {
    if (event.type != .flagsChanged) {
      if (keyEventCmdPressedAlone(currentKeyDown)) {
        refcon?.initialize(to: nil)
      }
    } else if (keyEventNoFlagsPressed(event) &&
      keyEventCmdPressedAlone(currentKeyDown))
    {
      refcon?.initialize(to: nil)
  // 55 = L Command
  // 54 = R Command

  if let savedEvent = refcon?.pointee {
    refcon?.initialize(to: nil)

      if (event.timestamp - currentKeyDown.timestamp <= 200_000_000) // ns
    if (keyEventCmdUp(event)) {
      if (event.timestamp - savedEvent.timestamp <= 200_000_000) // ns
      {
        let eventSource = CGEventSource(event: event)



@@ 60,15 69,8 @@ func commandPressToEscapePressTransformer(proxy: CGEventTapProxy,

        return nil
      }
    } else if (keyEventCmdPressedAlone(event) &&
      !keyEventNoFlagsPressed(currentKeyDown))
    {
      // Don't treat this as a true cmd-pressed-alone to be stored,
      // treat it as whatever's already been stored.
    } else {
      refcon?.initialize(to: event)
    }
  } else {
  } else if (keyEventCmdDown(event)) {
    refcon?.initialize(to: event)
  }