Handle setting the property value.
[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 IOHIDManagerRef get() { return manager_; }
31
32 private:
33 IOHIDManagerRef manager_;
34 };
35
36 template <typename T>
37 class ScopedCFTypeRef {
38 public:
39 ScopedCFTypeRef(T t = nullptr) : t_(t) {}
40 ~ScopedCFTypeRef() {
41 if (t_) {
42 CFRelease(t_);
43 }
44 }
45
46 ScopedCFTypeRef(const ScopedCFTypeRef<T>&) = delete;
47
48 operator bool() { return t_ != nullptr; }
49
50 T get() { return t_; }
51
52 T* pointer_to() { return &t_; }
53
54 private:
55 T t_;
56 };
57
58 bool IsIRAvailable() {
59 ScopedIOHIDManager manager;
60 IOHIDManagerSetDeviceMatching(manager.get(), nullptr);
61 ScopedCFTypeRef<CFSetRef> devices(IOHIDManagerCopyDevices(manager.get()));
62 if (!devices) {
63 ERROR("Failed to IOHIDManagerCopyDevices");
64 return false;
65 }
66 std::vector<void*> devices_array(CFSetGetCount(devices.get()), nullptr);
67 CFSetGetValues(devices.get(), const_cast<const void**>(devices_array.data()));
68 for (const auto& device : devices_array) {
69 CFTypeRef prop =
70 IOHIDDeviceGetProperty(reinterpret_cast<IOHIDDeviceRef>(device),
71 CFSTR("HIDRemoteControl"));
72 if (prop) {
73 if (VERBOSE) {
74 LOG("Located HIDRemoteControl:");
75 CFShow(device);
76 }
77 return true;
78 }
79 }
80 return false;
81 }
82
83 const char* GetBooleanDescription(CFTypeRef boolean) {
84 if (CFGetTypeID(boolean) != CFBooleanGetTypeID()) {
85 ERROR("Unexpected non-boolean CFTypeRef");
86 abort();
87 }
88 return CFBooleanGetValue(static_cast<CFBooleanRef>(boolean)) ? "on" : "off";
89 }
90
91 bool SynchronizePrefs() {
92 bool rv = CFPreferencesSynchronize(kPrefDomain, kCFPreferencesAnyUser,
93 kCFPreferencesCurrentHost);
94 if (!rv) {
95 ERROR("Failed to CFPreferencesSynchronize");
96 }
97 return rv;
98 }
99
100 CFTypeRef GetUserPropertyValue() {
101 return CFPreferencesCopyValue(kPrefEnabled, kPrefDomain,
102 kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
103 }
104
105 io_iterator_t CreateIOServiceIterator() {
106 CFMutableDictionaryRef matching_dict = IOServiceMatching("AppleIRController");
107 io_iterator_t iterator;
108 kern_return_t kr = IOServiceGetMatchingServices(
109 kIOMasterPortDefault, matching_dict, &iterator);
110 if (kr != KERN_SUCCESS) {
111 ERROR("Failed to IOServiceGetMatchingServices: 0x%x", kr);
112 return 0;
113 }
114 return iterator;
115 }
116
117 int HandleRead() {
118 if (!SynchronizePrefs())
119 return EXIT_FAILURE;
120
121 CFTypeRef user_prop = GetUserPropertyValue();
122 printf("Userspace property value: %s\n", GetBooleanDescription(user_prop));
123
124 io_iterator_t iterator = CreateIOServiceIterator();
125 if (!iterator)
126 return EXIT_FAILURE;
127
128 io_object_t service;
129 bool did_find = false;
130 while ((service = IOIteratorNext(iterator))) {
131 did_find = true;
132
133 io_name_t name;
134 kern_return_t kr = IORegistryEntryGetName(service, name);
135 if (kr != KERN_SUCCESS) {
136 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr);
137 continue;
138 }
139
140 LOG("Found AppleIRController: %s", name);
141
142 ScopedCFTypeRef<CFTypeRef> device_enabled(
143 IORegistryEntryCreateCFProperty(service, kPrefEnabled, nullptr, 0));
144 printf("Kernel property value %s: %s\n",
145 name, GetBooleanDescription(device_enabled.get()));
146
147 #if 0
148 ScopedCFTypeRef<CFMutableDictionaryRef> props;
149 kr = IORegistryEntryCreateCFProperties(service, props.pointer_to(), nullptr, 0);
150 if (kr != KERN_SUCCESS) {
151 ERROR("Failed to IORegistryEntryCreateCFProperties(%s) = 0x%x", name, kr);
152 continue;
153 }
154 CFShow(props.get());
155 #endif
156 }
157
158 if (!did_find) {
159 ERROR("Failed to match AppleIRController");
160 return EXIT_FAILURE;
161 }
162
163 return EXIT_SUCCESS;
164 }
165
166 int HandleWrite(bool enable) {
167 const CFBooleanRef enabled_value = enable ? kCFBooleanTrue : kCFBooleanFalse;
168
169 CFPreferencesSetValue(kPrefEnabled, enabled_value, kPrefDomain,
170 kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
171 if (!SynchronizePrefs())
172 return EXIT_FAILURE;
173
174 io_iterator_t iterator = CreateIOServiceIterator();
175 io_object_t service;
176 while ((service = IOIteratorNext(iterator))) {
177 io_name_t name;
178 kern_return_t kr = IORegistryEntryGetName(service, name);
179 if (kr != KERN_SUCCESS) {
180 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr);
181 continue;
182 }
183
184 LOG("Setting property for %s to %d", name, enable);
185
186 kr = IORegistryEntrySetCFProperty(service, kPrefEnabled, enabled_value);
187 if (kr != KERN_SUCCESS) {
188 ERROR("Failed to IORegistryEntrySetCFProperty: 0x%x", kr);
189 continue;
190 }
191 }
192
193 return HandleRead();
194 }
195
196 int main(int argc, char* argv[]) {
197 if (!IsIRAvailable()) {
198 ERROR("No HIDRemoteControl available");
199 return EXIT_FAILURE;
200 }
201
202 if (argc == 1) {
203 return HandleRead();
204 } else if (argc == 2) {
205 if (strcmp("on", argv[1]) == 0) {
206 return HandleWrite(true);
207 } else if (strcmp("off", argv[1]) == 0) {
208 return HandleWrite(false);
209 }
210 }
211
212 fprintf(stderr, "Usage: %s [on|off]\n", argv[0]);
213 return EXIT_FAILURE;
214 }