3 // Copyright (c) 2016, Robert Sesek <https://www.bluestatic.org>
5 // This program is free software: you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free Software
7 // Foundation, either version 3 of the License, or any later version.
10 #include <CoreFoundation/CoreFoundation.h>
11 #include <IOKit/hid/IOHIDLib.h>
12 #include <IOKit/IOKitLib.h>
16 const CFStringRef kPrefDomain
= CFSTR("com.apple.driver.AppleIRController");
17 const CFStringRef kPrefEnabled
= CFSTR("DeviceEnabled");
25 #define LOG(msg, ...) do { \
27 printf(msg " [%s:%d]\n", ##__VA_ARGS__, __FILE__, __LINE__); \
30 #define ERROR(msg, ...) fprintf(stderr, msg " [%s:%d]\n", ##__VA_ARGS__, __FILE__, __LINE__)
32 class ScopedIOHIDManager
{
35 : manager_(IOHIDManagerCreate(nullptr, kIOHIDManagerOptionNone
)) {}
37 ~ScopedIOHIDManager() {
38 IOHIDManagerClose(manager_
, kIOHIDManagerOptionNone
);
41 ScopedIOHIDManager(const ScopedIOHIDManager
&) = delete;
43 IOHIDManagerRef
get() { return manager_
; }
46 IOHIDManagerRef manager_
;
50 class ScopedCFTypeRef
{
52 ScopedCFTypeRef(T t
= nullptr) : t_(t
) {}
59 ScopedCFTypeRef(const ScopedCFTypeRef
<T
>&) = delete;
61 operator bool() { return t_
!= nullptr; }
63 T
get() { return t_
; }
65 T
* pointer_to() { return &t_
; }
71 bool IsIRAvailable() {
72 ScopedIOHIDManager manager
;
73 IOHIDManagerSetDeviceMatching(manager
.get(), nullptr);
74 ScopedCFTypeRef
<CFSetRef
> devices(IOHIDManagerCopyDevices(manager
.get()));
76 ERROR("Failed to IOHIDManagerCopyDevices");
80 std
::vector
<void*> devices_array(CFSetGetCount(devices
.get()), nullptr);
81 CFSetGetValues(devices
.get(), const_cast<const void**>(devices_array
.data()));
82 for (const auto& device
: devices_array
) {
84 IOHIDDeviceGetProperty(reinterpret_cast<IOHIDDeviceRef
>(device
),
85 CFSTR("HIDRemoteControl"));
88 LOG("Located HIDRemoteControl:");
97 const char* GetBooleanDescription(CFTypeRef boolean
) {
98 if (CFGetTypeID(boolean
) != CFBooleanGetTypeID()) {
99 ERROR("Unexpected non-boolean CFTypeRef");
102 return CFBooleanGetValue(static_cast<CFBooleanRef
>(boolean
)) ?
"on" : "off";
105 bool SynchronizePrefs() {
106 bool rv
= CFPreferencesSynchronize(kPrefDomain
, kCFPreferencesAnyUser
,
107 kCFPreferencesCurrentHost
);
109 ERROR("Failed to CFPreferencesSynchronize");
114 io_iterator_t
CreateIOServiceIterator() {
115 CFMutableDictionaryRef matching_dict
= IOServiceMatching("AppleIRController");
116 io_iterator_t iterator
;
117 kern_return_t kr
= IOServiceGetMatchingServices(
118 kIOMasterPortDefault
, matching_dict
, &iterator
);
119 if (kr
!= KERN_SUCCESS
) {
120 ERROR("Failed to IOServiceGetMatchingServices: 0x%x", kr
);
127 CFTypeRef user_prop
= CFPreferencesCopyValue(kPrefEnabled
, kPrefDomain
,
128 kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
129 printf("Userspace property value: %s\n", GetBooleanDescription(user_prop
));
131 io_iterator_t iterator
= CreateIOServiceIterator();
136 bool did_find
= false;
137 while ((service
= IOIteratorNext(iterator
))) {
141 kern_return_t kr
= IORegistryEntryGetName(service
, name
);
142 if (kr
!= KERN_SUCCESS
) {
143 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr
);
147 LOG("Found AppleIRController: %s", name
);
149 ScopedCFTypeRef
<CFTypeRef
> device_enabled(
150 IORegistryEntryCreateCFProperty(service
, kPrefEnabled
, nullptr, 0));
151 printf("Kernel property value %s: %s\n",
152 name
, GetBooleanDescription(device_enabled
.get()));
156 ERROR("Failed to match AppleIRController");
163 int HandleWrite(bool enable
) {
164 if (geteuid() != 0) {
165 ERROR("This operation must be performed as root");
169 const CFBooleanRef enabled_value
= enable ? kCFBooleanTrue
: kCFBooleanFalse
;
171 CFPreferencesSetValue(kPrefEnabled
, enabled_value
, kPrefDomain
,
172 kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
173 if (!SynchronizePrefs())
176 io_iterator_t iterator
= CreateIOServiceIterator();
178 while ((service
= IOIteratorNext(iterator
))) {
180 kern_return_t kr
= IORegistryEntryGetName(service
, name
);
181 if (kr
!= KERN_SUCCESS
) {
182 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr
);
186 LOG("Setting property for %s to %d", name
, enable
);
188 kr
= IORegistryEntrySetCFProperty(service
, kPrefEnabled
, enabled_value
);
189 if (kr
!= KERN_SUCCESS
) {
190 ERROR("Failed to IORegistryEntrySetCFProperty: 0x%x", kr
);
198 int main(int argc
, char* argv
[]) {
199 if (!IsIRAvailable()) {
200 ERROR("No HIDRemoteControl available");
206 } else if (argc
== 2) {
207 if (strcmp("on", argv
[1]) == 0) {
208 return HandleWrite(true);
209 } else if (strcmp("off", argv
[1]) == 0) {
210 return HandleWrite(false);
214 fprintf(stderr
, "Usage: %s [on|off]\n", argv
[0]);