Jump to content

  • Log In with Google      Sign In   
  • Create Account


In need of some architecture help

  • You cannot reply to this topic
8 replies to this topic

#1 Rld_   Members   -  Reputation: 1383

Like
0Likes
Like

Posted 20 July 2014 - 02:22 PM

Hey all,

 

For a while now I have been tinkering with my 3D engine, which is actually just a sandbox for me to try out some stuff in terms off well.. everything, not just graphics programming related, but pretty much everything. It's all for learning purposes!

 

When I started, I made everything (where needed) with raw pointers as I didn't have time to read up on smart pointers and I decided to dive into that now that I actually had the time to read up upon them, due to me being busy, I was kinda lost on where I was going with the engine and upon skimming through my code, I noticed I made a lot of use of singletons. Now whether they're good or bad is not really the point of this thread, but I decided I wanted to reduce them to a minimum and succeeded well enough to get the engine running like it did before and I only had one singleton left, which was my system class that is essentially the root of the engine where I initialize the window, renderer and scenemanager.

 

Now I got a bit curious if I could get rid of my system class being a singleton as well. I noticed I still had some dependencies that relied on system being a singleton, like my renderer class that needs a window handle to initialize DirectX11, but that was fixable by passing the right information.

 

Now I am facing a different kind of problem which has to do with event handling in windows. I have an abstract window class and a windows32 specific window class which both look like this:

 

Window.h

#ifndef _WINDOW_H_
#define _WINDOW_H_

namespace FX
{
	enum WindowEvent
	{
		eWindowEvent_Close,
		eWindowEvent_Minimize,
		eWindowEvent_Maximize,
		eWindowEvent_Resize,
		eWindowEvent_Move,
		eWindowEvent_Windowed,
		eWindowEvent_FullScreen
	};

	class System;
	class Window
	{
	public:

		virtual ~Window();
		Window();

		void Create(FX::System* a_System, const std::string& a_Title, int a_Width, int a_Height, int a_PosX, int a_PosY);

		const std::string& GetWindowTitle() const {return m_Title;}
		const int GetWindowWidth() const {return m_WindowWidth;}
		const int GetWindowHeight() const {return m_WindowHeight;}
		const int GetWindowPositionX() const {return m_WindowPositionX;}
		const int GetWindowPositionY() const {return m_WindowPositionY;}
		const bool IsFullScreen() const {return m_FullScreen;}

		virtual void SetWindowTitle(const std::string& a_Title) = 0;
		virtual void SetWindowWidth(int a_Width) = 0;
		virtual void SetWindowHeight(int a_Height) = 0;
		virtual void SetWindowPositionX(int a_X) = 0;
		virtual void SetWindowPositionY(int a_Y) = 0;
		virtual void SetFullScreen(bool a_FullScreen) = 0;
		virtual bool GetEvent() = 0;
		virtual bool ProcessEvent() = 0;

		static void WindowEventHandler(const WindowEvent a_WindowEvent, const int a_DataSize = 0, void* a_Data = NULL);

	protected:		

		std::string m_Title;

		int m_WindowWidth;
		int m_WindowHeight;
		int m_WindowPositionX;
		int m_WindowPositionY;

		bool m_FullScreen;

		FX::System* m_System;
	};
}
#endif

WindowWin32.h:

#ifndef _WINDOWWIN32_H_
#define _WINDOWWIN32_H_

#include "Window.h"

namespace FX
{
	class WindowWin32 : public Window
	{
		friend class Window;
	public:

		WindowWin32();
		virtual ~WindowWin32();

		const HINSTANCE GetHInstance() const {return m_hInstance;}
		const HWND GetHWnd() const {return m_hWnd;}
		const WNDCLASSEX* GetWndClassEx() const {return &m_WndClass;}

