SFML window resizing events blocking the main thread - windows

I just started using the SFML library and its fantastic. However when resizing a window by dragging the corner with my mouse i don't get the resize events until i release the mouse. This means i can't update my graphics until the mouse is released (game loop is on the gui thread) and is also causing a massive flood of events to come through of all the resize positions.
How can i make it so resizing doesn't block the thread?

Windows causes this. GetEvent() becomes blocking if you move or resize the window.
(Another funny little reason to hate this OS...)
I have solved this with a separate event-polling thread. Take a look at this:
(The interesting part for you is entirely in main.cpp.)
https://bitbucket.org/torokati44/netframework/src/93bdf20d0f65/main.cpp

Related

Detect moving the windows from one display to another

I have a window with an opengl view where content ist rendered. The problem I have in macOS is, that when I move the window from one monitor to the other, its content gets messed up. A redraw fixes the issue. Thus I need to redraw the GL Area when it gets moved from one monitor to the other. Is there any way to detect the transition of the window from one monitor to the other?
You can register for notifications that fire when the view's window changes screens: NSWindowDidChangeScreenNotification

Between windowDidMove and windowWillMove

I've been trying with windowDidMove and windowWillMove (NSWindowDelegate) but I think I need something between these two...
Is there any other way to detect when I move my window in cocoa?
I mean - I want to trigger a function if I drag a window to the bottom of the screen, but I want this function to be run even if I didn't yet release the window?
The middle ground you are seeking is handling the mouse events yourself and implementing the window dragging. If you do this you determine how dragging works; so you can constrain the window to an area of the screen, trigger events when the window reaches a screen edge, etc.
You'll need to do some reading, you could start with Apple's Handling Mouse Events.
If you have problems once you've done the reading, written some code, etc. ask a new question, showing your code, and explain the problem you've hit. Somebody will probably help you out.
HTH

Is [NSWindow setFrame:display:] API asynchronous?

I have a strange UI behavior which made me think that this API might be asynchronous. Assuming it is (async), I've made some changes and now the UI works as expected. I didn't find the answer in Apple's documentation.
Is anyone familiar or had some experience with this? Is it really async? Thanks!
EDIT: All UI changes are being made from the main thread.
setFrame:display: updates the frame and tells the system to redraw the window (and possibly the subviews) during its next drawing cycle. Cocoa drawing is always done during its drawing cycle, not at the moment you call a drawing method. See What is the most robust way to force a UIView to redraw? for some more discussion on that.
This is better thought of as drawing being "consolidated" rather than "asynchronous." Unless there are animations, all drawing should generally be complete before the next event loop. It's possible that a view will somehow delay its drawing to a later event loop, but rare. Layer-backed views in 10.8+ can support actual async drawing, but this is not the default.
From you description, it's possible you're calling setFrame:display: from a thread other than the main thread. AppKit drawing is not thread safe. You cannot call drawing methods from anywhere but the main thread, or you'll get "strange UI behavior." (This isn't completely true. On OS X, you can use lockFocus to draw from other threads, but they still won't actually be applied until the next drawing cycle after you unlock focus. But normally it's best just to draw from the main thread in modern OS X since GCD makes this simple.)

How to draw outside a window?

