Application Base, How You Do It?

Started by
9 comments, last by Ravyne 16 years, 5 months ago
I am sorry for a vague title, but the question is also. I suppose this question is more directed at people who do not use Libraries, maybe. Anyway I'll try to put up my question. I was wondering how you create the base for your application. For example, I have been having trouble with "Creating A Window", not for the fact that it is hard (using Windows API), but the fact I have no idea how to organize it. Also I am unsure whether I want to use C or C++. Yeah, not which one is better (so no flaming) but in fact which one I can use better. C I understand much more, C++ just looks cool. C++'s errors can drive me crazy, and get me into interesting situations. C I understand quite a bit (been using it for a long time) and understand how to do everything I need to do. Now, when I was prototyping some ideas for the C++ version, I wasn't sure how to code the beginning of the system. I wasn't wanting to create a whole Engine for the game, just wanted the game to have the functionality that was nessary. For this, I had the idea... CWindow - Basic Win32 Window CRenderWindow - Inherited From CWindow, Applies OpenGL Onto Window * 2 Event-Trigger Methods 1. Inherit CRenderWindow onto a generic class such as GameCore class, which controls all (doesn't look right though, because why would the core be a type of a window). 2. Function Pointers creating a callback system from events. I tried this one and the c++ version was acting very very strange, honestly not the slightest idea why. Ex: Setting a private variable to true, later on I check it and it is always false, even if nothing else modifies it). So basically, having GameCore inherit CRenderWindow makes no sense, so #1 is out. #2 would crash every time I try to implement the system. I only blame myself for not understanding the deeper complexities of C++. So I was really wondering how you guys name your classes. I know beginning a class with C is suppose to be bad (forget why but it even looks ugly!). I just couldn't come up with better names. Finally, tonight, I started re-writing my mini-game in C. I implemented a Window system, which OpenGL can be selectivly applied to it, and few other features. I was able to implement the callback system with ease, and works flawlessly. A few functions I have question on though, for the fact they don't make sense how their named. Example: I implemented CreateRenderWindow which will basically call Win32's CreateWindow (With other stuff too). The problem is, it isn't necessarily a RENDER window. Of course CreateWindow is out of the question, because it is already used (hah, of course!). So basically, I am having difficulty in naming my routines. Of course many of them I have an easy time, and describe the functionality very well. It is just the fact that simple and basic routines I can't name. For the sake of how I am implementing the system now, I'll give you the code. I am NOT asking you to not complain about my code. I would like to know what you guys think. I know, programming is like an art, but the type of paint/resources used can always give an artist a new perspective on things. ;) This isn't a simple question->answer thread, more of a "what is your opinion" thread. I am just asking that no one disagrees with anything anyone else says. No one is wrong. ;) Anyways, the quick 2-hour whipped up code... ***** Code From: Window.c *****
#include <windows.h>
#include "window.h"
#include "application.h"

LRESULT CALLBACK MessageCallback(HWND, UINT, WPARAM, LPARAM);


//####################################################################################################
//#									Global Functions Programming									 #
//# * Function prototypes can be found on the 'module's include file.								 #
//####################################################################################################
//============================================================
// Function: CreateRenderWindow
// Params: RE_Window Struct Pointer
// Return: None
//============================================================
void CreateRenderWindow(SE_Window *Window, const char *Title)
{
	WNDCLASSEX sWindowClass;

	// Fail-Safe
	if (!Window)
		return;
	memset(Window, 0, sizeof(SE_Window));

	// Register Window Class (Ewww, Might Need Updated!)
	sWindowClass.cbSize = sizeof(WNDCLASSEX);
	sWindowClass.style = 0;
	sWindowClass.lpfnWndProc = MessageCallback;
	sWindowClass.cbClsExtra = 0;
	sWindowClass.cbWndExtra = 0;
	sWindowClass.hInstance = Application.Instance;
	sWindowClass.hIcon = (HICON)LoadIcon(0, IDI_WINLOGO);
	sWindowClass.hCursor = (HCURSOR)LoadCursor(0, IDC_ARROW);
	sWindowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	sWindowClass.lpszMenuName = 0;
	sWindowClass.lpszClassName = "Flare";
	sWindowClass.hIconSm = (HICON)LoadIcon(0, IDI_WINLOGO);
	if (!RegisterClassEx(&sWindowClass))
	{
		MessageBox(0, "Class Registeration Failed", "Debug", MB_OK);
		return;
	}

	// Create Interface Window
	Window->Handle = CreateWindow("Flare", Title, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, 0, 0, Application.Instance, 0);
	if (Window->Handle == 0)
	{
		MessageBox(0, "Window Creation Failed", "Debug", MB_OK);
		return;
	}

	// Setup Misc. Material
	SetWindowLong(Window->Handle, GWL_USERDATA, (LONG)Window);
	Window->Active = TRUE;
}