		void SetWindowTitle(const std::string& a_Title);
		void SetWindowWidth(int a_Width);
		void SetWindowHeight(int a_Height);
		void SetWindowPositionX(int a_X);
		void SetWindowPositionY(int a_Y);
		void SetFullScreen(bool a_FullScreen);
		bool GetEvent();
		bool ProcessEvent();

	protected:		

		void UpdatePosition();

		HINSTANCE m_hInstance;
		HWND m_hWnd;
		WNDCLASSEX m_WndClass;
		MSG m_Msg;
		DWORD m_Style;
		RECT m_Dimensions;
	};
}
#endif

In Window.h I defined a static eventhandler function that gets called by the specific windows callback like this:

LRESULT CALLBACK WndProc(HWND a_HWND, UINT a_Message, WPARAM a_Param1, LPARAM a_Param2)
{
	switch(a_Message)
	{
	case WM_CLOSE:
	case WM_QUIT:
	case WM_DESTROY:
		{
			FX::Window::WindowEventHandler(FX::eWindowEvent_Close);
			break;
		}
	case WM_KEYDOWN:
		{
			//FX::Keyboard::Get()->InjectKeyDown(a_Param1);
			break;
		}
	case WM_KEYUP:
		{
			//FX::Keyboard::Get()->InjectKeyUp(a_Param1);
			break;
		}
	case WM_MOUSEMOVE:
		{
			//FX::Mouse::Get()->InjectPosition((float)LOWORD(a_Param2), (float)LOWORD(a_Param2));
			//FX::Mouse::Get()->Clip();
			break;
		}
	case WM_SIZE:
		{
			// 			FX::int2 newsize;
			// 			newsize.x = LOWORD(a_Param2);
			// 			newsize.y = HIWORD(a_Param2);
			// 
			// 			if(a_Param1 == SIZE_MINIMIZED)
			// 			{
			// 				FX::Window::WindowEventHandler(FX::eWindowEvent_Minimize, 8, &newsize);
			// 				break;
			// 			}
			// 			else if (a_Param1 == SIZE_MAXIMIZED)
			// 			{
			// 				FX::Window::WindowEventHandler(FX::eWindowEvent_Maximize, 8, &newsize);
			// 				break;
			// 			}

			break;
		}
	case WM_ACTIVATE:
		{
			// 			bool active = ((LOWORD(a_Param1) != WA_INACTIVE) && !((BOOL)HIWORD(a_Param1)));
			// 			if(!active)
			// 			{
			// 				Rld::Mouse::Get()->Clear();
			// 			}
			break;
		}
	}
	return DefWindowProc(a_HWND, a_Message, a_Param1, a_Param2);
}

Now the initial problem lies with system being a non static member that needs to be called in the static function WindowEventHandler in the Window base class, but this is not allowed. 

I could make it a non static function, but than I can't call it from windows32 specific callback anymore.

 

So how would (or should) I handle this situation? I could make the system class a singleton again, but I am curious how this would be implemented in a way like this. Any pointers, directions or different solutions would be much appreciated! 



Sponsor:

#2 wintertime   Members   -  Reputation: 1642

Like
6Likes
Like

Posted 20 July 2014 - 05:44 PM

You can let windows remember some data for you that you can retrieve inside the window procedure and cast back to the type of your window class.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591%28v=vs.85%29.aspx



#3 Krohm   Crossbones+   -  Reputation: 3049

Like
2Likes
Like

Posted 20 July 2014 - 11:21 PM

On creation, the window gets its messages in a well defined order. In particular, CreateWindowEx allows you to fetch an LPARAM to the WM_CREATE message. Pack there a pointer to your struct. You can also do something similar on the window class but I'd consider it a bit ugly.



#4 Rld_   Members   -  Reputation: 1383

Like
0Likes
Like

Posted 21 July 2014 - 01:50 AM

You can let windows remember some data for you that you can retrieve inside the window procedure and cast back to the type of your window class.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591%28v=vs.85%29.aspx

 

