Release v1.4
[hama_mce-eventclient.git] / hama_mce.cpp
blobba09b94261a7f908f49be384aa99bf903f8bc003
1 /*
2 * Copyright (c) 2010, AaYJFG - XBMC community
3 * All rights reserved.
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.
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/ioctl.h>
35 #include <sys/time.h>
36 #include <fcntl.h>
37 #include <signal.h>
38 #include <getopt.h>
39 #include <libusb-1.0/libusb.h>
41 #include "xbmcclient.h"
43 #define VERSION "1.4"
45 #define msg(x...) do { fprintf(stdout, x); } while (0)
46 #define err(x...) do { fprintf(stderr, x); } while (0)
48 #define PCHK(x...) \
49 do { \
50 int __err = (x); \
51 if (__err == -1) { \
52 err("*** POSIX ERROR *** in %s:%d [%s]: ", __FILE__, __LINE__, #x); \
53 perror(""); \
54 abort(); \
55 } \
56 } while (0)
58 #define UCHK(x...) \
59 do { \
60 int __err = (x); \
61 if (__err < 0) \
62 err("*** USB ERROR *** in %s:%d [%s]: %d\n", __FILE__, __LINE__, #x, __err); \
63 } while (0)
66 static CXBMCClient xbmc;
67 static bool disconnected = false;
68 static bool quit = false;
71 static void print_ir_code(const char *prefix, struct libusb_transfer *transfer)
73 msg("** %s : ", prefix);
74 for (int i = 0; i < transfer->actual_length; i++)
75 msg("%02x ", transfer->buffer[i] & 0xff);
76 msg("\n");
80 static void xbmc_action (const char *action)
82 msg("## send action '%s'\n", action);
83 xbmc.SendACTION(action);
86 static void xbmc_action_button (const char *btn)
88 msg("## send action '%s' (button)\n", btn);
89 xbmc.SendACTION(btn, ACTION_BUTTON);
92 static void xbmc_key (const char *key)
94 msg("## send key (R1) '%s'\n", key);
95 xbmc.SendButton(key, "R1", BTN_NO_REPEAT);
98 static void xbmc_keyr(const char *key)
100 msg("## send repeat key (R1) '%s'\n", key);
101 xbmc.SendButton(key, "R1", BTN_DOWN);
104 static void xbmc_key_kb (const char *key)
106 msg("## send key (KB) '%s'\n", key);
107 xbmc.SendButton(key, "KB", BTN_NO_REPEAT);
110 static void xbmc_keyr_kb(const char *key)
112 msg("## send repeat key (KB) '%s'\n", key);
113 xbmc.SendButton(key, "KB", BTN_DOWN);
116 static void xbmc_release_button()
118 msg("## send key released\n");
119 xbmc.SendButton(0x01, BTN_UP);
122 static void emit_left_click (void)
124 msg("## send left click\n");
125 xbmc_action_button("select");
128 static void emit_right_click (void)
130 msg("## send right click\n");
131 xbmc_action_button("contextmenu");
134 static void emit_info(void)
136 msg("## send info click\n");
137 xbmc_action_button("info");
140 static void emit_mouse (int dx, int dy)
142 if (dx != 0 || dy != 0) {
143 const int maxcoord = 65535;
144 static int x = maxcoord / 2;
145 static int y = maxcoord / 2;
146 x += dx * 64;
147 y += dy * 64;
148 if (x < 0)
149 x = 0;
150 if (y < 0)
151 y = 0;
152 if (x > maxcoord)
153 x = maxcoord;
154 if (y > maxcoord)
155 y = maxcoord;
156 msg("## mouse dx %d, dy %d -- x %d y %d\n", dx, dy, x, y);
157 xbmc.SendMOUSE(x, y);
161 static void transfer0x81_cb (struct libusb_transfer *transfer)
163 static const struct {
164 char modifier;
165 char code;
166 bool can_repeat;
167 void (*key_fn) (const char *key);
168 const char *key;
169 } translation_table [] = {
170 { 0x03, 0x17, false, xbmc_key, "mytv" }, /* (yellow) TV */
171 { 0x01, 0x10, false, xbmc_key, "mymusic" }, /* (blue) Music */
172 { 0x01, 0x0c, false, xbmc_key, "mypictures" }, /* (green) Pictures */
173 { 0x01, 0x08, false, xbmc_key, "myvideo" }, /* (red) Videos */
174 { 0x01, 0x12, false, xbmc_key, "recordedtv" },
175 { 0x01, 0x0a, false, xbmc_key, "guide" },
176 { 0x01, 0x17, false, xbmc_key, "livetv" },
177 { 0x03, 0x10, false, xbmc_key, "menu" },
178 { 0x03, 0x05, false, xbmc_key, "reverse" },
179 { 0x03, 0x09, false, xbmc_key, "forward" },
180 { 0x01, 0x15, false, xbmc_key, "record" },
181 { 0x00, 0x2a, false, xbmc_key, "back" },
182 { 0x00, 0x28, false, xbmc_key, "select" },
183 { 0x00, 0x4f, true, xbmc_keyr, "right" },
184 { 0x00, 0x50, true, xbmc_keyr, "left" },
185 { 0x00, 0x51, true, xbmc_keyr, "down" },
186 { 0x00, 0x52, true, xbmc_keyr, "up" },
187 { 0x00, 0x4b, true, xbmc_keyr, "pageplus" },
188 { 0x00, 0x4e, true, xbmc_keyr, "pageminus" },
189 { 0x0c, 0x28, false, xbmc_key, "start" },
190 { 0x00, 0x59, false, xbmc_key, "one" },
191 { 0x00, 0x5a, false, xbmc_key, "two" },
192 { 0x00, 0x5b, false, xbmc_key, "three" },
193 { 0x00, 0x5c, false, xbmc_key, "four" },
194 { 0x00, 0x5d, false, xbmc_key, "five" },
195 { 0x00, 0x5e, false, xbmc_key, "six" },
196 { 0x00, 0x5f, false, xbmc_key, "seven" },
197 { 0x00, 0x60, false, xbmc_key, "eight" },
198 { 0x00, 0x61, false, xbmc_key, "nine" },
199 { 0x00, 0x62, false, xbmc_key, "zero" },
200 { 0x00, 0x55, false, xbmc_key, "star" },
201 { 0x04, 0x5d, false, xbmc_key, "hash" },
202 { 0x00, 0x29, false, xbmc_key, "clear" },
203 { 0x04, 0x3d, false, xbmc_key, "subtitle" }, /* close button */
204 { 0x04, 0x5b, false, NULL, "Alt+3 [discard]" }, /* discard - hash */
205 { 0x00, 0x53, false, NULL, "NumLock [discard]" }, /* discard - key down */
206 { 0x0c, 0x00, false, NULL, "start [discard]" }, /* discard - mypictures*/
207 { 0x08, 0x00, false, NULL, "start [discard]" }, /* discard - start*/
208 { 0x04, 0x00, false, NULL, "Alt [discard]" }, /* discard - hash, close*/
209 { 0x03, 0x00, false, NULL, "Shift [discard]" }, /* discard - mytv, menu, rewind, fastforward*/
210 { 0x01, 0x00, false, NULL, "Ctrl [discard]" }, /* discard - key down */
212 static bool repeating = false;
213 if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
214 disconnected = true;
215 return;
217 if (transfer->actual_length == 8) {
218 char m = transfer->buffer[0];
219 char c = transfer->buffer[2];
220 if (m == 0x00 && c == 0x00) {
221 if (repeating)
222 xbmc_release_button();
223 goto done;
225 for (unsigned int i = 0; i < sizeof(translation_table)/sizeof(translation_table[0]); i++) {
226 if (translation_table[i].modifier == m && translation_table[i].code == c) {
227 print_ir_code(translation_table[i].key, transfer);
228 if (translation_table[i].key_fn != NULL) {
229 translation_table[i].key_fn(translation_table[i].key);
230 repeating = translation_table[i].can_repeat;
232 goto done;
236 if (transfer->actual_length > 0) {
237 msg("*** unknown keycode: ");
238 for (int i = 0; i < transfer->actual_length; i++)
239 msg("%02x ", transfer->buffer[i] & 0xff);
240 msg("\n");
242 done:
243 UCHK(libusb_submit_transfer(transfer));
246 static void transfer0x82_cb (struct libusb_transfer *transfer)
248 static const struct {
249 char code [5];
250 bool can_repeat;
251 void (*key_fn) (const char *key);
252 const char *key;
253 } translation_table [] = {
254 { { 0x03, 0x02, 0x55, 0x55, 0x55 }, false, xbmc_key, "power" },
255 { { 0x02, 0x02, 0x00, 0x00, 0x55 }, false, xbmc_key, "display" },
256 { { 0x02, 0x80, 0x00, 0x00, 0x55 }, false, xbmc_key, "skipminus" },
257 { { 0x02, 0x00, 0x02, 0x00, 0x55 }, false, xbmc_key, "skipplus" },
258 { { 0x02, 0x00, 0x00, 0x01, 0x55 }, false, xbmc_key, "stop" },
259 { { 0x02, 0x00, 0x00, 0x02, 0x55 }, false, xbmc_key, "pause" },
260 { { 0x02, 0x00, 0x01, 0x00, 0x55 }, false, xbmc_key, "mute" },
261 { { 0x02, 0x10, 0x00, 0x00, 0x55 }, true, xbmc_keyr, "volumeplus" },
262 { { 0x02, 0x00, 0x10, 0x00, 0x55 }, true, xbmc_keyr, "volumeminus" },
264 static bool repeating = false;
265 static int rclick_pending = 0;
266 if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
267 disconnected = true;
268 return;
270 if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
271 if (rclick_pending == 1) {
272 print_ir_code("info", transfer);
273 rclick_pending = 0;
274 emit_info();
276 goto done;
278 if (transfer->actual_length == 5) {
279 const char keycode_up [5] = { 0x02, 0x00, 0x00, 0x00, 0x55 };
280 const char keycode_up_power [5] = { 0x03, 0x00, 0x55, 0x55, 0x55 };
281 const char keycode_up_mouse [5] = { 0x01, 0x00, 0x00, 0x00, 0x00 };
282 if (memcmp(transfer->buffer, keycode_up, 5) == 0 ||
283 memcmp(transfer->buffer, keycode_up_power, 5) == 0 ||
284 memcmp(transfer->buffer, keycode_up_mouse, 5) == 0) {
285 if (repeating)
286 xbmc_release_button();
287 if (rclick_pending > 0 && --rclick_pending == 0) {
288 print_ir_code("right click", transfer);
289 emit_right_click();
291 goto done;
293 if (transfer->buffer[0] == 0x01) {
294 if (transfer->buffer[1] == 0x00) {
295 char dx = transfer->buffer[2];
296 char dy = transfer->buffer[3];
297 if (dx != 0 || dy != 0) {
298 print_ir_code("mouse", transfer);
299 emit_mouse(dx, dy);
302 else if (transfer->buffer[1] == 0x01) {
303 print_ir_code("left click", transfer);
304 emit_left_click();
306 else if (transfer->buffer[1] == 0x02) {
307 print_ir_code("catching right click/info", transfer);
308 rclick_pending = 2;
311 goto done;
314 for (unsigned int i = 0; i < sizeof(translation_table)/sizeof(translation_table[0]); i++) {
315 if (memcmp(translation_table[i].code, transfer->buffer, 5) == 0) {
316 print_ir_code(translation_table[i].key, transfer);
317 if (translation_table[i].key_fn != NULL) {
318 translation_table[i].key_fn(translation_table[i].key);
319 repeating = translation_table[i].can_repeat;
321 goto done;
324 msg("*** unknown keycode: ");
325 for (int i = 0; i < transfer->actual_length; i++)
326 msg("%02x ", transfer->buffer[i] & 0xff);
327 msg("\n");
329 done:
330 UCHK(libusb_submit_transfer(transfer));
333 static void handle_exit(int sig)
335 quit = true;
338 void print_usage(char* progname)
340 msg("HAMA MCE remote event client for XBMC\n");
341 msg("\tUsage: %s [options]\n", progname);
342 msg("\t\t -h --help\t\tdisplay usage summary\n");
343 msg("\t\t -v --version\t\tdisplay version\n");
344 msg("\t\t -d --daemon\t\trun in background\n");
347 int main (int argc, char **argv)
349 bool daemonize = false;
351 while (true) {
352 int c;
353 static struct option long_options[] =
355 {"help", no_argument, NULL, 'h'},
356 {"version", no_argument, NULL, 'v'},
357 {"daemon", no_argument, NULL, 'd'},
358 {"fork", no_argument, NULL, 'f'},
359 {0, 0, 0, 0}
362 c = getopt_long(argc, argv, "hvdf", long_options, NULL);
363 if (c == -1)
364 break;
365 switch (c) {
366 case 'h':
367 print_usage(argv[0]);
368 exit(EXIT_SUCCESS);
369 case 'v':
370 msg("HAMA MCE remote event client v%s for XBMC\n", VERSION);
371 exit(EXIT_SUCCESS);
372 case 'd':
373 daemonize = true;
374 break;
375 case 'f':
376 err("%s: The --fork argument is deprectiated, change it to -d or --daemon\n", argv[0]);
377 daemonize = true;
378 break;
379 default:
380 print_usage(argv[0]);
381 exit(EXIT_FAILURE);
385 if (optind < (argc - 1)) {
386 err("%s: too many arguments\n", argv[0]);
387 exit(EXIT_FAILURE);
390 struct sigaction sa;
391 sigemptyset(&sa.sa_mask);
392 sa.sa_handler = handle_exit;
393 PCHK(sigaction(SIGINT, &sa, NULL));
394 PCHK(sigaction(SIGTERM, &sa, NULL));
396 libusb_context *ctx;
397 libusb_device_handle *dev;
398 struct libusb_transfer *transfer0x81 = libusb_alloc_transfer(0);
399 struct libusb_transfer *transfer0x82 = libusb_alloc_transfer(0);
400 unsigned char buf0x81 [8];
401 unsigned char buf0x82 [5];
403 UCHK(libusb_init(&ctx));
405 if (!(dev = libusb_open_device_with_vid_pid(ctx, 0x05a4, 0x9881))) {
406 err("%s: No HAMA MCE remote control found.\n", argv[0]);
407 exit(EXIT_FAILURE);
410 int exit_code = EXIT_SUCCESS;
412 if (libusb_kernel_driver_active(dev, 0))
413 UCHK(libusb_detach_kernel_driver(dev, 0));
414 if (libusb_kernel_driver_active(dev, 1))
415 UCHK(libusb_detach_kernel_driver(dev, 1));
416 UCHK(libusb_claim_interface(dev, 0));
417 UCHK(libusb_claim_interface(dev, 1));
419 libusb_fill_interrupt_transfer(transfer0x81, dev, 0x81, buf0x81, sizeof(buf0x81), transfer0x81_cb, NULL, 215);
420 UCHK(libusb_submit_transfer(transfer0x81));
422 libusb_fill_interrupt_transfer(transfer0x82, dev, 0x82, buf0x82, sizeof(buf0x82), transfer0x82_cb, NULL, 200);
423 UCHK(libusb_submit_transfer(transfer0x82));
425 msg("Connected HAMA MCE Remote\n");
426 xbmc.SendHELO("HAMA MCE Remote", ICON_NONE);
428 if (daemonize) {
429 if (daemon(0,0) == -1) {
430 err("Failed to fork\n");
431 perror(argv[0]);
432 exit_code = EXIT_FAILURE;
433 goto exit;
437 while (!(disconnected || quit)) {
438 UCHK(libusb_handle_events(ctx));
440 exit:
441 if (disconnected) {
442 msg("Disconnected HAMA MCE Remote\n");
443 xbmc.SendNOTIFICATION("Disconnected", "HAMA MCE Remote", ICON_NONE);
445 else {
446 msg("Closing HAMA MCE Remote\n");
447 xbmc.SendNOTIFICATION("Closing", "HAMA MCE Remote", ICON_NONE);
449 xbmc.SendBYE();
451 libusb_free_transfer(transfer0x81);
452 libusb_free_transfer(transfer0x82);
454 if (!disconnected) {
455 // Release the remote back to the system
456 UCHK(libusb_release_interface(dev, 0));
457 UCHK(libusb_release_interface(dev, 1));
458 UCHK(libusb_attach_kernel_driver(dev, 0));
459 UCHK(libusb_attach_kernel_driver(dev, 1));
462 libusb_close(dev);
463 libusb_exit(ctx);
465 exit(exit_code);
468 /* vim:set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab: */