summaryrefslogtreecommitdiffstats
path: root/apps/action.c
blob: 286d4f39b333ae56943d12ad52a561241d21ad15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
/***************************************************************************
 *             __________               __   ___.
 *   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

#ifdef HAVE_BACKLIGHT
#include "backlight.h"
#if CONFIG_CHARGING
#include "power.h"
#endif
#endif /* HAVE_BACKLIGHT */

#define LOGF_ENABLE
#include "logf.h"

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;

#if defined(HAVE_BACKLIGHT) || !defined(HAS_BUTTON_HOLD)
static inline bool is_action_normal(int action);
static inline bool mask_has_flag(unsigned int mask, unsigned int flag);
static inline bool is_action_completed(int button);
#define LAST_FILTER_TICKS HZ/2 /* timeout between filtered actions */
#endif /* defined(HAVE_BACKLIGHT) || !defined(HAS_BUTTON_HOLD) */

#ifdef HAVE_BACKLIGHT
static unsigned int backlight_mask = SEL_ACTION_NONE;
static int do_backlight(int action, int context, bool is_pre_btn);
static void handle_backlight(bool backlight, bool ignore_next);
#endif /* HAVE_BACKLIGHT */

/* software keylock stuff */
#ifndef HAS_BUTTON_HOLD
static bool keys_locked = false;
static bool screen_has_lock = false;
static unsigned int softlock_mask = SEL_ACTION_NONE;
static inline int do_softlock_unlock_combo(int button, int context);
static int do_softlock(int button, int action, int context, bool is_pre_btn);
#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.
 * BE AWARE is_pre_button can miss pre buttons if a match is found first.
 */
static inline int do_button_check(const struct button_mapping *items,int button,
                                    int last_button, int *start, bool *prebtn)
{
    int i = 0;
    int ret = ACTION_UNKNOWN;

    while (items[i].button_code != BUTTON_NONE)
    {
        if (items[i].button_code == button)
        {
            /*
                CAVEAT: This will allways return the action without pre_button_code if it has a
                lower index in the list.
            */
            if ((items[i].pre_button_code == BUTTON_NONE)
                || (items[i].pre_button_code == last_button))
            {
                ret = items[i].action_code;
                break;
            }
        }
        else if (items[i].pre_button_code & button)
            *prebtn = true; /* determine if this could be another action */
        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;
}

#if defined(HAVE_GUI_BOOST) && defined(HAVE_ADJUSTABLE_CPU_FREQ)

/* Timeout for gui boost in seconds. */
#define GUI_BOOST_TIMEOUT (HZ)

/* Helper function which is called to boost / unboost CPU. This function
 * avoids to increase boost_count with each call of gui_boost(). */
static void gui_boost(bool want_to_boost)
{
    static bool boosted = false;

    if (want_to_boost && !boosted)
    {
        cpu_boost(true);
        boosted = true;
    }
    else if (!want_to_boost && boosted)
    {
        cpu_boost(false);
        boosted = false;
    }
}

/* gui_unboost_callback() is called GUI_BOOST_TIMEOUT seconds after the
 * last wheel scrolling event. */
static int gui_unboost_callback(struct timeout *tmo)
{
    (void)tmo;
    gui_boost(false);
    return 0;
}
#endif

/*
 * int get_action_worker(int context, int timeout, bool *is_pre_button,
                         struct button_mapping *user_mappings)
   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.
   BE AWARE is_pre_button can miss pre buttons if a match is found first.
    it is more for actions that are not yet completed in the desired context
     but are defined in a lower 'chained' context.
   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, bool *is_pre_button,
                             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);

#if defined(HAVE_GUI_BOOST) && defined(HAVE_ADJUSTABLE_CPU_FREQ)
    static struct timeout gui_unboost;
    /* Boost the CPU in case of wheel scrolling activity in the defined contexts.
     * Call unboost with a timeout of GUI_BOOST_TIMEOUT. */
    if (button != BUTTON_NONE)
    {
        gui_boost(true);
        timeout_register(&gui_unboost, gui_unboost_callback, GUI_BOOST_TIMEOUT, 0);
    }
#endif

    /* 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))
    {
        /* no button pressed so no point in waiting for release */
        if (button == BUTTON_NONE)
            wait_for_release = false;
        return button;
    }

    /* the special redraw button should result in a screen refresh */
    if (button == BUTTON_REDRAW)
        return ACTION_REDRAW;

    /* if action_wait_for_release() was called without a button being pressed
     * then actually waiting for release would do the wrong thing, i.e.
     * the next key press is entirely ignored. So, if here comes a normal
     * button press (neither release nor repeat) the press is a fresh one and
     * no point in waiting for release
     *
     * This logic doesn't work for touchscreen which can send normal
     * button events repeatedly before the first repeat (as in BUTTON_REPEAT).
     * These cannot be distinguished from the very first touch
     * but there's nothing we can do about it here */
    if ((button & (BUTTON_REPEAT|BUTTON_REL)) == 0)
        wait_for_release = false;

    /* 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 ((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);
    context &= ~ALLOW_SOFTLOCK;
    if (is_keys_locked())
    {
        ret = do_softlock_unlock_combo(button, context);
        if (!is_keys_locked())
            return ret;
    }
#if defined(HAVE_TOUCHPAD) || defined(HAVE_TOUCHSCREEN)
    else if (!mask_has_flag(softlock_mask, SEL_ACTION_NOTOUCH))
    {
        /* make sure touchpad get reactivated if we quit the screen */
        button_enable_touch(true);
    }
