| 135 | SInt32 macVersion; |
| 136 | size_t len = 512; |
| 137 | char hw_model[512] = "unknown"; |
| 138 | |
| 139 | // Figure out if we're running on the Apple TV, past version 2.3 |
| 140 | Gestalt(gestaltSystemVersion, &macVersion); |
| 141 | |
| 142 | |
| 143 | if (macVersion < 0x1050) // OSX 10.4 / AppleTV |
| 144 | { |
| 145 | sysctlbyname("hw.model", &hw_model, &len, NULL, 0); |
| 146 | |
| 147 | if ( strstr(hw_model,"AppleTV1,1") ) |
| 148 | { |
| 149 | FILE* inpipe; |
| 150 | |
| 151 | //Find the build version of the AppleTV OS |
| 152 | inpipe = popen("sw_vers -buildVersion", "r"); |
| 153 | char linebuf[1000]; |
| 154 | if (inpipe) |
| 155 | { |
| 156 | if ( fgets(linebuf, sizeof(linebuf) - 1, inpipe) ) |
| 157 | { |
| 158 | if ( !( strstr(linebuf,"8N5107") || // v1.0 |
| 159 | strstr(linebuf,"8N5239") || // v1.1 |
| 160 | strstr(linebuf,"8N5400") || // v2.0 |
| 161 | strstr(linebuf,"8N5455") || // v2.01 |
| 162 | strstr(linebuf,"8N5461") || // v2.02 |
| 163 | strstr(linebuf,"8N5519") || // v2.1 |
| 164 | strstr(linebuf,"8N5622") )) // v2.2 |
| 165 | { |
| 166 | VERBOSE(VB_IMPORTANT, LOC + "::AppleRemote detected Apple TV > v2.3"); |
| 167 | usingNewAtv = true; |
| 168 | mCallbackTimer = new QTimer(this); |
| 169 | connect(mCallbackTimer, SIGNAL(timeout()), this, SLOT(TimeoutHandler())); |
| 170 | mCallbackTimer->setSingleShot(true); |
| 171 | mCallbackTimer->setInterval(KEY_RESPONSE_TIME); |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | pclose(inpipe); |
| 176 | } |
| 177 | } |
127 | | // 10.4 sequences: |
128 | | cookieToButtonMapping["14_12_11_6_5_"] = Up; |
129 | | cookieToButtonMapping["14_13_11_6_5_"] = Down; |
130 | | cookieToButtonMapping["14_7_6_5_14_7_6_5_"] = Menu; |
131 | | cookieToButtonMapping["14_8_6_5_14_8_6_5_"] = Select; |
132 | | cookieToButtonMapping["14_9_6_5_14_9_6_5_"] = Right; |
133 | | cookieToButtonMapping["14_10_6_5_14_10_6_5_"] = Left; |
134 | | cookieToButtonMapping["14_6_5_4_2_"] = RightHold; |
135 | | cookieToButtonMapping["14_6_5_3_2_"] = LeftHold; |
136 | | cookieToButtonMapping["14_6_5_14_6_5_"] = MenuHold; |
137 | | cookieToButtonMapping["18_14_6_5_18_14_6_5_"] = PlayHold; |
138 | | cookieToButtonMapping["19_"] = ControlSwitched; |
139 | | |
140 | | // 10.5 sequences: |
141 | | cookieToButtonMapping["31_29_28_18_"] = Up; |
142 | | cookieToButtonMapping["31_30_28_18_"] = Down; |
143 | | cookieToButtonMapping["31_20_18_31_20_18_"] = Menu; |
144 | | cookieToButtonMapping["31_21_18_31_21_18_"] = Select; |
145 | | cookieToButtonMapping["31_22_18_31_22_18_"] = Right; |
146 | | cookieToButtonMapping["31_23_18_31_23_18_"] = Left; |
147 | | cookieToButtonMapping["31_18_4_2_"] = RightHold; |
148 | | cookieToButtonMapping["31_18_3_2_"] = LeftHold; |
149 | | cookieToButtonMapping["31_18_31_18_"] = MenuHold; |
150 | | cookieToButtonMapping["35_31_18_35_31_18_"] = PlayHold; |
151 | | cookieToButtonMapping["39_"] = ControlSwitched; |
152 | | |
153 | | // ATV 1.0, 2.0-2.02 |
154 | | cookieToButtonMapping["14_12_11_6_"] = Up; |
155 | | cookieToButtonMapping["14_13_11_6_"] = Down; |
156 | | cookieToButtonMapping["14_7_6_14_7_6_"] = Menu; |
157 | | cookieToButtonMapping["14_8_6_14_8_6_"] = Select; |
158 | | cookieToButtonMapping["14_9_6_14_9_6_"] = Right; |
159 | | cookieToButtonMapping["14_10_6_14_10_6_"] = Left; |
160 | | cookieToButtonMapping["14_6_4_2_"] = RightHold; |
161 | | cookieToButtonMapping["14_6_3_2_"] = LeftHold; |
162 | | cookieToButtonMapping["14_6_14_6_"] = MenuHold; |
163 | | cookieToButtonMapping["18_14_6_18_14_6_"] = PlayHold; |
164 | | |
165 | | // ATV 1.0, 2.1-2.2 |
166 | | cookieToButtonMapping["15_13_12_"] = Up; |
167 | | cookieToButtonMapping["15_14_12_"] = Down; |
168 | | cookieToButtonMapping["15_8_15_8_"] = Menu; |
169 | | cookieToButtonMapping["15_9_15_9_"] = Select; |
170 | | cookieToButtonMapping["15_10_15_10_"] = Right; |
171 | | cookieToButtonMapping["15_11_15_11_"] = Left; |
172 | | cookieToButtonMapping["15_5_3_"] = RightHold; |
173 | | cookieToButtonMapping["15_4_3_"] = LeftHold; |
174 | | cookieToButtonMapping["15_6_15_6_"] = MenuHold; |
175 | | cookieToButtonMapping["19_15_19_15_"] = PlayHold; |
176 | | |
177 | | // ATV 2.30 |
178 | | cookieToButtonMapping["80"] = Up; |
179 | | cookieToButtonMapping["48"] = Down; |
180 | | cookieToButtonMapping["64"] = Menu; |
181 | | cookieToButtonMapping["32"] = Select; |
182 | | cookieToButtonMapping["96"] = Right; |
183 | | cookieToButtonMapping["16"] = Left; |
184 | | |
185 | | // 10.6 sequences: |
186 | | cookieToButtonMapping["33_31_30_21_20_2_"] = Up; |
187 | | cookieToButtonMapping["33_32_30_21_20_2_"] = Down; |
188 | | cookieToButtonMapping["33_22_21_20_2_33_22_21_20_2_"] = Menu; |
189 | | cookieToButtonMapping["33_23_21_20_2_33_23_21_20_2_"] = Select; |
190 | | cookieToButtonMapping["33_24_21_20_2_33_24_21_20_2_"] = Right; |
191 | | cookieToButtonMapping["33_25_21_20_2_33_25_21_20_2_"] = Left; |
192 | | cookieToButtonMapping["33_21_20_14_12_2_"] = RightHold; |
193 | | cookieToButtonMapping["33_21_20_13_12_2_"] = LeftHold; |
194 | | cookieToButtonMapping["33_21_20_2_33_21_20_2_"] = MenuHold; |
195 | | cookieToButtonMapping["37_33_21_20_2_37_33_21_20_2_"] = PlayHold; |
196 | | |
197 | | // Aluminium remote which has an extra button: |
198 | | cookieToButtonMapping["33_21_20_8_2_33_21_20_8_2_"] = PlayPause; |
199 | | cookieToButtonMapping["33_21_20_3_2_33_21_20_3_2_"] = Select; |
200 | | cookieToButtonMapping["33_21_20_11_2_33_21_20_11_2_"] = PlayHold; |
| 192 | if (!usingNewAtv) |
| 193 | { |
| 194 | // 10.4 sequences: |
| 195 | cookieToButtonMapping["14_12_11_6_5_"] = Up; |
| 196 | cookieToButtonMapping["14_13_11_6_5_"] = Down; |
| 197 | cookieToButtonMapping["14_7_6_5_14_7_6_5_"] = Menu; |
| 198 | cookieToButtonMapping["14_8_6_5_14_8_6_5_"] = Select; |
| 199 | cookieToButtonMapping["14_9_6_5_14_9_6_5_"] = Right; |
| 200 | cookieToButtonMapping["14_10_6_5_14_10_6_5_"] = Left; |
| 201 | cookieToButtonMapping["14_6_5_4_2_"] = RightHold; |
| 202 | cookieToButtonMapping["14_6_5_3_2_"] = LeftHold; |
| 203 | cookieToButtonMapping["14_6_5_14_6_5_"] = MenuHold; |
| 204 | cookieToButtonMapping["18_14_6_5_18_14_6_5_"] = PlayHold; |
| 205 | cookieToButtonMapping["19_"] = ControlSwitched; |
| 206 | |
| 207 | // 10.5 sequences: |
| 208 | cookieToButtonMapping["31_29_28_18_"] = Up; |
| 209 | cookieToButtonMapping["31_30_28_18_"] = Down; |
| 210 | cookieToButtonMapping["31_20_18_31_20_18_"] = Menu; |
| 211 | cookieToButtonMapping["31_21_18_31_21_18_"] = Select; |
| 212 | cookieToButtonMapping["31_22_18_31_22_18_"] = Right; |
| 213 | cookieToButtonMapping["31_23_18_31_23_18_"] = Left; |
| 214 | cookieToButtonMapping["31_18_4_2_"] = RightHold; |
| 215 | cookieToButtonMapping["31_18_3_2_"] = LeftHold; |
| 216 | cookieToButtonMapping["31_18_31_18_"] = MenuHold; |
| 217 | cookieToButtonMapping["35_31_18_35_31_18_"] = PlayHold; |
| 218 | cookieToButtonMapping["39_"] = ControlSwitched; |
| 219 | |
| 220 | // ATV 1.0, 2.0-2.02 |
| 221 | cookieToButtonMapping["14_12_11_6_"] = Up; |
| 222 | cookieToButtonMapping["14_13_11_6_"] = Down; |
| 223 | cookieToButtonMapping["14_7_6_14_7_6_"] = Menu; |
| 224 | cookieToButtonMapping["14_8_6_14_8_6_"] = Select; |
| 225 | cookieToButtonMapping["14_9_6_14_9_6_"] = Right; |
| 226 | cookieToButtonMapping["14_10_6_14_10_6_"] = Left; |
| 227 | cookieToButtonMapping["14_6_4_2_"] = RightHold; |
| 228 | cookieToButtonMapping["14_6_3_2_"] = LeftHold; |
| 229 | cookieToButtonMapping["14_6_14_6_"] = MenuHold; |
| 230 | cookieToButtonMapping["18_14_6_18_14_6_"] = PlayHold; |
| 231 | |
| 232 | // ATV 1.0, 2.1-2.2 |
| 233 | cookieToButtonMapping["15_13_12_"] = Up; |
| 234 | cookieToButtonMapping["15_14_12_"] = Down; |
| 235 | cookieToButtonMapping["15_8_15_8_"] = Menu; |
| 236 | cookieToButtonMapping["15_9_15_9_"] = Select; |
| 237 | cookieToButtonMapping["15_10_15_10_"] = Right; |
| 238 | cookieToButtonMapping["15_11_15_11_"] = Left; |
| 239 | cookieToButtonMapping["15_5_3_"] = RightHold; |
| 240 | cookieToButtonMapping["15_4_3_"] = LeftHold; |
| 241 | cookieToButtonMapping["15_6_15_6_"] = MenuHold; |
| 242 | cookieToButtonMapping["19_15_19_15_"] = PlayHold; |
| 243 | |
| 244 | // 10.6 sequences: |
| 245 | cookieToButtonMapping["33_31_30_21_20_2_"] = Up; |
| 246 | cookieToButtonMapping["33_32_30_21_20_2_"] = Down; |
| 247 | cookieToButtonMapping["33_22_21_20_2_33_22_21_20_2_"] = Menu; |
| 248 | cookieToButtonMapping["33_23_21_20_2_33_23_21_20_2_"] = Select; |
| 249 | cookieToButtonMapping["33_24_21_20_2_33_24_21_20_2_"] = Right; |
| 250 | cookieToButtonMapping["33_25_21_20_2_33_25_21_20_2_"] = Left; |
| 251 | cookieToButtonMapping["33_21_20_14_12_2_"] = RightHold; |
| 252 | cookieToButtonMapping["33_21_20_13_12_2_"] = LeftHold; |
| 253 | cookieToButtonMapping["33_21_20_2_33_21_20_2_"] = MenuHold; |
| 254 | cookieToButtonMapping["37_33_21_20_2_37_33_21_20_2_"] = PlayHold; |
| 255 | |
| 256 | // Aluminium remote which has an extra button: |
| 257 | cookieToButtonMapping["33_21_20_8_2_33_21_20_8_2_"] = PlayPause; |
| 258 | cookieToButtonMapping["33_21_20_3_2_33_21_20_3_2_"] = Select; |
| 259 | cookieToButtonMapping["33_21_20_11_2_33_21_20_11_2_"] = PlayHold; |
| 260 | } |
| 261 | else |
| 262 | { |
| 263 | // ATV 2.30 |
| 264 | cookieToButtonMapping["17_9_280_80"] = Up; |
| 265 | cookieToButtonMapping["17_9_280_48"] = Down; |
| 266 | cookieToButtonMapping["17_9_280_64"] = Menu; |
| 267 | cookieToButtonMapping["17_9_280_32"] = Select; |
| 268 | cookieToButtonMapping["17_9_280_96"] = Right; |
| 269 | cookieToButtonMapping["17_9_280_16"] = Left; |
| 270 | } |
405 | | if (ii != cookieToButtonMapping.end() && _listener) |
| 513 | |
| 514 | |
| 515 | if (ii != cookieToButtonMapping.end() ) |
| 516 | { |
| 517 | AppleRemote::Event event = ii->second; |
| 518 | |
| 519 | if (!usingNewAtv) { |
| 520 | if (_listener) |
| 521 | { |
| 522 | _listener->appleRemoteButton(event, sumOfValues>0); |
| 523 | } |
| 524 | } |
| 525 | else |
| 526 | { |
| 527 | // With the ATV from 2.3 onwards, we just get IR events. We need to simulate the |
| 528 | // key up and hold events |
| 529 | |
| 530 | if (mLastEvent == Undefined) // new event |
| 531 | { |
| 532 | mEventCount = 1; |
| 533 | // Need to figure out if this is a long press or a short press, |
| 534 | // so can't just send a key down event right now. It will be |
| 535 | // scheduled to run |
| 536 | } |
| 537 | else if (event != mLastEvent) // a new event, faster than timer |
| 538 | { |
| 539 | mEventCount = 1; |
| 540 | mKeyIsDown = true; |
| 541 | |
| 542 | if (_listener) |
| 543 | { |
| 544 | // Only send key up events for events that have separateRelease |
| 545 | // defined as true in AppleRemoteListener.cpp |
| 546 | if (mLastEvent == Up || mLastEvent == Down || |
| 547 | mLastEvent == LeftHold || mLastEvent == RightHold) |
| 548 | { |
| 549 | _listener->appleRemoteButton(mLastEvent, /*pressedDown*/false); |
| 550 | } |
| 551 | _listener->appleRemoteButton(event, mKeyIsDown); |
| 552 | } |
| 553 | } |
| 554 | else // Same event again |
| 555 | { |
| 556 | mEventCount+=1; |
| 557 | AppleRemote::Event newEvent = Undefined; |
| 558 | |
| 559 | // Can the event have a hold state? |
| 560 | switch (event) |
| 561 | { |
| 562 | case Right: |
| 563 | newEvent = RightHold; |
| 564 | break; |
| 565 | case Left: |
| 566 | newEvent = LeftHold; |
| 567 | break; |
| 568 | case Menu: |
| 569 | newEvent = MenuHold; |
| 570 | break; |
| 571 | case Select: |
| 572 | newEvent = PlayHold; |
| 573 | break; |
| 574 | default: |
| 575 | newEvent = event; |
| 576 | } |
| 577 | |
| 578 | if (newEvent == event) // Doesn't have a long press |
| 579 | { |
| 580 | if (mKeyIsDown) |
| 581 | { |
| 582 | if (_listener) |
| 583 | { |
| 584 | // Only send key up events for events that have separateRelease |
| 585 | // defined as true in AppleRemoteListener.cpp |
| 586 | if (mLastEvent == Up || mLastEvent == Down || |
| 587 | mLastEvent == LeftHold || mLastEvent == RightHold) |
| 588 | { |
| 589 | _listener->appleRemoteButton(mLastEvent, /*pressedDown*/false); |
| 590 | } |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | mKeyIsDown = true; |
| 595 | if (_listener) |
| 596 | { |
| 597 | _listener->appleRemoteButton(newEvent, mKeyIsDown); |
| 598 | } |
| 599 | } |
| 600 | else if (mEventCount == LONG_PRESS_COUNT) |
| 601 | { |
| 602 | mKeyIsDown = true; |
| 603 | if (_listener) |
| 604 | { |
| 605 | _listener->appleRemoteButton(newEvent, mKeyIsDown); |
| 606 | } |
| 607 | } |
| 608 | } |
| 609 | |
| 610 | mLastEvent = event; |
| 611 | mCallbackTimer->start(); |
| 612 | } |
| 613 | } |
| 614 | } |
| 615 | |
| 616 | // Calls key down / up events on the ATV > v2.3 |
| 617 | void AppleRemote::TimeoutHandler() |
| 618 | { |
| 619 | if (_listener) |