[MaxGUI] Dynamic resize and movement update

BlitzMax Forums/BlitzMax Programming/[MaxGUI] Dynamic resize and movement update

ImaginaryHuman(Posted 2006) [#1]
Using MaxGUI, I am trying to have a window with a canvas on it which will obviously display the screen from my game ... and my window is resizeable. I have set up a hook and put most of the event detecting in the hook so that it is called even while the user is clicking things like dragging the title bar or resizing the window. For the most part this works, but there are some `gaps` in what normally would be full-time refreshing of the canvas as in a regular game.

The first problem is this: Window resize events only get created when you move the mouse away from the current coordinates while holding down the mouse button. When the mouse is stationary there is no resize event generated, so the hook is not called and the display doesn't update. So as you sit there holding down the mouse on the resize gadget, the game doesn't update.

The second problem is this: Almost the inverse of the first problem, when you click on the title bar and drag the window, window move events are generated only when the window has finished moving, at which point the game seems to update properly even if you're still holding the mouse down, but when the window is actually moving around with different mouse coords it stops updating.

How can I address these two issues so that the game screen is constantly being redrawn at 60hz or whatever rate, even when it is being moved or resized?




ImaginaryHuman(Posted 2006) [#2]
Do I need to switch from using WaitEvent() and use PeekEvent() and PollEvent(), so that I can then, in my main loop (not in the hook), keep updating and flipping the display? Will that update/flip when there is no calls to the event hook and while the user is holding down the mouse on the resize gadget?


Grisu(Posted 2006) [#3]
SetGadgetShape(window,0,0,lastdw,lastdh) 'resize
FreeGadget canvas
canvas=CreateCanvas(0,0,GadgetWidth(window),GadgetHeight(window),window)
ActivateGadget(canvas)

You should try to get rid of these.
Try "SetGadgetLayout" instead!
This way you won't need to free and recreate the canvas anew.


ImaginaryHuman(Posted 2006) [#4]
Cool, thanks, that actually increased my framerate :-)

So if I understand this right, the canvas is the gadget that's part of the window, and since I've asked it to be fixed to the edges of the window it will resize whenever the window resizes. So as you resize the window, the canvas gets bigger or smaller, and then so long as I do SetGraphiccs CanvasGraphics(Canvas) upon each resize event, I will have a dynamically resizing OpenGL screen.

I guess OpenGL must re-allocate new front and back buffers when it gets resized.

I still am not sure how to get it to update the canvas at all times regardless of modal clicking. Maybe I should detect some other event, or do away with WaitEvent()?


ImaginaryHuman(Posted 2006) [#5]
Update: I tried changing the WaitEvent() to a combination of PeekEvent() (to see if there is an event in the queue, and if not, fall through and update the display like in a regular game loop) and PolEvent() (to process the event if one is waiting). The result of this is that WHILE you are moving the window and haven't stopped yet, it does update the display, albeit slowly because it is competing with the o/s code. Also it hogs the CPU a lot, so I added Delay 1 or even Delay 2 but this doesn't seem to be enough time, on a slow machine, to allow the o/s's gadgets to be looked at. The resize gadget does not work because there isn't enough CPU time. Obviously I can't give it enough CPU time on a slower machine that is already below the ideal framerate as there just isn't enough to go around, and not being called through the hook means the o/s gets strangled. So window resize doesn't work at all, but the title bar drag works to update when possible.

The other thing I tried is putting a Case EVENT_MOUSEDOWN in the hook, but this appears to never to be called - probably the mouse event is trapped by the WINDOWRESIZE and WINDOWMOVE events. I hoped maybe it would generate a mousedown event that I could use, when the user clicks the title bar or the resize button. Maybe I just need to put the event check for the mousedown before the others? Or maybe there just is not continuous events generated when the user is holding down the mouse button?

Either way it still isn't doing it right yet.

I basically need what would be the same result as a movie player window, which is playing an animation at a constant rate and doesn't stop playing even if the user is trying to resize or move the window.

Any other ideas?

(maybe I should try to detect MOUSEDOWN and keep a flag that it is down and only update the display in the main loop while that is `True`, then when a MOUSEUP event comes along stop using the main loop to update and rely on the timer from the hook?

[EDIT] I tried this, didn't work. There are no mousedown events for resizing the window while the mouse is held down and not being moved around.

Basically my update freezes until the user either actually adjusts the window size, or finishes moving the window. How can I get constant refresh? Is it even possible in MaxGUI? Is it a limitation of when the o/s generates events? Is there a workaround?


ImaginaryHuman(Posted 2006) [#6]
Possible bug in MaxGUI:

When I try this program with a window that only has WINDOW_CLIENTCOORDS as the flags, ie no title bar, no resizing, my app does not show any graphics and it eventually crashes with:

Compile Error
53.262 CreateCanvas-WindowResize.debug[634] *** -[NSNextStepFrame contentAlpha]: selector not recognized
(this is shown in the error requester that pops up)

and..

-[NSNextStepFrame contentAlpha]: selector not recognized
2006-05-07 12:55:53.265 CreateCanvas-WindowResize.debug[634] Exception raised during posting of notification. Ignored. exception: *** -[NSNextStepFrame contentAlpha]: selector not recognized
(shown in the debug window)

Can't you have a canvas without a title bar present?

(It works with WINDOW_RESIZABLE and no WINDOW_TITLEBAR, which makes a title bar with a maximize button on it, but when there is no need for any title bar, it crashes.)

---------------------------------

Apart from that issue, the reason I wanted to try a canvas with no title bar and no resize, is that I have come to some conclusions about what is possible with MaxGUI (or seems to be).

First of all, from printing out every event that is passed to the hook, it seems there are absolutely no events generated when the user presses the mouse button on the title bar or the resize gadget, there are no events when the user is holding down that button and not moving the mouse (unless they were just moving it and hence got WINDOWMOVE and WINDOWSIZE events.

It seems that no events are generated at those times and the o/s is totally modal, hogging CPU control and making it impossible for me to do any updates.

So what I thought was, maybe I can do away with the title bar and resize gadget and *make my own*, so that I can get control back from the o/s while the user is holding down the mouse and not moving it. Upon removing WINDOW_RESIZABLE from the window flags it seems okay that you can use SetGadgetShape() to change the window size still, even though it doesn't have a resize gadget. That is fine. I could detect mouse down events, see if the mouse is in the corner and if so, start tracking mouse coord changes. If the mouse doesn't move while it's held down I could then call my update routine to draw the display, or when the mouse moves I can resize the window to follow the mouse. Seems plenty doable.

Then the other issue is moving the window from the title bar - since this doesn't generate events unless the mouse moves, I would have to do away with the default title bar, which would mean that on all three platforms I wouldn't have a native-looking title bar (unless I fake it, which is possible, but probably no desirable or reliable). I could just have no title bar and when the user moves into the title bar region with the mouse, some kind of game-styled title bar appears, and then if the user clicks the mouse it begins moving the window around as the mouse moves. It can keep calling `update` to refresh the display while the mouse is down and this should let everything keep running smoothly - bearing in mind the o/s takes some CPU time to redraw the window.

This *sounds* doable, as a way to get around the lack of events, and keeping it in an event-driven system rather than some hogging unpredictable main loop that isn't friendly to the o/s or GUI manipulation.

One concern though is that MOUSEMOVE events seem only to be generated when the mouse is *inside the window*. What if the user moves the mouse quite quickly and it is outside the window, no events are generated and the window won't get moved or resized! What can we do about that?


ImaginaryHuman(Posted 2006) [#7]
Well, I seem to have found a way to make this work. It seems to me that in actual fact, when you click on the title bar and move the window, when the mouse stops moving (still clicking) there are still TIMERTICK events that trickle through when there is enough CPU time to do so. Moving the window seems quite a bit more expensive on the CPU than resizing it, so the display updates less often, but it does update. Best we can hope for I guess.

Additionally, I've set the window to not be resized, but added my own little `hot zone` in the bottom right corner. When the mouse goes there and clicks a MOUSEDOWN event, the mousedown hook sets a variable to say that we're beginning with resizing the window, and exits. Then when normal MOUSEMOVE events come along it checks if we are in the middle of resizing, and if so, it uses SetGadgetShape() to resize the window according to the relative mouse movement (compared to the coords when first clicked). Then when there is a MOUSEUP event it sets the resizing flag to false to say we've ended our resizing. [EDIT] We could have a custom resize gadget in EVERY corner if we wanted to, and/or window-edge dragging :-D

This works, and allows you to resize the window while not being locked into the modal control of the o/s - you regain control of the CPU at all times and can continue checking other events and can update the display, plus you can limit the resizing to a minimum size AND a maximum size. Seems to work pretty great, and the display constantly updates while resizing.

I've got some `pause` code in there which is sort of along the lines of pausing things when the app is minimized or when focus is elsewhere, but it's really just an afterthought placeholder for trapping the events and doesn't work right at the moment. No biggie, it needs changing to separate `minimized` and `paused` flags because there are other reasons the game might be paused regardless of the window size. I'm also not quite sure what to do with maximizing the window - maximize doesn't necessarily mean use all the available client space of the desktop, but I'd like it to.

Also in the code some bits are repeated rather than put into nice functions and stuff, and no particular use of types or OOP. It's just an experiment to get the basics working and see that it is possible.

There are a couple of drawbacks to this technique .. Firstly you don't get the system-default appearance for the resizing gadget, if any. On the Mac this can be pretty minimal anyway and probably easily approximated with some small pixmaps for each platform, or I could just go with an entirely game-based custom gadget appearance. I could even make it into a button that shows that it is being clicked. The other issue is that at the moment I'm still finding that you have to have a title bar otherwise the canvas doesn't update and it crashes out. Still don't know what that is all about, but it'd be nice to have a fully borderless window and then have my own custom title bar.

NB: This code also fixes the problem that if you just resize the window and you don't call SetGraphics CanvasGraphics() after the resize, you still get the same sized canvas with coords being in the same relative place in proportion to the window size, rather than having whole complete pixels, and it scales ugly in realtime. It's better to make a resized canvas and make a new graphics object from it so you get pixel-for-pixel output.

Anyway, here's the code.

A question for BRL - why does dragging the title bar allow TIMERTICK events to come through inbetween WINDOWMOVE events, but WINDOWRESIZE events don't allow any TIMERTICK events to get through?

A question for everyone else - why am I the only person writing in this thread? lol