private bool HandleSpecialButtonCombination(MouseKeyboardHook.MouseHookEventArgs e)
        {
            if (_captured)
            {
                return(false);
            }

            var mouseSwapped   = Native.GetSystemMetrics(Native.SystemMetric.SM_SWAPBUTTON) != 0;
            var lButtonPressed = Native.GetAsyncKeyState(mouseSwapped ? Keys.RButton : Keys.LButton) < 0;
            var shiftPressed   = Native.GetAsyncKeyState(Keys.ShiftKey) < 0;

            if (e.Msg == MouseMsg.WM_MBUTTONDOWN && lButtonPressed)
            {
                if (shiftPressed)
                {
                    Post(WM.GUI_REQUEST, (int)GUI_RequestType.ShowHideTray);
                }
                else
                {
                    Post(WM.GUI_REQUEST, (int)GUI_RequestType.PauseResume);
                }
                return(true);
            }
            return(false);
        }
Example #2
0
        //Note: Hook runs on separate thread!
        private void _hook_MouseHookEvent(MouseKeyboardHook.MouseHookEventArgs e)
        {
            if (Paused)
            {
                return;
            }

            if (_activeCollideEdge != null && e.Msg == MouseMsg.WM_LBUTTONUP || e.Msg == MouseMsg.WM_RBUTTONUP)
            {
                _activeCollideEdge     = null;
                _hLastPtOutsideRedline = new PointAndTime();
                _vLastPtOutsideRedline = new PointAndTime();
            }

            if (e.Msg != MouseMsg.WM_MOUSEMOVE)
            {
                return;
            }

            if (Native.GetAsyncKeyState(System.Windows.Forms.Keys.LButton) < 0 ||
                Native.GetAsyncKeyState(System.Windows.Forms.Keys.RButton) < 0)    //LRButton
            {
                return;
            }

            var screenBounds = Common.OsSpecific.Windows.Screen.ScreenBoundsFromPoint(e.Pos);

            if (screenBounds == null)
            {
                return;
            }
            _screenBounds = screenBounds.Value;

            _dpiScale = (Native.GetScreenDpi() / 96.0f);

            //convert pos to cursor screen coord
            var posOnCursorScreen = new Point(e.X - _screenBounds.X, e.Y - _screenBounds.Y);


            //DetectCollide(e);
            DetectRub(posOnCursorScreen);
        }
