Fix a typo and remap the close button (bottom left on the remote) to subtitle because...
[hama_mce-eventclient.git] / hama_mce.cpp
blob5b2997f2a393447d1cb41b79fafed387bf45ddad
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 <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)
45 #define PCHK(x...) \
46 do { \
47 int __err = (x); \
48 if (__err == -1) { \
49 err("*** POSIX ERROR *** in %s:%d [%s]: ", __FILE__, __LINE__, #x); \
50 perror(""); \
51 abort(); \
52 } \
53 } while (0)
55 #define UCHK(x...) \
56 do { \
57 int __err = (x); \
58 if (__err < 0) \
59 err("*** USB ERROR *** in %s:%d [%s]: %d\n", __FILE__, __LINE__, #x, __err); \
60 } while (0)
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;
109 x += dx * 64;
110 y += dy * 64;
111 if (x < 0)
112 x = 0;
113 if (y < 0)
114 y = 0;
115 if (x > maxcoord)
116 x = maxcoord;
117 if (y > maxcoord)
118 y = maxcoord;
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 {
127 char modifier;
128 char code;
129 bool can_repeat;
130 void (*key_fn) (const char *key);
131 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) {
178 disconnected = 1;
179 return;
181 if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
182 if (repeat_fn != NULL && repeat_key != NULL)
183 repeat_fn(repeat_key);
184 goto done;
186 if (transfer->actual_length == 8) {
187 char m = transfer->buffer[0];
188 char c = transfer->buffer[2];
189 if (m == 0x00 && c == 0x00) {
190 repeat_fn = NULL;
191 repeat_key = NULL;
192 goto done;
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;
202 goto done;
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);
210 msg("\n");
212 done:
213 UCHK(libusb_submit_transfer(transfer));
216 static void transfer0x82_cb (struct libusb_transfer *transfer)
218 static const struct {
219 char code [5];
220 void (*key_fn) (const char *key);
221 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) {
237 disconnected = 1;
238 return;
240 if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
241 if (rclick_pending == 1) {
242 rclick_pending = 0;
243 xbmc_key("info");
245 if (repeat_fn != NULL && repeat_key != NULL)
246 repeat_fn(repeat_key);
247 goto done;
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)
255 emit_mouse(dx, dy);
256 else if (rclick_pending > 0) {
257 if (--rclick_pending == 0)
258 emit_right_click();
260 } else if (transfer->buffer[1] == 0x01)
261 emit_left_click();
262 else if (transfer->buffer[1] == 0x02)
263 rclick_pending = 2;
265 repeat_fn = NULL;
266 repeat_key = NULL;
267 goto done;
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) {
274 repeat_fn = NULL;
275 repeat_key = NULL;
276 goto done;
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;
284 goto done;
287 msg("*** unknown keycode: ");
288 for (int i = 0; i < transfer->actual_length; i++)
289 msg("%02x ", transfer->buffer[i] & 0xff);
290 msg("\n");
292 done:
293 UCHK(libusb_submit_transfer(transfer));
296 static void handle_exit(int sig)
298 disconnected = 1;
301 int main (int argc, char **argv)
303 if (argc > 1 && strcmp(argv[1],"--fork") == 0) {
304 pid_t pID = fork();
305 if (pID < 0) { // failed to fork
306 err("Failed to fork\n");
307 exit(1);
309 else if (pID > 0) { // parent
310 msg("FORKED!");
311 exit(0);
315 struct sigaction sa;
316 sigemptyset(&sa.sa_mask);
317 sa.sa_handler = handle_exit;
318 PCHK(sigaction(SIGINT, &sa, NULL));
320 libusb_context *ctx;
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];
326 int i;
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]);
332 return -1;
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));
367 libusb_close(dev);
368 libusb_exit(ctx);
370 return 0;