This might be useful. So if I am not mistaken, I could pass in my system class and get it back with its get version?

On creation, the window gets its messages in a well defined order. In particular, CreateWindowEx allows you to fetch an LPARAM to the WM_CREATE message. Pack there a pointer to your struct. You can also do something similar on the window class but I'd consider it a bit ugly.

 

I might have missed something in there, but it seems to only really be useful on window creation, but doesn't really provide anything useful if I want to resize or move the window mid-session does it?

 

I haven't done all that much window(s) programming so I am not all that familiar with all the functionality and/or if the above mentioned solutions are commonly used or are considered best practice or not. Perhaps instead of making my code work, how would providing resizing (from WM_SIZE) functionality from a class or letting my application quit properly (WM_CLOSE/QUIT/DESTROY) be handled in a situation like this? Should the proc function be part of a class so I don't have to make use of static functions/members? Is there a general best practice at all?

 

Thanks for the replies so far! :)



#5 Krohm   Crossbones+   -  Reputation: 3049

Like
1Likes
Like

Posted 21 July 2014 - 02:28 AM


I might have missed something in there, but it seems to only really be useful on window creation, but doesn't really provide anything useful if I want to resize or move the window mid-session does it?
What it gives you is the ability to pass there a pointer to an object you can use to interface with your "real" object so even though the WndProc is static, it can forward calls to non-static functions through a object pointer.

#6 megadan   Members   -  Reputation: 482

Like
2Likes
Like

Posted 21 July 2014 - 10:46 PM

This would be one way:

Pass in instance of the window class to the lpParam parameter of CreateWindow or CreateWindowEx.  This will get passed to the WM_NCCREATE message through the lParam.
 

m_handle = CreateWindow( 
        ...
        this );

 
The static message handler checks for the WM_NCCREATE message and gets the instance to the window class from the lParam and stores it in the GWL_USERDATA for the window. If it's any other message, it will get the instance to the window from the GWL_USERDATA where it was stored earlier, and then you can call the non-static message handler for the class.
 

LRESULT CALLBACK WindowW32::WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {
    WindowW32* window = NULL;

    if ( message == WM_NCCREATE ) {
        // Store a pointer to the window in the GWL_USERDATA
        LPCREATESTRUCT createStruct = (LPCREATESTRUCT)lParam;
        window = reinterpret_cast<WindowW32*>( createStruct->lpCreateParams );
        SetWindowLong( hWnd, GWL_USERDATA, reinterpret_cast<long>( window ) );
    } else {
        // Get the window pointer from GWL_USERDATA and call the non-static message handler
        LONG windowAddress = GetWindowLong( hWnd, GWL_USERDATA );
        window = reinterpret_cast<WindowW32*>( windowAddress );
        return window->MessageHandler( hWnd, message, wParam, lParam );
    }
}

Edited by megadan, 22 July 2014 - 10:57 AM.


#7 Rld_   Members   -  Reputation: 1383

Like
0Likes
Like

Posted 21 July 2014 - 11:18 PM

Thanks guys!

 

I'll try this out when I have the chance! :)



#8 yellowsputnik   Members   -  Reputation: 604

Like
5Likes
Like

Posted 22 July 2014 - 05:30 AM

wintertime, on 21 Jul 2014 - 01:44 AM, said:

You can let windows remember some data for you that you can retrieve inside the window procedure and cast back to the type of your window class.

http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591%28v=vs.85%29.aspx

 

Note that it's better to use SetWindowLongPtr and GetWindowLongPtr - with GWLP_USERDATA - as also noted in the article you linked to. This avoids issues if you want to make a 64bit version of the program/game.



#9 Rld_   Members   -  Reputation: 1383

Like
0Likes
Like

Posted 22 July 2014 - 12:10 PM

Just wanted to give a heads up that I got it working properly with your suggestions!

 

Thanks a bunch! :)







PARTNERS