2 * Copyright (c) 2010, AaYJFG - XBMC community
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the XBMC community nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL AaYJFG BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/types.h>
34 #include <sys/ioctl.h>
38 #include <libusb-1.0/libusb.h>
40 #include "xbmcclient.h"
42 #define msg(x...) do { fprintf(stdout, x); } while (0)
43 #define err(x...) do { fprintf(stderr, x); } while (0)
49 err("*** POSIX ERROR *** in %s:%d [%s]: ", __FILE__, __LINE__, #x); \
59 err("*** USB ERROR *** in %s:%d [%s]: %d\n", __FILE__, __LINE__, #x, __err); \
63 static CXBMCClient xbmc
;
64 static int disconnected
= 0;
67 static void xbmc_action (const char *action
)
69 msg("send action '%s'\n", action
);
70 xbmc
.SendACTION(action
);
73 static void xbmc_action_button (const char *btn
)
75 msg("send action '%s' (button)\n", btn
);
76 xbmc
.SendACTION(btn
, ACTION_BUTTON
);
79 static void xbmc_key (const char *key
)
81 msg("send key '%s'\n", key
);
82 xbmc
.SendButton(key
, "R1", BTN_NO_REPEAT
);
85 static void xbmc_key_kb (const char *key
)
87 msg("send key '%s'\n", key
);
88 xbmc
.SendButton(key
, "KB", BTN_NO_REPEAT
);
91 static void emit_left_click (void)
93 msg("send left click\n");
94 xbmc_action_button("select");
97 static void emit_right_click (void)
99 msg("send right click\n");
100 xbmc_action_button("contextmenu");
103 static void emit_mouse (int dx
, int dy
)
105 if (dx
!= 0 || dy
!= 0) {
106 const int maxcoord
= 65535;
107 static int x
= maxcoord
/ 2;
108 static int y
= maxcoord
/ 2;
119 msg("mouse dx %d, dy %d -- x %d y %d\n", dx
, dy
, x
, y
);
120 xbmc
.SendMOUSE(x
, y
);
124 static void transfer0x81_cb (struct libusb_transfer
*transfer
)
126 static const struct {
130 void (*key_fn
) (const char *key
);
132 } translation_table
[] = {
133 { 0x03, 0x17, false, xbmc_key
, "mytv" }, /* (yellow) TV */
134 { 0x01, 0x10, false, xbmc_key
, "mymusic" }, /* (blue) Music */
135 { 0x01, 0x0c, false, xbmc_key
, "mypictures" }, /* (green) Pictures */
136 { 0x01, 0x08, false, xbmc_key
, "myvideo" }, /* (red) Videos */
137 { 0x01, 0x12, false, xbmc_key
, "recordedtv" },
138 { 0x01, 0x0a, false, xbmc_key
, "guide" },
139 { 0x01, 0x17, false, xbmc_key
, "livetv" },
140 { 0x03, 0x10, false, xbmc_key
, "menu" },
141 { 0x03, 0x05, false, xbmc_key
, "reverse" },
142 { 0x03, 0x09, false, xbmc_key
, "forward" },
143 { 0x01, 0x15, false, xbmc_key
, "record" },
144 { 0x00, 0x2a, false, xbmc_key
, "back" },
145 { 0x00, 0x28, false, xbmc_key
, "select" },
146 { 0x00, 0x4f, true, xbmc_key
, "right" },
147 { 0x00, 0x50, true, xbmc_key
, "left" },
148 { 0x00, 0x51, true, xbmc_key
, "down" },
149 { 0x00, 0x52, true, xbmc_key
, "up" },
150 { 0x00, 0x4b, true, xbmc_key
, "pageplus" },
151 { 0x00, 0x4e, true, xbmc_key
, "pageminus" },
152 { 0x0c, 0x28, false, xbmc_key
, "start" },
153 { 0x00, 0x59, false, xbmc_key
, "one" },
154 { 0x00, 0x5a, false, xbmc_key
, "two" },
155 { 0x00, 0x5b, false, xbmc_key
, "three" },
156 { 0x00, 0x5c, false, xbmc_key
, "four" },
157 { 0x00, 0x5d, false, xbmc_key
, "five" },
158 { 0x00, 0x5e, false, xbmc_key
, "six" },
159 { 0x00, 0x5f, false, xbmc_key
, "seven" },
160 { 0x00, 0x60, false, xbmc_key
, "eight" },
161 { 0x00, 0x61, false, xbmc_key
, "nine" },
162 { 0x00, 0x62, false, xbmc_key
, "zero" },
163 { 0x00, 0x55, false, xbmc_key
, "star" },
164 { 0x04, 0x5d, false, xbmc_key
, "hash" },
165 { 0x00, 0x29, false, xbmc_key
, "clear" },
166 { 0x04, 0x3d, false, xbmc_key
, "subtitle" }, /* close button */
167 { 0x04, 0x5b, false, NULL
, NULL
}, /* discard - hash */
168 { 0x00, 0x53, false, NULL
, NULL
}, /* discard - key down */
169 { 0x0c, 0x00, false, NULL
, NULL
}, /* discard - mypictures*/
170 { 0x08, 0x00, false, NULL
, NULL
}, /* discard - start*/
171 { 0x04, 0x00, false, NULL
, NULL
}, /* discard - hash, close*/
172 { 0x03, 0x00, false, NULL
, NULL
}, /* discard - mytv, menu, rewind, fastforward*/
173 { 0x01, 0x00, false, NULL
, NULL
}, /* discard - key down */
175 static void (*repeat_fn
) (const char *key
) = NULL
;
176 static const char *repeat_key
= NULL
;
177 if (transfer
->status
== LIBUSB_TRANSFER_NO_DEVICE
) {
181 if (transfer
->status
== LIBUSB_TRANSFER_TIMED_OUT
) {
182 if (repeat_fn
!= NULL
&& repeat_key
!= NULL
)
183 repeat_fn(repeat_key
);
186 if (transfer
->actual_length
== 8) {
187 char m
= transfer
->buffer
[0];
188 char c
= transfer
->buffer
[2];
189 if (m
== 0x00 && c
== 0x00) {
194 for (int i
= 0; i
< sizeof(translation_table
)/sizeof(translation_table
[0]); i
++) {
195 if (translation_table
[i
].modifier
== m
&& translation_table
[i
].code
== c
) {
196 if (translation_table
[i
].key_fn
!= NULL
)
197 translation_table
[i
].key_fn(translation_table
[i
].key
);
198 if (translation_table
[i
].can_repeat
) {
199 repeat_fn
= translation_table
[i
].key_fn
;
200 repeat_key
= translation_table
[i
].key
;
206 if (transfer
->actual_length
> 0) {
207 msg("*** unknown keycode: ");
208 for (int i
= 0; i
< transfer
->actual_length
; i
++)
209 msg("%02x ", transfer
->buffer
[i
] & 0xff);
213 UCHK(libusb_submit_transfer(transfer
));
216 static void transfer0x82_cb (struct libusb_transfer
*transfer
)
218 static const struct {
220 void (*key_fn
) (const char *key
);
222 } translation_table
[] = {
223 { { 0x03, 0x02, 0x55, 0x55, 0x55 }, xbmc_key
, "power" },
224 { { 0x02, 0x02, 0x00, 0x00, 0x55 }, xbmc_key
, "title" },
225 { { 0x02, 0x80, 0x00, 0x00, 0x55 }, xbmc_key
, "skipminus" },
226 { { 0x02, 0x00, 0x02, 0x00, 0x55 }, xbmc_key
, "skipplus" },
227 { { 0x02, 0x00, 0x00, 0x01, 0x55 }, xbmc_key
, "stop" },
228 { { 0x02, 0x00, 0x00, 0x02, 0x55 }, xbmc_key
, "pause" },
229 { { 0x02, 0x00, 0x01, 0x00, 0x55 }, xbmc_key
, "mute" },
230 { { 0x02, 0x10, 0x00, 0x00, 0x55 }, xbmc_key
, "volumeplus" },
231 { { 0x02, 0x00, 0x10, 0x00, 0x55 }, xbmc_key
, "volumeminus" },
233 static void (*repeat_fn
) (const char *key
) = NULL
;
234 static const char *repeat_key
= NULL
;
235 static int rclick_pending
= 0;
236 if (transfer
->status
== LIBUSB_TRANSFER_NO_DEVICE
) {
240 if (transfer
->status
== LIBUSB_TRANSFER_TIMED_OUT
) {
241 if (rclick_pending
== 1) {
245 if (repeat_fn
!= NULL
&& repeat_key
!= NULL
)
246 repeat_fn(repeat_key
);
249 if (transfer
->actual_length
== 5) {
250 if (transfer
->buffer
[0] == 0x01) {
251 if (transfer
->buffer
[1] == 0x00) {
252 char dx
= transfer
->buffer
[2];
253 char dy
= transfer
->buffer
[3];
254 if (dx
!= 0 || dy
!= 0)
256 else if (rclick_pending
> 0) {
257 if (--rclick_pending
== 0)
260 } else if (transfer
->buffer
[1] == 0x01)
262 else if (transfer
->buffer
[1] == 0x02)
270 const char keycode_up
[5] = { 0x02, 0x00, 0x00, 0x00, 0x55 };
271 const char keycode_up_power
[5] = { 0x03, 0x00, 0x55, 0x55, 0x55 };
272 if (memcmp(transfer
->buffer
, keycode_up
, 5) == 0 ||
273 memcmp(transfer
->buffer
, keycode_up_power
, 5) == 0) {
278 for (int i
= 0; i
< sizeof(translation_table
)/sizeof(translation_table
[0]); i
++) {
279 if (memcmp(translation_table
[i
].code
, transfer
->buffer
, 5) == 0) {
280 if (translation_table
[i
].key_fn
!= NULL
)
281 translation_table
[i
].key_fn(translation_table
[i
].key
);
282 repeat_fn
= translation_table
[i
].key_fn
;
283 repeat_key
= translation_table
[i
].key
;
287 msg("*** unknown keycode: ");
288 for (int i
= 0; i
< transfer
->actual_length
; i
++)
289 msg("%02x ", transfer
->buffer
[i
] & 0xff);
293 UCHK(libusb_submit_transfer(transfer
));
296 static void handle_exit(int sig
)
301 int main (int argc
, char **argv
)
303 if (argc
> 1 && strcmp(argv
[1],"--fork") == 0) {
305 if (pID
< 0) { // failed to fork
306 err("Failed to fork\n");
309 else if (pID
> 0) { // parent
316 sigemptyset(&sa
.sa_mask
);
317 sa
.sa_handler
= handle_exit
;
318 PCHK(sigaction(SIGINT
, &sa
, NULL
));
321 libusb_device_handle
*dev
;
322 struct libusb_transfer
*transfer0x81
= libusb_alloc_transfer(0);
323 struct libusb_transfer
*transfer0x82
= libusb_alloc_transfer(0);
324 unsigned char buf0x81
[8];
325 unsigned char buf0x82
[5];
328 UCHK(libusb_init(&ctx
));
330 if (!(dev
= libusb_open_device_with_vid_pid(ctx
, 0x05a4, 0x9881))) {
331 err("%s: No HAMA MCE remote control found.\n", argv
[0]);
335 msg("Connected HAMA MCE Remote\n");
336 xbmc
.SendHELO("HAMA MCE Remote", ICON_NONE
);
338 if (libusb_kernel_driver_active(dev
, 0))
339 UCHK(libusb_detach_kernel_driver(dev
, 0));
340 if (libusb_kernel_driver_active(dev
, 1))
341 UCHK(libusb_detach_kernel_driver(dev
, 1));
342 UCHK(libusb_claim_interface(dev
, 0));
343 UCHK(libusb_claim_interface(dev
, 1));
345 libusb_fill_interrupt_transfer(transfer0x81
, dev
, 0x81, buf0x81
, sizeof(buf0x81
), transfer0x81_cb
, NULL
, 215);
346 UCHK(libusb_submit_transfer(transfer0x81
));
348 libusb_fill_interrupt_transfer(transfer0x82
, dev
, 0x82, buf0x82
, sizeof(buf0x82
), transfer0x82_cb
, NULL
, 200);
349 UCHK(libusb_submit_transfer(transfer0x82
));
351 while (!disconnected
)
352 UCHK(libusb_handle_events(ctx
));
354 msg("Disconnected HAMA MCE Remote\n");
355 xbmc
.SendNOTIFICATION("Disconnected", "HAMA MCE Remote", ICON_NONE
);
356 // xbmc.SendBYE(); /* Not Implemented Yet */
358 libusb_free_transfer(transfer0x81
);
359 libusb_free_transfer(transfer0x82
);
361 /* not needed, if we arrive here, the device has been disconnected.
362 UCHK(libusb_release_interface(dev, 0));
363 UCHK(libusb_release_interface(dev, 1));
364 UCHK(libusb_attach_kernel_driver(dev, 0));
365 UCHK(libusb_attach_kernel_driver(dev, 1));