aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorChase Douglas <chase.douglas@canonical.com>2010-08-31 21:56:23 -0400
committerJiri Kosina <jkosina@suse.cz>2010-09-03 12:20:47 -0400
commita462230e16acc8664145216da3c928d03556691a (patch)
treec6c4464789f324d8bee320bcacf253fdcf51f166 /drivers
parent6de048bf1dd2ad35fe9b2326bf9d6d23fb2fff7a (diff)
HID: magicmouse: enable Magic Trackpad support
The trackpad speaks a similar, but different, protocol from the magic mouse. However, only small code tweaks here and there are needed to make basic multitouch work. Extra logic is required for single-touch emulation of the touchpad. The changes made here take the approach that only one finger may emulate the single pointer when multiple fingers have touched the screen. Once that finger is raised, all touches must be raised before any further single touch events can be sent. Sometimes the magic trackpad sends two distinct touch reports as one big report. Simply splitting the packet in two and resending them through magicmouse_raw_event ensures they are handled properly. I also added myself to the copyright statement. Signed-off-by: Chase Douglas <chase.douglas@canonical.com> Acked-by: Michael Poole <mdpoole@troilus.org> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/hid-magicmouse.c192
3 files changed, 155 insertions, 39 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 0c52899be964..d2f9c4aa4b52 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1248,6 +1248,7 @@ static const struct hid_device_id hid_blacklist[] = {
1248 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, 1248 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
1249 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, 1249 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
1250 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, 1250 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },
1251 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) },
1251 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, 1252 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
1252 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) }, 1253 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
1253 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) }, 1254 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 85c6d13c9ffa..5ad4ea02157f 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -63,6 +63,7 @@
63#define USB_VENDOR_ID_APPLE 0x05ac 63#define USB_VENDOR_ID_APPLE 0x05ac
64#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 64#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
65#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d 65#define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d
66#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e
66#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e 67#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
67#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f 68#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
68#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 69#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 402682e0c2f4..8791a084693e 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -2,6 +2,7 @@
2 * Apple "Magic" Wireless Mouse driver 2 * Apple "Magic" Wireless Mouse driver
3 * 3 *
4 * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org> 4 * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org>
5 * Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com>
5 */ 6 */
6 7
7/* 8/*
@@ -53,7 +54,9 @@ static bool report_undeciphered;
53module_param(report_undeciphered, bool, 0644); 54module_param(report_undeciphered, bool, 0644);
54MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); 55MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
55 56
56#define TOUCH_REPORT_ID 0x29 57#define TRACKPAD_REPORT_ID 0x28
58#define MOUSE_REPORT_ID 0x29
59#define DOUBLE_REPORT_ID 0xf7
57/* These definitions are not precise, but they're close enough. (Bits 60/* These definitions are not precise, but they're close enough. (Bits
58 * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem 61 * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
59 * to be some kind of bit mask -- 0x20 may be a near-field reading, 62 * to be some kind of bit mask -- 0x20 may be a near-field reading,
@@ -67,6 +70,15 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
67 70
68#define SCROLL_ACCEL_DEFAULT 7 71#define SCROLL_ACCEL_DEFAULT 7
69 72
73/* Single touch emulation should only begin when no touches are currently down.
74 * This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches
75 * are down and the touch providing for single touch emulation is lifted,
76 * single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is
77 * occuring, single_touch_id corresponds with the tracking id of the touch used.
78 */
79#define NO_TOUCHES -1
80#define SINGLE_TOUCH_UP -2
81
70/** 82/**
71 * struct magicmouse_sc - Tracks Magic Mouse-specific data. 83 * struct magicmouse_sc - Tracks Magic Mouse-specific data.
72 * @input: Input device through which we report events. 84 * @input: Input device through which we report events.
@@ -93,6 +105,7 @@ struct magicmouse_sc {
93 u8 size; 105 u8 size;
94 } touches[16]; 106 } touches[16];
95 int tracking_ids[16]; 107 int tracking_ids[16];
108 int single_touch_id;
96}; 109};
97 110
98static int magicmouse_firm_touch(struct magicmouse_sc *msc) 111static int magicmouse_firm_touch(struct magicmouse_sc *msc)
@@ -158,15 +171,29 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
158static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata) 171static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
159{ 172{
160 struct input_dev *input = msc->input; 173 struct input_dev *input = msc->input;
161 int id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; 174 int id, x, y, size, orientation, touch_major, touch_minor, state, down;
162 int x = (tdata[1] << 28 | tdata[0] << 20) >> 20; 175
163 int y = -((tdata[2] << 24 | tdata[1] << 16) >> 20); 176 if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
164 int size = tdata[5] & 0x3f; 177 id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
165 int orientation = (tdata[6] >> 2) - 32; 178 x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
166 int touch_major = tdata[3]; 179 y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
167 int touch_minor = tdata[4]; 180 size = tdata[5] & 0x3f;
168 int state = tdata[7] & TOUCH_STATE_MASK; 181 orientation = (tdata[6] >> 2) - 32;
169 int down = state != TOUCH_STATE_NONE; 182 touch_major = tdata[3];
183 touch_minor = tdata[4];
184 state = tdata[7] & TOUCH_STATE_MASK;
185 down = state != TOUCH_STATE_NONE;
186 } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
187 id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
188 x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
189 y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
190 size = tdata[6] & 0x3f;
191 orientation = (tdata[7] >> 2) - 32;
192 touch_major = tdata[4];
193 touch_minor = tdata[5];
194 state = tdata[8] & TOUCH_STATE_MASK;
195 down = state != TOUCH_STATE_NONE;
196 }
170 197
171 /* Store tracking ID and other fields. */ 198 /* Store tracking ID and other fields. */
172 msc->tracking_ids[raw_id] = id; 199 msc->tracking_ids[raw_id] = id;
@@ -217,6 +244,13 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
217 } 244 }
218 } 245 }
219 246
247 if (down) {
248 msc->ntouches++;
249 if (msc->single_touch_id == NO_TOUCHES)
250 msc->single_touch_id = id;
251 } else if (msc->single_touch_id == id)
252 msc->single_touch_id = SINGLE_TOUCH_UP;
253
220 /* Generate the input events for this touch. */ 254 /* Generate the input events for this touch. */
221 if (report_touches && down) { 255 if (report_touches && down) {
222 input_report_abs(input, ABS_MT_TRACKING_ID, id); 256 input_report_abs(input, ABS_MT_TRACKING_ID, id);
@@ -226,14 +260,15 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
226 input_report_abs(input, ABS_MT_POSITION_X, x); 260 input_report_abs(input, ABS_MT_POSITION_X, x);
227 input_report_abs(input, ABS_MT_POSITION_Y, y); 261 input_report_abs(input, ABS_MT_POSITION_Y, y);
228 262
229 if (report_undeciphered) 263 if (report_undeciphered) {
230 input_event(input, EV_MSC, MSC_RAW, tdata[7]); 264 if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
265 input_event(input, EV_MSC, MSC_RAW, tdata[7]);
266 else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
267 input_event(input, EV_MSC, MSC_RAW, tdata[8]);
268 }
231 269
232 input_mt_sync(input); 270 input_mt_sync(input);
233 } 271 }
234
235 if (down)
236 msc->ntouches++;
237} 272}
238 273
239static int magicmouse_raw_event(struct hid_device *hdev, 274static int magicmouse_raw_event(struct hid_device *hdev,
@@ -241,7 +276,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,
241{ 276{
242 struct magicmouse_sc *msc = hid_get_drvdata(hdev); 277 struct magicmouse_sc *msc = hid_get_drvdata(hdev);
243 struct input_dev *input = msc->input; 278 struct input_dev *input = msc->input;
244 int x, y, ii, clicks, npoints; 279 int x = 0, y = 0, ii, clicks = 0, npoints;
245 280
246 switch (data[0]) { 281 switch (data[0]) {
247 case 0x10: 282 case 0x10:
@@ -251,7 +286,30 @@ static int magicmouse_raw_event(struct hid_device *hdev,
251 y = (__s16)(data[4] | data[5] << 8); 286 y = (__s16)(data[4] | data[5] << 8);
252 clicks = data[1]; 287 clicks = data[1];
253 break; 288 break;
254 case TOUCH_REPORT_ID: 289 case TRACKPAD_REPORT_ID:
290 /* Expect four bytes of prefix, and N*9 bytes of touch data. */
291 if (size < 4 || ((size - 4) % 9) != 0)
292 return 0;
293 npoints = (size - 4) / 9;
294 msc->ntouches = 0;
295 for (ii = 0; ii < npoints; ii++)
296 magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
297
298 /* We don't need an MT sync here because trackpad emits a
299 * BTN_TOUCH event in a new frame when all touches are released.
300 */
301 if (msc->ntouches == 0)
302 msc->single_touch_id = NO_TOUCHES;
303
304 clicks = data[1];
305
306 /* The following bits provide a device specific timestamp. They
307 * are unused here.
308 *
309 * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
310 */
311 break;
312 case MOUSE_REPORT_ID:
255 /* Expect six bytes of prefix, and N*8 bytes of touch data. */ 313 /* Expect six bytes of prefix, and N*8 bytes of touch data. */
256 if (size < 6 || ((size - 6) % 8) != 0) 314 if (size < 6 || ((size - 6) % 8) != 0)
257 return 0; 315 return 0;
@@ -277,6 +335,14 @@ static int magicmouse_raw_event(struct hid_device *hdev,
277 * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; 335 * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
278 */ 336 */
279 break; 337 break;
338 case DOUBLE_REPORT_ID:
339 /* Sometimes the trackpad sends two touch reports in one
340 * packet.
341 */
342 magicmouse_raw_event(hdev, report, data + 2, data[1]);
343 magicmouse_raw_event(hdev, report, data + 2 + data[1],
344 size - 2 - data[1]);
345 break;
280 case 0x20: /* Theoretically battery status (0-100), but I have 346 case 0x20: /* Theoretically battery status (0-100), but I have
281 * never seen it -- maybe it is only upon request. 347 * never seen it -- maybe it is only upon request.
282 */ 348 */
@@ -288,9 +354,25 @@ static int magicmouse_raw_event(struct hid_device *hdev,
288 return 0; 354 return 0;
289 } 355 }
290 356
291 magicmouse_emit_buttons(msc, clicks & 3); 357 if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
292 input_report_rel(input, REL_X, x); 358 magicmouse_emit_buttons(msc, clicks & 3);
293 input_report_rel(input, REL_Y, y); 359 input_report_rel(input, REL_X, x);
360 input_report_rel(input, REL_Y, y);
361 } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
362 input_report_key(input, BTN_MOUSE, clicks & 1);
363 input_report_key(input, BTN_TOUCH, msc->ntouches > 0);
364 input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1);
365 input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2);
366 input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3);
367 input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4);
368 if (msc->single_touch_id >= 0) {
369 input_report_abs(input, ABS_X,
370 msc->touches[msc->single_touch_id].x);
371 input_report_abs(input, ABS_Y,
372 msc->touches[msc->single_touch_id].y);
373 }
374 }
375
294 input_sync(input); 376 input_sync(input);
295 return 1; 377 return 1;
296} 378}
@@ -326,18 +408,27 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
326 input->dev.parent = hdev->dev.parent; 408 input->dev.parent = hdev->dev.parent;
327 409
328 __set_bit(EV_KEY, input->evbit); 410 __set_bit(EV_KEY, input->evbit);
329 __set_bit(BTN_LEFT, input->keybit); 411
330 __set_bit(BTN_RIGHT, input->keybit); 412 if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
331 if (emulate_3button) 413 __set_bit(BTN_LEFT, input->keybit);
332 __set_bit(BTN_MIDDLE, input->keybit); 414 __set_bit(BTN_RIGHT, input->keybit);
333 __set_bit(BTN_TOOL_FINGER, input->keybit); 415 if (emulate_3button)
334 416 __set_bit(BTN_MIDDLE, input->keybit);
335 __set_bit(EV_REL, input->evbit); 417
336 __set_bit(REL_X, input->relbit); 418 __set_bit(EV_REL, input->evbit);
337 __set_bit(REL_Y, input->relbit); 419 __set_bit(REL_X, input->relbit);
338 if (emulate_scroll_wheel) { 420 __set_bit(REL_Y, input->relbit);
339 __set_bit(REL_WHEEL, input->relbit); 421 if (emulate_scroll_wheel) {
340 __set_bit(REL_HWHEEL, input->relbit); 422 __set_bit(REL_WHEEL, input->relbit);
423 __set_bit(REL_HWHEEL, input->relbit);
424 }
425 } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
426 __set_bit(BTN_MOUSE, input->keybit);
427 __set_bit(BTN_TOOL_FINGER, input->keybit);
428 __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
429 __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
430 __set_bit(BTN_TOOL_QUADTAP, input->keybit);
431 __set_bit(BTN_TOUCH, input->keybit);
341 } 432 }
342 433
343 if (report_touches) { 434 if (report_touches) {
@@ -347,16 +438,26 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h
347 input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); 438 input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0);
348 input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); 439 input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0);
349 input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); 440 input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0);
350 input_set_abs_params(input, ABS_MT_POSITION_X, -1100, 1358, 441
351 4, 0);
352 /* Note: Touch Y position from the device is inverted relative 442 /* Note: Touch Y position from the device is inverted relative
353 * to how pointer motion is reported (and relative to how USB 443 * to how pointer motion is reported (and relative to how USB
354 * HID recommends the coordinates work). This driver keeps 444 * HID recommends the coordinates work). This driver keeps
355 * the origin at the same position, and just uses the additive 445 * the origin at the same position, and just uses the additive
356 * inverse of the reported Y. 446 * inverse of the reported Y.
357 */ 447 */
358 input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047, 448 if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
359 4, 0); 449 input_set_abs_params(input, ABS_MT_POSITION_X, -1100,
450 1358, 4, 0);
451 input_set_abs_params(input, ABS_MT_POSITION_Y, -1589,
452 2047, 4, 0);
453 } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
454 input_set_abs_params(input, ABS_X, -2909, 3167, 4, 0);
455 input_set_abs_params(input, ABS_Y, -2456, 2565, 4, 0);
456 input_set_abs_params(input, ABS_MT_POSITION_X, -2909,
457 3167, 4, 0);
458 input_set_abs_params(input, ABS_MT_POSITION_Y, -2456,
459 2565, 4, 0);
460 }
360 } 461 }
361 462
362 if (report_undeciphered) { 463 if (report_undeciphered) {
@@ -385,6 +486,8 @@ static int magicmouse_probe(struct hid_device *hdev,
385 msc->quirks = id->driver_data; 486 msc->quirks = id->driver_data;
386 hid_set_drvdata(hdev, msc); 487 hid_set_drvdata(hdev, msc);
387 488
489 msc->single_touch_id = NO_TOUCHES;
490
388 ret = hid_parse(hdev); 491 ret = hid_parse(hdev);
389 if (ret) { 492 if (ret) {
390 dev_err(&hdev->dev, "magicmouse hid parse failed\n"); 493 dev_err(&hdev->dev, "magicmouse hid parse failed\n");
@@ -400,7 +503,16 @@ static int magicmouse_probe(struct hid_device *hdev,
400 /* we are handling the input ourselves */ 503 /* we are handling the input ourselves */
401 hidinput_disconnect(hdev); 504 hidinput_disconnect(hdev);
402 505
403 report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID); 506 if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
507 report = hid_register_report(hdev, HID_INPUT_REPORT,
508 MOUSE_REPORT_ID);
509 else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
510 report = hid_register_report(hdev, HID_INPUT_REPORT,
511 TRACKPAD_REPORT_ID);
512 report = hid_register_report(hdev, HID_INPUT_REPORT,
513 DOUBLE_REPORT_ID);
514 }
515
404 if (!report) { 516 if (!report) {
405 dev_err(&hdev->dev, "unable to register touch report\n"); 517 dev_err(&hdev->dev, "unable to register touch report\n");
406 ret = -ENOMEM; 518 ret = -ENOMEM;
@@ -451,8 +563,10 @@ static void magicmouse_remove(struct hid_device *hdev)
451} 563}
452 564
453static const struct hid_device_id magic_mice[] = { 565static const struct hid_device_id magic_mice[] = {
454 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), 566 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
455 .driver_data = 0 }, 567 USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
568 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
569 USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
456 { } 570 { }
457}; 571};
458MODULE_DEVICE_TABLE(hid, magic_mice); 572MODULE_DEVICE_TABLE(hid, magic_mice);