A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 673 lines 19 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2002 by Daniel Stenberg 11 * 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. 16 * 17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 * KIND, either express or implied. 19 * 20 ****************************************************************************/ 21 22/* 23 * Rockbox button functions 24 */ 25 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#if defined(HAVE_SDL) 37#include <SDL.h> 38#if (SDL_MAJOR_VERSION > 1) 39#include "button-sdl.h" 40#else 41#include "button-target.h" 42#endif 43#endif /* HAVE_SDL */ 44#ifdef HAVE_REMOTE_LCD 45#include "lcd-remote.h" 46#endif 47#if defined(HAVE_TRANSFLECTIVE_LCD) && defined(HAVE_LCD_SLEEP) 48#include "lcd.h" /* lcd_active() prototype */ 49#endif 50 51#if defined(IPOD_ACCESSORY_PROTOCOL) && (defined(IPOD_COLOR) || defined(IPOD_4G) || defined(IPOD_MINI) || defined(IPOD_MINI2G)) 52#include "iap.h" 53#endif 54 55static long lastbtn; /* Last valid button status */ 56static long last_read; /* Last button status, for debouncing/filtering */ 57static bool flipped; /* buttons can be flipped to match the LCD flip */ 58 59#ifdef HAVE_BACKLIGHT /* Filter first keypress function pointer */ 60static bool (*keypress_filter_fn)(int, int); 61#ifdef HAVE_REMOTE_LCD 62static bool (*remote_keypress_filter_fn)(int, int); 63#endif 64#endif /* HAVE_BACKLIGHT */ 65 66#ifdef HAVE_SW_POWEROFF 67static bool enable_sw_poweroff = true; 68#endif 69 70/* how long until repeat kicks in, in centiseconds */ 71#define REPEAT_START (30*HZ/100) 72 73/* The next two make repeat "accelerate", which is nice for lists 74 * which begin to scroll a bit faster when holding until the 75 * real list acceleration kicks in (this smooths acceleration). 76 * 77 * Note that touchscreen pointing events are not subject to this 78 * acceleration and always use REPEAT_INTERVAL_TOUCH. (Do repeat 79 * events even do anything sane for touchscreens??) 80 */ 81 82/* the speed repeat starts at, in centiseconds */ 83#define REPEAT_INTERVAL_START (16*HZ/100) 84/* speed repeat finishes at, in centiseconds */ 85#define REPEAT_INTERVAL_FINISH (5*HZ/100) 86/* repeat interval for touch events */ 87#define REPEAT_INTERVAL_TOUCH (5*HZ/100) 88 89static int lastdata = 0; 90static int button_read(int *data); 91 92#ifdef HAVE_TOUCHSCREEN 93static long last_touchscreen_touch; 94#endif 95 96static void button_remote_post(void) 97{ 98#if defined(HAS_SERIAL_REMOTE) && !defined(SIMULATOR) 99 /* Post events for the remote control */ 100 int btn = remote_control_rx(); 101 if(btn) 102 button_queue_try_post(btn, 0); 103#endif 104} 105 106#if defined(HAVE_HEADPHONE_DETECTION) 107static int hp_detect_callback(struct timeout *tmo) 108{ 109 /* Try to post only transistions */ 110 const long id = tmo->data ? SYS_PHONE_PLUGGED : SYS_PHONE_UNPLUGGED; 111 button_queue_post_remove_head(id, 0); 112 113#if defined(IPOD_ACCESSORY_PROTOCOL) && (defined(IPOD_COLOR) || defined(IPOD_4G) || defined(IPOD_MINI) || defined(IPOD_MINI2G)) 114 if (id == SYS_PHONE_UNPLUGGED) 115 iap_reset_state(IF_IAP_MP(1)); 116#endif 117 118 return 0; 119 /*misc.c:hp_unplug_change*/ 120} 121#endif 122 123#if defined(HAVE_LINEOUT_DETECTION) 124static int lo_detect_callback(struct timeout *tmo) 125{ 126 /* Try to post only transistions */ 127 const long id = tmo->data ? SYS_LINEOUT_PLUGGED : SYS_LINEOUT_UNPLUGGED; 128 button_queue_post_remove_head(id, 0); 129 130 return 0; 131 /*misc.c:lo_unplug_change*/ 132} 133#endif 134 135static void check_audio_peripheral_state(void) 136{ 137#if defined(HAVE_HEADPHONE_DETECTION) 138 static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */ 139 static bool phones_present = false; 140 141 if (headphones_inserted() != phones_present) 142 { 143 /* Use the autoresetting oneshot to debounce the detection signal */ 144 phones_present = !phones_present; 145 timeout_register(&hp_detect_timeout, hp_detect_callback, 146 HZ/2, phones_present); 147 } 148#endif 149#if defined(HAVE_LINEOUT_DETECTION) 150 static struct timeout lo_detect_timeout; /* Debouncer for lineout plug/unplug */ 151 static bool lineout_present = false; 152 153 if (lineout_inserted() != lineout_present) 154 { 155 /* Use the autoresetting oneshot to debounce the detection signal */ 156 lineout_present = !lineout_present; 157 timeout_register(&lo_detect_timeout, lo_detect_callback, 158 HZ/2, lineout_present); 159 } 160#endif 161} 162 163#ifdef HAVE_BACKLIGHT 164/* disabled function is shared between Main & Remote LCDs */ 165static bool filter_first_keypress_disabled(int button, int data) 166{ 167 button_queue_try_post(button, data); 168 return false; 169} 170 171static bool filter_first_keypress_enabled(int button, int data) 172{ 173#if defined(HAVE_TRANSFLECTIVE_LCD) && defined(HAVE_LCD_SLEEP) 174 if (is_backlight_on(false) && lcd_active()) 175#else 176 if (is_backlight_on(false)) 177#endif 178 { 179 return filter_first_keypress_disabled(button, data); 180 } 181 return true; 182} 183 184#ifdef HAVE_REMOTE_LCD 185static bool filter_first_remote_keypress_enabled(int button, int data) 186{ 187 if (is_remote_backlight_on(false) 188#if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES) 189 || (remote_type()==REMOTETYPE_H300_NONLCD) 190#endif 191 ) 192 { 193 return filter_first_keypress_disabled(button, data); 194 } 195 return true; 196} 197#endif /* def HAVE_REMOTE_LCD */ 198#endif /* def HAVE_BACKLIGHT */ 199 200static void button_tick(void) 201{ 202 static int count = 0; 203 static int repeat_speed = REPEAT_INTERVAL_START; 204 static int repeat_count = 0; 205 static bool repeat = false; 206 static bool post = false; 207#ifdef HAVE_BACKLIGHT 208 static bool skip_release = false; 209#ifdef HAVE_REMOTE_LCD 210 static bool skip_remote_release = false; 211#endif 212#endif 213 int diff; 214 int btn; 215 int data = 0; 216 217 button_remote_post(); 218 219 btn = button_read(&data); 220 221 check_audio_peripheral_state(); 222 223 /* Find out if a key has been released */ 224 diff = btn ^ lastbtn; 225 if(diff && (btn & diff) == 0) 226 { 227#ifdef HAVE_BACKLIGHT 228#ifdef HAVE_REMOTE_LCD 229 if(diff & BUTTON_REMOTE) 230 if(!skip_remote_release) 231 button_queue_try_post(BUTTON_REL | diff, data); 232 else 233 skip_remote_release = false; 234 else 235#endif 236 if(!skip_release) 237 button_queue_try_post(BUTTON_REL | diff, data); 238 else 239 skip_release = false; 240#else 241 button_queue_try_post(BUTTON_REL | diff, data); 242#endif 243 } 244 else 245 { 246 if ( btn ) 247 { 248 /* normal keypress */ 249 if ( btn != lastbtn ) 250 { 251 post = true; 252 repeat = false; 253 repeat_speed = REPEAT_INTERVAL_START; 254 } 255 else /* repeat? */ 256 { 257 258#if defined(DX50) || defined(DX90) 259 /* 260 Power button on these devices reports two distinct key codes, which are 261 triggerd by a short or medium duration press. Additionlly a long duration press 262 will trigger a hard reset, which is hardwired. 263 264 The time delta between medium and long duration press is not large enough to 265 register here as power off repeat. A hard reset is triggered before Rockbox 266 can power off. 267 268 To cirumvent the hard reset, Rockbox will shutdown on the first POWEROFF_BUTTON 269 repeat. POWEROFF_BUTTON is associated with the a medium duration press of the 270 power button. 271 */ 272 if(btn & POWEROFF_BUTTON) 273 { 274 sys_poweroff(); 275 } 276#endif 277 278 if ( repeat ) 279 { 280 if (!post) 281 count--; 282 if (count == 0) { 283 post = true; 284 /* yes we have repeat */ 285 if (repeat_speed > REPEAT_INTERVAL_FINISH) 286 repeat_speed--; 287#ifdef HAVE_TOUCHSCREEN 288 if(btn & BUTTON_TOUCHSCREEN) 289 repeat_speed = REPEAT_INTERVAL_TOUCH; 290#endif 291 292 count = repeat_speed; 293 294 repeat_count++; 295 296 /* Send a SYS_POWEROFF event if we have a device 297 which doesn't shut down easily with the OFF 298 key */ 299#ifdef HAVE_SW_POWEROFF 300 if (enable_sw_poweroff && 301 (btn & POWEROFF_BUTTON 302#ifdef RC_POWEROFF_BUTTON 303 || btn == RC_POWEROFF_BUTTON 304#endif 305 ) && 306#if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING) 307 !charger_inserted() && 308#endif 309 repeat_count > POWEROFF_COUNT) 310 { 311 /* Tell the main thread that it's time to 312 power off */ 313 sys_poweroff(); 314 315 /* Safety net for players without hardware 316 poweroff */ 317#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 318 if(repeat_count > POWEROFF_COUNT * 10) 319 power_off(); 320#endif 321 } 322#endif 323 } 324 } 325 else 326 { 327 if (count++ > REPEAT_START) 328 { 329 post = true; 330 repeat = true; 331 repeat_count = 0; 332 /* initial repeat */ 333 count = REPEAT_INTERVAL_START; 334 } 335#ifdef HAVE_TOUCHSCREEN 336 else if (lastdata != data && btn == lastbtn) 337 { /* only coordinates changed, post anyway */ 338 if (touchscreen_get_mode() == TOUCHSCREEN_POINT) 339 post = true; 340 } 341#endif 342 } 343 } 344 if ( post ) 345 { 346 if (repeat) 347 { 348 /* Only post repeat events if the queue is empty, 349 * to avoid afterscroll effects. */ 350 if (button_queue_try_post(BUTTON_REPEAT | btn, data)) 351 { 352#ifdef HAVE_BACKLIGHT 353#ifdef HAVE_REMOTE_LCD 354 skip_remote_release = false; 355#endif 356 skip_release = false; 357#endif 358 post = false; 359 /* Need to post back/buttonlight_on() on repeat buttons */ 360#ifdef HAVE_BACKLIGHT 361#ifdef HAVE_REMOTE_LCD 362 if (btn & BUTTON_REMOTE) { 363 remote_backlight_on(); 364 } 365 else 366#endif 367 { 368 backlight_on(); 369 buttonlight_on(); 370 } 371#endif 372 } 373 } 374 else 375 { 376#ifdef HAVE_BACKLIGHT 377#ifdef HAVE_REMOTE_LCD 378 if (btn & BUTTON_REMOTE) { 379 skip_remote_release = remote_keypress_filter_fn(btn, data); 380 remote_backlight_on(); 381 } 382 else 383#endif 384 { 385 skip_release = keypress_filter_fn(btn, data); 386 backlight_on(); 387 buttonlight_on(); 388 } 389#else /* no backlight, nothing to skip */ 390 button_queue_try_post(btn, data); 391#endif 392 post = false; 393 } 394 reset_poweroff_timer(); 395 } 396 } 397 else 398 { 399 repeat = false; 400 count = 0; 401 } 402 } 403 lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT); 404 405 lastdata = data; 406} 407 408void button_init(void) 409{ 410 int temp; 411 /* Init used objects first */ 412 button_queue_init(); 413 414 /* hardware inits */ 415 button_init_device(); 416 417 button_read(&temp); 418 lastbtn = button_read(&temp); 419 420 reset_poweroff_timer(); 421 422 flipped = false; 423#ifdef HAVE_BACKLIGHT 424 set_backlight_filter_keypress(false); 425#ifdef HAVE_REMOTE_LCD 426 set_remote_backlight_filter_keypress(false); 427#endif 428#endif 429#ifdef HAVE_TOUCHSCREEN 430 last_touchscreen_touch = -1; 431#endif 432 /* Start polling last */ 433 tick_add_task(button_tick); 434} 435 436#ifdef BUTTON_DRIVER_CLOSE 437void button_close(void) 438{ 439 tick_remove_task(button_tick); 440} 441#endif /* BUTTON_DRIVER_CLOSE */ 442 443#ifdef HAVE_LCD_FLIP 444/* 445 * helper function to swap LEFT/RIGHT, UP/DOWN (if present) 446 */ 447static int button_flip(int button) 448{ 449 int newbutton = button; 450 451#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 452 newbutton &= ~( 453#if defined(BUTTON_LEFT) && defined(BUTTON_RIGHT) 454 BUTTON_LEFT | BUTTON_RIGHT 455#else 456#warning "LEFT/RIGHT not defined!" 457 0 458#endif 459#if defined(BUTTON_UP) && defined(BUTTON_DOWN) 460 | BUTTON_UP | BUTTON_DOWN 461#endif 462#if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD) 463 | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD 464#endif 465#if (CONFIG_KEYPAD == SANSA_C200_PAD) || (CONFIG_KEYPAD == SANSA_CLIP_PAD) ||\ 466 (CONFIG_KEYPAD == GIGABEAT_PAD) || (CONFIG_KEYPAD == GIGABEAT_S_PAD) 467 | BUTTON_VOL_UP | BUTTON_VOL_DOWN 468#endif 469#if CONFIG_KEYPAD == PHILIPS_SA9200_PAD 470 | BUTTON_VOL_UP | BUTTON_VOL_DOWN 471 | BUTTON_NEXT | BUTTON_PREV 472#endif 473 ); 474 475#if defined(BUTTON_LEFT) && defined(BUTTON_RIGHT) 476 if (button & BUTTON_LEFT) 477 newbutton |= BUTTON_RIGHT; 478 if (button & BUTTON_RIGHT) 479 newbutton |= BUTTON_LEFT; 480#else 481#warning "LEFT/RIGHT not defined!" 482#endif 483 484#if defined(BUTTON_UP) && defined(BUTTON_DOWN) 485 if (button & BUTTON_UP) 486 newbutton |= BUTTON_DOWN; 487 if (button & BUTTON_DOWN) 488 newbutton |= BUTTON_UP; 489#endif 490#if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD) 491 if (button & BUTTON_SCROLL_BACK) 492 newbutton |= BUTTON_SCROLL_FWD; 493 if (button & BUTTON_SCROLL_FWD) 494 newbutton |= BUTTON_SCROLL_BACK; 495#endif 496#if (CONFIG_KEYPAD == SANSA_C200_PAD) || (CONFIG_KEYPAD == SANSA_CLIP_PAD) ||\ 497 (CONFIG_KEYPAD == GIGABEAT_PAD) || (CONFIG_KEYPAD == GIGABEAT_S_PAD) 498 if (button & BUTTON_VOL_UP) 499 newbutton |= BUTTON_VOL_DOWN; 500 if (button & BUTTON_VOL_DOWN) 501 newbutton |= BUTTON_VOL_UP; 502#endif 503#if CONFIG_KEYPAD == PHILIPS_SA9200_PAD 504 if (button & BUTTON_VOL_UP) 505 newbutton |= BUTTON_VOL_DOWN; 506 if (button & BUTTON_VOL_DOWN) 507 newbutton |= BUTTON_VOL_UP; 508 if (button & BUTTON_NEXT) 509 newbutton |= BUTTON_PREV; 510 if (button & BUTTON_PREV) 511 newbutton |= BUTTON_NEXT; 512#endif 513#endif /* !SIMULATOR */ 514 return newbutton; 515} 516 517/* 518 * set the flip attribute 519 * better only call this when the queue is empty 520 */ 521void button_set_flip(bool flip) 522{ 523 if (flip != flipped) /* not the current setting */ 524 { 525 /* avoid race condition with the button_tick() */ 526 int oldlevel = disable_irq_save(); 527 lastbtn = button_flip(lastbtn); 528 flipped = flip; 529 restore_irq(oldlevel); 530 } 531} 532#endif /* HAVE_LCD_FLIP */ 533 534#ifdef HAVE_BACKLIGHT 535void set_backlight_filter_keypress(bool value) 536{ 537 if (!value) 538 keypress_filter_fn = filter_first_keypress_disabled; 539 else 540 keypress_filter_fn = filter_first_keypress_enabled; 541} 542#ifdef HAVE_REMOTE_LCD 543void set_remote_backlight_filter_keypress(bool value) 544{ 545 if (!value) 546 remote_keypress_filter_fn = filter_first_keypress_disabled; 547 else 548 remote_keypress_filter_fn = filter_first_remote_keypress_enabled; 549} 550#endif 551#endif 552 553/* 554 * Get button pressed from hardware 555 */ 556 557static int button_read(int *data) 558{ 559#ifdef HAVE_BUTTON_DATA 560 int btn = button_read_device(data); 561#else 562 (void) data; 563 int btn = button_read_device(); 564#endif 565 int retval; 566 567#ifdef HAVE_LCD_FLIP 568 if (btn && flipped) 569 btn = button_flip(btn); /* swap upside down */ 570#endif /* HAVE_LCD_FLIP */ 571 572#ifdef HAVE_TOUCHSCREEN 573 if (btn & BUTTON_TOUCHSCREEN) 574 last_touchscreen_touch = current_tick; 575#endif 576 /* Filter the button status. It is only accepted if we get the same 577 status twice in a row. */ 578#ifndef HAVE_TOUCHSCREEN 579 if (btn != last_read) 580 retval = lastbtn; 581 else 582#endif 583 retval = btn; 584 last_read = btn; 585 586 return retval; 587} 588 589int button_status(void) 590{ 591 return lastbtn; 592} 593 594#ifdef HAVE_BUTTON_DATA 595int button_status_wdata(int *pdata) 596{ 597 *pdata = lastdata; 598 return lastbtn; 599} 600#endif 601 602#ifdef HAVE_TOUCHSCREEN 603long touchscreen_last_touch(void) 604{ 605 return last_touchscreen_touch; 606} 607#endif 608 609#ifdef HAVE_WHEEL_ACCELERATION 610/* WHEEL_ACCEL_FACTOR = 2^16 / WHEEL_ACCEL_START */ 611#define WHEEL_ACCEL_FACTOR (1<<16)/WHEEL_ACCEL_START 612/** 613 * data: 614 * [31] Use acceleration 615 * [30:24] Message post count (skipped + 1) (1-127) 616 * [23:0] Velocity - degree/sec 617 * 618 * WHEEL_ACCEL_FACTOR: 619 * Value in degree/sec -- configurable via settings -- above which 620 * the accelerated scrolling starts. Factor is internally scaled by 621 * 1<<16 in respect to the following 32bit integer operations. 622 */ 623int button_apply_acceleration(const unsigned int data) 624{ 625 int delta = (data >> 24) & 0x7f; 626 627 if ((data & (1 << 31)) != 0) 628 { 629 /* read driver's velocity from data */ 630 unsigned int v = data & 0xffffff; 631 632 /* v = 28.4 fixed point */ 633 v = (WHEEL_ACCEL_FACTOR * v)>>(16-4); 634 635 /* Calculate real numbers item to scroll based upon acceleration 636 * setting, use correct roundoff */ 637#if (WHEEL_ACCELERATION == 1) 638 v = (v*v + (1<< 7))>> 8; 639#elif (WHEEL_ACCELERATION == 2) 640 v = (v*v*v + (1<<11))>>12; 641#elif (WHEEL_ACCELERATION == 3) 642 v = (v*v*v*v + (1<<15))>>16; 643#endif 644 645 if (v > 1) 646 delta *= v; 647 } 648 649 return delta; 650} 651#endif /* HAVE_WHEEL_ACCELERATION */ 652 653#if (defined(HAVE_TOUCHPAD) || defined(HAVE_TOUCHSCREEN)) && !defined(HAS_BUTTON_HOLD) 654void button_enable_touch(bool en) 655{ 656#ifdef HAVE_TOUCHPAD 657 touchpad_enable(en); 658#endif 659#ifdef HAVE_TOUCHSCREEN 660 touchscreen_enable(en); 661#endif 662} 663#endif 664 665#ifdef HAVE_SW_POWEROFF 666void button_set_sw_poweroff_state(bool en) { 667 enable_sw_poweroff = en; 668} 669 670bool button_get_sw_poweroff_state() { 671 return enable_sw_poweroff; 672} 673#endif