#endif

#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); */
    *is_pre_button = false; /* could the button be another actions pre_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, is_pre_button);

        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 && is_action_normal(ret))
        ret = do_softlock(button, ret, last_context & ~ALLOW_SOFTLOCK, is_pre_button);
#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;

#if CONFIG_CODEC == SWCODEC
    /* Produce keyclick */
    keyclick_click(false, ret);
#endif

    return ret;
}/* get_action_worker */

int get_action(int context, int timeout)
{
    bool is_pre_button = false;
    int  button = get_action_worker(context, timeout, &is_pre_button, NULL);

#ifdef HAVE_TOUCHSCREEN
    if (button == ACTION_TOUCHSCREEN)
        button = sb_touch_to_button(context);
#endif

#ifdef HAVE_BACKLIGHT
    if (mask_has_flag(backlight_mask, SEL_ACTION_ENABLED) &&
                                                        is_action_normal(button))
        button = do_backlight(button, context & ~ALLOW_SOFTLOCK, is_pre_button);
#endif

    return button;
}

int get_custom_action(int context,int timeout,
                      const struct button_mapping* (*get_context_map)(int))
{
    bool is_pre_button = false;
    return get_action_worker(context,timeout, &is_pre_button, get_context_map);
}

bool action_userabort(int timeout)
{
    bool is_pre_button = false;
    int action = get_action_worker(CONTEXT_STD,timeout, &is_pre_button, NULL);
    bool ret = (action == ACTION_STD_CANCEL);
    if (!ret)
    {
        default_event_handler(action);
    }
    return ret;
}

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.
 *
 * Note this doesn't currently work for touchscreen targets if called
 * when the screen isn't currently touched, because they can send normal
 * (non-BUTTON_REPEAT) events repeatedly, if the touch coordinates change.
 * This cannot be distinguished from normal buttons events.
 */
void action_wait_for_release(void)
{
    wait_for_release = true;
}