Example #3
0
        private void DetectCollide(MouseKeyboardHook.MouseHookEventArgs e)
        {
            //TODO: make configurable
            var redLineDist       = 100 * _dpiScale;
            var minSpeed          = 0.3 * _dpiScale;
            var maxAllowedBias    = 100 * _dpiScale;
            var cornerExcludeDist = 100 * _dpiScale;
            var edgeThick         = 4;

            var now = PointAndTime.RecordNow(e.Pos);

            //Debug.WriteLine("wtf " + e.Pos);
            if (_activeCollideEdge == null)
            {
                if (e.Pos.X >= redLineDist && e.Pos.X <= _screenBounds.Width - redLineDist)
                {
                    _hLastPtOutsideRedline = now;
                }
                if (e.Pos.Y >= redLineDist && e.Pos.Y <= _screenBounds.Height - redLineDist)
                {
                    _vLastPtOutsideRedline = now;
                }
                float      bias;
                float      speed;
                ScreenEdge side;

                if (e.Pos.X <= edgeThick && (e.Pos.Y < _screenBounds.Height - cornerExcludeDist && e.Pos.Y > cornerExcludeDist))
                {
                    bias  = now.Pos.Y - _hLastPtOutsideRedline.Pos.Y;
                    speed = -PointAndTime.CalSpeedVec(_hLastPtOutsideRedline, now).X;
                    side  = ScreenEdge.Left;
                }
                else if (e.Pos.X >= _screenBounds.Width - edgeThick && (e.Pos.Y < _screenBounds.Height - cornerExcludeDist && e.Pos.Y > cornerExcludeDist))
                {
                    bias  = now.Pos.Y - _hLastPtOutsideRedline.Pos.Y;
                    speed = PointAndTime.CalSpeedVec(_hLastPtOutsideRedline, now).X;
                    side  = ScreenEdge.Right;
                }
                else if (e.Pos.Y <= edgeThick && (e.Pos.X < _screenBounds.Width - cornerExcludeDist && e.Pos.X > cornerExcludeDist))
                {
                    bias  = now.Pos.X - _vLastPtOutsideRedline.Pos.X;
                    speed = -PointAndTime.CalSpeedVec(_vLastPtOutsideRedline, now).Y;
                    side  = ScreenEdge.Top;
                }
                else if (e.Pos.Y >= _screenBounds.Height - edgeThick && (e.Pos.X < _screenBounds.Width - cornerExcludeDist && e.Pos.X > cornerExcludeDist))
                {
                    bias  = now.Pos.X - _vLastPtOutsideRedline.Pos.X;
                    speed = PointAndTime.CalSpeedVec(_vLastPtOutsideRedline, now).Y;
                    side  = ScreenEdge.Bottom;
                }
                else
                {
                    return;
                }

                if (speed >= minSpeed && Math.Abs(bias) <= maxAllowedBias)
                {
                    //Debug.WriteLine("X  ");
                    _collidePoint      = now;
                    _currentBias       = Math.Abs(bias);
                    _activeCollideEdge = side;
                }
            }
            else
            {
                float speed = 0;
                float bias  = 0;
                switch (_activeCollideEdge)
                {
                case ScreenEdge.Left:
                    bias = Math.Abs(_hLastPtOutsideRedline.Pos.Y - now.Pos.Y);
                    if (bias > _currentBias)
                    {
                        _currentBias = bias;
                    }
                    if (e.Pos.X >= redLineDist)
                    {
                        speed = PointAndTime.CalSpeedVec(_collidePoint, now).X;
                    }
                    else
                    {
                        return;
                    }
                    break;

                case ScreenEdge.Right:
                    bias = Math.Abs(_hLastPtOutsideRedline.Pos.Y - now.Pos.Y);
                    if (bias > _currentBias)
                    {
                        _currentBias = bias;
                    }
                    if (e.Pos.X <= _screenBounds.Width - redLineDist)
                    {
                        speed = -PointAndTime.CalSpeedVec(_collidePoint, now).X;
                    }
                    else
                    {
                        return;
                    }
                    break;

                case ScreenEdge.Top:
                    bias = Math.Abs(_vLastPtOutsideRedline.Pos.X - now.Pos.X);
                    if (bias > _currentBias)
                    {
                        _currentBias = bias;
                    }
                    if (e.Pos.Y >= redLineDist)

                    {
                        speed = PointAndTime.CalSpeedVec(_collidePoint, now).Y;
                    }
                    else
                    {
                        return;
                    }
                    break;

                case ScreenEdge.Bottom:
                    bias = Math.Abs(_vLastPtOutsideRedline.Pos.X - now.Pos.X);
                    if (bias > _currentBias)
                    {
                        _currentBias = bias;
                    }
                    if (e.Pos.Y <= _screenBounds.Height - redLineDist)
                    {
                        speed = -PointAndTime.CalSpeedVec(_collidePoint, now).Y;
                    }
                    else
                    {
                        return;
                    }
                    break;
                }


                if (speed >= minSpeed && _currentBias <= maxAllowedBias)
                {
                    Debug.WriteLine("O  " + _currentBias);
                    OnCollide(_activeCollideEdge.Value);
                }
                _activeCollideEdge = null; //Reset
            }
        }
        //NOTE: hook procs run in a separate thread.
        private void MouseHookProc(MouseKeyboardHook.MouseHookEventArgs e)
        {
            //处理 左键 + 中键 用于 暂停继续的情形
            if (HandleSpecialButtonCombination(e))
            {
                return;
            }
            if (_isPaused)
            {
                return;
            }

            var mouseData = (Native.MSLLHOOKSTRUCT)Marshal.PtrToStructure(e.lParam, typeof(Native.MSLLHOOKSTRUCT));

            //fixme: 判断是否在模拟事件, 为什么不一定可靠?
            if (_simulatingInput || mouseData.dwExtraInfo.ToInt64() == SIMULATED_EVENT_TAG)
            {
                Debug.WriteLine("Simulated:" + e.Msg);
                if (InitialStayTimeout && _isInitialTimeout)
                {
                    Debug.WriteLine("_captured=false");
                    _captured = false;
                }
                return;
            }

            var prevPos = _curPos;

            _curPos = new Point(e.X, e.Y);

            var m = e.Msg;

            switch (m)
            {
            //必须在这里立即决定是否应该捕获
            case MouseMsg.WM_RBUTTONDOWN:
            case MouseMsg.WM_MBUTTONDOWN:
            case MouseMsg.WM_XBUTTONDOWN:
                if (!_captured)
                {
                    if (m == MouseMsg.WM_MBUTTONDOWN && (TriggerButton & GestureTriggerButton.Middle) == 0 ||
                        m == MouseMsg.WM_RBUTTONDOWN && (TriggerButton & GestureTriggerButton.Right) == 0 ||
                        m == MouseMsg.WM_XBUTTONDOWN && (TriggerButton & GestureTriggerButton.X) == 0)
                    {
                        return;
                    }
                    try
                    {
                        //notice: 这个方法在钩子线程中运行,因此必须足够快,而且不能失败
                        _captured = OnBeforePathStart();
                    }
                    catch (Exception ex)
                    {
#if DEBUG
                        throw;
#endif
                        //如果出错,则不捕获手势
                        _captured = false;
                    }

                    if (_captured)
                    {
                        //_gestureBtn = (m == MouseMsg.WM_RBUTTONDOWN ? GestureButtons.RightButton : GestureButtons.MiddleButton);
                        switch (m)    //TODO: extract function
                        {
                        case MouseMsg.WM_RBUTTONDOWN:
                            _gestureBtn = GestureTriggerButton.Right;
                            break;

                        case MouseMsg.WM_MBUTTONDOWN:
                            _gestureBtn = GestureTriggerButton.Middle;
                            break;

                        case MouseMsg.WM_XBUTTONDOWN:
                            var x = (XButtonNumber)(mouseData.mouseData >> 16);         //which X Button
                            _gestureBtn = x == XButtonNumber.One ? GestureTriggerButton.X1 : GestureTriggerButton.X2;
                            break;

                        default:
                            Debug.Assert(false, "WTF! shouldn't happen");
                            break;
                        }

                        _modifierEventHappendPrevTime = new DateTime(0);
                        e.Handled = true;
                        Post(WM.GESTBTN_DOWN);
                    }
                }
                else                         //另一个键作为手势键的时候,作为修饰键
                {
                    GestureModifier gestMod; // = m == MouseMsg.WM_RBUTTONDOWN ? GestureModifier.RightButtonDown : GestureModifier.MiddleButtonDown;

                    switch (m)               //TODO: extract function
                    {
                    case MouseMsg.WM_RBUTTONDOWN:
                        gestMod = GestureModifier.RightButtonDown;
                        break;

                    case MouseMsg.WM_MBUTTONDOWN:
                        gestMod = GestureModifier.MiddleButtonDown;
                        break;

                    case MouseMsg.WM_XBUTTONDOWN:
                        var x = (XButtonNumber)(mouseData.mouseData >> 16);         //which X Button
                        gestMod = x == XButtonNumber.One ? GestureModifier.X1 : GestureModifier.X2;
                        break;

                    default:
                        gestMod = GestureModifier.LeftButtonDown;
                        break;
                    }

                    e.Handled = HandleModifier(gestMod);
                }
                break;

            case MouseMsg.WM_MOUSEMOVE:
                if (_captured)
                {
                    //永远不拦截move消息,所以不设置e.Handled = true
                    Post(WM.GESTBTN_MOVE);
                }
                else
                {
                    if (_isVirtualGesturing)
                    {
                        //忽略禁用列表
                        OnBeforePathStart();
                        _captured   = true;
                        _gestureBtn = GestureTriggerButton.Right;
                        Post(WM.GESTBTN_DOWN, 1);
                    }
                    //未捕获的情况下才允许hotcorner
                    HotCornerHitTest();
                }
                break;

            case MouseMsg.WM_MOUSEWHEEL:
                if (_captured)
                {
                    //获得滚动方向
                    int delta   = (short)(mouseData.mouseData >> 16);
                    var gestMod = delta > 0 ? GestureModifier.WheelForward : GestureModifier.WheelBackward;

                    e.Handled = HandleModifier(gestMod);
                }
                else if (DateTime.UtcNow - _modifierEventHappendPrevTime < TimeSpan.FromMilliseconds(300))    //延迟一下,因为 中键手势 + 滚动,可能导致快捷键还没结束,而滚轮事件发送到了目标窗口,可鞥解释成其他功能(比如ctrl + 滚轮 = 缩放)
                {
                    e.Handled = true;
                }
                break;

            case MouseMsg.WM_LBUTTONDOWN:
                if (_captured)
                {
                    e.Handled = HandleModifier(GestureModifier.LeftButtonDown);
                }
                break;

            case MouseMsg.WM_RBUTTONUP:
            case MouseMsg.WM_MBUTTONUP:
            case MouseMsg.WM_XBUTTONUP:
                if (_captured)
                {
                    var gestBtn_as_MouseMsg = (MouseMsg)(-1);
                    switch (_gestureBtn)
                    {
                    case GestureTriggerButton.Middle:
                        gestBtn_as_MouseMsg = MouseMsg.WM_MBUTTONUP;
                        break;

                    case GestureTriggerButton.Right:
                        gestBtn_as_MouseMsg = MouseMsg.WM_RBUTTONUP;
                        break;

                    case GestureTriggerButton.X1:
                    case GestureTriggerButton.X2:
                        gestBtn_as_MouseMsg = MouseMsg.WM_XBUTTONUP;
                        break;
                    }

                    //是手势键up
                    if (m == gestBtn_as_MouseMsg)
                    {
                        _captured = false;
                        Post(WM.GESTBTN_UP);
                    }

                    e.Handled = true;
                }
                break;

            default:
                //其他消息不处理
                break;
            }
        }