Hi,
codyd51 wrote:
Each refresh, starting from the screen's root window, we traverse the entire hierarchy and draw from the bottom up.
This means that if there's 20 windows that overlap, you write to the same area of the buffer 20 times. For the simple case (flat rectangles parallel with screen) it's easy to avoid this by drawing from top to bottom.
Imagine you have an "updateRectangle()" that draws a source rectangle (a window) in a destination rectangle in the buffer. Initially the source rectangle is the top window and the destination rectangle is the entire screen. Use the edges of the source rectangle to split the destination rectangle into (a maximum of) 5 smaller rectangles: center, top, bottom, left and right. Copy the source rectangle into the center sub-rectangle.
For the top, bottom, left and right sub-rectangles (if they exist); use recursion (and the next window in Z order) to update them. If there isn't any more windows; use the desktop/background image as the source rectangle.
In this way, you only ever write to each pixel in the buffer once. Of course this method can't handle "less simple" cases (any transparency, and any windows that aren't rectangular). However, if some windows are opaque and some are transparent it's easy enough to modify this to handle transparency (if the source rectangle is transparent, use recursion to update the entire rectangle first, then draw the center sub-rectangle after).
Next; with one buffer for each window there's a synchronisation problem - you can be refreshing the screen while an application is in the middle of updating its window, and this can cause ugly "tearing" effects. To avoid that you need 2 buffers for each window - a "current buffer" that the application can't touch (that's displayed if/when you refresh the screen), and a "working buffer" that the application draws into. When the application is finished drawing something in the "working buffer", it tells you to replace the "current buffer" with the "working buffer" (and that's when you set the "needs refresh" flag for the application's "current buffer").
Next; limit the number of times you update the screen. For example; if the top window tells you to refresh the screen but the screen has been refreshed in the last 1/120th of a second, set a time delay for "1/120th of a second since screen refreshed last" and refresh the screen when the time delay expires. This means that if the top window is trying to refresh the screen 12345 times per second you only update the screen (a maximum of) 120 times per second. Windows that are in the background are less important; so for the second from top window use "1/60th of a second" for the time delay, for the third from top window use "1/30th of a second" for the time delay, and for anything further in the background use "1/15th of a second" as the time delay. Because the "further in the background" windows are likely to be obscured, and because the user is less likely to care about them, the user probably won't notice this (and won't mind if they do notice). Because the monitor can only display (e.g.) 60 frame per second this can avoid a large amount of pointless refreshes.
Cheers,
Brendan