#if defined(HAVE_BACKLIGHT) || !defined(HAS_BUTTON_HOLD)
/* HELPER FUNCTIONS
* Selective softlock and backlight use this lookup based on mask to decide
* which actions are filtered, or could be filtered but not currently set.
* returns false if the action isn't found, true if the action is found
*/
static bool is_action_filtered(int action, unsigned int mask, int context)
{
    bool match = false;

    switch (action)
    {
        case ACTION_NONE:
            break;
/*Actions that are not mapped will not turn on the backlight option NOUNMAPPED*/
        case ACTION_UNKNOWN:
            match = mask_has_flag(mask, SEL_ACTION_NOUNMAPPED);
            break;
        case ACTION_WPS_PLAY:
        case ACTION_FM_PLAY:
            match = mask_has_flag(mask, SEL_ACTION_PLAY);
            break;
        case ACTION_STD_PREVREPEAT:
        case ACTION_STD_NEXTREPEAT:
        case ACTION_WPS_SEEKBACK:
        case ACTION_WPS_SEEKFWD:
        case ACTION_WPS_STOPSEEK:
            match = mask_has_flag(mask, SEL_ACTION_SEEK);
            break;
        case ACTION_STD_PREV:
        case ACTION_STD_NEXT:
        case ACTION_WPS_SKIPNEXT:
        case ACTION_WPS_SKIPPREV:
        case ACTION_FM_NEXT_PRESET:
        case ACTION_FM_PREV_PRESET:
            match = mask_has_flag(mask, SEL_ACTION_SKIP);
            break;
        case ACTION_WPS_VOLUP:
        case ACTION_WPS_VOLDOWN:
            match = mask_has_flag(mask, SEL_ACTION_VOL);
            break;
        case ACTION_SETTINGS_INC:/*FMS*/
        case ACTION_SETTINGS_INCREPEAT:/*FMS*/
        case ACTION_SETTINGS_DEC:/*FMS*/
        case ACTION_SETTINGS_DECREPEAT:/*FMS*/
            match = (context == CONTEXT_FM) && mask_has_flag(mask, SEL_ACTION_VOL);
            break;
        default:
            /* display action code of unfiltered actions */
            logf ("unfiltered actions: context: %d action: %d, last btn: %d, \
                  mask: %d", context, action, last_button, mask);
            break;
    }/*switch*/

    return match;
}
/* compares mask to a flag return true if set false otherwise*/
static inline bool mask_has_flag(unsigned int mask, unsigned int flag)
{
    return ((mask & flag) != 0);
}
/* returns true if the supplied context is to be filtered by selective BL/SL*/
static inline bool is_context_filtered(int context)
{
    return (context == CONTEXT_FM || context == CONTEXT_WPS);
}
/* returns true if action can be passed on to selective backlight/softlock */
static inline bool is_action_normal(int action)
{
    return (action != ACTION_REDRAW && (action & SYS_EVENT) == 0);
}
/*returns true if Button & released, repeated; or won't generate those events*/
static inline bool is_action_completed(int button)
{
    return ((button & (BUTTON_REPEAT | BUTTON_REL)) != 0
#ifdef HAVE_SCROLLWHEEL
        /* Scrollwheel doesn't generate release events  */
            || (button & (BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD)) != 0
#endif
        );
}

/* most every action takes two rounds through get_action_worker,
 * once for the keypress and once for the key release,
 * actions with pre_button codes take even more, some actions however, only
 * take once; actions defined with only a button and no release/repeat event,
 * these actions should be acted upon immediately except when we have
 * selective backlighting/softlock enabled and in this case we only act upon
 * them immediately if there is no chance they have another event tied to them
 * determined using is_pre_button and is_action_completed()
 *returns true if event was not filtered and false if it was
*/
static bool is_action_unfiltered(int action,int button, bool is_pre_button,
                               bool filtered, int *tick)
{
    bool ret = false;
        /*directly after a match a key release event may trigger another*/
        if (filtered && action != ACTION_UNKNOWN)
            *tick = current_tick + LAST_FILTER_TICKS;
        /* has button been rel/rep or is this the only action it could be */
        if (is_action_completed(button) || !is_pre_button)
        {
            /* reset last action , if the action is not filtered and
               this isn't just a key release event then return true */
            if (!filtered && *tick < current_tick)
            {
                *tick = 0;
                ret = true;
            }
         }/*is_action_completed() || !is_pre_button*/
    return ret;
}
#endif /*defined(HAVE_BACKLIGHT) || !defined(HAS_BUTTON_HOLD) HELPER FUNCTIONS*/

