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");
20 const bool VERBOSE
= false;
22 const bool VERBOSE
= true;
25 #define LOCATION_LINE(x) #x
26 #define LOCATION(line) __FILE__ ":" LOCATION_LINE(line)
28 #define LOG(msg, ...) do { \
30 printf("[" LOCATION(__LINE__) "] " msg "\n", ##__VA_ARGS__); \
33 #define ERROR(msg, ...) fprintf(stderr, "[" LOCATION(__LINE__) "] " msg "\n", ##__VA_ARGS__)
35 class ScopedIOHIDManager
{
38 : manager_(IOHIDManagerCreate(nullptr, kIOHIDManagerOptionNone
)) {}
40 ~ScopedIOHIDManager() {
41 IOHIDManagerClose(manager_
, kIOHIDManagerOptionNone
);
44 ScopedIOHIDManager(const ScopedIOHIDManager
&) = delete;
46 IOHIDManagerRef
get() { return manager_
; }
49 IOHIDManagerRef manager_
;
53 class ScopedCFTypeRef
{
55 ScopedCFTypeRef(T t
= nullptr) : t_(t
) {}
62 ScopedCFTypeRef(const ScopedCFTypeRef
<T
>&) = delete;
64 operator bool() { return t_
!= nullptr; }
66 T
get() { return t_
; }
68 T
* pointer_to() { return &t_
; }
74 bool IsIRAvailable() {
75 ScopedIOHIDManager manager
;
76 IOHIDManagerSetDeviceMatching(manager
.get(), nullptr);
77 ScopedCFTypeRef
<CFSetRef
> devices(IOHIDManagerCopyDevices(manager
.get()));
79 ERROR("Failed to IOHIDManagerCopyDevices");
83 std
::vector
<void*> devices_array(CFSetGetCount(devices
.get()), nullptr);
84 CFSetGetValues(devices
.get(), const_cast<const void**>(devices_array
.data()));
85 for (const auto& device
: devices_array
) {
87 IOHIDDeviceGetProperty(reinterpret_cast<IOHIDDeviceRef
>(device
),
88 CFSTR("HIDRemoteControl"));
91 LOG("Located HIDRemoteControl:");
100 const char* GetBooleanDescription(CFTypeRef boolean
) {
101 if (CFGetTypeID(boolean
) != CFBooleanGetTypeID()) {
102 ERROR("Unexpected non-boolean CFTypeRef");
105 return CFBooleanGetValue(static_cast<CFBooleanRef
>(boolean
)) ?
"on" : "off";
108 bool SynchronizePrefs() {
109 bool rv
= CFPreferencesSynchronize(kPrefDomain
, kCFPreferencesAnyUser
,
110 kCFPreferencesCurrentHost
);
112 ERROR("Failed to CFPreferencesSynchronize");
117 bool CreateIOServiceIterator(io_iterator_t
* iterator
) {
118 CFMutableDictionaryRef matching_dict
= IOServiceMatching("AppleIRController");
119 kern_return_t kr
= IOServiceGetMatchingServices(
120 kIOMasterPortDefault
, matching_dict
, iterator
);
121 if (kr
!= KERN_SUCCESS
) {
122 ERROR("Failed to IOServiceGetMatchingServices: 0x%x", kr
);
129 CFTypeRef user_prop
= CFPreferencesCopyValue(kPrefEnabled
, kPrefDomain
,
130 kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
131 printf("Userspace property value: %s\n", GetBooleanDescription(user_prop
));
133 io_iterator_t iterator
;
134 if (!CreateIOServiceIterator(&iterator
))
138 bool did_find
= false;
139 while ((service
= IOIteratorNext(iterator
))) {
143 kern_return_t kr
= IORegistryEntryGetName(service
, name
);
144 if (kr
!= KERN_SUCCESS
) {
145 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr
);
149 LOG("Found AppleIRController: %s", name
);
151 ScopedCFTypeRef
<CFTypeRef
> device_enabled(
152 IORegistryEntryCreateCFProperty(service
, kPrefEnabled
, nullptr, 0));
153 printf("Kernel property value %s: %s\n",
154 name
, GetBooleanDescription(device_enabled
.get()));
158 ERROR("Failed to match any AppleIRController");
165 int HandleWrite(bool enable
) {
166 if (geteuid() != 0) {
167 ERROR("This operation must be performed as root");
171 const CFBooleanRef enabled_value
= enable ? kCFBooleanTrue
: kCFBooleanFalse
;
173 CFPreferencesSetValue(kPrefEnabled
, enabled_value
, kPrefDomain
,
174 kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
175 if (!SynchronizePrefs())
178 io_iterator_t iterator
;
179 if (!CreateIOServiceIterator(&iterator
))
183 while ((service
= IOIteratorNext(iterator
))) {
185 kern_return_t kr
= IORegistryEntryGetName(service
, name
);
186 if (kr
!= KERN_SUCCESS
) {
187 ERROR("Failed to IORegistryEntryGetName: 0x%x", kr
);
191 LOG("Setting property for %s to %d", name
, enable
);
193 kr
= IORegistryEntrySetCFProperty(service
, kPrefEnabled
, enabled_value
);
194 if (kr
!= KERN_SUCCESS
) {
195 ERROR("Failed to IORegistryEntrySetCFProperty: 0x%x", kr
);
203 int main(int argc
, char* argv
[]) {
204 if (!IsIRAvailable()) {
205 ERROR("No HIDRemoteControl available");
211 } else if (argc
== 2) {
212 if (strcmp("on", argv
[1]) == 0) {
213 return HandleWrite(true);
214 } else if (strcmp("off", argv
[1]) == 0) {
215 return HandleWrite(false);
219 fprintf(stderr
, "Usage: %s [on|off]\n", argv
[0]);