Better output for HandleRead().
[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 CFTypeRef GetUserPropertyValue() {
92 return CFPreferencesCopyValue(kPrefEnabled, kPrefDomain,
93 kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
94 }
95
96 io_iterator_t CreateIOServiceIterator() {
97 CFMutableDictionaryRef matching_dict = IOServiceMatching("AppleIRController");
98 io_iterator_t iterator;
99 kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dict, &iterator);
100 if (kr != KERN_SUCCESS) {
101 ERROR("Failed to IOServiceGetMatchingServices: 0x%x", kr);
102 return 0;
103 }
104 return iterator;
105 }
106
107 int HandleRead() {
108 CFTypeRef user_prop = GetUserPropertyValue();
109 printf("Userspace property value: %s\n", GetBooleanDescription(user_prop));
110
111 io_iterator_t iterator = CreateIOServiceIterator();
112 if (!iterator)
113 return EXIT_FAILURE;
114
115 io_object_t service;
116 bool did_find = false;
117 while ((service = IOIteratorNext(iterator))) {
118 did_find = true;
119
120 io_name_t name;
121 kern_return_t kr = IORegistryEntryGetName(service, name);
122 if (kr != KERN_SUCCESS) {
123 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr);
124 continue;
125 }
126
127 LOG("Found AppleIRController: %s", name);
128
129 ScopedCFTypeRef<CFTypeRef> device_enabled(
130 IORegistryEntryCreateCFProperty(service, kPrefEnabled, nullptr, 0));
131 printf("Kernel property value %s: %s\n",
132 name, GetBooleanDescription(device_enabled.get()));
133
134 #if 0
135 ScopedCFTypeRef<CFMutableDictionaryRef> props;
136 kr = IORegistryEntryCreateCFProperties(service, props.pointer_to(), nullptr, 0);
137 if (kr != KERN_SUCCESS) {
138 ERROR("Failed to IORegistryEntryCreateCFProperties(%s) = 0x%x", name, kr);
139 continue;
140 }
141 CFShow(props.get());
142 #endif
143 }
144
145 if (!did_find) {
146 ERROR("Failed to match AppleIRController");
147 return EXIT_FAILURE;
148 }
149
150 return EXIT_SUCCESS;
151 }
152
153 int HandleWrite(bool enable) {
154 return EXIT_SUCCESS;
155 }
156
157 int main(int argc, char* argv[]) {
158 if (!IsIRAvailable()) {
159 ERROR("No HIDRemoteControl available");
160 return EXIT_FAILURE;
161 }
162
163 if (argc == 1) {
164 return HandleRead();
165 } else if (argc == 2) {
166 if (strcmp("on", argv[1])) {
167 return HandleWrite(true);
168 } else if (strcmp("off", argv[1])) {
169 return HandleWrite(false);
170 }
171 }
172
173 fprintf(stderr, "Usage: %s [on|off]\n", argv[0]);
174 return EXIT_FAILURE;
175 }