#ifdef HAVE_BACKLIGHT
static void handle_backlight(bool backlight, bool ignore_next)
{
    if (backlight)
    {
        backlight_on_ignore(false, 0);
        backlight_on();
#ifdef HAVE_BUTTON_LIGHT
        buttonlight_on_ignore(false, 0);
        buttonlight_on();
    }
    buttonlight_on_ignore(ignore_next, 5*HZ);/* as a precautionary fallback */
#else
    }
#endif
    backlight_on_ignore(ignore_next, 5*HZ);/*must be set everytime we handle bl*/
}

    /* Need to look up actions before we can decide to turn on backlight, if
     * selective backlighting is true filter first keypress events need to be
     * taken into account as well
     */
static int do_backlight(int action, int context, bool is_pre_btn)
{
    static int last_filtered_tick = 0;

    bool bl_is_active = is_backlight_on(false);
    bool bl_activate = false;
    bool filtered;

#if CONFIG_CHARGING /* disable if on external power */
    if (!bl_is_active && is_context_filtered(context) &&
    !(mask_has_flag(backlight_mask, SEL_ACTION_NOEXT) && power_input_present()))
#else /* skip if backlight is on or incorrect context */
    if (!bl_is_active && is_context_filtered(context))
#endif
    {
        filtered = is_action_filtered(action, backlight_mask, context);
        bl_activate = is_action_unfiltered(action, last_button, is_pre_btn,
                                               filtered, &last_filtered_tick);
    }/*is_context_filtered(context)*/
    else
        bl_activate = true;

    if (action != ACTION_NONE && bl_activate)
    {
        handle_backlight(true, true);

        if (mask_has_flag(backlight_mask, SEL_ACTION_FFKEYPRESS) && !bl_is_active)
        {
            action      = ACTION_NONE;
            last_button = BUTTON_NONE;
        }
    }
    else
        handle_backlight(false, true);/* set ignore next true */

    return action;
}

/* Enable selected actions to leave the backlight off */
void set_selective_backlight_actions(bool selective, unsigned int mask,
                                                              bool filter_fkp)
{
    handle_backlight(true, selective);
    if (selective) /* we will handle filter_first_keypress here so turn it off*/
    {
        set_backlight_filter_keypress(false);/* turnoff ffkp in button.c */
        backlight_mask = mask | SEL_ACTION_ENABLED;
        if(filter_fkp)
            backlight_mask |= SEL_ACTION_FFKEYPRESS;
    }
    else
    {
        set_backlight_filter_keypress(filter_fkp);
        backlight_mask = SEL_ACTION_NONE;
    }
}
#endif /* HAVE_BACKLIGHT */

#ifndef HAS_BUTTON_HOLD
bool is_keys_locked(void)
{
    return (screen_has_lock && keys_locked);
}

static inline void do_key_lock(bool lock)
{
    keys_locked = lock;
    last_button = BUTTON_NONE;
    button_clear_queue();
#if defined(HAVE_TOUCHPAD) || defined(HAVE_TOUCHSCREEN)
 /* disable touch device on keylock if std behavior or selected disable touch */
     if (!mask_has_flag(softlock_mask, SEL_ACTION_ENABLED) ||
          mask_has_flag(softlock_mask, SEL_ACTION_NOTOUCH))
            button_enable_touch(!lock);
#endif
}

