Edit: I would like the dragging to appear "smooth" -- the refresh rate of each docked window while dragging to stay above 15 frames per second.
I've written this AutoHotKey script to demonstrate the concept but the animation is far from smooth.
(For this demo script to work, you must open 5 copies of notepad and save the documents as XXX1 ... XXX5)
SetTitleMatchMode, 2
WinGetPos,x1,y1,w1,h1,XXX1
WinGetPos,x2,y2,w2,h2,XXX2
WinGetPos,x3,y3,w3,h3,XXX3
WinGetPos,x4,y4,w4,h4,XXX4
WinGetPos,x5,y5,w5,h5,XXX5
yy1:=y1
Loop
{
xx1:=x1
WinGetPos,x1,y1,w1,h1,XXX1
If (x1<>xx1)
moveAll()
}
return
moveAll()
{
global
WinMove,XXX1,,%x1%,%yy1%
x:=x1+w1
WinMove,XXX2,,%x%,%yy1%
x+=w2
WinMove,XXX3,,%x%,%yy1%
x+=w3
WinMove,XXX4,,%x%,%yy1%
x+=w4
WinMove,XXX5,,%x%,%yy1%
}
Can anyone either optimize this script or suggest a proven method? (Or even show that it can't be done.) If it needs to be written in another language, so be it. It appears that Windows is limiting the repainting of windows to a certain total number of repaints on screen per second.
As per the comments, SetWinDelay, -1 and SetBatchLines, -1 will make it smoother. I disagree with using SetWinDelay, 5, since we want the pause between two WinMoves to be as small as possible. Although probably not noticebale to the naked eye, -1 will minimize that pause.
Related
Some years ago (2008) I wrote an online Child Support Calculator using PHP/Javascript/Ajax/css Advanced Child Support Calculator - May help to explain what I am replicating.
Some time after (2009 I think) I started writing an equivalent for Windows and am now revisiting this. The fundamentals of this version are working.
However, I have an issue with the window noticeably flickering/changing when controls are dynamically added and the window is redrawn/rebuilt.
Note! This calculator is specific to Australia.
In short I'm looking for a way, if possible, to only refresh/re-display/redraw the window after all components have been added.
Basically, the windows controls need to be dynamically added/removed depending upon the scenario (number of adults and children involved).
Adding or removing a child or adult or performing the calculation, results in a complete rebuild of the window. That is, all existing controls are destroyed and then all valid controls are added (this could perhaps be minimised via complex logic).
The issue is that controls are removed but briefly reappear (in an ordered fashion) causing the display to flicker (for want of a better description).
The following screen shots demonstrate the complexity factor (such as a child has a drop down for each defined adult), but not the flicckering(sic).
Here's a screen shot of the Initial display (OK pretty ugly at present) :-
And then if an Adult is added (note that the child now has an extra dropdown for Adult 3, the new adult):-
And now, an Adult (as above) and a Child:-
Coding wise, there is a RebuildAll function. This has two main stages. (1) The removal of the controls. (2) The rebuild (recreation) of the appropriate of the controls (Create Window, SendMessage's and then ShowWindow)
At a minimum there are 61 controls. The number of controls is
23 + ((#children * 12) - 2) + (#children * (#Adults * 8 )) + ((#adults * 10) -4). It's likely suffice to say that the number of controls increases rapidly/exponentially.
I suspect that it might be possible to postpone the ShowWindow untill after all of the builds have been done. Is the solution, as simple (theoretically) as this, assuming that this is feasible or is there another way that would circumvent the need to change all the code to remove the 'ShowWindow' and add a ShowWindow at the end of the builds?
Note replacing the individual ShowWindow's with one significantly reduced the "flicker" but didn't eliminate it. (as per Update below).
As an endnote I'm pretty sure that something should be feasible as the
windows program I have to date is a very poor reflection of the speed
of the browser/javascript version, which basically does the same thing
on windows (albeit 64bit).
Update
I went through and commented out the ShowWindows in all the AddItem???? functions and added a ShowWindows to the function that calls all the functions. This has improved matters. However, the numerous DestroyWindow calls still causes flickering when removing all of the controls. So now I guess that I'm looking for something that can disable them apparently doing an equivalent of ShowWindow.
Update 2
I have found SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); (TRUE to turn drawing back on). However, this appears to supress the DestroyWindow in that the display aspect of the control remains (the controls themselves don't appear to respond though).
By a process of elimination I have cured the flicker with a combination of ShowWindow's and WM_SETREDRAW's at pertinent points.
To briefly re-describe the issue. When adding a child or adult, or performing a calculation. The entire display is rebuilt. The rebuild basically consists of 2 phases. First, destroying the existing control windows and then adding the new/replacement set of window's controls.
The resolution was to :-
Issue a ShowWindow(hwnd,SW_SHOW); (for the main/containing
window) after all the pre-existing windows control's had been
destroyed.
Issue a WM_SETREDRAW FALSE message as per SendMessage(hwnd,
WM_SETREDRAW, FALSE, 0); before rebuilding the windows controls.
Without any ShowWindow's being used.
Build the windows controls via the various functions.
Finally, when all the windows controls have been built, issue a
ShowWindow(hwnd,SW_SHOW); (for the main/containing window).
Rather than seeing the controls appear and then disappear, "the flickering". The window is blank for a while and then the controls are displayed all at once.
I'm still confused about what is actually going on though, due to:-
After some thought I decided to use a MessageBox after all the destroys. When the message was displayed the window was blank. However, when the button was clicked and processing resumed then, in situations when the number of controls was relatively high, controls would appear and then disappear in an orderly fashion, albeit it momentarily.
My guess, is that creating a control in an area where an existing
control had been, was causing that previous control to momentarily
display.
I have a cocoa application window (NSWindow) which position on the screen should be updated frequently (depending on some calculation). As noticed in the documentation, UI changes should be made on the main thread:
void calculationThread()
{
while(true)
{
calculatePosition();
if(positionChanged)
{
dispatch_async(dispatch_get_main_queue(), ^{ setWindowPos(); });
}
}
}
void setWindowPos()
{
[window setFrame:_newFrame display:YES];
}
Now the problem I have is that the window movement is very slow and delayed. After making some profiling I see that the calculation process takes about 40mSec, meaning that I'm queueing up a backlog of UI updates 25 times a second.
I've read here that this might be faster than they can be processed and timer should be used to fire the changes every tenth of a second or so. But, wouldn't it be too slow for the human eye (I mean, in that case the movement wouldn't be delayed but would be lagged causing pretty much the same affect).
I will appreciate some knowledge sharing on this. Actually my main 2 questions are:
Are 25-30 UI updates per second really to much?
If yes, what is the recommended UI changes frequency?
The frequency at which a window can be moved around onscreen without problems will of course depend upon the speed of the user's machine, the video card they have, the size of the window, and probably a bunch of other factors. There is no single good answer to this. However, if you just drag a window around on your screen, you will notice that it can probably be moved very smoothly (unless your machine is very busy or very low on memory or something); I would not expect 25 times per second to produce a problem on a modern Mac. Not even close, in fact.
#RobNapier's points about Core Animation etc. are fine, but overstated I think; there is nothing inherently wrong with changing your UI using a timer or other periodic update if that is what you actually want to do. CoreAnimation is a toolkit for making some types of animation easier; using it is not required, and it is not suited to every problem. Similarly, if you want to make changes that are actually synched to screen refresh then CVDisplayLink is useful, but it doesn't really sound like that's what you want to do.
For your purposes, your basic approach seems fine, although I would suggest adding an NSDate check in order to skip updates if the previous update was less than, say, 1/60th of a second previous. After all, the calculation appears to take 40mSec on your machine, but it might be much faster on some other machine; you want to throttle your drawing to a reasonable rate just to be a good citizen.
So what is the problem, then? I suspect the issue might actually be your call [window setFrame:_newFrame display:YES]. If you look at Apple's docs for that method, they state "When YES the window sends a displayIfNeeded message down its view hierarchy, thus redrawing all views." Each time you call that method, then, you are not only moving your window (which I gather is your intention); you are redrawing all of the contents of the window, too, and that is slow. If you don't need to do that, then that is the overhead you need to eliminate. Call setFrameOrigin: or setFrameTopLeftPoint: instead (which make the semantics clear, that you are moving the window without resizing it or redrawing it), or perhaps just setFrame:display: passing NO instead of YES, and I'm guessing your performance problem will vanish.
If you do in fact need to redraw the window contents every time, then please edit the problem description to reflect that. In that case, the solution will have to involve profiling why your window drawing is slow, and figuring out ways to optimize that, which is an entirely different problem.
As you've discovered, you should never try to drive the UI from a tight loop. You should let the UI drive you. There are three primary tools for that.
For simple problems, AppKit is capable of moving windows around the screen. Just call [NSWindow setFrame:display:animate:]. You can override animationResizeTime: to modify the timing.
In many cases AppKit doesn't give enough control. In those case, the best tool is almost always Core Animation. You should tell the system using Core Animation how you where you want UI elements to wind up, and over what period and path, and let it do the work of getting them there. See the Core Animation Programming Guide for extensive documentation on how to use that. It focuses on animating CALayer, but the techniques are similar for NSWindow. You'll use [NSWindow setAnimations:] to add your animation. Look at the NSAnimatablePropertyContainer protocol (which NSWindow conforms to) for more information. For a simple sample project of animating NSWindow, see Just Say No from CIMGF.
In a few cases, you really do need to update the screen manually at the screen update frequency. I must stress how rare this situation is. In almost all cases, Core Animation is the correct tool. But in those rare case (some kinds of video for instance), you can use a CVDisplayLink to handle this. That will call you each time the screen would like to refresh, giving you an opportunity to update your content to match.
I am trying to implement frame independent movement in SFML, but am having trouble getting it to work. When I had this problem earlier with SDL, I found somewhere that Windows pauses the main thread of an application whenever it is moved (by dragging the title-bar). The problem comes because when the window is being dragged, the clock updates but the movement is not drawn until I let go of the window. When I move the window, the window is no longer being drawn to, but the time is still increasing. Thus, when I let go of the window, the units immediately jump to where they would be if the window had not been dragged.
I tried thinking of a solution, and since Windows only pauses the main thread, I considered just running the entire game in a separate thread, and launching it in main() but that does not appear to work as the same result occurs. I also thought about the extremely low FPS's I get as a result, but I would have no way of being able to differentiate between someone dragging the window and if their game is just naturally running slowly... There has to be a way to either prevent windows from pausing the main thread, or doing something that prevents this issue, but I haven't found any sort of solution on the internet...
Here is a link to a zip file which demonstrates the problem. Both Demo0 and Demo1 are the same, except Demo1 uses a second thread to run the program, yet the same effect occurs. Just run both and watch as the delta value is output to command line. Then drag the window and move it to some other part of the screen. When you let go, you should see a very large delta value and the circle should jump ahead depending on how long you had the window suspended. The source code is all there (in the "src" folder), so I hope people can understand the exact problem: http://www.sendspace.com/file/4er8f4
I see two solutions to the problem:
You can try to see if the sf::Events LostFocus or Resized picks up a window drag, and if they do, simply pause the clock on the game. More information can be found here: http://www.sfml-dev.org/tutorials/2.0/window-events.php
However, if that doesn't work, I would simply add an upper-cap on your delta. Meaning, if the game goes above a certain threshold of delta (1/60 or 1/30), you set delta to a lower value. In your situation though, this cap could probably be really big, like 1/15.
if(delta > 1/15.0f)
delta = 1/15.0f;
Chances are you don't expect your game to be playable at 15fps anyways, and if the user drags the window while moving, the worst you'll have to deal with is a 15fps delta on resume.
I'm working on a game design / UI project to redesign an existing game's control scheme (in this case, Trine) to use minimalistic input. I am trying to map lateral movement and the jump function to a Win8 tablet's volume buttons. Here is the basic code I am using:
Volume_Up::
Loop 5
{
Send {right down}
Sleep 50
}
Send {right up}
Return
Volume_Down::
Loop 5
{
Send {left down}
Sleep 50
}
Send {left up}
Return
This is working fairly well and is pretty responsive for moving left and right. However, the desired behavior that I want is to trigger jump (i.e. up) when BOTH buttons are depressed. For instance:
Player holds VolumeUp to move right.
Player comes to an obstacle.
Player continues to hold VolumeUp to queue right-bound movement and;
Player taps VolumeDown momentarily
Player jumps, movement continues up-and-over obstacle toward the right.
I have tried various permutations on using another script with the (Volume_Up & Volume_Down::) syntax to trigger this interaction, but that always seems to interfere with the movement commands. I think this may call for a nested If statement inside the move-left / move-right commands, to check if both buttons are depressed, but the Autohotkey documentation is not very clear and I'm unsure how to code that (I'm more of a game designer than I am a programmer). Any help would be really appreciated!
First of all, give attention to the comment from MCL. Pressing a button down 5 times and never releasing it in between does not seem to do much.
I think that you want multiple threads to be able to run at the same time.
Look up threads, but be aware about the following:
"Although AutoHotkey doesn't actually use multiple threads, it simulates some of that behavior: If a second thread is started -- such as by pressing another hotkey while the previous is still running -- the current thread will be interrupted (temporarily halted) to allow the new thread to become current. If a third thread is started while the second is still running, both the second and first will be in a dormant state, and so on."
I develop audio plugins, which are run inside their hosts and work realtime. Each plugin has its own window with controls, which often contains some kind of analysis pane, a pretty big rectangle that gets repeatedly painted (e.g. 20-50x per second). This is all working well.
The trouble comes when the user adjusts a parameter - the plugin uses WM_MOUSEMOVE to track mouse movements and on each change calls ::InvalidateRect to make the relevant portion of the window be redrawn. If you move quickly enough, the window really gets quickly repainted, however there seems no time for the host and other windows to be redrawn and these usually perform some kind of analysis feedback too, so it is really not ideal.
No my questions:
1) Assuming the host and other window are using ::InvalidateRect too, why mine is prioritized?
2) How to make ::InvalidateRect not prioritized, meaning the window needs to be invalidated, but it may be later, the rest of the system must get time for their redrawing too.
Thanks in advance!