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); }
//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); }
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; } }