/apps/action.c
C | 445 lines | 332 code | 44 blank | 69 comment | 101 complexity | e03490b1129f30307530038a4fa7aa74 MD5 | raw file
- /***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2006 Jonathan Gordon
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include "config.h"
- #include "lang.h"
- #include "appevents.h"
- #include "button.h"
- #include "action.h"
- #include "kernel.h"
- #include "debug.h"
- #include "splash.h"
- #include "settings.h"
- #include "pcmbuf.h"
- #include "misc.h"
- #if defined(HAVE_LCD_BITMAP) && !defined(BOOTLOADER)
- #include "language.h"
- #endif
- #include "viewport.h"
- #ifdef HAVE_TOUCHSCREEN
- #include "statusbar-skinned.h"
- #endif
- static int last_button = BUTTON_NONE|BUTTON_REL; /* allow the ipod wheel to
- work on startup */
- static intptr_t last_data = 0;
- static int last_action = ACTION_NONE;
- static bool repeated = false;
- static bool wait_for_release = false;
- #ifdef HAVE_TOUCHSCREEN
- static bool short_press = false;
- #endif
- #define REPEAT_WINDOW_TICKS HZ/4
- static int last_action_tick = 0;
- /* software keylock stuff */
- #ifndef HAS_BUTTON_HOLD
- static bool keys_locked = false;
- static int unlock_combo = BUTTON_NONE;
- static bool screen_has_lock = false;
- #endif /* HAVE_SOFTWARE_KEYLOCK */
- /*
- * do_button_check is the worker function for get_default_action.
- * returns ACTION_UNKNOWN or the requested return value from the list.
- */
- static inline int do_button_check(const struct button_mapping *items,
- int button, int last_button, int *start)
- {
- int i = 0;
- int ret = ACTION_UNKNOWN;
- while (items[i].button_code != BUTTON_NONE)
- {
- if (items[i].button_code == button)
- {
- if ((items[i].pre_button_code == BUTTON_NONE)
- || (items[i].pre_button_code == last_button))
- {
- ret = items[i].action_code;
- break;
- }
- }
- i++;
- }
- *start = i;
- return ret;
- }
- #if defined(HAVE_LCD_BITMAP) && !defined(BOOTLOADER)
- /*
- * button is horizontally inverted to support RTL language if the given language
- * and context combination require that
- */
- static int button_flip_horizontally(int context, int button)
- {
- int newbutton;
- if (!(lang_is_rtl() && ((context == CONTEXT_STD) ||
- (context == CONTEXT_TREE) || (context == CONTEXT_LIST) ||
- (context == CONTEXT_MAINMENU))))
- {
- return button;
- }
- newbutton = button &
- ~(BUTTON_LEFT | BUTTON_RIGHT
- #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD) && \
- !defined(SIMULATOR)
- | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD
- #endif
- #if defined(BUTTON_MINUS) && defined(BUTTON_PLUS) && \
- !defined(SIMULATOR)
- | BUTTON_MINUS | BUTTON_PLUS
- #endif
- );
- if (button & BUTTON_LEFT)
- newbutton |= BUTTON_RIGHT;
- if (button & BUTTON_RIGHT)
- newbutton |= BUTTON_LEFT;
- #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD) && \
- !defined(SIMULATOR)
- if (button & BUTTON_SCROLL_BACK)
- newbutton |= BUTTON_SCROLL_FWD;
- if (button & BUTTON_SCROLL_FWD)
- newbutton |= BUTTON_SCROLL_BACK;
- #endif
- #if defined(BUTTON_MINUS) && defined(BUTTON_PLUS) && \
- !defined(SIMULATOR)
- if (button & BUTTON_MINUS)
- newbutton |= BUTTON_PLUS;
- if (button & BUTTON_PLUS)
- newbutton |= BUTTON_MINUS;
- #endif
- return newbutton;
- }
- #endif
- static inline int get_next_context(const struct button_mapping *items, int i)
- {
- while (items[i].button_code != BUTTON_NONE)
- i++;
- return (items[i].action_code == ACTION_NONE ) ?
- CONTEXT_STD :
- items[i].action_code;
- }
- /*
- * int get_action_worker(int context, struct button_mapping *user_mappings,
- int timeout)
- This function searches the button list for the given context for the just
- pressed button.
- If there is a match it returns the value from the list.
- If there is no match..
- the last item in the list "points" to the next context in a chain
- so the "chain" is followed until the button is found.
- putting ACTION_NONE will get CONTEXT_STD which is always the last list checked.
- Timeout can be TIMEOUT_NOBLOCK to return immediatly
- TIMEOUT_BLOCK to wait for a button press
- Any number >0 to wait that many ticks for a press
- */
- static int get_action_worker(int context, int timeout,
- const struct button_mapping* (*get_context_map)(int) )
- {
- const struct button_mapping *items = NULL;
- int button;
- int i=0;
- int ret = ACTION_UNKNOWN;
- static int last_context = CONTEXT_STD;
-
-
- send_event(GUI_EVENT_ACTIONUPDATE, NULL);
- if (timeout == TIMEOUT_NOBLOCK)
- button = button_get(false);
- else if (timeout == TIMEOUT_BLOCK)
- button = button_get(true);
- else
- button = button_get_w_tmo(timeout);
- /* Data from sys events can be pulled with button_get_data
- * multimedia button presses don't go through the action system */
- if (button == BUTTON_NONE || button & (SYS_EVENT|BUTTON_MULTIMEDIA))
- return button;
- /* Don't send any buttons through untill we see the release event */
- if (wait_for_release)
- {
- if (button&BUTTON_REL)
- {
- /* remember the button for the below button eating on context
- * change */
- last_button = button;
- wait_for_release = false;
- }
- return ACTION_NONE;
- }
-
- #if CONFIG_CODEC == SWCODEC
- /* Produce keyclick */
- keyclick_click(button);
- #endif
- if ((context != last_context) && ((last_button & BUTTON_REL) == 0)
- #ifdef HAVE_SCROLLWHEEL
- /* Scrollwheel doesn't generate release events */
- && !(last_button & (BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD))
- #endif
- )
- {
- if (button & BUTTON_REL)
- {
- last_button = button;
- last_action = ACTION_NONE;
- }
- /* eat all buttons until the previous button was |BUTTON_REL
- (also eat the |BUTTON_REL button) */
- return ACTION_NONE; /* "safest" return value */
- }
- last_context = context;
- #ifndef HAS_BUTTON_HOLD
- screen_has_lock = ((context & ALLOW_SOFTLOCK) == ALLOW_SOFTLOCK);
- if (screen_has_lock && keys_locked)
- {
- if (button == unlock_combo)
- {
- last_button = BUTTON_NONE;
- keys_locked = false;
- splash(HZ/2, str(LANG_KEYLOCK_OFF));
- return ACTION_REDRAW;
- }
- else
- #if (BUTTON_REMOTE != 0)
- if ((button & BUTTON_REMOTE) == 0)
- #endif
- {
- if ((button & BUTTON_REL))
- splash(HZ/2, str(LANG_KEYLOCK_ON));
- return ACTION_REDRAW;
- }
- }
- context &= ~ALLOW_SOFTLOCK;
- #endif /* HAS_BUTTON_HOLD */
- #ifdef HAVE_TOUCHSCREEN
- if (button & BUTTON_TOUCHSCREEN)
- {
- repeated = false;
- short_press = false;
- if (last_button & BUTTON_TOUCHSCREEN)
- {
- if ((button & BUTTON_REL) &&
- ((last_button & BUTTON_REPEAT)==0))
- {
- short_press = true;
- }
- else if (button & BUTTON_REPEAT)
- repeated = true;
- }
- last_button = button;
- return ACTION_TOUCHSCREEN;
- }
- #endif
- #if defined(HAVE_LCD_BITMAP) && !defined(BOOTLOADER)
- button = button_flip_horizontally(context, button);
- #endif
- /* logf("%x,%x",last_button,button); */
- while (1)
- {
- /* logf("context = %x",context); */
- #if (BUTTON_REMOTE != 0)
- if (button & BUTTON_REMOTE)
- context |= CONTEXT_REMOTE;
- #endif
- if ((context & CONTEXT_PLUGIN) && get_context_map)
- items = get_context_map(context);
- else
- items = get_context_mapping(context);
- if (items == NULL)
- break;
- ret = do_button_check(items,button,last_button,&i);
- if (ret == ACTION_UNKNOWN)
- {
- context = get_next_context(items,i);
- if (context != (int)CONTEXT_STOPSEARCHING)
- {
- i = 0;
- continue;
- }
- }
- /* Action was found or STOPSEARCHING was specified */
- break;
- }
- /* DEBUGF("ret = %x\n",ret); */
- #ifndef HAS_BUTTON_HOLD
- if (screen_has_lock && (ret == ACTION_STD_KEYLOCK))
- {
- unlock_combo = button;
- keys_locked = true;
- splash(HZ/2, str(LANG_KEYLOCK_ON));
- button_clear_queue();
- return ACTION_REDRAW;
- }
- #endif
- if ((current_tick - last_action_tick < REPEAT_WINDOW_TICKS)
- && (ret == last_action))
- repeated = true;
- else
- repeated = false;
- last_button = button;
- last_action = ret;
- last_data = button_get_data();
- last_action_tick = current_tick;
- return ret;
- }
- int get_action(int context, int timeout)
- {
- int button = get_action_worker(context,timeout,NULL);
- #ifdef HAVE_TOUCHSCREEN
- if (button == ACTION_TOUCHSCREEN)
- button = sb_touch_to_button(context);
- #endif
- return button;
- }
- int get_custom_action(int context,int timeout,
- const struct button_mapping* (*get_context_map)(int))
- {
- return get_action_worker(context,timeout,get_context_map);
- }
- bool action_userabort(int timeout)
- {
- int action = get_action_worker(CONTEXT_STD,timeout,NULL);
- bool ret = (action == ACTION_STD_CANCEL);
- if(!ret)
- {
- default_event_handler(action);
- }
- return ret;
- }
- #ifndef HAS_BUTTON_HOLD
- bool is_keys_locked(void)
- {
- return (screen_has_lock && keys_locked);
- }
- #endif
- intptr_t get_action_data(void)
- {
- return last_data;
- }
- int get_action_statuscode(int *button)
- {
- int ret = 0;
- if (button)
- *button = last_button;
- if (last_button & BUTTON_REMOTE)
- ret |= ACTION_REMOTE;
- if (repeated)
- ret |= ACTION_REPEAT;
- return ret;
- }
- #ifdef HAVE_TOUCHSCREEN
- /* return BUTTON_NONE on error
- * BUTTON_REPEAT if repeated press
- * BUTTON_REPEAT|BUTTON_REL if release after repeated press
- * BUTTON_REL if it's a short press = release after press
- * BUTTON_TOUCHSCREEN if press
- */
- int action_get_touchscreen_press(short *x, short *y)
- {
- static int last_data = 0;
- int data;
- if ((last_button & BUTTON_TOUCHSCREEN) == 0)
- return BUTTON_NONE;
- data = button_get_data();
- if (last_button & BUTTON_REL)
- {
- *x = (last_data&0xffff0000)>>16;
- *y = (last_data&0xffff);
- }
- else
- {
- *x = (data&0xffff0000)>>16;
- *y = (data&0xffff);
- }
- last_data = data;
- if (repeated)
- return BUTTON_REPEAT;
- if (short_press)
- return BUTTON_REL;
- /* This is to return a BUTTON_REL after a BUTTON_REPEAT. */
- if (last_button & BUTTON_REL)
- return BUTTON_REPEAT|BUTTON_REL;
- return BUTTON_TOUCHSCREEN;
- }
- int action_get_touchscreen_press_in_vp(short *x1, short *y1, struct viewport *vp)
- {
- short x, y;
- int ret;
- ret = action_get_touchscreen_press(&x, &y);
- if (ret != BUTTON_NONE && viewport_point_within_vp(vp, x, y))
- {
- *x1 = x - vp->x;
- *y1 = y - vp->y;
- return ret;
- }
- if (ret & BUTTON_TOUCHSCREEN)
- return ACTION_UNKNOWN;
- return BUTTON_NONE;
- }
- #endif
- /* Don't let get_action*() return any ACTION_* values until the current buttons
- * have been released. SYS_* and BUTTON_NONE will go through.
- * Any actions relying on _RELEASE won't get seen.
- */
- void action_wait_for_release(void)
- {
- wait_for_release = true;
- }
-