Maemo port: Exclude plugins requiring a keymap from packaging
[maemo-rb.git] / firmware / drivers / button.c
blobc165e8f566e38338df77143c2d65039c02b13867
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Daniel Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 * Rockbox button functions
26 #include <stdlib.h>
27 #include "config.h"
28 #include "system.h"
29 #include "button.h"
30 #include "kernel.h"
31 #include "thread.h"
32 #include "backlight.h"
33 #include "serial.h"
34 #include "power.h"
35 #include "powermgmt.h"
36 #ifdef HAVE_SDL
37 #include "button-sdl.h"
38 #else
39 #include "button-target.h"
40 #endif
41 #ifdef HAVE_REMOTE_LCD
42 #include "lcd-remote.h"
43 #endif
45 struct event_queue button_queue SHAREDBSS_ATTR;
47 static long lastbtn; /* Last valid button status */
48 static long last_read; /* Last button status, for debouncing/filtering */
49 static intptr_t button_data; /* data value from last message dequeued */
50 #ifdef HAVE_LCD_BITMAP
51 static bool flipped; /* buttons can be flipped to match the LCD flip */
52 #endif
53 #ifdef HAVE_BACKLIGHT
54 static bool filter_first_keypress;
55 #ifdef HAVE_REMOTE_LCD
56 static bool remote_filter_first_keypress;
57 #endif
58 #endif /* HAVE_BACKLIGHT */
59 #ifdef HAVE_HEADPHONE_DETECTION
60 static bool phones_present = false;
61 #endif
63 /* how long until repeat kicks in, in centiseconds */
64 #define REPEAT_START (30*HZ/100)
66 #ifndef HAVE_TOUCHSCREEN
67 /* the next two make repeat "accelerate", which is nice for lists
68 * which begin to scroll a bit faster when holding until the
69 * real list accerelation kicks in (this smoothes acceleration)
72 /* the speed repeat starts at, in centiseconds */
73 #define REPEAT_INTERVAL_START (16*HZ/100)
74 /* speed repeat finishes at, in centiseconds */
75 #define REPEAT_INTERVAL_FINISH (5*HZ/100)
76 #else
78 * on touchscreen it's different, scrolling is done by swiping over the
79 * screen (potentially very quickly) and is completely different from button
80 * targets
81 * So, on touchscreen we don't want to artifically slow down early repeats,
82 * it'd have the contrary effect of making rockbox appear lagging
84 #define REPEAT_INTERVAL_START (5*HZ/100)
85 #define REPEAT_INTERVAL_FINISH (5*HZ/100)
86 #endif
88 #ifdef HAVE_BUTTON_DATA
89 static int button_read(int *data);
90 #else
91 static int button_read(void);
92 #endif
94 #ifdef HAVE_TOUCHSCREEN
95 static int last_touchscreen_touch;
96 static int lastdata = 0;
97 #endif
98 #if defined(HAVE_HEADPHONE_DETECTION)
99 static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */
100 /* This callback can be used for many different functions if needed -
101 just check to which object tmo points */
102 static int btn_detect_callback(struct timeout *tmo)
104 /* Try to post only transistions */
105 const long id = tmo->data ? SYS_PHONE_PLUGGED : SYS_PHONE_UNPLUGGED;
106 queue_remove_from_head(&button_queue, id);
107 queue_post(&button_queue, id, 0);
108 return 0;
110 #endif
112 static bool button_try_post(int button, int data)
114 #ifdef HAVE_TOUCHSCREEN
115 /* one can swipe over the scren very quickly,
116 * for this to work we want to forget about old presses and
117 * only respect the very latest ones */
118 const bool force_post = true;
119 #else
120 /* Only post events if the queue is empty,
121 * to avoid afterscroll effects.
122 * i.e. don't post new buttons if previous ones haven't been
123 * processed yet - but always post releases */
124 const bool force_post = button & BUTTON_REL;
125 #endif
127 bool ret = queue_empty(&button_queue);
128 if (!ret && force_post)
130 queue_remove_from_head(&button_queue, button);
131 ret = true;
134 if (ret)
135 queue_post(&button_queue, button, data);
137 /* on touchscreen we posted unconditionally */
138 return ret;
141 static void button_tick(void)
143 static int count = 0;
144 static int repeat_speed = REPEAT_INTERVAL_START;
145 static int repeat_count = 0;
146 static bool repeat = false;
147 static bool post = false;
148 #ifdef HAVE_BACKLIGHT
149 static bool skip_release = false;
150 #ifdef HAVE_REMOTE_LCD
151 static bool skip_remote_release = false;
152 #endif
153 #endif
154 int diff;
155 int btn;
156 #ifdef HAVE_BUTTON_DATA
157 int data = 0;
158 #else
159 const int data = 0;
160 #endif
162 #if defined(HAS_SERIAL_REMOTE) && !defined(SIMULATOR)
163 /* Post events for the remote control */
164 btn = remote_control_rx();
165 if(btn)
166 button_try_post(btn, 0);
167 #endif
169 #ifdef HAVE_BUTTON_DATA
170 btn = button_read(&data);
171 #else
172 btn = button_read();
173 #endif
174 #if defined(HAVE_HEADPHONE_DETECTION)
175 if (headphones_inserted() != phones_present)
177 /* Use the autoresetting oneshot to debounce the detection signal */
178 phones_present = !phones_present;
179 timeout_register(&hp_detect_timeout, btn_detect_callback,
180 HZ/2, phones_present);
182 #endif
184 /* Find out if a key has been released */
185 diff = btn ^ lastbtn;
186 if(diff && (btn & diff) == 0)
188 #ifdef HAVE_BACKLIGHT
189 #ifdef HAVE_REMOTE_LCD
190 if(diff & BUTTON_REMOTE)
191 if(!skip_remote_release)
192 button_try_post(BUTTON_REL | diff, data);
193 else
194 skip_remote_release = false;
195 else
196 #endif
197 if(!skip_release)
198 button_try_post(BUTTON_REL | diff, data);
199 else
200 skip_release = false;
201 #else
202 button_try_post(BUTTON_REL | diff, data);
203 #endif
205 else
207 if ( btn )
209 /* normal keypress */
210 if ( btn != lastbtn )
212 post = true;
213 repeat = false;
214 repeat_speed = REPEAT_INTERVAL_START;
216 else /* repeat? */
218 if ( repeat )
220 if (!post)
221 count--;
222 if (count == 0) {
223 post = true;
224 /* yes we have repeat */
225 if (repeat_speed > REPEAT_INTERVAL_FINISH)
226 repeat_speed--;
227 count = repeat_speed;
229 repeat_count++;
231 /* Send a SYS_POWEROFF event if we have a device
232 which doesn't shut down easily with the OFF
233 key */
234 #ifdef HAVE_SW_POWEROFF
235 if ((btn & POWEROFF_BUTTON
236 #ifdef RC_POWEROFF_BUTTON
237 || btn == RC_POWEROFF_BUTTON
238 #endif
239 ) &&
240 #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
241 !charger_inserted() &&
242 #endif
243 repeat_count > POWEROFF_COUNT)
245 /* Tell the main thread that it's time to
246 power off */
247 sys_poweroff();
249 /* Safety net for players without hardware
250 poweroff */
251 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
252 if(repeat_count > POWEROFF_COUNT * 10)
253 power_off();
254 #endif
256 #endif
259 else
261 if (count++ > REPEAT_START)
263 post = true;
264 repeat = true;
265 repeat_count = 0;
266 /* initial repeat */
267 count = REPEAT_INTERVAL_START;
269 #ifdef HAVE_TOUCHSCREEN
270 else if (lastdata != data && btn == lastbtn)
271 { /* only coordinates changed, post anyway */
272 if (touchscreen_get_mode() == TOUCHSCREEN_POINT)
273 post = true;
275 #endif
278 if ( post )
280 if (repeat)
282 /* Only post repeat events if the queue is empty,
283 * to avoid afterscroll effects. */
284 if (button_try_post(BUTTON_REPEAT | btn, data))
286 #ifdef HAVE_BACKLIGHT
287 #ifdef HAVE_REMOTE_LCD
288 skip_remote_release = false;
289 #endif
290 skip_release = false;
291 #endif
292 post = false;
295 else
297 #ifdef HAVE_BACKLIGHT
298 #ifdef HAVE_REMOTE_LCD
299 if (btn & BUTTON_REMOTE) {
300 if (!remote_filter_first_keypress
301 || is_remote_backlight_on(false)
302 #if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
303 || (remote_type()==REMOTETYPE_H300_NONLCD)
304 #endif
306 button_try_post(btn, data);
307 else
308 skip_remote_release = true;
310 else
311 #endif
312 if (!filter_first_keypress || is_backlight_on(false)
313 #if BUTTON_REMOTE
314 || (btn & BUTTON_REMOTE)
315 #endif
317 button_try_post(btn, data);
318 else
319 skip_release = true;
320 #else /* no backlight, nothing to skip */
321 button_try_post(btn, data);
322 #endif
323 post = false;
325 #ifdef HAVE_REMOTE_LCD
326 if(btn & BUTTON_REMOTE)
327 remote_backlight_on();
328 else
329 #endif
331 backlight_on();
332 #ifdef HAVE_BUTTON_LIGHT
333 buttonlight_on();
334 #endif
337 reset_poweroff_timer();
340 else
342 repeat = false;
343 count = 0;
346 lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT);
347 #ifdef HAVE_BUTTON_DATA
348 lastdata = data;
349 #endif
352 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
353 static void button_boost(bool state)
355 static bool boosted = false;
357 if (state && !boosted)
359 cpu_boost(true);
360 boosted = true;
362 else if (!state && boosted)
364 cpu_boost(false);
365 boosted = false;
368 #endif /* HAVE_ADJUSTABLE_CPU_FREQ */
370 int button_queue_count( void )
372 return queue_count(&button_queue);
375 long button_get(bool block)
377 struct queue_event ev;
378 int pending_count = queue_count(&button_queue);
380 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
381 /* Control the CPU boost trying to keep queue empty. */
382 if (pending_count == 0)
383 button_boost(false);
384 else if (pending_count > 2)
385 button_boost(true);
386 #endif
388 if ( block || pending_count )
390 queue_wait(&button_queue, &ev);
392 button_data = ev.data;
393 return ev.id;
396 return BUTTON_NONE;
399 long button_get_w_tmo(int ticks)
401 struct queue_event ev;
403 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
404 /* Be sure to keep boosted state. */
405 if (!queue_empty(&button_queue))
406 return button_get(true);
408 button_boost(false);
409 #endif
411 queue_wait_w_tmo(&button_queue, &ev, ticks);
412 if (ev.id == SYS_TIMEOUT)
413 ev.id = BUTTON_NONE;
414 else
415 button_data = ev.data;
416 return ev.id;
419 intptr_t button_get_data(void)
421 return button_data;
424 void button_init(void)
426 /* Init used objects first */
427 queue_init(&button_queue, true);
429 #ifdef HAVE_BUTTON_DATA
430 int temp;
431 #endif
432 /* hardware inits */
433 button_init_device();
435 #ifdef HAVE_BUTTON_DATA
436 button_read(&temp);
437 lastbtn = button_read(&temp);
438 #else
439 button_read();
440 lastbtn = button_read();
441 #endif
443 reset_poweroff_timer();
445 #ifdef HAVE_LCD_BITMAP
446 flipped = false;
447 #endif
448 #ifdef HAVE_BACKLIGHT
449 filter_first_keypress = false;
450 #ifdef HAVE_REMOTE_LCD
451 remote_filter_first_keypress = false;
452 #endif
453 #endif
454 #ifdef HAVE_TOUCHSCREEN
455 last_touchscreen_touch = 0xffff;
456 #endif
457 /* Start polling last */
458 tick_add_task(button_tick);
461 #ifdef BUTTON_DRIVER_CLOSE
462 void button_close(void)
464 tick_remove_task(button_tick);
466 #endif /* BUTTON_DRIVER_CLOSE */
468 #ifdef HAVE_LCD_FLIP
470 * helper function to swap LEFT/RIGHT, UP/DOWN (if present), and F1/F3 (Recorder)
472 static int button_flip(int button)
474 int newbutton = button;
476 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
477 newbutton &=
478 ~(BUTTON_LEFT | BUTTON_RIGHT
479 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
480 | BUTTON_UP | BUTTON_DOWN
481 #endif
482 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
483 | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD
484 #endif
485 #if CONFIG_KEYPAD == RECORDER_PAD
486 | BUTTON_F1 | BUTTON_F3
487 #endif
488 #if (CONFIG_KEYPAD == SANSA_C200_PAD) || (CONFIG_KEYPAD == SANSA_CLIP_PAD) ||\
489 (CONFIG_KEYPAD == GIGABEAT_PAD) || (CONFIG_KEYPAD == GIGABEAT_S_PAD)
490 | BUTTON_VOL_UP | BUTTON_VOL_DOWN
491 #endif
492 #if CONFIG_KEYPAD == PHILIPS_SA9200_PAD
493 | BUTTON_VOL_UP | BUTTON_VOL_DOWN
494 | BUTTON_NEXT | BUTTON_PREV
495 #endif
498 if (button & BUTTON_LEFT)
499 newbutton |= BUTTON_RIGHT;
500 if (button & BUTTON_RIGHT)
501 newbutton |= BUTTON_LEFT;
502 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
503 if (button & BUTTON_UP)
504 newbutton |= BUTTON_DOWN;
505 if (button & BUTTON_DOWN)
506 newbutton |= BUTTON_UP;
507 #endif
508 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
509 if (button & BUTTON_SCROLL_BACK)
510 newbutton |= BUTTON_SCROLL_FWD;
511 if (button & BUTTON_SCROLL_FWD)
512 newbutton |= BUTTON_SCROLL_BACK;
513 #endif
514 #if CONFIG_KEYPAD == RECORDER_PAD
515 if (button & BUTTON_F1)
516 newbutton |= BUTTON_F3;
517 if (button & BUTTON_F3)
518 newbutton |= BUTTON_F1;
519 #endif
520 #if (CONFIG_KEYPAD == SANSA_C200_PAD) || (CONFIG_KEYPAD == SANSA_CLIP_PAD) ||\
521 (CONFIG_KEYPAD == GIGABEAT_PAD) || (CONFIG_KEYPAD == GIGABEAT_S_PAD)
522 if (button & BUTTON_VOL_UP)
523 newbutton |= BUTTON_VOL_DOWN;
524 if (button & BUTTON_VOL_DOWN)
525 newbutton |= BUTTON_VOL_UP;
526 #endif
527 #if CONFIG_KEYPAD == PHILIPS_SA9200_PAD
528 if (button & BUTTON_VOL_UP)
529 newbutton |= BUTTON_VOL_DOWN;
530 if (button & BUTTON_VOL_DOWN)
531 newbutton |= BUTTON_VOL_UP;
532 if (button & BUTTON_NEXT)
533 newbutton |= BUTTON_PREV;
534 if (button & BUTTON_PREV)
535 newbutton |= BUTTON_NEXT;
536 #endif
537 #endif /* !SIMULATOR */
538 return newbutton;
542 * set the flip attribute
543 * better only call this when the queue is empty
545 void button_set_flip(bool flip)
547 if (flip != flipped) /* not the current setting */
549 /* avoid race condition with the button_tick() */
550 int oldlevel = disable_irq_save();
551 lastbtn = button_flip(lastbtn);
552 flipped = flip;
553 restore_irq(oldlevel);
556 #endif /* HAVE_LCD_FLIP */
558 #ifdef HAVE_BACKLIGHT
559 void set_backlight_filter_keypress(bool value)
561 filter_first_keypress = value;
563 #ifdef HAVE_REMOTE_LCD
564 void set_remote_backlight_filter_keypress(bool value)
566 remote_filter_first_keypress = value;
568 #endif
569 #endif
572 * Get button pressed from hardware
574 #ifdef HAVE_BUTTON_DATA
575 static int button_read(int *data)
577 int btn = button_read_device(data);
578 #else
579 static int button_read(void)
581 int btn = button_read_device();
582 #endif
583 int retval;
585 #ifdef HAVE_LCD_FLIP
586 if (btn && flipped)
587 btn = button_flip(btn); /* swap upside down */
588 #endif /* HAVE_LCD_FLIP */
590 #ifdef HAVE_TOUCHSCREEN
591 if (btn & BUTTON_TOUCHSCREEN)
592 last_touchscreen_touch = current_tick;
593 #endif
594 /* Filter the button status. It is only accepted if we get the same
595 status twice in a row. */
596 #ifndef HAVE_TOUCHSCREEN
597 if (btn != last_read)
598 retval = lastbtn;
599 else
600 #endif
601 retval = btn;
602 last_read = btn;
604 return retval;
607 int button_status(void)
609 return lastbtn;
612 #ifdef HAVE_BUTTON_DATA
613 int button_status_wdata(int *pdata)
615 *pdata = lastdata;
616 return lastbtn;
618 #endif
620 void button_clear_queue(void)
622 queue_clear(&button_queue);
625 #ifdef HAVE_TOUCHSCREEN
626 int touchscreen_last_touch(void)
628 return last_touchscreen_touch;
630 #endif
632 #ifdef HAVE_WHEEL_ACCELERATION
633 /* WHEEL_ACCEL_FACTOR = 2^16 / WHEEL_ACCEL_START */
634 #define WHEEL_ACCEL_FACTOR (1<<16)/WHEEL_ACCEL_START
636 * data:
637 * [31] Use acceleration
638 * [30:24] Message post count (skipped + 1) (1-127)
639 * [23:0] Velocity - degree/sec
641 * WHEEL_ACCEL_FACTOR:
642 * Value in degree/sec -- configurable via settings -- above which
643 * the accelerated scrolling starts. Factor is internally scaled by
644 * 1<<16 in respect to the following 32bit integer operations.
646 int button_apply_acceleration(const unsigned int data)
648 int delta = (data >> 24) & 0x7f;
650 if ((data & (1 << 31)) != 0)
652 /* read driver's velocity from data */
653 unsigned int v = data & 0xffffff;
655 /* v = 28.4 fixed point */
656 v = (WHEEL_ACCEL_FACTOR * v)>>(16-4);
658 /* Calculate real numbers item to scroll based upon acceleration
659 * setting, use correct roundoff */
660 #if (WHEEL_ACCELERATION == 1)
661 v = (v*v + (1<< 7))>> 8;
662 #elif (WHEEL_ACCELERATION == 2)
663 v = (v*v*v + (1<<11))>>12;
664 #elif (WHEEL_ACCELERATION == 3)
665 v = (v*v*v*v + (1<<15))>>16;
666 #endif
668 if (v > 1)
669 delta *= v;
672 return delta;
674 #endif /* HAVE_WHEEL_ACCELERATION */