/* user selected autolock based on backlight timeout; toggles autolock on / off
    by ACTION_STD_KEYLOCK presses, Activates autolock if set on backlight timeout
*/
#ifdef HAVE_BACKLIGHT
static inline int do_auto_softlock(int button, int action, int *unlock_combo)
{
    if(mask_has_flag(softlock_mask, SEL_ACTION_ALOCK_OK) && !is_backlight_on(false))
        do_key_lock(true);

    else if (action == ACTION_STD_KEYLOCK)
    {
        *unlock_combo = button;/* set unlock combo to allow unlock */
        softlock_mask ^= SEL_ACTION_ALOCK_OK;
        handle_backlight(true, false);
            /* If we don't wait for a moment for the backlight queue
             *  to process, the user will never see the message */
        if (!is_backlight_on(false))
            sleep(HZ/2);

        if (mask_has_flag(softlock_mask, SEL_ACTION_ALOCK_OK))
        {
            splash(HZ/2, ID2P(LANG_ACTION_AUTOLOCK_ON));
            action = ACTION_REDRAW;
        }
        else
            splash(HZ/2, ID2P(LANG_ACTION_AUTOLOCK_OFF));
    }
    return action;
}
#endif

/* Allows unlock softlock when action is not yet known but unlock_combo set*/
static inline int do_softlock_unlock_combo(int button, int context)
{
return do_softlock(button, ACTION_NONE, context, false);
}

/* Handles softlock once action is known */
static int do_softlock(int button, int action, int context, bool is_pre_btn)
{
    static int last_filtered_tick = 0;
    static int unlock_combo = BUTTON_NONE; /*Moved from GLOBAL*/
    bool filtered = true;
    bool notify_user = false;
    bool sl_activate = true; /* standard softlock behavior */

#ifdef HAVE_BACKLIGHT
    if (!keys_locked && mask_has_flag(softlock_mask, SEL_ACTION_AUTOLOCK))
        action = do_auto_softlock(button, action, &unlock_combo);
#endif
    /* Lock/Unlock toggled by ACTION_STD_KEYLOCK presses*/
    if ((action == ACTION_STD_KEYLOCK) || (keys_locked && unlock_combo == button))
    {
        unlock_combo = button;
        do_key_lock(!keys_locked);
        notify_user = true;
    }
#if (BUTTON_REMOTE != 0)/* Allow remote actions through */
    else if (mask_has_flag(button, BUTTON_REMOTE))
            notify_user = false;
#endif
    else if (keys_locked && action != ACTION_NONE && action != ACTION_REDRAW)
    {
        if (mask_has_flag(softlock_mask, SEL_ACTION_ENABLED))
        {
            filtered = is_action_filtered(action, softlock_mask, context);

            sl_activate = is_action_unfiltered(action, button, is_pre_btn,
                                                   filtered, &last_filtered_tick);
        }
        /*All non-std softlock options are set to 0 if advanced sl is disabled*/
        if (sl_activate)
        {
            if (!mask_has_flag(softlock_mask, SEL_ACTION_NONOTIFY))
            {   /* always true on standard softlock behavior*/
                notify_user = mask_has_flag(button, BUTTON_REL);
                action = ACTION_REDRAW;
            }
            else
                action = ACTION_NONE;
        }
        else if (!filtered)/*catch blocked actions on fast repeated presses*/
                action = ACTION_NONE;
     } /* keys_locked */

#ifdef BUTTON_POWER /*always notify if power button pressed while keys locked*/
    notify_user |= (mask_has_flag(button, BUTTON_POWER) && keys_locked);
#endif

    if (notify_user)
    {
#ifdef HAVE_BACKLIGHT
        handle_backlight(true, false);
            /* If we don't wait for a moment for the backlight queue
             *  to process, the user will never see the message */
        if (!is_backlight_on(false))
            sleep(HZ/2);
#endif
        if (keys_locked)
            splash(HZ/2, ID2P(LANG_KEYLOCK_ON));
        else
            splash(HZ/2, ID2P(LANG_KEYLOCK_OFF));

        last_button = BUTTON_NONE;
        action      = ACTION_REDRAW;
        button_clear_queue();
    }

    return action;
}

void set_selective_softlock_actions(bool selective, unsigned int mask)
{
    keys_locked = false;
    if(selective)
        softlock_mask = mask | SEL_ACTION_ENABLED;
    else
        softlock_mask = SEL_ACTION_NONE;
}

#endif /* HAS_BUTTON_HOLD */