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 if (!SynchronizePrefs())
130 CFTypeRef user_prop
= CFPreferencesCopyValue(kPrefEnabled
, kPrefDomain
,
131 kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
132 printf("Userspace property value: %s\n", GetBooleanDescription(user_prop
));
134 io_iterator_t iterator
= CreateIOServiceIterator();
139 bool did_find
= false;
140 while ((service
= IOIteratorNext(iterator
))) {
144 kern_return_t kr
= IORegistryEntryGetName(service
, name
);
145 if (kr
!= KERN_SUCCESS
) {
146 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr
);
150 LOG("Found AppleIRController: %s", name
);
152 ScopedCFTypeRef
<CFTypeRef
> device_enabled(
153 IORegistryEntryCreateCFProperty(service
, kPrefEnabled
, nullptr, 0));
154 printf("Kernel property value %s: %s\n",
155 name
, GetBooleanDescription(device_enabled
.get()));
159 ERROR("Failed to match AppleIRController");
166 int HandleWrite(bool enable
) {
167 if (geteuid() != 0) {
168 ERROR("This operation must be performed as root");
172 const CFBooleanRef enabled_value
= enable ? kCFBooleanTrue
: kCFBooleanFalse
;
174 CFPreferencesSetValue(kPrefEnabled
, enabled_value
, kPrefDomain
,
175 kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
176 if (!SynchronizePrefs())
179 io_iterator_t iterator
= CreateIOServiceIterator();
181 while ((service
= IOIteratorNext(iterator
))) {
183 kern_return_t kr
= IORegistryEntryGetName(service
, name
);
184 if (kr
!= KERN_SUCCESS
) {
185 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr
);
189 LOG("Setting property for %s to %d", name
, enable
);
191 kr
= IORegistryEntrySetCFProperty(service
, kPrefEnabled
, enabled_value
);
192 if (kr
!= KERN_SUCCESS
) {
193 ERROR("Failed to IORegistryEntrySetCFProperty: 0x%x", kr
);
201 int main(int argc
, char* argv
[]) {
202 if (!IsIRAvailable()) {
203 ERROR("No HIDRemoteControl available");
209 } else if (argc
== 2) {
210 if (strcmp("on", argv
[1]) == 0) {
211 return HandleWrite(true);
212 } else if (strcmp("off", argv
[1]) == 0) {
213 return HandleWrite(false);
217 fprintf(stderr
, "Usage: %s [on|off]\n", argv
[0]);