Report abuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// Map right-side Option to Enter in OS X
// by Henrik Nyh <http://henrik.nyh.se> 2008-03-15.
// Free to modify and redistribute with credit.
//
// Based on http://www.osxbook.com/book/bonus/chapter2/alterkeys/.
//
// See http://henrik.nyh.se/2008/03/map-option-to-enter for what this application does.
//
// Compile like:
//   gcc -Wall -o opt2enter opt2enter.c -framework ApplicationServices -arch i386 -arch ppc
// and put the binary in /usr/local/bin. Then keep the app running by sticking
//
//   <?xml version="1.0" encoding="UTF-8"?>
//   <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
//   <plist version="1.0">
//   <dict>
//    <key>KeepAlive</key>
//    <true/>
//    <key>Label</key>
//    <string>name.henriknyh.opt2enter</string>
//    <key>Program</key>
//    <string>/usr/local/bin/opt2enter</string>
//    <key>RunAtLoad</key>
//    <true/>
//   </dict>
//   </plist>
//
// in /Library/LaunchDaemons/name.henriknyh.opt2enter.plist and running the command
//   sudo launchctl load -w /Library/LaunchDaemons/name.henriknyh.opt2enter.plist
//
// Half-broken bonus feature:
// Enter is only sent if you press no other key at the same
// time; otherwise it still sends Option. So you can still e.g. use
// right-Option plus "." for ellipsis. Half-broken because you'll get
// a line break too, after you hit Option and before you hit "."
// TODO: Fix this by attaching to keyup instead? But then we'd need to
// make sure we don't send Option+Enter, and keep-pressed-for-multiples
// would become trickier.
//
// TODO: Respect other modifiers, so eg Ctrl+Enter works.
// TODO: Bit logic instead of just checking integer. Necessary for the "other modifiers" TODO.
// TODO: Keeping it pressed should send multiple Enters every few milliseconds.
//
// http://gemma.apple.com/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html
// is a helpful reference for parts of this code.

#include <ApplicationServices/ApplicationServices.h>

const CGKeyCode ENTER     = (CGKeyCode)76;
const CGKeyCode RIGHT_OPT = (CGKeyCode)61;
const int RIGHT_OPT_DOWN  = 524608;
const int MODIFIER_UP     = 256;


CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
        
  CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
  CGEventFlags modifiers = CGEventGetFlags(event);

  // Debug
  // printf("Type: %i Modifiers: %i Keycode: %i\n", type, (int)modifiers, keycode);

  if (modifiers == RIGHT_OPT_DOWN && keycode == RIGHT_OPT) {
    // Debug
    // printf("Swap!\n");
    CGEventRef press, release;
    press   = CGEventCreateKeyboardEvent(NULL, ENTER, true);
    release = CGEventCreateKeyboardEvent(NULL, ENTER, false);
    CGEventPost(kCGHIDEventTap, press);
    CGEventPost(kCGHIDEventTap, release);
  }

  return event;
}


int main(void) {

  CFMachPortRef      eventTap;
  CFRunLoopSourceRef runLoopSource;

  CGEventFlags oldFlags = CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState);
  eventTap = CGEventTapCreate(kCGSessionEventTap,
                              kCGHeadInsertEventTap,
                              0,
                              CGEventMaskBit(kCGEventFlagsChanged),
                              myCGEventCallback,
                              &oldFlags);
  if (!eventTap) {
    fprintf(stderr, "Failed to create event tap!\n");
    exit(1);
  }

  runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
  CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
  CGEventTapEnable(eventTap, true);
  CFRunLoopRun();

  exit(0);

}