Windows’ window coordinate systemsI spent a little time earlier just getting the code up and running, but here we will go more in-depth! (and I mean REALLY in-depth :) ) Screen coordinates start with 0, 0 at the upper left hand corner of the screen, and increase as you move toward the lower right corner of the screen. The width and height of the screen are the maximum values for x and y. As a visual aid, I just copied my desktop, shrunk it, and placed the area covered by screen coordinates inside the black rectangle with an X to make it more visible. Client coordinates start with 0, 0 at the upper left hand corner of the client area of window, and increase as you move toward the lower right corner of the client area of the window. I took the same copy, but this time the client coordinates for the open window are enclosed by the black box. As you can see, the screen coordinates can address the whole screen, while the client coordinates can only access a certain area of a window. Therefore, when you set the position of your window with CreateWindow()’s parameters, you are setting them in screen coordinates. When you draw from a WM_PAINT message, you draw in client coordinates, because you only want to draw inside your window, and don’t need to be concerned with calculating its position on the screen. In other words, screen coordinates are absolute coordinates, while client coordinates are relative to the position of the window, the size of the title bar, etc. There are times when you need to convert from screen coordinates to client coordinates, and vice versa. Windows provides two very helpful little functions to do just that: ScreenToClient() and ClientToScreen(). Both take any point in either coordinate system and find the same physical position on the screen in the other coordinate system. DirectDraw always uses the screen coordinate system (or absolute coordinate system), because it allows you to draw on the entire screen. Finally, the window’s client area is not always the same size, either. When you call CreateWindow(), you specify an x,y coordinate on the screen, plus a width and height for the window. However, things like title bars, borders, menus, toolbars, status bars, etc. take up some of that room, too. So the client area of the window may be different on computers with different Windows settings, like a different font size for the title bar. It is impossible to calculate all the different variations; fortunately, it isn’t too hard to get an exact client area size on any Windows machine. To do this, we simply create the window with the CW_USEDEFAULT constant for the x,y and width and height parameters. Then, we subtract the default client rect from the one we want, and increase/decrease the size of the window by the difference. Simple, huh? To make it easier to use, we’ll put the whole thing into a function, and call it:
That function takes the handle of the window we want to adjust, and the desired size of the client area. Then it calculates the new window rect and returns it. Now we have to add some code to that function to make it work properly. (Don’t be afraid if you can’t understand this code right away; for now just skim over it and you can go back to it as we discuss it in detail.) Add this code:
Whew! That’s a lot of code! The best way to learn it is to step through it in the debugger, to see which values change and what the calculations really mean. Now that we have a function that’s relatively easy to use, where do we use it? In our trusty SwitchMode function, of course! :) We’ll need to change the size of the window primarily when switching modes, so it makes sense to use it there. We’ll also need a function that can adjust the main window’s size; that way, the SwitchMode function is more readable. We’ll call that function:
It may look and act similar to a Windows API function, but we’ll add some rather unique things to that function later on... ;) Let’s take a look at the code for now:
First off, we hide the window to keep from repainting everything and making a general mess until we’re finished. (This is completely optional – you can remove the ShowWindow calls if you want.) That if() statement just executes the code based on the new mode. For exclusive mode, there is no need to adjust the size. For windowed mode, we must change the size of the window. First, we create a temporary size structure to hold the width and height of the new client area, then we pass that with the main window handle to CalculateWindowRect to get the new window rect. Then we just pass that to MoveWindow, which resizes the window, and re-show the window now that we’re done. (Showing the window here allows for repainting of the title bar and other windows stuff.) Now, to test it all out! Add this code to the SwitchMode function, in between the call to DestroySurfaces and CreateSurfaces:
The nWidth and nHeight variables are the second and third parameters from SwitchMode, so you can do this:
The width and height we pass to SwitchMode is really only specifying the size of the drawing surface we want – we don’t care whether it’s running fullscreen or windowed. It all works the same now! (Well, except for the bit-depth, which you can’t (shouldn’t) change in windowed mode...) |
||||||||||||||||||||