FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / firmware / drivers / button.c
blob6f6eb8f2220a35d29044a868a430b45fbaa5694c
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 #include "button-target.h"
38 #ifdef HAVE_REMOTE_LCD
39 #include "lcd-remote.h"
40 #endif
42 #ifndef SIMULATOR
43 #if 0
44 /* Older than MAX_EVENT_AGE button events are going to be ignored.
45 * Used to prevent for example volume going up uncontrollable when events
46 * are getting queued and UI is lagging too much.
48 #define MAX_EVENT_AGE HZ
49 #endif
51 struct event_queue button_queue;
53 static long lastbtn; /* Last valid button status */
54 static long last_read; /* Last button status, for debouncing/filtering */
55 static intptr_t button_data; /* data value from last message dequeued */
56 #ifdef HAVE_LCD_BITMAP
57 static bool flipped; /* buttons can be flipped to match the LCD flip */
58 #endif
59 #ifdef HAVE_BACKLIGHT
60 static bool filter_first_keypress;
61 #ifdef HAVE_REMOTE_LCD
62 static bool remote_filter_first_keypress;
63 #endif
64 #endif /* HAVE_BACKLIGHT */
65 #ifdef HAVE_HEADPHONE_DETECTION
66 static bool phones_present = false;
67 #endif
69 /* how long until repeat kicks in, in ticks */
70 #define REPEAT_START 30
72 /* the speed repeat starts at, in ticks */
73 #define REPEAT_INTERVAL_START 16
75 /* speed repeat finishes at, in ticks */
76 #define REPEAT_INTERVAL_FINISH 5
78 #ifdef HAVE_BUTTON_DATA
79 static int button_read(int *data);
80 #else
81 static int button_read(void);
82 #endif
84 #ifdef HAVE_TOUCHSCREEN
85 int last_touchscreen_touch;
86 #endif
87 #if defined(HAVE_HEADPHONE_DETECTION)
88 static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */
89 /* This callback can be used for many different functions if needed -
90 just check to which object tmo points */
91 static int btn_detect_callback(struct timeout *tmo)
93 /* Try to post only transistions */
94 const long id = tmo->data ? SYS_PHONE_PLUGGED : SYS_PHONE_UNPLUGGED;
95 queue_remove_from_head(&button_queue, id);
96 queue_post(&button_queue, id, 0);
97 return 0;
99 #endif
101 static void button_tick(void)
103 static int count = 0;
104 static int repeat_speed = REPEAT_INTERVAL_START;
105 static int repeat_count = 0;
106 static bool repeat = false;
107 static bool post = false;
108 #ifdef HAVE_BACKLIGHT
109 static bool skip_release = false;
110 #ifdef HAVE_REMOTE_LCD
111 static bool skip_remote_release = false;
112 #endif
113 #endif
114 int diff;
115 int btn;
116 #ifdef HAVE_BUTTON_DATA
117 int data = 0;
118 #else
119 const int data = 0;
120 #endif
122 #ifdef HAS_SERIAL_REMOTE
123 /* Post events for the remote control */
124 btn = remote_control_rx();
125 if(btn)
127 queue_post(&button_queue, btn, 0);
129 #endif
131 #ifdef HAVE_BUTTON_DATA
132 btn = button_read(&data);
133 #else
134 btn = button_read();
135 #endif
137 #if defined(HAVE_HEADPHONE_DETECTION)
138 if (headphones_inserted() != phones_present)
140 /* Use the autoresetting oneshot to debounce the detection signal */
141 phones_present = !phones_present;
142 timeout_register(&hp_detect_timeout, btn_detect_callback,
143 HZ, phones_present);
145 #endif
147 /* Find out if a key has been released */
148 diff = btn ^ lastbtn;
149 if(diff && (btn & diff) == 0)
151 #ifdef HAVE_BACKLIGHT
152 #ifdef HAVE_REMOTE_LCD
153 if(diff & BUTTON_REMOTE)
154 if(!skip_remote_release)
155 queue_post(&button_queue, BUTTON_REL | diff, data);
156 else
157 skip_remote_release = false;
158 else
159 #endif
160 if(!skip_release)
161 queue_post(&button_queue, BUTTON_REL | diff, data);
162 else
163 skip_release = false;
164 #else
165 queue_post(&button_queue, BUTTON_REL | diff, data);
166 #endif
168 else
170 if ( btn )
172 /* normal keypress */
173 if ( btn != lastbtn )
175 post = true;
176 repeat = false;
177 repeat_speed = REPEAT_INTERVAL_START;
179 else /* repeat? */
181 if ( repeat )
183 if (!post)
184 count--;
185 if (count == 0) {
186 post = true;
187 /* yes we have repeat */
188 if (repeat_speed > REPEAT_INTERVAL_FINISH)
189 repeat_speed--;
190 count = repeat_speed;
192 repeat_count++;
194 /* Send a SYS_POWEROFF event if we have a device
195 which doesn't shut down easily with the OFF
196 key */
197 #ifdef HAVE_SW_POWEROFF
198 if ((btn == POWEROFF_BUTTON
199 #ifdef RC_POWEROFF_BUTTON
200 || btn == RC_POWEROFF_BUTTON
201 #endif
202 ) &&
203 #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
204 !charger_inserted() &&
205 #endif
206 repeat_count > POWEROFF_COUNT)
208 /* Tell the main thread that it's time to
209 power off */
210 sys_poweroff();
212 /* Safety net for players without hardware
213 poweroff */
214 if(repeat_count > POWEROFF_COUNT * 10)
215 power_off();
217 #endif
220 else
222 if (count++ > REPEAT_START)
224 post = true;
225 repeat = true;
226 repeat_count = 0;
227 /* initial repeat */
228 count = REPEAT_INTERVAL_START;
232 if ( post )
234 if (repeat)
236 /* Only post repeat events if the queue is empty,
237 * to avoid afterscroll effects. */
238 if (queue_empty(&button_queue))
240 queue_post(&button_queue, BUTTON_REPEAT | btn, data);
241 #ifdef HAVE_BACKLIGHT
242 #ifdef HAVE_REMOTE_LCD
243 skip_remote_release = false;
244 #endif
245 skip_release = false;
246 #endif
247 post = false;
250 else
252 #ifdef HAVE_BACKLIGHT
253 #ifdef HAVE_REMOTE_LCD
254 if (btn & BUTTON_REMOTE) {
255 if (!remote_filter_first_keypress
256 || is_remote_backlight_on(false)
257 #if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
258 || (remote_type()==REMOTETYPE_H300_NONLCD)
259 #endif
261 queue_post(&button_queue, btn, data);
262 else
263 skip_remote_release = true;
265 else
266 #endif
267 if (!filter_first_keypress || is_backlight_on(false)
268 #if BUTTON_REMOTE
269 || (btn & BUTTON_REMOTE)
270 #endif
272 queue_post(&button_queue, btn, data);
273 else
274 skip_release = true;
275 #else /* no backlight, nothing to skip */
276 queue_post(&button_queue, btn, data);
277 #endif
278 post = false;
280 #ifdef HAVE_REMOTE_LCD
281 if(btn & BUTTON_REMOTE)
282 remote_backlight_on();
283 else
284 #endif
286 backlight_on();
287 #ifdef HAVE_BUTTON_LIGHT
288 buttonlight_on();
289 #endif
292 reset_poweroff_timer();
295 else
297 repeat = false;
298 count = 0;
301 lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT);
304 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
305 static void button_boost(bool state)
307 static bool boosted = false;
309 if (state && !boosted)
311 cpu_boost(true);
312 boosted = true;
314 else if (!state && boosted)
316 cpu_boost(false);
317 boosted = false;
320 #endif /* HAVE_ADJUSTABLE_CPU_FREQ */
322 int button_queue_count( void )
324 return queue_count(&button_queue);
327 long button_get(bool block)
329 struct queue_event ev;
330 int pending_count = queue_count(&button_queue);
332 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
333 /* Control the CPU boost trying to keep queue empty. */
334 if (pending_count == 0)
335 button_boost(false);
336 else if (pending_count > 2)
337 button_boost(true);
338 #endif
340 if ( block || pending_count )
342 queue_wait(&button_queue, &ev);
344 #if 0
345 /* Ignore if the event was too old and for simplicity, just
346 * wait for a new button_get() request. */
347 if (current_tick - ev.tick > MAX_EVENT_AGE)
348 return BUTTON_NONE;
349 #endif
350 button_data = ev.data;
351 return ev.id;
354 return BUTTON_NONE;
357 long button_get_w_tmo(int ticks)
359 struct queue_event ev;
361 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
362 /* Be sure to keep boosted state. */
363 if (!queue_empty(&button_queue))
364 return button_get(true);
366 button_boost(false);
367 #endif
369 queue_wait_w_tmo(&button_queue, &ev, ticks);
370 if (ev.id == SYS_TIMEOUT)
371 ev.id = BUTTON_NONE;
372 else
373 button_data = ev.data;
374 return ev.id;
377 intptr_t button_get_data(void)
379 return button_data;
382 void button_init(void)
384 /* Init used objects first */
385 queue_init(&button_queue, true);
387 #ifdef HAVE_BUTTON_DATA
388 int temp;
389 #endif
390 /* hardware inits */
391 button_init_device();
393 #ifdef HAVE_BUTTON_DATA
394 button_read(&temp);
395 lastbtn = button_read(&temp);
396 #else
397 button_read();
398 lastbtn = button_read();
399 #endif
401 reset_poweroff_timer();
403 #ifdef HAVE_LCD_BITMAP
404 flipped = false;
405 #endif
406 #ifdef HAVE_BACKLIGHT
407 filter_first_keypress = false;
408 #ifdef HAVE_REMOTE_LCD
409 remote_filter_first_keypress = false;
410 #endif
411 #endif
412 #ifdef HAVE_TOUCHSCREEN
413 last_touchscreen_touch = 0xffff;
414 #endif
415 /* Start polling last */
416 tick_add_task(button_tick);
419 #ifdef BUTTON_DRIVER_CLOSE
420 void button_close(void)
422 tick_remove_task(button_tick);
424 #endif /* BUTTON_DRIVER_CLOSE */
426 #ifdef HAVE_LCD_BITMAP /* only bitmap displays can be flipped */
428 * helper function to swap LEFT/RIGHT, UP/DOWN (if present), and F1/F3 (Recorder)
430 static int button_flip(int button)
432 int newbutton;
434 newbutton = button &
435 ~(BUTTON_LEFT | BUTTON_RIGHT
436 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
437 | BUTTON_UP | BUTTON_DOWN
438 #endif
439 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
440 | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD
441 #endif
442 #if CONFIG_KEYPAD == RECORDER_PAD
443 | BUTTON_F1 | BUTTON_F3
444 #endif
445 #if CONFIG_KEYPAD == SANSA_C200_PAD
446 | BUTTON_VOL_UP | BUTTON_VOL_DOWN
447 #endif
448 #if CONFIG_KEYPAD == PHILIPS_SA9200_PAD
449 | BUTTON_VOL_UP | BUTTON_VOL_DOWN
450 | BUTTON_NEXT | BUTTON_PREV
451 #endif
454 if (button & BUTTON_LEFT)
455 newbutton |= BUTTON_RIGHT;
456 if (button & BUTTON_RIGHT)
457 newbutton |= BUTTON_LEFT;
458 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
459 if (button & BUTTON_UP)
460 newbutton |= BUTTON_DOWN;
461 if (button & BUTTON_DOWN)
462 newbutton |= BUTTON_UP;
463 #endif
464 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
465 if (button & BUTTON_SCROLL_BACK)
466 newbutton |= BUTTON_SCROLL_FWD;
467 if (button & BUTTON_SCROLL_FWD)
468 newbutton |= BUTTON_SCROLL_BACK;
469 #endif
470 #if CONFIG_KEYPAD == RECORDER_PAD
471 if (button & BUTTON_F1)
472 newbutton |= BUTTON_F3;
473 if (button & BUTTON_F3)
474 newbutton |= BUTTON_F1;
475 #endif
476 #if CONFIG_KEYPAD == SANSA_C200_PAD
477 if (button & BUTTON_VOL_UP)
478 newbutton |= BUTTON_VOL_DOWN;
479 if (button & BUTTON_VOL_DOWN)
480 newbutton |= BUTTON_VOL_UP;
481 #endif
482 #if CONFIG_KEYPAD == PHILIPS_SA9200_PAD
483 if (button & BUTTON_VOL_UP)
484 newbutton |= BUTTON_VOL_DOWN;
485 if (button & BUTTON_VOL_DOWN)
486 newbutton |= BUTTON_VOL_UP;
487 if (button & BUTTON_NEXT)
488 newbutton |= BUTTON_PREV;
489 if (button & BUTTON_PREV)
490 newbutton |= BUTTON_NEXT;
491 #endif
493 return newbutton;
497 * set the flip attribute
498 * better only call this when the queue is empty
500 void button_set_flip(bool flip)
502 if (flip != flipped) /* not the current setting */
504 /* avoid race condition with the button_tick() */
505 int oldlevel = disable_irq_save();
506 lastbtn = button_flip(lastbtn);
507 flipped = flip;
508 restore_irq(oldlevel);
511 #endif /* HAVE_LCD_BITMAP */
513 #ifdef HAVE_BACKLIGHT
514 void set_backlight_filter_keypress(bool value)
516 filter_first_keypress = value;
518 #ifdef HAVE_REMOTE_LCD
519 void set_remote_backlight_filter_keypress(bool value)
521 remote_filter_first_keypress = value;
523 #endif
524 #endif
527 * Get button pressed from hardware
529 #ifdef HAVE_BUTTON_DATA
530 static int button_read(int *data)
532 int btn = button_read_device(data);
533 #else
534 static int button_read(void)
536 int btn = button_read_device();
537 #endif
538 int retval;
540 #ifdef HAVE_LCD_BITMAP
541 if (btn && flipped)
542 btn = button_flip(btn); /* swap upside down */
543 #endif
544 #ifdef HAVE_TOUCHSCREEN
545 if (btn & BUTTON_TOUCHSCREEN)
546 last_touchscreen_touch = current_tick;
547 #endif
548 /* Filter the button status. It is only accepted if we get the same
549 status twice in a row. */
550 #ifndef HAVE_TOUCHSCREEN
551 if (btn != last_read)
552 retval = lastbtn;
553 else
554 #endif
555 retval = btn;
556 last_read = btn;
558 return retval;
561 int button_status(void)
563 return lastbtn;
566 void button_clear_queue(void)
568 queue_clear(&button_queue);
571 #ifdef HAVE_TOUCHSCREEN
572 int touchscreen_last_touch(void)
574 return last_touchscreen_touch;
576 #endif
577 #endif /* SIMULATOR */
579 #ifdef HAVE_WHEEL_ACCELERATION
580 /* WHEEL_ACCEL_FACTOR = 2^16 / WHEEL_ACCEL_START */
581 #define WHEEL_ACCEL_FACTOR (1<<16)/WHEEL_ACCEL_START
583 * data:
584 * [31] Use acceleration
585 * [30:24] Message post count (skipped + 1) (1-127)
586 * [23:0] Velocity - degree/sec
588 * WHEEL_ACCEL_FACTOR:
589 * Value in degree/sec -- configurable via settings -- above which
590 * the accelerated scrolling starts. Factor is internally scaled by
591 * 1<<16 in respect to the following 32bit integer operations.
593 int button_apply_acceleration(const unsigned int data)
595 int delta = (data >> 24) & 0x7f;
597 if ((data & (1 << 31)) != 0)
599 /* read driver's velocity from data */
600 unsigned int v = data & 0xffffff;
602 /* v = 28.4 fixed point */
603 v = (WHEEL_ACCEL_FACTOR * v)>>(16-4);
605 /* Calculate real numbers item to scroll based upon acceleration
606 * setting, use correct roundoff */
607 #if (WHEEL_ACCELERATION == 1)
608 v = (v*v + (1<< 7))>> 8;
609 #elif (WHEEL_ACCELERATION == 2)
610 v = (v*v*v + (1<<11))>>12;
611 #elif (WHEEL_ACCELERATION == 3)
612 v = (v*v*v*v + (1<<15))>>16;
613 #endif
615 if (v > 1)
616 delta *= v;
619 return delta;
621 #endif /* HAVE_WHEEL_ACCELERATION */