Open PaulBol opened 5 years ago
I will have to go back through the history to see why we did it this way. The problem with PostMessage
is that it could potentially result in wrong order in relation to WM_MOUSEMOVE events (eg. receiving WM_MOUSEMOVE before WM_MOUSE_ENTER).
I'll hopefully have more information soon, just wanted to let you know that I am looking into it.
Thanks for your feedback. I see your point. I was thinking that PostMessage may be closer to the Windows and Carbon implementation but it may not be worth risking to break something else. I have a workaround in my code so it's fine for me.
I'd love it to be closer to the Windows implementation, but emulating it accurately would likely incur too big of a performance penalty. There are two things to consider:
1) How message queues operate on Windows and how are the messages prioritized. Is there an accurate way to translate that into Cocoa?
2) How the TrackMouseEvent
API behaves and how is it internally implemented in respect to message prioritization.
Luckily the answers could be found in MSDN, Wine and ReactOS code:
The important part about message queues is documented in the PeekMessage
API documentation. If no filter is specified, messages are processed in the following order:
There are separate queues/flags for each of the above categories. According to ReactOS test suite (and probably the Wine one as well; both excelent sources of the precise message sequences used on Windows) and source code the WM_MOUSELEAVE message is indeed posted and as such would have higher priority than any other input messages (such as WM_MOUSEMOVE). Once there are no posted messages (hence no WM_MOUSELEAVE) the internal input messages are delivered to the application. Only at this point the actual window for any WM_MOUSEMOVE message is determined. There's no WM_MOUSE_ENTER on Windows (a concept only present in the Mono WinForms implementation) and the relevant OnMouseEnter
messages are generated from the WM_MOUSEMOVE messages. Basically the worst inconsistency that can happen due to the implementation details is that you would receive delayed OnMouseLeave
followed by OnMouseEnter
after the next mouse move (unless my mental picture is wrong, which could easily be the case).
I didn't investigate how Windows/Wine/ReactOS handle the case of SetCapture
/ReleaseCapture
APIs and whether the WM_MOUSELEAVE event is generated in that case and how. Most likely ReleaseCapture
simply simulates low-level mouse move by 0 pixels and let's the system figure out which messages need to be generated and into which queue.
On Cocoa emulating all this is tricky. We don't have cross-process SendMessage
. PostMessage
is emulated by creating native Cocoa events and running them through the Cocoa message pump. The Cocoa messages in the pump are processed by the native Cocoa mechanism (ie. routing through NSWindow
, NSView
and NSResponder
chains). We receive the transleted mouse events in MonoView
, MonoWindow
and WindowsEventResponder
- our classes delivered from NSView
, NSWindow
and NSResponder
respectively. The mouse messages (such as WM_MOUSEMOVE) are generated there and delivered using SendMessage
to skip going through the message queue again.
Moreover, Cocoa has no concept of "mouse enter" / "mouse leave" events, so they have to emulated. In addition to that the "mouse move" in Cocoa behaves differently from Windows and it's expensive. While Windows sends WM_MOUSEMOVE to one specific control (since Windows don't make distinction between windows and controls) on Cocoa it's delivered to NSWindow and then any further dispatching is handled by the NSWindow. Specifically for mouse move events it uses a concept of tracking areas. We register a tracking area for each MonoView
, which unfortunately results in the side effect that the mouse move events are delivered to the all the controls in the hierarchy under the mouse cursor (eg. when a Button is inside a Panel then both underlying MonoView
objects would be notified when mouse moves over the button). Converting this to the Windows messages is quite tricky, rather easy to break and technically challenging to get right, unfortunately.
Thanks for explaining the background. You clearly have a far more profound knowledge of the message loop implementation. My idea of simply replacing SendMessage with PostMessage apparently means much more of a change than I had thought and is not worth the risk. Should we close this issue?
Let's keep the issue open so we can track the problem, but I cannot promise it will be fixed any time soon. We are currently facing some issue internally that may be related to it, but it uses our internal controls, message filters and we didn't isolate it yet.
Something else that I noticed...
UngrabWindow
inXplatUICocoa.cs
includes these linesThis leads to a nested call of
WndProc
withWM_MOUSELEAVE
when the mouse button is released (WM_LBUTTONUP
) while captured.My code was not prepared to deal with this. I'm not daring to suggest this is a bug in
XplatUICocoa.cs
that needs to be fixed. Only wondering if it should bePostMessage
instead ofSendMessage
so that the button up message handling is completed beforeWM_MOUSELEAVE
. I figured out this is whatSystem.Windows.Forms.CarbonInternal.MouseHandler
does inTranslateMessage
.