Looking at a Windows tooltips class hint window, i see that it draws its drop-shadow outside the hint window's actual rectangle.
Using SpyXX - i can get the tooltip's window rect, and class styles:
Rectangle: (440, 229)-(544, 249), 104x20
Restored Rect: (440, 229)-(544, 249), 104x20
Client Rect: (0, 0)-(104, 20), 104x20
You'll notice that the drop shadow you see is physically outside the window that's being drawn. How can i draw a shadow outside around my window, while being outside my window?
Note: The shadow is not drawn using the standard CS_DROPSHADOW class style. i've confirmed this experimentally, and can also see the class style's for the window in SpyXX; it does not use CS_DROPSHADOW:
Windows Styles: 94000001
WS_POPUP 80000000
WS_VISIBLE 10000000
WS_CLIPSIBLINGS 4000000
TTS_ALWAYSTIP 1
Extended Styles: 00080088
WS_EX_LAYERED 80000
WS_EX_TOOLWIN 80
WS_EX_TOPMOST 8
So how can i draw outside my window?
Note: Trying to draw on the desktop DC is out. From Greg Schechter's Redirecting GDI, DirectX, and WPF applications:
Drawing To and Reading From the Screen
-- Baaaad!
Lastly, since we're on the redirection
topic, one particularly dangerous
practice is writing to the screen,
either through the use of GetDC(NULL)
and writing to that, or attempting to
do XOR rubber-band lines, etc. There
are two big reasons that writing to
the screen is bad:
It's expensive... writing to the
screen itself isn't expensive, but it
is almost always accompanied by
reading from the screen because one
typically does read-modify-write
operations like XOR when writing to
the screen. Reading from the video
memory surface is very expensive,
requires synchronization with the DWM,
and stalls the entire GPU pipe, as
well as the DWM application pipe.
It's unpredictable... if you somehow
manage to get to the actual primary
and write to it, there can be no
predictability as to how long what you
wrote to the primary will remain on
screen. Since the UCE doesn't know
about it, it may get cleared in the
next frame refresh, or it may persist
for a very long time, depending on
what else needs to be updated on the
screen. (We really don't allow direct
writing to the primary anyhow, for
that very reason... if you try to
access the DirectDraw primary, for
instance, the DWM will turn off until
the accessing application exits)
You can't draw outside your window in the manner you describe.
If you right click your desktop then go to properties/appearance/effects and uncheck 'Show shadows under menus' ... you will no longer have the shadow.
Bottom line is that this is a product of the window manager not your program.
Q: How do you draw outside of one window? A: Draw inside another window!
First thing to note is that the tooltip class actually does use the CS_DROPSHADOW style - but note that this is a class style, not a window style, so you have to look at the Class tab in the Spy++ properties dialog to find it. You'll see that the tooltips_class32 windows does indeed have this - and a few others.
But that just leads to the next question - how does that work? Well, it seems that Windows implements this by creating a helper HWND to draw the shadow - presumably it's creating another popup window the same size and shape as the one it's shadowing, filling it with gray, placing it directly underneath the main window, and setting it as a WS_EX_LAYERED window so that the shadow can be transparent and fade out around the edges using alpha-blending. And there's nothing to stop you from using the same or similar techniques yourself if you want to add a different type of shadow effect to one of your own windows.
So, long story short: if you want to draw outside of your own window, create a helper transparent window in the general area that you want to draw on, and draw on that helper window instead.
--
Now, if you try to find one of these helper shadow windows in Spy++, you won't find much. Unlike the tooltip_class32 windows, which are long-lived and just hide/show themselves as needed, these shadow windows are a more elusive creature: they are only created for as long as needed, so you'd have to refresh Spy++ while there's a tooltip or popup menu or other window using the shadow present - and that's tricky, since most tooltips and menus will disappear as soon as you move the mouse to switch to Spy++. But it turns out that the tooltips on Spy++'s own toolbar will stick around: so start Spy++, hover over an item in the toolbar, and hit F5 to refresh the HWND tree while the tooltip and shadow are present. Now scroll down, and you should see the third and fourth visible HWNDs in the tree are the tooltip itself, and right after that, a SysShadow window. Unfortunately, since the tooltip and shadow have by now disappeared, if you attempt to get the properties dialog for that HWND, you'll get a get a blank property dialog with an 'Invalid Window' message. If you really want to poke around and see how that SysShadow works, what styles it itself uses and so on, you could create a target app with a long-lived popup that uses CS_DROPSHADOW that you can then explore in Spy++ at leisure.
(Finally, note that these shadows are a completely different thing than the shadows that you see when one app window is on top of another above another since Vista: this type of shadow is part of Aero Glass mode, and handled by the same Desktop Composition Manager that adds the glass titlebar effect, and it doesn't use or need helper windows to implement the shadows.)
I wouldn't be surprised if that shadow is intimately tied to the window manager itself; it is after all the window manager who decides what window gets to paint which parts of itself and when it can do it. I don't see it as rocket science to paint that shadow if control over all that is gained, which the window manager has.

How can I trigger Core Animation on an animator proxy during a call to resizeSubviewsWithOldSize?

I have some NSViews that I'm putting in one of two layouts depending on the size of my window.
I'm adjusting the layout when the relevant superview receives the resizeSubviewsWithOldSize method.
This works, but I'd like to animate the change. So naturally I tried calling the animator proxy when I set the new frames, but the animation won't run while the user is still dragging. If I release the mouse before the animation is scheduled to be done I can see the tail end of the animation, but nothing until then. I tried making sure kCATransactionDisableActions was set to NO, but that didn't help.
Is it possible to start a new animation and actually have it run during the resize?
I don't think you can do this easily because CA's animations are run via a timer and the timer won't fire during the runloop modes that are active while the user is dragging.
If you can control the runloop as the user is dragging, play around with the runloop modes. That'll make it work. I don't think you can change it on the CA side.
This really isn't an answer, but I would advise against animating anything while dragging to resize a window. The screen is already animating (from the window moving) - further animations are likely going to be visually confusing and extraneous.
CoreAnimation effects are best used to move from one known state to another - for example, when a preference window is resizing to accompany a new pane's contents, and you know both the old and new sizes, or when you are fading an object in or out (or both). Doing animation while the window is resizing is going to be visually confusing and make it harder for the user to focus on getting the size of the window where they want it to be.

Resources