Initial version of apple-ir-control.
[apple-ir-control.git] / apple-ir-control.cc
1 #include <CoreFoundation/CoreFoundation.h>
2 #include <IOKit/hid/IOHIDLib.h>
3 #include <IOKit/IOKitLib.h>
4 #include <unistd.h>
5 #include <vector>
6
7 const CFStringRef kPrefDomain = CFSTR("com.apple.driver.AppleIRController");
8 const CFStringRef kPrefEnabled = CFSTR("DeviceEnabled");
9
10 bool VERBOSE = true;
11
12 #define LOG(msg, ...) do { \
13 if (VERBOSE) { \
14 printf(msg " [%s:%d]\n", ##__VA_ARGS__, __FILE__, __LINE__); \
15 } \
16 } while(0)
17 #define ERROR(msg, ...) fprintf(stderr, msg " [%s:%d]\n", ##__VA_ARGS__, __FILE__, __LINE__)
18
19 class ScopedIOHIDManager {
20 public:
21 ScopedIOHIDManager()
22 : manager_(IOHIDManagerCreate(nullptr, kIOHIDManagerOptionNone)) {}
23
24 ~ScopedIOHIDManager() {
25 IOHIDManagerClose(manager_, kIOHIDManagerOptionNone);
26 }
27
28 ScopedIOHIDManager(const ScopedIOHIDManager&) = delete;
29
30 bool Open() {
31 IOReturn ret = IOHIDManagerOpen(manager_, kIOHIDManagerOptionNone);
32 if (ret != kIOReturnSuccess) {
33 ERROR("Failed to IOHIDManagerOpen: 0x%x", ret);
34 return false;
35 }
36 return true;
37 }
38
39 IOHIDManagerRef get() { return manager_; }
40
41 private:
42 IOHIDManagerRef manager_;
43 };
44
45 template <typename T>
46 class ScopedCFTypeRef {
47 public:
48 ScopedCFTypeRef(T t = nullptr) : t_(t) {}
49 ~ScopedCFTypeRef() {
50 if (t_) {
51 CFRelease(t_);
52 }
53 }
54
55 ScopedCFTypeRef(const ScopedCFTypeRef<T>&) = delete;
56
57 operator bool() { return t_ != nullptr; }
58
59 T get() { return t_; }
60
61 T* pointer_to() { return &t_; }
62
63 private:
64 T t_;
65 };
66
67 bool IsIRAvailable() {
68 ScopedIOHIDManager manager;
69 IOHIDManagerSetDeviceMatching(manager.get(), nullptr);
70 if (!manager.Open()) {
71 //return false;
72 }
73 ScopedCFTypeRef<CFSetRef> devices(IOHIDManagerCopyDevices(manager.get()));
74 if (!devices) {
75 ERROR("Failed to IOHIDManagerCopyDevices");
76 return false;
77 }
78 std::vector<void*> devices_array(CFSetGetCount(devices.get()), nullptr);
79 CFSetGetValues(devices.get(), const_cast<const void**>(devices_array.data()));
80 for (const auto& device : devices_array) {
81 CFTypeRef prop =
82 IOHIDDeviceGetProperty(reinterpret_cast<IOHIDDeviceRef>(device),
83 CFSTR("HIDRemoteControl"));
84 if (prop) {
85 if (VERBOSE) {
86 LOG("Located HIDRemoteControl:");
87 CFShow(device);
88 }
89 return true;
90 }
91 }
92 return false;
93 }
94
95 CFTypeRef GetUserPropertyValue() {
96 return CFPreferencesCopyValue(kPrefEnabled, kPrefDomain,
97 kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
98 }
99
100 int HandleRead() {
101 CFTypeRef user_prop = GetUserPropertyValue();
102 CFShow(user_prop);
103
104 CFMutableDictionaryRef matching_dict = IOServiceMatching("AppleIRController");
105 io_iterator_t iterator;
106 kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dict, &iterator);
107 if (kr != KERN_SUCCESS) {
108 ERROR("Failed to IOServiceGetMatchingServices: 0x%x", kr);
109 return EXIT_FAILURE;
110 }
111
112 io_object_t service;
113 bool did_find = false;
114 while ((service = IOIteratorNext(iterator))) {
115 did_find = true;
116
117 io_name_t name;
118 kern_return_t kr = IORegistryEntryGetName(service, name);
119 if (kr != KERN_SUCCESS) {
120 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr);
121 continue;
122 }
123
124 LOG("Found AppleIRController: %s", name);
125
126 ScopedCFTypeRef<CFTypeRef> device_enabled(
127 IORegistryEntryCreateCFProperty(service, kPrefEnabled, nullptr, 0));
128 CFShow(device_enabled.get());
129
130 #if 0
131 ScopedCFTypeRef<CFMutableDictionaryRef> props;
132 kr = IORegistryEntryCreateCFProperties(service, props.pointer_to(), nullptr, 0);
133 if (kr != KERN_SUCCESS) {
134 ERROR("Failed to IORegistryEntryCreateCFProperties(%s) = 0x%x", name, kr);
135 continue;
136 }
137 CFShow(props.get());
138 #endif
139 }
140
141 if (!did_find) {
142 ERROR("Failed to match AppleIRController");
143 return EXIT_FAILURE;
144 }
145
146 return EXIT_SUCCESS;
147 }
148
149 int HandleWrite(bool enable) {
150 return EXIT_SUCCESS;
151 }
152
153 int main(int argc, char* argv[]) {
154 if (!IsIRAvailable()) {
155 ERROR("No HIDRemoteControl available");
156 return EXIT_FAILURE;
157 }
158
159 if (argc == 1) {
160 return HandleRead();
161 } else if (argc == 2) {
162 if (strcmp("on", argv[1])) {
163 return HandleWrite(true);
164 } else if (strcmp("off", argv[1])) {
165 return HandleWrite(false);
166 }
167 }
168
169 fprintf(stderr, "Usage: %s [on|off]\n", argv[0]);
170 return EXIT_FAILURE;
171 }