public void GetACPFromPoint(int viewCookie, ref UnsafeNativeMethods.POINT tsfPoint, UnsafeNativeMethods.GetPositionFromPointFlags flags, out int positionCP) { SecurityHelper.DemandUnmanagedCode(); PresentationSource source; IWin32Window win32Window; CompositionTarget compositionTarget; ITextView view; Point milPoint; ITextPointer position; NativeMethods.POINT point; point = new NativeMethods.POINT(tsfPoint.x, tsfPoint.y); GetVisualInfo(out source, out win32Window, out view); compositionTarget = source.CompositionTarget; // Convert to client coordinates. SafeNativeMethods.ScreenToClient(new HandleRef(null,win32Window.Handle), point); // Convert to mil measure units. milPoint = new Point(point.x, point.y); milPoint = compositionTarget.TransformFromDevice.Transform(milPoint); // Convert to local coordinates. GeneralTransform transform = compositionTarget.RootVisual.TransformToDescendant(RenderScope); if (transform != null) { // transform.TryTransform(milPoint, out milPoint); } // Validate layout information on TextView if (!view.Validate(milPoint)) { throw new COMException(SR.Get(SRID.TextStore_TS_E_NOLAYOUT), UnsafeNativeMethods.TS_E_NOLAYOUT); } // Do the hittest. position = view.GetTextPositionFromPoint(milPoint, (flags & UnsafeNativeMethods.GetPositionFromPointFlags.GXFPF_NEAREST) != 0 /* snapToText */); if (position == null) { // GXFPF_ROUND_NEAREST was clear and we didn't hit a char. throw new COMException(SR.Get(SRID.TextStore_TS_E_INVALIDPOINT), UnsafeNativeMethods.TS_E_INVALIDPOINT); } positionCP = position.CharOffset; if ((flags & UnsafeNativeMethods.GetPositionFromPointFlags.GXFPF_ROUND_NEAREST) == 0) { // Check if the point is on the backward position of the TextPosition. Rect rectCur; Rect rectPrev; Point milPointTopLeft; Point milPointBottomRight; ITextPointer positionCur = position.CreatePointer(LogicalDirection.Backward); ITextPointer positionPrev = position.CreatePointer(LogicalDirection.Forward); positionPrev.MoveToNextInsertionPosition(LogicalDirection.Backward); rectCur = view.GetRectangleFromTextPosition(positionCur); rectPrev = view.GetRectangleFromTextPosition(positionPrev); // Take the "extended" union of the previous char's bounding box. milPointTopLeft = new Point(Math.Min(rectPrev.Left, rectCur.Left), Math.Min(rectPrev.Top, rectCur.Top)); milPointBottomRight = new Point(Math.Max(rectPrev.Left, rectCur.Left), Math.Max(rectPrev.Bottom, rectCur.Bottom)); // The rect of the previous char. Rect rectTest = new Rect(milPointTopLeft, milPointBottomRight); if (rectTest.Contains(milPoint)) positionCP--; } }
private IntPtr CreateWindow(NativeMethods.BitmapHandle hBitmap, int width, int height, bool topMost) { if (_defWndProc == null) { _defWndProc = new MS.Win32.NativeMethods.WndProc(UnsafeNativeMethods.DefWindowProc); } MS.Win32.NativeMethods.WNDCLASSEX_D wndClass = new MS.Win32.NativeMethods.WNDCLASSEX_D(); wndClass.cbSize = Marshal.SizeOf(typeof(MS.Win32.NativeMethods.WNDCLASSEX_D)); wndClass.style = 3; /* CS_HREDRAW | CS_VREDRAW */ wndClass.lpfnWndProc = null; wndClass.hInstance = _hInstance; wndClass.hCursor = IntPtr.Zero; wndClass.lpszClassName = CLASSNAME; wndClass.lpszMenuName = string.Empty; wndClass.lpfnWndProc = _defWndProc; // We chose to ignore re-registration errors in RegisterClassEx on the off chance that the user // wants to open multiple splash screens. _wndClass = MS.Win32.UnsafeNativeMethods.IntRegisterClassEx(wndClass); if (_wndClass == 0) { if (Marshal.GetLastWin32Error() != 0x582) /* class already registered */ throw new Win32Exception(); } int screenWidth = MS.Win32.UnsafeNativeMethods.GetSystemMetrics(SM.CXSCREEN); int screenHeight = MS.Win32.UnsafeNativeMethods.GetSystemMetrics(SM.CYSCREEN); int x = (screenWidth - width) / 2; int y = (screenHeight - height) / 2; HandleRef nullHandle = new HandleRef(null, IntPtr.Zero); int windowCreateFlags = (int) NativeMethods.WS_EX_WINDOWEDGE | NativeMethods.WS_EX_TOOLWINDOW | NativeMethods.WS_EX_LAYERED | (topMost ? NativeMethods.WS_EX_TOPMOST : 0); // CreateWindowEx will either succeed or throw IntPtr hWnd = MS.Win32.UnsafeNativeMethods.CreateWindowEx( windowCreateFlags, CLASSNAME, SR.Get(SRID.SplashScreenIsLoading), MS.Win32.NativeMethods.WS_POPUP | MS.Win32.NativeMethods.WS_VISIBLE, x, y, width, height, nullHandle, nullHandle, new HandleRef(null, _hInstance), IntPtr.Zero); // Display the image on the window IntPtr hScreenDC = UnsafeNativeMethods.GetDC(new HandleRef()); IntPtr memDC = UnsafeNativeMethods.CreateCompatibleDC(new HandleRef(null, hScreenDC)); IntPtr hOldBitmap = UnsafeNativeMethods.SelectObject(new HandleRef(null, memDC), hBitmap.MakeHandleRef(null).Handle); NativeMethods.POINT newSize = new NativeMethods.POINT(width, height); NativeMethods.POINT newLocation = new NativeMethods.POINT(x, y); NativeMethods.POINT sourceLocation = new NativeMethods.POINT(0, 0); _blendFunc = new NativeMethods.BLENDFUNCTION(); _blendFunc.BlendOp = NativeMethods.AC_SRC_OVER; _blendFunc.BlendFlags = 0; _blendFunc.SourceConstantAlpha = 255; _blendFunc.AlphaFormat = 1; /*AC_SRC_ALPHA*/ bool result = UnsafeNativeMethods.UpdateLayeredWindow(hWnd, hScreenDC, newLocation, newSize, memDC, sourceLocation, 0, ref _blendFunc, NativeMethods.ULW_ALPHA); UnsafeNativeMethods.SelectObject(new HandleRef(null, memDC), hOldBitmap); UnsafeNativeMethods.ReleaseDC(new HandleRef(), new HandleRef(null, memDC)); UnsafeNativeMethods.ReleaseDC(new HandleRef(), new HandleRef(null, hScreenDC)); if (result == false) { UnsafeNativeMethods.HRESULT.Check(Marshal.GetHRForLastWin32Error()); } return hWnd; }
private NativeMethods.POINT GetWindowScreenLocation(FlowDirection flowDirection) { Debug.Assert(IsSourceWindowNull != true, "IsSourceWindowNull cannot be true here"); NativeMethods.POINT pt = new NativeMethods.POINT(0, 0); if (flowDirection == FlowDirection.RightToLeft) { NativeMethods.RECT rc = new NativeMethods.RECT(0, 0, 0, 0); // with RTL window, GetClientRect returns reversed coordinates SafeNativeMethods.GetClientRect(new HandleRef(this, CriticalHandle), ref rc); // note that we use rc.right here for the RTL case and client to screen that point pt = new NativeMethods.POINT(rc.right, rc.top); } UnsafeNativeMethods.ClientToScreen(new HandleRef(this, _sourceWindow.CriticalHandle), pt); return pt; }
private static UnsafeNativeMethods.RECT TransformRootRectToScreenCoordinates(Point milPointTopLeft, Point milPointBottomRight, IWin32Window win32Window, PresentationSource source) { UnsafeNativeMethods.RECT rect; NativeMethods.POINT clientPoint; CompositionTarget compositionTarget; rect = new UnsafeNativeMethods.RECT(); // Transform to device units. compositionTarget = source.CompositionTarget; milPointTopLeft = compositionTarget.TransformToDevice.Transform(milPointTopLeft); milPointBottomRight = compositionTarget.TransformToDevice.Transform(milPointBottomRight); IntPtr hwnd = IntPtr.Zero; new UIPermission(UIPermissionWindow.AllWindows).Assert(); // BlessedAssert try { hwnd = win32Window.Handle; } finally { CodeAccessPermission.RevertAssert(); } // Transform to screen coords. clientPoint = new NativeMethods.POINT(); UnsafeNativeMethods.ClientToScreen(new HandleRef(null, hwnd), /* ref by interop */ clientPoint); rect.left = (int)(clientPoint.x + milPointTopLeft.X); rect.right = (int)(clientPoint.x + milPointBottomRight.X); rect.top = (int)(clientPoint.y + milPointTopLeft.Y); rect.bottom = (int)(clientPoint.y + milPointBottomRight.Y); return rect; }
private void PossiblyDeactivate(IntPtr hwndCapture, bool stillActiveIfOverSelf) { // we may have been disposed by a re-entrant call (bug 1536643). // If so, there's nothing more to do. if (null == _source || null == _source.Value ) { return; } if(_isDwmProcess) { return; } //Console.WriteLine("PossiblyDeactivate(" + hwndCapture + ")"); // We are now longer active ourselves, but it is possible that the // window the mouse is going to intereact with is in the same // Dispatcher as ourselves. If so, we don't want to deactivate the // mouse input stream because the other window hasn't activated it // yet, and it may result in the input stream "flickering" between // active/inactive/active. This is ugly, so we try to supress the // uneccesary transitions. // IntPtr hwndToCheck = hwndCapture; if(hwndToCheck == IntPtr.Zero) { NativeMethods.POINT ptCursor = new NativeMethods.POINT(); int messagePos = 0; try { messagePos = SafeNativeMethods.GetMessagePos(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetMessagePos failed!"); } ptCursor.x = NativeMethods.SignedLOWORD(messagePos); ptCursor.y = NativeMethods.SignedHIWORD(messagePos); //Console.WriteLine(" GetMessagePos: ({0},{1})", ptCursor.x, ptCursor.y); try { hwndToCheck = UnsafeNativeMethods.WindowFromPoint(ptCursor.x, ptCursor.y); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: WindowFromPoint failed!"); } if (!stillActiveIfOverSelf && hwndToCheck == _source.Value.CriticalHandle) { hwndToCheck = IntPtr.Zero; } if(hwndToCheck != IntPtr.Zero) { // We need to check if the point is over the client or // non-client area. We only care about being over the // non-client area. try { NativeMethods.RECT rcClient = new NativeMethods.RECT(); SafeNativeMethods.GetClientRect(new HandleRef(this,hwndToCheck), ref rcClient); SafeNativeMethods.ScreenToClient(new HandleRef(this,hwndToCheck), ptCursor); if(ptCursor.x < rcClient.left || ptCursor.x >= rcClient.right || ptCursor.y < rcClient.top || ptCursor.y >= rcClient.bottom) { // We are not over the non-client area. We can bail out. //Console.WriteLine(" No capture, mouse outside of client area."); //Console.WriteLine(" Client Area: ({0},{1})-({2},{3})", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); //Console.WriteLine(" Mouse: ({0},{1})", ptCursor.x, ptCursor.y); hwndToCheck = IntPtr.Zero; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetClientRect or ScreenToClient failed!"); } } } // If the window the mouse is over is ours, we'll just let it activate // without deactivating the mouse input stream for this window. This prevents // the mouse input stream from flickering. bool deactivate = !IsOurWindow(hwndToCheck); //Console.WriteLine(" Deactivate=" + deactivate); // Only deactivate the mouse input stream if needed. if(deactivate) { ReportInput(_source.Value.CriticalHandle, InputMode.Foreground, _msgTime, RawMouseActions.Deactivate, 0, 0, 0); } else { // We are not deactivating the mouse input stream because the // window that is going to provide mouse input next is one of // our Avalon windows. This optimization keeps the mouse input // stream from flickering by transitioning to null. // // But this window itself should not be active anymore. _active = false; } }
IntPtr GetCurrentMonitorFromMousePosition() { SecurityHelper.DemandUnmanagedCode(); // center on the screen on which the mouse is on NativeMethods.POINT pt = new NativeMethods.POINT(); UnsafeNativeMethods.TryGetCursorPos(pt); NativeMethods.POINTSTRUCT ptStruct = new NativeMethods.POINTSTRUCT(pt.x, pt.y); return SafeNativeMethods.MonitorFromPoint(ptStruct, NativeMethods.MONITOR_DEFAULTTONEAREST); }
bool IMouseInputProvider.CaptureMouse() { if(_isDwmProcess) { return true; } bool success = true; Debug.Assert(null != _source && null != _source.Value); try { SafeNativeMethods.SetCapture(new HandleRef(this,_source.Value.CriticalHandle)); IntPtr capture = SafeNativeMethods.GetCapture(); if (capture != _source.Value.CriticalHandle) { success = false; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: SetCapture or GetCapture failed!"); success = false; } // MITIGATION_ if(success) { _haveCapture = true; } // WORKAROUND for bug 969748 if(success && !_active) { NativeMethods.POINT ptCursor = new NativeMethods.POINT(); success = UnsafeNativeMethods.TryGetCursorPos(ptCursor); if(success) { try { SafeNativeMethods.ScreenToClient(new HandleRef(this, _source.Value.CriticalHandle), ptCursor); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: ScreenToClient failed!"); success = false; } if(success) { ReportInput(_source.Value.CriticalHandle, InputMode.Foreground, _msgTime, RawMouseActions.AbsoluteMove, ptCursor.x, ptCursor.y, 0); } } } return success; }
internal IntPtr FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; // It is possible to be re-entered during disposal. Just return. if(null == _source || null == _source.Value) { return result; } /* NativeMethods.POINT ptCursor = new NativeMethods.POINT(); int messagePos = 0; try { messagePos = SafeNativeMethods.GetMessagePos(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetMessagePos failed!"); } ptCursor.x = NativeMethods.SignedLOWORD(messagePos); ptCursor.y = NativeMethods.SignedHIWORD(messagePos); Console.WriteLine("HwndMouseInputProvider.FilterMessage: hwnd: {0} msg: {1} wParam: {2} lParam: {3} MessagePos: ({4},{5})", hwnd, msg, wParam, lParam, ptCursor.x, ptCursor.y); */ _msgTime = 0; try { _msgTime = SafeNativeMethods.GetMessageTime(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetMessageTime failed!"); } // if(msg == WindowMessage.WM_MOUSEQUERY) { if(!_isDwmProcess) { _isDwmProcess = true; } unsafe { // Currently only sending WM_MOUSEMOVE through until we rip out the prototype bits. UnsafeNativeMethods.MOUSEQUERY* pmq = (UnsafeNativeMethods.MOUSEQUERY*)lParam; if((WindowMessage)pmq->uMsg == WindowMessage.WM_MOUSEMOVE) { msg = (WindowMessage)pmq->uMsg; wParam = pmq->wParam; lParam = MakeLPARAM(pmq->ptX, pmq->ptY); } } } switch(msg) { // Compatibility Note: // // WM_POINTERUP, WM_POINTERDOWN // // These messages were introduced in Win8 to unify the various // mechanisms for processing events from pointing devices, // including stylus, touch, mouse, etc. WPF does not // currently support these messages; we still rely on the // classic WM_MOUSE messages. For classic applications, this // is supported by default in Windows. However, for immersive // applications, the default is to report mouse input through // these new WM_POINTER messages. Blend - the only immersive // WPF application - must explicitly request the mouse input // be delivered through the traditional WM_MOUSE messages by // calling EnableMouseInPointer(false). If WPF ever supports // the WM_POINTER messages, we need to be careful not to break // Blend. case WindowMessage.WM_NCDESTROY: { //Console.WriteLine("WM_NCDESTROY"); // This is the normal clean-up path. HwndSource destroys the // HWND first, which should trigger this code, before it // explicitly disposes us. This allows us to call // PossiblyDeactivate since our window is no longer on the // screen. Dispose(); } break; case WindowMessage.WM_MOUSEMOVE: { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); //Console.WriteLine("WM_MOUSEMOVE: " + x + "," + y); // Abort the pending operation waiting to update the cursor, because we // are going to update it as part of this mouse move processing. if (_queryCursorOperation != null) { _queryCursorOperation.Abort(); _queryCursorOperation = null; } // MITIGATION_SETCURSOR if (_haveCapture) { // When we have capture we don't receive WM_SETCURSOR // prior to a mouse move. So that we don't erroneously think // we're in "Help Mode" we'll pretend we've received a set // cursor message. _setCursorState = SetCursorState.SetCursorReceived; } else { if (_setCursorState == SetCursorState.SetCursorNotReceived) { _setCursorState = SetCursorState.SetCursorDisabled; } else if(_setCursorState == SetCursorState.SetCursorReceived) { _setCursorState = SetCursorState.SetCursorNotReceived; } } // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.AbsoluteMove, x, y, 0); } break; case WindowMessage.WM_MOUSEWHEEL: { int wheel = NativeMethods.SignedHIWORD(wParam); int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); // The WM_MOUSEWHEEL gives the coordinates relative to the desktop. NativeMethods.POINT pt = new NativeMethods.POINT(x,y); try { SafeNativeMethods.ScreenToClient(new HandleRef(this,hwnd), pt); x = pt.x; y = pt.y; //Console.WriteLine("WM_MOUSEWHEEL: " + x + "," + y + "," + wheel); // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.VerticalWheelRotate, x, y, wheel); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: ScreenToClient failed!"); } } break; case WindowMessage.WM_LBUTTONDBLCLK: case WindowMessage.WM_LBUTTONDOWN: { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); //Console.WriteLine("WM_LBUTTONDOWN: " + x + "," + y); // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.Button1Press, x, y, 0); } break; case WindowMessage.WM_LBUTTONUP: { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); //Console.WriteLine("WM_LBUTTONUP: " + x + "," + y); // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.Button1Release, x, y, 0); } break; case WindowMessage.WM_RBUTTONDBLCLK: case WindowMessage.WM_RBUTTONDOWN: { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.Button2Press, x, y, 0); } break; case WindowMessage.WM_RBUTTONUP: { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.Button2Release, x, y, 0); } break; case WindowMessage.WM_MBUTTONDBLCLK: case WindowMessage.WM_MBUTTONDOWN: { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.Button3Press, x, y, 0); } break; case WindowMessage.WM_MBUTTONUP: { int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.Button3Release, x, y, 0); } break; case WindowMessage.WM_XBUTTONDBLCLK: case WindowMessage.WM_XBUTTONDOWN: { int button = NativeMethods.SignedHIWORD(wParam); int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); RawMouseActions actions = 0; if(button == 1) { actions = RawMouseActions.Button4Press; } else if(button == 2) { actions = RawMouseActions.Button5Press; } // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, actions, x, y, 0); } break; case WindowMessage.WM_XBUTTONUP: { int button = NativeMethods.SignedHIWORD(wParam); int x = NativeMethods.SignedLOWORD(lParam); int y = NativeMethods.SignedHIWORD(lParam); RawMouseActions actions = 0; if(button == 1) { actions = RawMouseActions.Button4Release; } else if(button == 2) { actions = RawMouseActions.Button5Release; } // handled = ReportInput(hwnd, InputMode.Foreground, _msgTime, actions, x, y, 0); } break; case WindowMessage.WM_MOUSELEAVE: { //Console.WriteLine("WM_MOUSELEAVE"); // When the mouse moves off the window, we receive a // WM_MOUSELEAVE. We'll start tracking again when the // mouse moves back over us. StopTracking(hwnd); // It is possible that we have capture but we still receive // a mouse leave event. This can happen in the case of // "soft capture". In such cases, we defer the actual // deactivation until the capture is lost. // // See the note on WM_CAPTURECHANGED for more details. try { IntPtr hwndCapture = SafeNativeMethods.GetCapture(); IntPtr hwndCurrent = _source.Value.CriticalHandle; if (hwndCapture != hwndCurrent) { PossiblyDeactivate(hwndCapture, false); } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetCapture failed!"); } } break; case WindowMessage.WM_CAPTURECHANGED: { //Console.WriteLine("WM_CAPTURECHANGED"); // Win32 has two concepts for capture: // // Hard Capture // When a mouse button is pressed, Win32 finds the window // underneath the mouse and assigns it as the MouseOwner. // All mouse input is directed to this window until the // mouse is button released. The window does not even // have to request capture. Certain window types are // excluded from this processing. // // Soft Capture // This is accessed via the SetCapture API. It assigns // the window that should receive mouse input for the // queue. Win32 decides which queue the mouse input // should go to without considering this type of capture. // Once the input is in the queue, it is sent to the // window with capture. This means that the mouse // messages will generally be sent to the specified window // in the application, but other applications will work // too. // // If another application calls SetCapture, the current // application will receive a WM_CAPTURECHANGED. // // If the window took capture while Win32 was enforcing // Hard Capture, and releases capture when the mouse // button is released, then everything works as you // probably expect. But if the application retains // capture after the mouse button is released, it is // possible to receive a WM_MOUSELEAVE even though the // window still has capture. // Losing capture *after* a WM_MOUSELEAVE means we // probably want to deactivate the mouse input stream. // If someone else is taking capture, we may need // to deactivate the mouse input stream too. if(lParam != _source.Value.CriticalHandle) // Ignore odd messages that claim we are losing capture to ourselves. { // MITIGATION_SETCURSOR _haveCapture = false; if(_setCursorState == SetCursorState.SetCursorReceived) { _setCursorState = SetCursorState.SetCursorNotReceived; } if(!IsOurWindow(lParam) && _active) { ReportInput(hwnd, InputMode.Foreground, _msgTime, RawMouseActions.CancelCapture, 0, 0, 0); } if(lParam != IntPtr.Zero || // someone else took capture !_tracking) // OR no one has capture and the mouse is not over us { PossiblyDeactivate(lParam, true); } } } break; case WindowMessage.WM_CANCELMODE: { // MITIGATION: NESTED_MESSAGE_PUMPS_INTERFERE_WITH_INPUT // // When a nested message pump runs, it intercepts all messages // before they are dispatched, and thus before they can be sent // to the window with capture. // // This means that an element can take capture on MouseDown, // expecting to receive either MouseUp or LostCapture. But, in // fact, neither event may be raised if a nested message pump // runs. // // An example of this is displaying a dialog box in response to // MouseDown. // // There isn't much we can do about the general case, but // well-behaved message pumps (such as a dialog box) are // supposed to send the WM_CANCELMODE message. In response // to this we release capture if we currently have it. try { if(_source.Value.HasCapture ) { SafeNativeMethods.ReleaseCapture(); } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetCapture failed!"); } } break; case WindowMessage.WM_SETCURSOR: { if (_queryCursorOperation == null) { // It is possible that a WM_SETCURSOR is not followed by a WM_MOUSEMOVE, in which // case we need a backup mechanism to query the cursor and update it. So we post to // the queue to do this work. If a WM_MOUSEMOVE comes in earlier, then the operation // is aborted, else it comes through and we update the cursor. _queryCursorOperation = Dispatcher.BeginInvoke(DispatcherPriority.Input, (DispatcherOperationCallback)delegate(object sender) { // Since this is an asynchronous operation and an arbitrary amount of time has elapsed // since we received the WM_SETCURSOR, we need to be careful that the mouse hasn't // been deactivated in the meanwhile. This is also another reason that we do not ReportInput, // because the implicit assumption in doing that is to activate the MouseDevice. All we want // to do is passively try to update the cursor. if (_active) { Mouse.UpdateCursor(); } _queryCursorOperation = null; return null; }, null); } // MITIGATION_SETCURSOR _setCursorState = SetCursorState.SetCursorReceived; // Note: We get this message BEFORE we get WM_MOUSEMOVE. This means that Avalon // still thinks the mouse is over the "old" element. This is awkward, and we think // people will find it confusing to get a QueryCursor event before a MouseMove event. // Further, this means we would have to do a special hit-test, and route the // QueryCursor event differently than the other mouse events. // // Another difference is that Win32 passes us a hit-test code, which was calculated // by an earlier WM_NCHITTEST message. The problem with this is that it is a fixed // enum. We don't have a similar concept in Avalon. // // So instead, the MouseDevice will raise the QueryCursor event after every MouseMove // event. We think this is a better ordering. And the application can return whatever // cursor they want (not limited to a fixed enum of hit-test codes). // // Of course, this is different than Win32. One example of where this can cause a // problem is that sometimes Win32 will NOT send a WM_SETCURSOR message and just send // a WM_MOUSEMOVE. This is for cases like when the mouse is captured, or when the // the "help mode" is active (clicking the little question mark in the title bar). // To accomodate this, we use the _setCursorState to prevent the user from changing // the cursor when we haven't received a WM_SETCURSOR message - which means that the // cursor is NOT supposed to change as it moves over new windows/elements/etc. Note // that Avalon will raise the QueryCursor event, but the result is ignored. // // But: We MUST mark this Win32 message as "handled" or windows will change the cursor to // the default cursor, which will cause annoying flicker if the app is trying to set // a custom one. Of course, only do this for the client area. // int hittestCode = NativeMethods.SignedLOWORD((int) lParam); if(hittestCode == NativeMethods.HTCLIENT) { handled = true; } } break; } if (handled && EventTrace.IsEnabled(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info)) { // Anything can (and does) happen in ReportInput. We can be (and have been) // re-entered and Dispose()ed. Then returning from ReportInput // needs to check for that. int dispatcherHashCode = 0; if( _source != null && !_source.Value.IsDisposed && _source.Value.CompositionTarget != null) dispatcherHashCode = _source.Value.CompositionTarget.Dispatcher.GetHashCode(); // The ETW manifest for this event declares the lParam and // wParam values to be integers. This is not always true for // 64-bit systems, which sometimes pass pointers and handles // through this parameters. However, we can't change the ETW // manifest in an in-place upgrade, so we are just going to // cast to an int. Note that IntPtr defines the explicit int // cast operator to used a checked block, which will throw an // overflow exception if the IntPtr contains too big of a value. // So we do the cast ourselves and ignore the overflow. int wParamInt = (int) (long) wParam;; int lParamInt = (int) (long) lParam; EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientInputMessage, EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, dispatcherHashCode, hwnd.ToInt64(), msg, wParamInt, lParamInt); } return result; }
private void GetWindowRectsInScreenCoordinates() { NativeMethods.RECT rcClient = new NativeMethods.RECT(); // // Get the window and client rectangles // SafeNativeMethods.GetWindowRect(_hWnd.MakeHandleRef(this), ref _hwndWindowRectInScreenCoords); SafeNativeMethods.GetClientRect(_hWnd.MakeHandleRef(this), ref rcClient); NativeMethods.POINT ptClientTopLeft = new NativeMethods.POINT(rcClient.left, rcClient.top); UnsafeNativeMethods.ClientToScreen(_hWnd.MakeHandleRef(this), ptClientTopLeft); NativeMethods.POINT ptClientBottomRight = new NativeMethods.POINT(rcClient.right, rcClient.bottom); UnsafeNativeMethods.ClientToScreen(_hWnd.MakeHandleRef(this), ptClientBottomRight); if(ptClientBottomRight.x >= ptClientTopLeft.x) { _hwndClientRectInScreenCoords.left = ptClientTopLeft.x; _hwndClientRectInScreenCoords.right = ptClientBottomRight.x; } else { // RTL windows will cause the right edge to be on the left... _hwndClientRectInScreenCoords.left = ptClientBottomRight.x; _hwndClientRectInScreenCoords.right = ptClientTopLeft.x; } if(ptClientBottomRight.y >= ptClientTopLeft.y) { _hwndClientRectInScreenCoords.top = ptClientTopLeft.y; _hwndClientRectInScreenCoords.bottom = ptClientBottomRight.y; } else { // RTL windows will cause the right edge to be on the left... // This doesn't affect top/bottom, but the code should be symmetrical. _hwndClientRectInScreenCoords.top = ptClientBottomRight.y; _hwndClientRectInScreenCoords.bottom = ptClientTopLeft.y; } // }
private bool ReportInput( IntPtr hwnd, InputMode mode, int timestamp, RawMouseActions actions, int x, int y, int wheel) { // if there's no HwndSource, we shouldn't get here. But just in case... Debug.Assert(null != _source && null != _source.Value); if (_source == null || _source.Value == null) { return false; } PresentationSource source = _source.Value; CompositionTarget ct = source.CompositionTarget; // Input reports should only be generated if the window is still valid. if(_site == null || source.IsDisposed || ct == null ) { if(_active) { // We are still active, but the window is dead. Force a deactivate. actions = RawMouseActions.Deactivate; } else { return false; } } if((actions & RawMouseActions.Deactivate) == RawMouseActions.Deactivate) { // Stop tracking the mouse since we are deactivating. StopTracking(hwnd); _active = false; } else if((actions & RawMouseActions.CancelCapture) == RawMouseActions.CancelCapture) { // We have lost capture, but don't do anything else. } else if(!_active && (actions & RawMouseActions.VerticalWheelRotate) == RawMouseActions.VerticalWheelRotate) { // report mouse wheel events as if they came from the window that // is under the mouse (even though they are reported to the window // with keyboard focus) MouseDevice mouse = _site.Value.CriticalInputManager.PrimaryMouseDevice; if (mouse != null && mouse.CriticalActiveSource != null) { source = mouse.CriticalActiveSource; } } else { // If we are not active, we need to activate first. if(!_active) { // But first, check for "spurious" mouse events... // // Sometimes we get a mouse move for window "A" AFTER another // window ("B") has become active. This would cause "A" to think // that it is active, and to tell Avalon. Now both "A" and "B" think // they are active, and Avalon thinks "A" is, but REALLY, "B" is. // // Confused yet? // // To avoid this, if this window ("A") gets a mouse move, // we verify that either "A" has capture, or the mouse is over "A" IntPtr hwndToCheck = SafeNativeMethods.GetCapture(); if(hwnd != hwndToCheck) { // If we get this far, "A" does NOT have capture // - now ensure mouse is over "A" NativeMethods.POINT ptCursor = new NativeMethods.POINT(); try { UnsafeNativeMethods.GetCursorPos(ptCursor); } catch(System.ComponentModel.Win32Exception) { // Sometimes Win32 will fail this call, such as if you are // not running in the interactive desktop. For example, // a secure screen saver may be running. System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetCursorPos failed!"); } try { hwndToCheck = UnsafeNativeMethods.WindowFromPoint(ptCursor.x, ptCursor.y); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: WindowFromPoint failed!"); } if(hwnd != hwndToCheck) { // If we get this far: // - the mouse is NOT over "A" // - "A" does NOT have capture // We consider this a "spurious" mouse move and ignore it. (Win32 bug?) System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: Spurious mouse event received!"); return false; } } // We need to collect the current state of the mouse. // Include the activation action. actions |= RawMouseActions.Activate; // Remember that we are active. _active = true; _lastX = x; _lastY = y; //Console.WriteLine("Activating the mouse."); } // Make sure we are tracking the mouse so we know when it // leaves the window. StartTracking(hwnd); // Even if a move isn't explicitly reported, we still may need to // report one if the coordinates are different. This is to cover // some ugly edge cases with context menus and such. if((actions & RawMouseActions.AbsoluteMove) == 0) { if(x != _lastX || y != _lastY) { actions |= RawMouseActions.AbsoluteMove; } } else { _lastX = x; _lastY = y; } // record mouse motion so that GetIntermediatePoints has the // information it needs if ((actions & RawMouseActions.AbsoluteMove) != 0) { RecordMouseMove(x, y, _msgTime); } // MITIGATION: WIN32_AND_AVALON_RTL // // When a window is marked with the WS_EX_LAYOUTRTL style, Win32 // mirrors the coordinates received for mouse movement as well as // mirroring the output of drawing to a GDI DC. // // Avalon also sets up mirroring transforms so that we properly // mirror the output since we render to DirectX, not a GDI DC. // // Unfortunately, this means that our input is already mirrored // by Win32, and Avalon mirrors it again. To work around this // problem, we un-mirror the input from Win32 before passing // it into Avalon. // if((actions & (RawMouseActions.AbsoluteMove | RawMouseActions.Activate)) != 0) { try { //This has a SUC on it and accesses CriticalHandle int windowStyle = SafeNativeMethods.GetWindowStyle(new HandleRef(this, _source.Value.CriticalHandle), true); if((windowStyle & NativeMethods.WS_EX_LAYOUTRTL) == NativeMethods.WS_EX_LAYOUTRTL) { NativeMethods.RECT rcClient = new NativeMethods.RECT(); SafeNativeMethods.GetClientRect(new HandleRef(this,_source.Value.Handle), ref rcClient); x = rcClient.right - x; } } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetWindowStyle or GetClientRect failed!"); } } } // Get the extra information sent along with the message. //There exists a SUC for this native method call IntPtr extraInformation = IntPtr.Zero; try { extraInformation = UnsafeNativeMethods.GetMessageExtraInfo(); } catch(System.ComponentModel.Win32Exception) { System.Diagnostics.Debug.WriteLine("HwndMouseInputProvider: GetMessageExtraInfo failed!"); } RawMouseInputReport report = new RawMouseInputReport(mode, timestamp, source, actions, x, y, wheel, extraInformation); bool handled = _site.Value.ReportInput(report); return handled; }
internal IntPtr FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, ref bool handled) { IntPtr result = IntPtr.Zero ; // It is possible to be re-entered during disposal. Just return. if(null == _source || null == _source.Value) { return result; } switch(msg) { case WindowMessage.WM_ENABLE: _stylusLogic.Value.OnWindowEnableChanged(hwnd, (int)NativeMethods.IntPtrToInt32(wParam) == 0); break; case WindowMessage.WM_TABLET_QUERYSYSTEMGESTURESTATUS: handled = true; NativeMethods.POINT pt1 = new NativeMethods.POINT( NativeMethods.SignedLOWORD(lParam), NativeMethods.SignedHIWORD(lParam)); SafeNativeMethods.ScreenToClient(new HandleRef(this, hwnd), pt1); Point ptClient1 = new Point(pt1.x, pt1.y); IInputElement inputElement = StylusDevice.LocalHitTest(_source.Value, ptClient1); if (inputElement != null) { // walk up the parent chain DependencyObject elementCur = (DependencyObject)inputElement; bool isPressAndHoldEnabled = Stylus.GetIsPressAndHoldEnabled(elementCur); bool isFlicksEnabled = Stylus.GetIsFlicksEnabled(elementCur); bool isTapFeedbackEnabled = Stylus.GetIsTapFeedbackEnabled(elementCur); bool isTouchFeedbackEnabled = Stylus.GetIsTouchFeedbackEnabled(elementCur); uint flags = 0; if (!isPressAndHoldEnabled) { flags |= TABLET_PRESSANDHOLD_DISABLED; } if (!isTapFeedbackEnabled) { flags |= TABLET_TAPFEEDBACK_DISABLED; } if (isTouchFeedbackEnabled) { flags |= TABLET_TOUCHUI_FORCEON; } else { flags |= TABLET_TOUCHUI_FORCEOFF; } if (!isFlicksEnabled) { flags |= TABLET_FLICKS_DISABLED; } result = new IntPtr(flags); } break; case WindowMessage.WM_TABLET_FLICK: handled = true; int flickData = NativeMethods.IntPtrToInt32(wParam); // We always handle any scroll actions if we are enabled. We do this when we see the SystemGesture Flick come through. // Note: Scrolling happens on window flicked on even if it is not the active window. if(_stylusLogic != null && _stylusLogic.Value.Enabled && (StylusLogic.GetFlickAction(flickData) == 1)) { result = new IntPtr(0x0001); // tell UIHub the flick has already been handled. } break; } if (handled && EventTrace.IsEnabled(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info)) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientInputMessage, EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, (_source.Value.CompositionTarget != null ? _source.Value.CompositionTarget.Dispatcher.GetHashCode() : 0), hwnd.ToInt64(), msg, (int)wParam, (int)lParam); } return result; }
//------------------------------------------------------ // // Interface ITransformProvider // //------------------------------------------------------ #region Interface ITransformProvider void ITransformProvider.Move( double x, double y ) { if ( ! ((ITransformProvider)this).CanMove ) throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); int extendedStyle = GetWindowExStyle(); if ( IsBitSet(extendedStyle, SafeNativeMethods.WS_EX_MDICHILD) ) { // we always deal in screen pixels. But if its an MDI window it interprets these as // client pixels so convert them to get the expected results. NativeMethods.POINT point = new NativeMethods.POINT((int)x, (int)y); NativeMethods.HWND hwndParent = SafeNativeMethods.GetAncestor(NativeMethods.HWND.Cast(_hwnd), SafeNativeMethods.GA_PARENT); if (!MapWindowPoints(NativeMethods.HWND.NULL, hwndParent, ref point, 1)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } x = point.x; y = point.y; // Make sure the MDI child stays on the parents client area. NativeMethods.RECT currentRect = new NativeMethods.RECT(); if (!Misc.GetWindowRect(_hwnd, out currentRect)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } int currentHeight = currentRect.bottom - currentRect.top; int currentWidth = currentRect.right - currentRect.left; int dx = SafeNativeMethods.GetSystemMetrics(SafeNativeMethods.SM_CXHSCROLL); int dy = SafeNativeMethods.GetSystemMetrics(SafeNativeMethods.SM_CYHSCROLL); // If to far to the left move right edge to be visible. // Move the left edge the absalute differance of the right to the origin plus a little more to be visible. if (x + currentWidth < 0) { x += ((x + currentWidth) * -1 + dx); } // If to far off the top move bottom edge down to be visible. // Move the top edge the absalute differance of the bottom to the origin plus a little more to be visible. if (y + currentHeight < 0) { y += ((y + currentHeight) * -1 + dy); } NativeMethods.RECT parentRect = new NativeMethods.RECT(); if (!Misc.GetClientRect(hwndParent, out parentRect)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // If to far to the right move left edge to be visible. // Move the left edge back the diffance of it and the parent's client area right side plus a little more to be visible. if (x > parentRect.right) { x -= (x - parentRect.right + dx); } // If to far off the bottome move top edge down to be visible. // Move the top edge up the diffance of it and the parent's client area bottom side plus a little more to be visible. if (y > parentRect.bottom) { y -= (y - parentRect.bottom + dy); } } // position the window keeping the zorder the same and not resizing. // We do this first so that the window is moved in terms of screen coordinates. // The WindowPlacement APIs take in to account the workarea which ends up // putting the window in the wrong place if (!Misc.SetWindowPos(_hwnd, NativeMethods.HWND.NULL, (int)x, (int)y, 0, 0, UnsafeNativeMethods.SWP_NOSIZE | UnsafeNativeMethods.SWP_NOZORDER | UnsafeNativeMethods.SWP_NOACTIVATE)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } UnsafeNativeMethods.WINDOWPLACEMENT wp = new UnsafeNativeMethods.WINDOWPLACEMENT(); wp.length = Marshal.SizeOf(typeof(UnsafeNativeMethods.WINDOWPLACEMENT)); // get the WINDOWPLACEMENT information. This includes the coordinates in // terms of the workarea. if (!Misc.GetWindowPlacement(_hwnd, ref wp)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } int style = GetWindowStyle(); if (style == 0) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } if ( IsBitSet(style, SafeNativeMethods.WS_MINIMIZE) ) { // If the window is minimized the parameters have to be setup differently wp.ptMinPosition.y = (int) y; wp.ptMinPosition.x = (int) x; wp.flags = UnsafeNativeMethods.WPF_SETMINPOSITION; // Use SetWindowPlacement to move the window because it handles the case where the // window is move completly off the screen even in the multi-mon case. If this happens // it will place the window on the primary monitor at a location closest to the taget. if (!Misc.SetWindowPlacement(_hwnd, ref wp)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } } else { NativeMethods.RECT currentRect = new NativeMethods.RECT(); if (!Misc.GetWindowRect(_hwnd, out currentRect)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // Use SetWindowPlacement to move the window because it handles the case where the // window is move completly off the screen even in the multi-mon case. If this happens // it will place the window on the primary monitor at a location closest to the taget. if (!Misc.SetWindowPlacement(_hwnd, ref wp)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } // check to make sure SetWindowPlacement has not changed the size of our window // There may be a bug in SetWindowPlacement. int currentHeight = currentRect.bottom - currentRect.top; int currentWidth = currentRect.right - currentRect.left; if (!Misc.GetWindowPlacement(_hwnd, ref wp)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } int newHeight = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; int newWidth = wp.rcNormalPosition.right -wp.rcNormalPosition.left; if ( currentHeight != newHeight || currentWidth != newWidth ) { wp.rcNormalPosition.bottom = wp.rcNormalPosition.top + currentHeight; wp.rcNormalPosition.right = wp.rcNormalPosition.left + currentWidth; if (!Misc.SetWindowPlacement(_hwnd, ref wp)) { throw new InvalidOperationException(SR.Get(SRID.OperationCannotBePerformed)); } } } }
// Get the child window at the specified point. // Returns IntPtr.NULL on error; returns original window if point is not // on any child window. // When the returned window is a child, the out param isClientArea indicates // whether the returned point is on the client area of the returned HWND. private static NativeMethods.HWND ChildWindowFromPoint( NativeMethods.HWND hwnd, double x, double y, out bool isClientArea ) { NativeMethods.HWND hBestFitTransparent = NativeMethods.HWND.NULL; NativeMethods.RECT rcBest = new NativeMethods.RECT(); isClientArea = true; IntPtr hrgn = Misc.CreateRectRgn(0, 0, 0, 0); // NOTE: Must be deleted before returning if (hrgn == IntPtr.Zero) { return NativeMethods.HWND.NULL; } // Infinite looping is 'possible' (though unlikely) when // using GetWindow(...NEXT), so we counter-limit this loop... int SanityLoopCount = 1024; for (NativeMethods.HWND hChild = Misc.GetWindow(hwnd, SafeNativeMethods.GW_CHILD); hChild != NativeMethods.HWND.NULL && --SanityLoopCount > 0 ; hChild = Misc.GetWindow(hChild, SafeNativeMethods.GW_HWNDNEXT)) { // Skip invisible... if( ! IsWindowReallyVisible( hChild ) ) continue; // Check for rect... NativeMethods.RECT rc = new NativeMethods.RECT(); if (!Misc.GetWindowRect(hChild, out rc)) { continue; } // If on Vista, convert the incoming physical screen coords to hwndChild-relative // logical coords before using them in [logical] rect comparisons... double xLogical = x; double yLogical = y; if (Environment.OSVersion.Version.Major >= 6) { NativeMethods.HWND hwndTopLevel = SafeNativeMethods.GetAncestor(hChild, SafeNativeMethods.GA_ROOT); NativeMethods.POINT pt = new NativeMethods.POINT((int)x, (int)y); try { SafeNativeMethods.PhysicalToLogicalPoint(hwndTopLevel, ref pt); xLogical = pt.x; yLogical = pt.y; } catch (EntryPointNotFoundException) { // Ignore. } } if(!PtInRect(rc, xLogical, yLogical)) { continue; } // Skip disabled child windows with other controls beneath it. // (only a few places actually use this, // eg. the Date&Time properties window, which has a disabled edit window // over the edits/static for the time components - see WinClient#856699) int style = GetWindowStyle(hChild); if ((style & SafeNativeMethods.WS_CHILD) != 0 && (style & SafeNativeMethods.WS_DISABLED) != 0) { int x1 = (rc.left + rc.right) / 2; int y1 = (rc.top + rc.bottom) / 2; IntPtr hwndCompare = UnsafeNativeMethods.WindowFromPhysicalPoint(x1, y1); // The WindowFromPoint function does not retrieve a handle to a hidden or disabled window, // even if the point is within the window. So we should either get the parents hwnd or // the controls hwnd underneath the disabled control, if one exsists. // if (hwndCompare != (IntPtr)hwnd) { // This means that there is another child under `the disabled child, so we want to exclude // `the disabled child high in the z-order. continue; } } // Check for transparent layered windows (eg as used by menu and tooltip shadows) // (Note that WS_EX_TRANSPARENT has a specific meaning when used with WS_EX_LAYERED // that is different then when it is used alone, so we must check both flags // together.) int exStyle = GetWindowExStyle(hChild); if( ( exStyle & SafeNativeMethods.WS_EX_LAYERED ) != 0 && ( exStyle & SafeNativeMethods.WS_EX_TRANSPARENT ) != 0 ) { continue; } // If window is using a region (eg. Media Player), check whether // point is in it... if (SafeNativeMethods.GetWindowRgn(hChild.h, hrgn) == SafeNativeMethods.COMPLEXREGION) { // hrgn is relative to window (not client or screen), so offset point appropriately... if (!SafeNativeMethods.PtInRegion(hrgn, (int)xLogical - rc.left, (int)yLogical - rc.top)) { continue; } } // Try for transparency and/or non-client areas: IntPtr lr = Misc.SendMessageTimeout( hChild, UnsafeNativeMethods.WM_NCHITTEST, IntPtr.Zero, MAKELPARAM( (int)x, (int)y ) ); if( lr == UnsafeNativeMethods.HTTRANSPARENT ) { // For reasons best known to the writers of USER, statics - used // as labels - claim to be transparent. So that we do hit-test // to these, we remember the hwnd here, so if nothing better // comes along, we'll use this. // If we come accross two or more of these, we remember the // one that fits inside the other - if any. That way, // we hit-test to siblings 'within' siblings - eg. statics in // a groupbox. if( hBestFitTransparent == NativeMethods.HWND.NULL ) { hBestFitTransparent = hChild; if (!Misc.GetWindowRect(hChild, out rcBest)) { continue; } } else { // Is this child within the last remembered transparent? // If so, remember it instead. NativeMethods.RECT rcChild = new NativeMethods.RECT(); if (!Misc.GetWindowRect(hChild, out rcChild)) { continue; } if( Rect1InRect2( rcChild, rcBest ) ) { hBestFitTransparent = hChild; rcBest = rcChild; } } continue; } // Got the window! // Using the hit-test result and compairing against HTCLIENT is not good enough. The Shell_TrayWnd control, // i.e. the task bar, will never returns a value of HTCLIENT, so check to see if the point is in the client area. NativeMethods.RECT rcClient = new NativeMethods.RECT(); if (!Misc.GetClientRect(hChild, out rcClient) || !MapWindowPoints(hChild, NativeMethods.HWND.NULL, ref rcClient, 2) || !PtInRect(rcClient, xLogical, yLogical)) { isClientArea = false; } Misc.DeleteObject(hrgn); // finished with region return hChild; } Misc.DeleteObject(hrgn); // finished with region if( SanityLoopCount == 0 ) { Debug.Assert(false, "too many children or inf loop?"); return NativeMethods.HWND.NULL; } // Did we find a transparent (eg. a static) on our travels? If so, since // we couldn't find anything better, may as well use it. if( hBestFitTransparent != NativeMethods.HWND.NULL ) { return hBestFitTransparent; } // Otherwise return the original window (not NULL!) if no child found... return hwnd; }
internal NativeMethods.POINT GetMouseCursorPos(Visual targetVisual) { if (Mouse.DirectlyOver != null) { // get target window info HwndSource hwndSource = null; if (targetVisual != null) { hwndSource = PopupSecurityHelper.GetPresentationSource(targetVisual) as HwndSource; } IInputElement relativeTarget = targetVisual as IInputElement; if (relativeTarget != null) { Point pt = Mouse.GetPosition(relativeTarget); if ((hwndSource != null) && !hwndSource.IsDisposed) { Visual rootVisual = hwndSource.RootVisual; CompositionTarget ct = hwndSource.CompositionTarget; if ((rootVisual != null) && (ct != null)) { // Transform the point from the targetVisual to client device units GeneralTransform transformTo = targetVisual.TransformToAncestor(rootVisual); Matrix transform = PointUtil.GetVisualTransform(rootVisual) * ct.TransformToDevice; transformTo.TryTransform(pt, out pt); pt = transform.Transform(pt); // Convert from device client units to screen units return ClientToScreen(hwndSource, pt); } } } } // This is a fallback if we couldn't convert Mouse.GetPosition NativeMethods.POINT mousePoint = new NativeMethods.POINT(0, 0); UnsafeNativeMethods.TryGetCursorPos(mousePoint); return mousePoint; }
public static extern int IntScreenToClient(HandleRef hWnd, [In, Out] NativeMethods.POINT pt);