//============================================================
// Function: ProcessWindowMessages
// Params: None
// Return: None
//============================================================
void ProcessWindowMessages(SE_Window *Window)
{
	MSG sMessage;

	if (PeekMessage(&sMessage, 0, 0, 0, PM_NOREMOVE))
	{
		if (!GetMessage(&sMessage, 0, 0, 0))
			Window->Active = FALSE;
		TranslateMessage(&sMessage);
		DispatchMessage(&sMessage);
	}
}


//============================================================
// Function: RegisterWindowEvent
// Params: Callback Type, Function Pointer
// Return: None
//============================================================
void RegisterWindowEvent(SE_Window *Window, BYTE bType, void (*fp))
{
	switch (bType)
	{
		case WINDOW_CALLBACK_KEYPRESS:
			Window->cp_OnKeyPress = fp;
			break;
		case WINDOW_CALLBACK_KEYRELEASE:
			Window->cp_OnKeyRelease = fp;
			break;
		case WINDOW_CALLBACK_MOUSEMOVE:
			Window->cp_OnMouseMove = fp;
			break;
	}
}


//============================================================
// Function: SetWindowParam
// Params: Window Pointer, Param Type, Param A, Param B
// Return: None
//============================================================
void SetWindowParam(SE_Window *Window, BYTE ParamType, DWORD ValueA, DWORD ValueB)
{
	DEVMODE sScreenSettings;
	DWORD Result;

	switch (ParamType)
	{
		case WINDOW_PARAM_POSITION:
			Window->X = ValueA;
			Window->Y = ValueB;
			SetWindowPos(Window->Handle, 0, ValueA, ValueB, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
			break;
		case WINDOW_PARAM_SIZE:
			Window->Width = ValueA;
			Window->Height = ValueB;
			if (Window->Fullscreen)
				SetWindowParam(Window, WINDOW_PARAM_FULLSCREEN, WINDOW_ENABLE, 0);
			else
				SetWindowPos(Window->Handle, 0, 0, 0, ValueA, ValueB, SWP_NOZORDER | SWP_NOMOVE);
			break;
		case WINDOW_PARAM_FULLSCREEN:
			if (ValueA == WINDOW_ENABLE)
			{
				sScreenSettings.dmSize = sizeof(DEVMODE);
				sScreenSettings.dmPelsWidth = Window->Width;
				sScreenSettings.dmPelsHeight = Window->Height;
				sScreenSettings.dmBitsPerPel = 16;
				sScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
				Result = ChangeDisplaySettings(&sScreenSettings, CDS_FULLSCREEN);
				if (Result != DISP_CHANGE_SUCCESSFUL)
					return;

				SetWindowLong(Window->Handle, GWL_style, WS_POPUP | WS_VISIBLE);
				SetWindowPos(Window->Handle, HWND_TOP, 0, 0, Window->Width, Window->Height, SWP_SHOWWINDOW);
				Window->Fullscreen = TRUE;
			}
			else
			{
				SetWindowLong(Window->Handle, GWL_style, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
				SetWindowPos(Window->Handle, HWND_TOP, Window->X, Window->Y, Window->Width, Window->Height, SWP_SHOWWINDOW);
				ChangeDisplaySettings(0, 0);
				Window->Fullscreen = FALSE;
			}
			break;
	}
}


//============================================================
// Function: DestroyRenderWindow
// Params: Window Struct Pointer
// Return: None
//============================================================
void DestroyRenderWindow(SE_Window *Window)
{
	// Fail-Safe Check
	if (!Window)
		return;

	// Restore Resolution
	if (Window->Fullscreen)
		ChangeDisplaySettings(0, 0);
}


//####################################################################################################
//#										Local Functions Programming									 #
//# * Function prototypes are at top of file.														 #
//####################################################################################################
//============================================================
// Function: MessageCallback (Private to Module)
// ** I think we all know the params ;) **
// ** Or is there just too many to list? Hmmm... **
//============================================================
LRESULT CALLBACK MessageCallback(HWND Handle, UINT Message, WPARAM wParam, LPARAM lParam)
{
	SE_Window *Window = (SE_Window*)GetWindowLong(Handle, GWL_USERDATA);

	switch (Message)
	{
		case WM_CREATE:
			/* ALERT: Window Not Referenced Yet On WM_CREATE!!!!!!! */
			break;
		case WM_KEYDOWN:
			if (Window && Window->cp_OnKeyPress)
				(Window->cp_OnKeyPress)(Window,Message,wParam);
			break;
		case WM_KEYUP:
			if (Window && Window->cp_OnKeyRelease)
				(Window->cp_OnKeyRelease)(Window,Message,wParam);
			break;
		case WM_MOUSEMOVE:
			if (Window && Window->cp_OnMouseMove)
				(Window->cp_OnMouseMove)(Window,Message,wParam,LOWORD(lParam),HIWORD(lParam));
			break;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(Handle, Message, wParam, lParam);
	}
	return 0;
}
***** Code From: main.c *****
#include <windows.h>
#include "system/application.h"
#include "system/window.h"

void Game_OnKeyPress(SE_Window *Window, DWORD Event, BYTE Key)
{
	if (Key == 'Q')
		SendMessage(Window->Handle, WM_CLOSE, 0, 0);
}

void Game_OnKeyRelease(SE_Window *Window, DWORD Event, BYTE Key)
{
	//MessageBox(0, "Event: Key Released", "Event Debugger", MB_OK);
}

void Game_OnMouseMove(SE_Window *Window, DWORD Event, BYTE Special, WORD MouseX, WORD MouseY)
{
	char Buffer[128];
	sprintf(Buffer, "Mouse(%u:%u)", MouseX, MouseY);
	SetWindowText(Window->Handle, Buffer);
}


void EntryPoint(void)
{
	SE_Window Window;
	CreateRenderWindow(&Window, "Flare Game");
	SetWindowParam(&Window, WINDOW_PARAM_SIZE, 1680, 1050);
	SetWindowParam(&Window, WINDOW_PARAM_FULLSCREEN, WINDOW_ENABLE, 0);
	RegisterWindowEvent(&Window, WINDOW_CALLBACK_KEYPRESS, &Game_OnKeyPress);
	RegisterWindowEvent(&Window, WINDOW_CALLBACK_KEYRELEASE, &Game_OnKeyRelease);
	RegisterWindowEvent(&Window, WINDOW_CALLBACK_MOUSEMOVE, &Game_OnMouseMove);

	while(Window.Active)
	{
		ProcessWindowMessages(&Window);
		//Sleep(1);
	}
}
Advertisement
Slightly offtopic:

I click 'make new windows application'. Done.

This is yet another example why C and C++ are relatively poor languages. You waste your time dealing with this crap.


Ontopic: I generally have wndproc code cut/pasted somewhere to link the events into my input. The actual windowing code I generally do not even think about until enough is written to test. The actual windowing code is part of the application, the highest level bits that tie all the miscellaneous subparts into the game. (And is thus largely not reusable, by design)
Quote:Original post by Telastyn
Slightly offtopic:

I click 'make new windows application'. Done.

This is yet another example why C and C++ are relatively poor languages. You waste your time dealing with this crap.

I've been using XNA lately. Looking at this post reminded me just how much simpler it makes things. You don't have to worry about all the details of making a window and just get on with your game.

APE
Well for most large and well designed games the window/message pump would be rather small. Since input etc wouldn't come from the window procedure, rather from an input libraery such as DirectInput.
I just have a generic "Window" class in a dll which does exactly what it should... create a window.

I build a couple of classes off it (such as in my editor DLL). But they are only minor changes (such as being able to specify an external HWND or something).

Rendering code is handled in the render DLLs. The OpenGL DLL creates its own pixels formats, etc. Same with the Direct3D DLL.

That said, the Quake 3 renderer (even though there is only one, it's in a .lib) creates its own window. So I guess its up to you.

The benefit of my design, I think, is that I have one single renderer which just relies on the base Window class. But I can have a number of different Window configurations because they don't depend on the renderer at all.

Quote:Original post by thre3dee
Well for most large and well designed games the window/message pump would be rather small. Since input etc wouldn't come from the window procedure, rather from an input libraery such as DirectInput.


I use standard Win32 stuff. Though I am probably in the minority.
[size="1"]
I thought Microsoft told everybody to use Win32 messages instead of DirectInput these days anyway?
Quote:Original post by mightypigeon
Quote:Original post by thre3dee
Well for most large and well designed games the window/message pump would be rather small. Since input etc wouldn't come from the window procedure, rather from an input libraery such as DirectInput.


I use standard Win32 stuff. Though I am probably in the minority.


Yeh me too. I prefer plain old know-exactly-what-its-doing C++. But I can see how XNA and other such frameworks can help.
Quote:Original post by Kylotan
I thought Microsoft told everybody to use Win32 messages instead of DirectInput these days anyway?


Oh they do. And that's what I tell everyone who makes a "Keyboard Input in DirectInput8" thread. Evil Steve actually made a list of reasons why you shouldn't use DI for keyboard input in his journal recently, so its handy just to link to that these days.

Quote:Original post by Kylotan
I thought Microsoft told everybody to use Win32 messages instead of DirectInput these days anyway?


Yes indeed.


To the OP/Mods -

Can Someone please put some source tags around that mess?


To the OP -

You seem to lack the concept of inheritance vs. aggregation. Inheritance expresses the "is a" relationship (a Truck "is a" Vehicle) while aggregation expresses the "has a" relationship (A Vehicle "has a" Tire (in this case, one or more)).

In my framework, there is a Window class and a WindowProc class as well as an optional, generic BaseApplication class. Whether I inherit from BaseApplication or roll a custom ApplicationClass, they aggregate the Window and WindowProc -- This expresses the relationship: An Application has a Window and a WindowProc (of course, another window might have its own WindowProc as well.)

Somewhat unfortunately, Windows does its best to convince you that the Window itself should really be the "outside" of your application since Windows and their WindowProcs are the basis for nearly all interaction within the windows environment.

throw table_exception("(? ???)? ? ???");

Quote:Original post by ravyne2001
Somewhat unfortunately, Windows does its best to convince you that the Window itself should really be the "outside" of your application since Windows and their WindowProcs are the basis for nearly all interaction within the windows environment.


This seems like an appropriate view to take for games. I have maybe 200-300 lines of boiler plate code that I use for initializing DirectX and dealing with the Win32 API and the rest of the work is just transferred off to my own code. Windows messages of interest get translated into a custom message struct that's passed down to whatever subsystems need input. Maybe I'm weird, but for something like a game I generally want to insulate myself from the operating system as much as is reasonably possible. It doesn't really matter to me if it takes five lines or five hundred to initialize the window and get things going, because it's something that I do exactly once and that occupies probably less than 1% of my development time on even a small project.

This topic is closed to new replies.

Advertisement