ControllerTracker HandleButtonDown(EControllerButton btn, EEventSet event_set)
        {
            if ((bitmask_buttons_down & (1U << (int)btn)) == 0)
            {
                return(null);    /* not actually pressing this button */
            }
            ControllerTracker tracker = FindHandler(event_set);

            if (tracker != null)
            {
                if (tracker == tracker_hover && btn != EControllerButton.Menu)
                {
                    tracker_hover_lock |= 1U << (int)btn;
                }

                ControllerEvent ev = null;
                switch (btn)
                {
                case EControllerButton.Trigger:  ev = tracker._i_onTriggerDown;    break;

                case EControllerButton.Grip:     ev = tracker._i_onGripDown;       break;

                case EControllerButton.Menu:     ev = tracker._i_onMenuClick;      break;
                }
                tracker._Call(ev, this);
            }
            return(tracker);
        }
        void CallControllerMove()
        {
            if (active_trigger != null)
            {
                active_trigger._Call(active_trigger._i_onTriggerDrag, this);
            }

            if (active_grip != null)
            {
                active_grip._Call(active_grip._i_onGripDrag, this);
            }

            uint lock_ignore = MANUAL_LOCK;

            if (active_touchpad != null)
            {
                switch (active_touchpad_state)
                {
                case ActiveTouchpadState.Action1:
                    active_touchpad._Call(active_touchpad._i_onTouchPressDrag, this);
                    break;

                case ActiveTouchpadState.Action2:
                    Vector2 p = touchpadPosition;
                    Vector2 d = p - touch_original_pos2;
                    touch_original_pos2 = p;
                    active_touchpad._Call(active_touchpad._i_onTouchScroll, this, d);

                    if (active_touchpad.isHapticScrollEnabled)
                    {
                        /* in the Action2 mode, active_touchpad_timeout is abused to decrease not
                         * based on time but based on distance */
                        active_touchpad_timeout -= d.magnitude;
                        if (active_touchpad_timeout < 0)
                        {
                            const int   TOUCHPAD_HAPTIC_STRENGTH = 200;
                            const float TOUCHPAD_HAPTIC_DISTANCE = 0.1f;

                            HapticPulse(TOUCHPAD_HAPTIC_STRENGTH);
                            active_touchpad_timeout = TOUCHPAD_HAPTIC_DISTANCE;
                        }
                    }
                    break;

                case ActiveTouchpadState.Action3:
                    active_touchpad._Call(active_touchpad._i_onTouchDrag, this);
                    break;

                case ActiveTouchpadState.SmallDelay:
                    lock_ignore |= 1U << (int)EControllerButton.Touchpad;
                    break;
                }
            }

            if (tracker_hover != null && ((tracker_hover_lock & ~lock_ignore) == 0))
            {
                tracker_hover._Call(tracker_hover._i_onMoveOver, this);
            }
        }
        void LeaveNow()
        {
            ControllerTracker prev = tracker_hover;

            tracker_hover      = null;
            tracker_hover_lock = 0;
            prev._Call(prev._i_onLeave, this);
            SetPointer("");
        }
        void CallEnterEvents()
        {
            if (tracker_hover_next == null)
            {
                return;
            }

            if (tracker_hover == null)
            {
                tracker_hover = tracker_hover_next;
                tracker_hover._Call(tracker_hover._i_onEnter, this);
            }
            Debug.Assert(tracker_hover == tracker_hover_next);
        }