1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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
32 #include "backlight.h"
35 #include "powermgmt.h"
37 #include "button-sdl.h"
39 #include "button-target.h"
41 #ifdef HAVE_REMOTE_LCD
42 #include "lcd-remote.h"
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 */
54 static bool filter_first_keypress
;
55 #ifdef HAVE_REMOTE_LCD
56 static bool remote_filter_first_keypress
;
58 #endif /* HAVE_BACKLIGHT */
59 #ifdef HAVE_HEADPHONE_DETECTION
60 static bool phones_present
= false;
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)
78 * on touchscreen it's different, scrolling is done by swiping over the
79 * screen (potentially very quickly) and is completely different from button
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)
88 #ifdef HAVE_BUTTON_DATA
89 static int button_read(int *data
);
91 static int button_read(void);
94 #ifdef HAVE_TOUCHSCREEN
95 static int last_touchscreen_touch
;
96 static int lastdata
= 0;
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);
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;
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
;
127 bool ret
= queue_empty(&button_queue
);
128 if (!ret
&& force_post
)
130 queue_remove_from_head(&button_queue
, button
);
135 queue_post(&button_queue
, button
, data
);
137 /* on touchscreen we posted unconditionally */
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;
156 #ifdef HAVE_BUTTON_DATA
162 #if defined(HAS_SERIAL_REMOTE) && !defined(SIMULATOR)
163 /* Post events for the remote control */
164 btn
= remote_control_rx();
166 button_try_post(btn
, 0);
169 #ifdef HAVE_BUTTON_DATA
170 btn
= button_read(&data
);
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
);
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
);
194 skip_remote_release
= false;
198 button_try_post(BUTTON_REL
| diff
, data
);
200 skip_release
= false;
202 button_try_post(BUTTON_REL
| diff
, data
);
209 /* normal keypress */
210 if ( btn
!= lastbtn
)
214 repeat_speed
= REPEAT_INTERVAL_START
;
224 /* yes we have repeat */
225 if (repeat_speed
> REPEAT_INTERVAL_FINISH
)
227 count
= repeat_speed
;
231 /* Send a SYS_POWEROFF event if we have a device
232 which doesn't shut down easily with the OFF
234 #ifdef HAVE_SW_POWEROFF
235 if ((btn
& POWEROFF_BUTTON
236 #ifdef RC_POWEROFF_BUTTON
237 || btn
== RC_POWEROFF_BUTTON
240 #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
241 !charger_inserted() &&
243 repeat_count
> POWEROFF_COUNT
)
245 /* Tell the main thread that it's time to
249 /* Safety net for players without hardware
251 #if (CONFIG_PLATFORM & PLATFORM_NATIVE)
252 if(repeat_count
> POWEROFF_COUNT
* 10)
261 if (count
++ > REPEAT_START
)
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
)
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;
290 skip_release
= false;
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
)
306 button_try_post(btn
, data
);
308 skip_remote_release
= true;
312 if (!filter_first_keypress
|| is_backlight_on(false)
314 || (btn
& BUTTON_REMOTE
)
317 button_try_post(btn
, data
);
320 #else /* no backlight, nothing to skip */
321 button_try_post(btn
, data
);
325 #ifdef HAVE_REMOTE_LCD
326 if(btn
& BUTTON_REMOTE
)
327 remote_backlight_on();
332 #ifdef HAVE_BUTTON_LIGHT
337 reset_poweroff_timer();
346 lastbtn
= btn
& ~(BUTTON_REL
| BUTTON_REPEAT
);
347 #ifdef HAVE_BUTTON_DATA
352 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
353 static void button_boost(bool state
)
355 static bool boosted
= false;
357 if (state
&& !boosted
)
362 else if (!state
&& boosted
)
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)
384 else if (pending_count
> 2)
388 if ( block
|| pending_count
)
390 queue_wait(&button_queue
, &ev
);
392 button_data
= ev
.data
;
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);
411 queue_wait_w_tmo(&button_queue
, &ev
, ticks
);
412 if (ev
.id
== SYS_TIMEOUT
)
415 button_data
= ev
.data
;
419 intptr_t button_get_data(void)
424 void button_init(void)
426 /* Init used objects first */
427 queue_init(&button_queue
, true);
429 #ifdef HAVE_BUTTON_DATA
433 button_init_device();
435 #ifdef HAVE_BUTTON_DATA
437 lastbtn
= button_read(&temp
);
440 lastbtn
= button_read();
443 reset_poweroff_timer();
445 #ifdef HAVE_LCD_BITMAP
448 #ifdef HAVE_BACKLIGHT
449 filter_first_keypress
= false;
450 #ifdef HAVE_REMOTE_LCD
451 remote_filter_first_keypress
= false;
454 #ifdef HAVE_TOUCHSCREEN
455 last_touchscreen_touch
= 0xffff;
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 */
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)
478 ~(BUTTON_LEFT
| BUTTON_RIGHT
479 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
480 | BUTTON_UP
| BUTTON_DOWN
482 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
483 | BUTTON_SCROLL_BACK
| BUTTON_SCROLL_FWD
485 #if CONFIG_KEYPAD == RECORDER_PAD
486 | BUTTON_F1
| BUTTON_F3
488 #if (CONFIG_KEYPAD == SANSA_C200_PAD) || (CONFIG_KEYPAD == SANSA_CLIP_PAD)
489 | BUTTON_VOL_UP
| BUTTON_VOL_DOWN
491 #if CONFIG_KEYPAD == PHILIPS_SA9200_PAD
492 | BUTTON_VOL_UP
| BUTTON_VOL_DOWN
493 | BUTTON_NEXT
| BUTTON_PREV
497 if (button
& BUTTON_LEFT
)
498 newbutton
|= BUTTON_RIGHT
;
499 if (button
& BUTTON_RIGHT
)
500 newbutton
|= BUTTON_LEFT
;
501 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
502 if (button
& BUTTON_UP
)
503 newbutton
|= BUTTON_DOWN
;
504 if (button
& BUTTON_DOWN
)
505 newbutton
|= BUTTON_UP
;
507 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
508 if (button
& BUTTON_SCROLL_BACK
)
509 newbutton
|= BUTTON_SCROLL_FWD
;
510 if (button
& BUTTON_SCROLL_FWD
)
511 newbutton
|= BUTTON_SCROLL_BACK
;
513 #if CONFIG_KEYPAD == RECORDER_PAD
514 if (button
& BUTTON_F1
)
515 newbutton
|= BUTTON_F3
;
516 if (button
& BUTTON_F3
)
517 newbutton
|= BUTTON_F1
;
519 #if (CONFIG_KEYPAD == SANSA_C200_PAD) || (CONFIG_KEYPAD == SANSA_CLIP_PAD)
520 if (button
& BUTTON_VOL_UP
)
521 newbutton
|= BUTTON_VOL_DOWN
;
522 if (button
& BUTTON_VOL_DOWN
)
523 newbutton
|= BUTTON_VOL_UP
;
525 #if CONFIG_KEYPAD == PHILIPS_SA9200_PAD
526 if (button
& BUTTON_VOL_UP
)
527 newbutton
|= BUTTON_VOL_DOWN
;
528 if (button
& BUTTON_VOL_DOWN
)
529 newbutton
|= BUTTON_VOL_UP
;
530 if (button
& BUTTON_NEXT
)
531 newbutton
|= BUTTON_PREV
;
532 if (button
& BUTTON_PREV
)
533 newbutton
|= BUTTON_NEXT
;
535 #endif /* !SIMULATOR */
540 * set the flip attribute
541 * better only call this when the queue is empty
543 void button_set_flip(bool flip
)
545 if (flip
!= flipped
) /* not the current setting */
547 /* avoid race condition with the button_tick() */
548 int oldlevel
= disable_irq_save();
549 lastbtn
= button_flip(lastbtn
);
551 restore_irq(oldlevel
);
554 #endif /* HAVE_LCD_FLIP */
556 #ifdef HAVE_BACKLIGHT
557 void set_backlight_filter_keypress(bool value
)
559 filter_first_keypress
= value
;
561 #ifdef HAVE_REMOTE_LCD
562 void set_remote_backlight_filter_keypress(bool value
)
564 remote_filter_first_keypress
= value
;
570 * Get button pressed from hardware
572 #ifdef HAVE_BUTTON_DATA
573 static int button_read(int *data
)
575 int btn
= button_read_device(data
);
577 static int button_read(void)
579 int btn
= button_read_device();
585 btn
= button_flip(btn
); /* swap upside down */
586 #endif /* HAVE_LCD_FLIP */
588 #ifdef HAVE_TOUCHSCREEN
589 if (btn
& BUTTON_TOUCHSCREEN
)
590 last_touchscreen_touch
= current_tick
;
592 /* Filter the button status. It is only accepted if we get the same
593 status twice in a row. */
594 #ifndef HAVE_TOUCHSCREEN
595 if (btn
!= last_read
)
605 int button_status(void)
610 #ifdef HAVE_BUTTON_DATA
611 int button_status_wdata(int *pdata
)
618 void button_clear_queue(void)
620 queue_clear(&button_queue
);
623 #ifdef HAVE_TOUCHSCREEN
624 int touchscreen_last_touch(void)
626 return last_touchscreen_touch
;
630 #ifdef HAVE_WHEEL_ACCELERATION
631 /* WHEEL_ACCEL_FACTOR = 2^16 / WHEEL_ACCEL_START */
632 #define WHEEL_ACCEL_FACTOR (1<<16)/WHEEL_ACCEL_START
635 * [31] Use acceleration
636 * [30:24] Message post count (skipped + 1) (1-127)
637 * [23:0] Velocity - degree/sec
639 * WHEEL_ACCEL_FACTOR:
640 * Value in degree/sec -- configurable via settings -- above which
641 * the accelerated scrolling starts. Factor is internally scaled by
642 * 1<<16 in respect to the following 32bit integer operations.
644 int button_apply_acceleration(const unsigned int data
)
646 int delta
= (data
>> 24) & 0x7f;
648 if ((data
& (1 << 31)) != 0)
650 /* read driver's velocity from data */
651 unsigned int v
= data
& 0xffffff;
653 /* v = 28.4 fixed point */
654 v
= (WHEEL_ACCEL_FACTOR
* v
)>>(16-4);
656 /* Calculate real numbers item to scroll based upon acceleration
657 * setting, use correct roundoff */
658 #if (WHEEL_ACCELERATION == 1)
659 v
= (v
*v
+ (1<< 7))>> 8;
660 #elif (WHEEL_ACCELERATION == 2)
661 v
= (v
*v
*v
+ (1<<11))>>12;
662 #elif (WHEEL_ACCELERATION == 3)
663 v
= (v
*v
*v
*v
+ (1<<15))>>16;
672 #endif /* HAVE_WHEEL_ACCELERATION */