Gültigkeitsbereich von #define

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Fitim
Beiträge: 12
Registriert: 27.05.2012, 19:29

Gültigkeitsbereich von #define

Beitrag von Fitim »

Hallo zusammen.

Nun ich habe wieder mal ein wenig Zeit gefunden, um mein kleines Projekt fortzusetzen.
Mittlerweile habe ich eine brauchbare Fensterumgebung sammt Renderdevice implementiert.
Wie bereits in einem früheren Post geschrieben, habe ich schon eine Weile nichts mehr mit C/C++ programmiert. Zu Testzwecken habe ich meinen gesammten Fenstercode in einer einzigen Quelldatei. Nun ist es an der Zeit, diesen "Megablob" in Header- und Quelldateien aufzuteilen.
Nun habe ich eine Frage zu #define und dessen Gültigkeitsbereich. Doch zuerst mal der Code:

Code: Alles auswählen

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Test
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN		// -> Nur die nötigsten Komponten verwenden.
#define STRICT					// -> Keine eigenen typedefs() erlauben.
#define NOGDICAPMASKS			// -> CC_*, LC_*, PC_*, CP_*, TC_*, RC_
#define NOSYSMETRICS			// -> SM_*
#define NOMENUS					// -> MF_*
#define NOKEYSTATES				// -> MK_*
#define NORASTEROPS				// -> Binary und Tertiary raster ops.
#define NOATOM					// -> Atom Manager Routinen.
#define NOCLIPBOARD				// -> Clipboard Routinen.
#define NOCOLOR					// -> Screen colors.
#define NOCTLMGR				// -> Control und Dialog Routinen.
#define NODRAWTEXT				// -> DrawText() und DT_*
#define NOKERNEL				// -> Alle KERNEL defines und Routinen.
#define NOMEMMGR				// -> GMEM_*, LMEM_*, GHND, LHND, dazugehörige Routinen.
#define NOMETAFILE				// -> typedef METAFILEPICT
#define NOMINMAX				// -> Macros min(a,b) und max(a,b)
#define NOOPENFILE				// -> ::OpenFile(), ::OemToAnsi(), ::AnsiToOem(), und OF_*
#define NOSCROLL				// -> SB_* und scrolling Routinen.
#define NOSERVICE				// -> Alle Service Controller Routinen, SERVICE_ equates, etc.
#define NOSOUND					// -> Sound driver Routinen
#define NOTEXTMETRIC			// -> typedef TEXTMETRIC und dazugehörige Routinen.
#define NOWH					// -> ::SetWindowsHook() und WH_*
#define NOCOMM					// -> COMM driver Routinen.
#define NOKANJI					// -> Kanji support Komponenten.
#define NOHELP					// -> Help engine Schnittstelle.
#define NOPROFILER				// -> Profiler Schnittstelle.
#define NODEFERWINDOWPOS		// -> ::DeferWindowPos() Routinen.
#define NOMCX					// -> Modem Configuration Extensions.

// #define NOICONS				// -> IDI_*
// #define NOSYSCOMMANDS		// -> SC_*
// #define NOSHOWWINDOW			// -> SW_*
// #define NOGDI				// -> Alle GDI defines und Routinen -> ::GetStockObject()
// #define NOUSER				// -> Alle USER defines und Routinen -> ::CreateWindowExA/W()
// #define NONLS				// -> Alle NLS defines und Routinen -> ::WideCharToMultiByte()
// #define NOMB					// -> MB_* -> ::MessageBoxA/W()
// #define NOMSG				// -> typedef MSG und dazugehörige Routinen.
// #define NOWINOFFSETS			// -> GWL_*, GCL_*, und dazugehörige Routinen.
// #define NOVIRTUALKEYCODES	// -> VK_*
// #define NOWINMESSAGES		// -> WM_*, EM_*, LB_*, CB_*
// #define NOWINSTYLES			// -> WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*

#include <Windows.h>
#include <DXGI.h>
#include <D3D11.h>
#include <DxErr.h>

#pragma comment(lib, "DXGI.lib")
#pragma comment(lib, "D3D11.lib")
#pragma comment(lib, "DxErr.lib")

#include <exception>
#include <string>
#include <sstream>
#include <memory>				// -> std::unique_ptr
#include <utility>				// -> std::pair
#include <cassert>

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Manifest.hpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#if defined _M_IX86
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#elif defined _M_IA64 
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#elif defined _M_X64 
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#else 
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// INonCopyAssignable.hpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	// Bewirkt, dass eine Instanz weder kopiert noch zugewiesen werden kann.
	class INonCopyAssignable
	{
	protected:

		INonCopyAssignable() {}

	private:

		INonCopyAssignable(INonCopyAssignable const &);
		INonCopyAssignable & operator = (INonCopyAssignable const &);
	};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Exception.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	class Exception : public std::exception
	{
	public:

		explicit Exception(std::string const & message) : exception(message.c_str()) {}
	};

	void ThrowException(std::string const & message)
	{
		std::stringstream format;
		format << "Exception -> " << message;
		throw Exception(format.str());
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsException.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	class WindowsException : public Exception
	{
	public:

		explicit WindowsException(std::string const & message) : Exception(message) {}
	};

	void ThrowWindowsException(std::string const & message, ::DWORD error = ::GetLastError())
	{
		std::stringstream format;
		format << "Windows Exception [" << error << "] -> " << message; // TODO: WindowsErrorToStringA() ...
		throw WindowsException(format.str());
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DirectXException.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	class DirectXException : public Exception
	{
	public:

		explicit DirectXException(std::string const & message) : Exception(message) {}
	};

	void ThrowDirectXException(std::string const & message, ::HRESULT error)
	{
		std::stringstream format;
		format << "DirectX Exception [" << ::DXGetErrorStringA(error) << "] -> " << message;
		throw WindowsException(format.str());
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsUTF8Encode.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		// Nicht die schnellste Implementation, aber es funktioniert.
		std::string UTF8Encode(std::wstring const & original)
		{
			auto const newSize(::WideCharToMultiByte(
				CP_UTF8, 
				0,
				&original[0], 
				static_cast<int>(original.size()),
				nullptr, 
				0, 
				nullptr, 
				nullptr));
			
			std::string output(newSize, 0);		
			
			::WideCharToMultiByte(
				CP_UTF8, 
				0,
				&original[0], 
				static_cast<int>(original.size()), 
				&output[0], 
				newSize, 
				nullptr, 
				nullptr);
			
			return output; // Sollte zu keiner Kopie führen.
		}

		// Nicht die schnellste Implementation, aber es funktioniert.
		std::wstring UTF8Encode(std::string const & original)
		{
			auto const newSize(::MultiByteToWideChar(
				CP_UTF8, 
				0, 
				&original[0], 
				static_cast<int>(original.size()),
				nullptr, 
				0));
			
			std::wstring output(newSize, 0);
			
			::MultiByteToWideChar(
				CP_UTF8, 
				0, 
				&original[0], 
				static_cast<int>(original.size()), 
				&output[0], 
				newSize);
			
			return output; // Sollte zu keiner Kopie führen.
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsWindowClass.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		class WindowClass : protected INonCopyAssignable
		{
		public:

			explicit WindowClass(::WNDCLASSEXW const & description) : myDescription(description)
			{
				if (!::RegisterClassExW(&myDescription))
				{
					ThrowWindowsException("::RegisterClassExW() failed.");
				}
			}

			// Darf keine Ausnahme auslösen.
			~WindowClass()
			{
				::UnregisterClassW(myDescription.lpszClassName, myDescription.hInstance);
			}

		private:

			::WNDCLASSEXW const myDescription;
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsListener.hpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{		
		// Bewirkt, dass eine Instanz Fensternachrichten verarbeiten kann.
		class IListener
		{
		public:

			// Darf keine Ausnahme auslösen.
			virtual bool MessageHandler(::UINT /* message */, ::WPARAM /* firstParameter */, ::LPARAM /* secondParameter */)
			{
				return true;
			}

		protected:

			IListener() {}
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsInternal.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		namespace Internal
		{
			// Darf keine Ausnahme auslösen.
			::LRESULT CALLBACK MessageProcedure(::HWND window, ::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
			{
				// Die beim Erstellen des Fensters übergebene Instanz speichern damit dessen Nachrichtenhandler
				// aufgerufen werden kann.
				if (message == WM_NCCREATE)
				{
					::SetWindowLongPtrW(
						window,
						GWL_USERDATA,
						reinterpret_cast<::LONG_PTR>(
						reinterpret_cast<::LPCREATESTRUCTW>(secondParameter)->lpCreateParams));
				}

				auto const toListener(reinterpret_cast<IListener *>(::GetWindowLongPtrW(window, GWL_USERDATA)));

				// Dem Nachrichtenhandler vertrauen dass dieser keine Ausnahme auslöst.
				if (toListener && !toListener->MessageHandler(message, firstParameter, secondParameter))
				{
					return 0;
				}

				// Nicht weiterverarbeiten da ansonsten ::DestroyWindow() aufgerufen wird. Dies aber erledigt
				// der jeweilige Destruktor des Fensters selber.
				if (message == WM_CLOSE)
				{
					return 0;
				}

				return ::DefWindowProcW(window, message, firstParameter, secondParameter);
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsWindow.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		class Window : protected INonCopyAssignable
		{
		public:

			Window(
				::DWORD style,
				::DWORD extendedStyle,
				::LPCWSTR toClassName,
				::LPCWSTR toWindowName,
				int positionX,
				int positionY,
				int width,
				int height,
				::HWND parentWindow,
				::HMENU menu,
				::HINSTANCE moduleInstance,
				IListener * toListener) :

				myWindow(::CreateWindowExW(
					extendedStyle,
					toClassName,
					toWindowName,
					style,
					positionX,
					positionY,
					width,
					height,
					parentWindow,
					menu,
					moduleInstance,
					reinterpret_cast<::LPVOID>(toListener)))
			{
				if (myWindow == nullptr)
				{
					ThrowWindowsException("::CreateWindowExW() failed.");
				}
			}

			// Darf keine Ausnahme auslösen.
			~Window()
			{
				::DestroyWindow(myWindow);
			}

			// Bewirkt, dass die Fensterklasse als ::HWND Argument übergeben werden kann.
			operator ::HWND const () const
			{
				return myWindow;
			}

			::HWND const GetHandle() const
			{
				return myWindow;
			}

		private:

			::HWND myWindow;
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsUtility.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		typedef std::pair<::UINT, ::UINT> SizeType;
		typedef std::pair<::DWORD, ::DWORD> StyleType;

		namespace Area
		{
			enum Type
			{
				Desktop,
				Workspace
			};
		}

		// Verwendet standardmässig die interne Nachrichtenprozedur.
		::WNDCLASSEXW CreateWindowClassDescription(
			::UINT size,
			::UINT style,
			int extraBytesForClass,
			int extraBytesForWindow,
			::HINSTANCE moduleInstance,
			::HICON icon,
			::HICON smallIcon,
			::HCURSOR cursor,
			::HBRUSH backgroundColor,
			::LPCWSTR toMenuName,
			::LPCWSTR toClassName)
		{
			::WNDCLASSEXW windowClass;
			windowClass.cbSize = size;
			windowClass.style = style;
			windowClass.lpfnWndProc = &Internal::MessageProcedure;
			windowClass.cbClsExtra = extraBytesForClass;
			windowClass.cbWndExtra = extraBytesForWindow;
			windowClass.hInstance = moduleInstance;
			windowClass.hIcon = icon;
			windowClass.hIconSm = smallIcon;
			windowClass.hCursor = cursor;
			windowClass.hbrBackground = backgroundColor;
			windowClass.lpszMenuName = toMenuName;
			windowClass.lpszClassName = toClassName;

			return windowClass; // Sollte zu keiner Kopie führen.
		}

		// Platziert das Fenster in der Mitte des nächstgelegenen Desktops oder Arbeitsbereiches.
		// Der Arbeitsbereich entspricht dem Desktop minus der Taskleiste.
		void CenterWindowAtNearestArea(::HWND const window, Area::Type area)
		{
			assert(window != nullptr);

			::RECT rectangle;
			::GetWindowRect(window, &rectangle);
            
			auto const width(rectangle.right - rectangle.left);
			auto const height(rectangle.bottom - rectangle.top);
			auto const monitor(::MonitorFromRect(&rectangle, MONITOR_DEFAULTTONEAREST));

			::MONITORINFO monitorInfo;
			monitorInfo.cbSize = sizeof(monitorInfo);			
			::GetMonitorInfoW(monitor, &monitorInfo);

			const auto right((area == Area::Desktop) ? 
				monitorInfo.rcMonitor.right : 
				monitorInfo.rcWork.right);
			
			const auto bottom((area == Area::Desktop) ? 
				monitorInfo.rcMonitor.bottom : 
				monitorInfo.rcWork.bottom);

			::SetWindowPos(
				window,
				nullptr, 
				static_cast<int>((right - width) * 0.5),
				static_cast<int>((bottom - height) * 0.5),
				0,
				0, 
				SWP_NOSIZE |
				SWP_NOZORDER |
				SWP_NOACTIVATE);
		}

		SizeType GetWindowClientSize(::HWND const window)
		{
			assert(window != nullptr);

			::RECT rectangle = { 0 };
			::GetClientRect(window, &rectangle);
						
			// Sollte zu keiner Kopie führen.
			return SizeType(
				static_cast<::UINT>(rectangle.right), 
				static_cast<::UINT>(rectangle.bottom));
		}

		void SetWindowClientSize(::HWND const window, ::UINT width, ::UINT height)
		{
			assert(window != nullptr);

			::RECT rectangle = { 0 };
			rectangle.right = static_cast<::LONG>(width);
			rectangle.bottom = static_cast<::LONG>(height);
			
			::AdjustWindowRectEx(
				&rectangle,
				static_cast<::DWORD>(::GetWindowLongPtrW(window, GWL_STYLE)), 
				FALSE, 
				static_cast<::DWORD>(::GetWindowLongPtrW(window, GWL_EXSTYLE)));
		
			::SetWindowPos(
				window,
				nullptr, 
				0, 
				0, 
				static_cast<int>(rectangle.right - rectangle.left), 
				static_cast<int>(rectangle.bottom - rectangle.top), 
				SWP_NOMOVE | 
				SWP_NOZORDER |
				SWP_NOACTIVATE);
		}

		StyleType GetWindowStyle(::HWND const window)
		{
			assert(window != nullptr);

			// Sollte zu keiner Kopie führen.
			return StyleType(
				static_cast<::DWORD>(::GetWindowLongPtrW(window, GWL_STYLE)),
				static_cast<::DWORD>(::GetWindowLongPtrW(window, GWL_EXSTYLE)));
		}

		void SetWindowStyle(::HWND const window, ::DWORD style, ::DWORD extendedStyle)
		{
			assert(window != nullptr);

			::SetWindowLongPtrW(window, GWL_STYLE, static_cast<::LONG_PTR>(style));
			::SetWindowLongPtrW(window, GWL_EXSTYLE, static_cast<::LONG_PTR>(extendedStyle));

			::SetWindowPos(
				window,
				HWND_TOP, 
				0, 
				0,
				0,
				0,
				SWP_NOMOVE |
				SWP_NOSIZE | 
				SWP_NOZORDER |
				SWP_SHOWWINDOW |
				SWP_FRAMECHANGED);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsWindowPlacement.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		class WindowPlacement
		{
		public:

			WindowPlacement()
			{
				ZeroMemory(&myPlacement, sizeof(::WINDOWPLACEMENT));
				myPlacement.length = sizeof(::WINDOWPLACEMENT);
			}

			void Get(::HWND const window)
			{
				assert(window != nullptr);
				::GetWindowPlacement(window, &myPlacement);
			}

			void Set(::HWND const window)
			{
				assert(window != nullptr);
				::SetWindowPlacement(window, &myPlacement);
			}

		private:

			::WINDOWPLACEMENT myPlacement;
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsHiddenCursor.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		class HiddenCursor : protected INonCopyAssignable
		{
		public:

			HiddenCursor()
			{
				::ShowCursor(FALSE);
			}

			// Darf keine Ausnahme auslösen.
			~HiddenCursor()
			{
				::ShowCursor(TRUE);
			}
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsTimer.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		// Ein Hight-Resolution Timer.
		class Timer
		{
		public:

			Timer() : myRunningTime(0.0f), myElapsedTime(0.0f)
			{
				if (!::QueryPerformanceFrequency(&myFrequency))
				{
					ThrowException("::QueryPerformanceFrequency() failed.");
				}

				if (!::QueryPerformanceCounter(&myStartTime))
				{
					ThrowException("::QueryPerformanceCounter() failed.");
				}

				myPreviousTime = myStartTime;
			}

			float GetRunningTime() const
			{
				return myRunningTime;
			}

			float GetElapsedTime() const
			{
				return myElapsedTime;
			}

			void Update()
			{
				::LARGE_INTEGER currentTime;
				::QueryPerformanceCounter(&currentTime);

				myRunningTime = static_cast<float>(
					static_cast<double>(currentTime.QuadPart - myStartTime.QuadPart) / 
					static_cast<double>(myFrequency.QuadPart));
		
				myElapsedTime = static_cast<float>(
					static_cast<double>(currentTime.QuadPart - myPreviousTime.QuadPart) / 
					static_cast<double>(myFrequency.QuadPart));

				if (myPreviousTime.QuadPart < currentTime.QuadPart) // Sicherheitshalber.
				{
					myPreviousTime = currentTime;
				}

				if (myRunningTime < 0.0f) // Sicherheitshalber.
				{
					myRunningTime = 0.0f;
				}

				if (myElapsedTime < 0.0f) // Sicherheitshalber.
				{
					myElapsedTime = 0.0f;
				}
			}

		private:

			float myRunningTime;
			float myElapsedTime;

			::LARGE_INTEGER myFrequency;
			::LARGE_INTEGER myStartTime;
			::LARGE_INTEGER myPreviousTime;
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsKeyboardState.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows
	{
		// Speichert einen Tastaturstatus.
		class KeyboardState
		{
		public:

			KeyboardState()
			{
				ZeroMemory(&myBuffer, sizeof(bool) * 256);
			}
	
			bool & operator[](::DWORD virtualKey)
			{
				assert(virtualKey >= 0 && virtualKey < 255);
				return myBuffer[virtualKey];
			}
	
			bool const & operator[](::DWORD virtualKey) const
			{
				assert(virtualKey >= 0 && virtualKey < 255);
				return myBuffer[virtualKey];
			}

		private:

			bool myBuffer[256]; // Sollte ausreichen.
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WindowsKeyboard.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace Windows	
	{
		class Keyboard
		{
		public:

			void Update()
			{
				myOldState = myNewState;
				myNewState = myCurrentState;
			}
	
			bool IsDown(::DWORD virtualKey) const
			{
				return myNewState[virtualKey];
			}
	
			bool IsToggled(::DWORD virtualKey) const
			{
				return myNewState[virtualKey] && !myOldState[virtualKey];
			}
	
			// Verarbeitet Tastaturnachrichten.
			// Darf keine Ausnahme auslösen.
			void MessageHandler(::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
			{
				switch (message)
				{
					case WM_KEYDOWN:
					case WM_SYSKEYDOWN:
					{
						myCurrentState[static_cast<::DWORD>(firstParameter & 0xFF)] = true;
						myCurrentState[VK_MENU] = (secondParameter & (1 << 29)) != 0;
						break;
					}
					case WM_KEYUP:
					case WM_SYSKEYUP:
					{
						myCurrentState[static_cast<::DWORD>(firstParameter & 0xFF)] = false;
						myCurrentState[VK_MENU] = (secondParameter & (1 << 29)) != 0;
						break;
					}
				}
			}

		private:

			KeyboardState myOldState;
			KeyboardState myNewState;
			KeyboardState myCurrentState;
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DirectXReference.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace DirectX
	{		
		void AddReference(::IUnknown * toReference)
		{
			if (toReference)
			{
				toReference->AddRef();
			}
		}

		// Darf keine Ausnahme auslösen -> Wird von einem Destruktor aufgerufen.
		void ReleaseReference(::IUnknown * toReference)
		{
			if (toReference)
			{
				toReference->Release();
			}
		}

		// Spezialisierung für ein Devicekontext damit alle ausstehenden Renderbefehle verworfen werden können.
		// Darf keine Ausnahme auslösen -> Wird von einem Destruktor aufgerufen.
		void ReleaseReference(::ID3D11DeviceContext * toContext)
		{
			if (toContext)
			{
				// Prüfen, ob es die letzte Referenz ist.				
				if (toContext->AddRef() == 2)
				{
					toContext->ClearState();
					toContext->Flush();
				}
				
				toContext->Release();				
				toContext->Release();
			}
		}

		// Spezialisierung für ein Swapchain da dieses nur im Fensterstatus freigegeben werden kann.
		// Darf keine Ausnahme auslösen -> Wird von einem Destruktor aufgerufen.
		void ReleaseReference(::IDXGISwapChain * toSwapChain)
		{
			if (toSwapChain)
			{
				// Prüfen, ob es die letzte Referenz ist.
				if (toSwapChain->AddRef() == 2)
				{					
					toSwapChain->SetFullscreenState(FALSE, nullptr);
				}
					
				toSwapChain->Release();
				toSwapChain->Release();
			}
		}	
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DirectXPointer.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace DirectX
	{
		template <typename InterfaceType>
		class Pointer
		{
		public:

			// Initialisiert die Schnittstelle mit nullptr.	
			Pointer() :	toMyInterface(nullptr) {}

			// Initialisiert die Schnittstelle.	
			explicit Pointer(InterfaceType * toInterface) : toMyInterface(toInterface) {}
		
			// Erstellt eine Kopie eines Zeigers.
			// Inkrementiert anschliessend den Referenzzähler der neuen Schnittstelle.	
			Pointer(Pointer const & pointer) : toMyInterface(pointer.toMyInterface)
			{
				AddReference(toMyInterface);
			}

			// Move-Konstruktor -> Initialisiert die Schnittstelle mit einem Zeiger.	
			Pointer(Pointer && pointer) : toMyPointer(std::move(pointer.toMyInterface))
			{			
				pointer.toMyInterface = nullptr;
			}

			// Dekrementiert den Referenzzähler der Schnittstelle.
			// Darf keine Ausnahme auslösen.	
			~Pointer()
			{
				ReleaseReference(toMyInterface);				
			}
		
			// Weist einen Zeiger zu.
			// Dekrementiert zuerst den Referenzzähler der bestehenden Schnittstelle.
			// Inkrementiert anschliessend den Referenzzähler der neuen Schnittstelle.	
			Pointer & operator = (Pointer const & pointer)
			{
				if (this != pointer) // Selbstzuweisung verhindern.
				{
					ReleaseReference(toMyInterface);
					toMyInterface = pointer.toMyInterface;
					AddReference(toMyInterface);
				}

				return *this;
			}

			// Move-Operator -> Weist einen Zeiger zu.
			// Dekrementiert zuerst den Referenzzähler der bestehenden Schnittstelle.	
			Pointer & operator = (Pointer && pointer)
			{
				if (this != pointer) // Selbstzuweisung verhindern.
				{
					ReleaseReference(toMyInterface);
					toMyInterface = std::move(pointer.toMyInterface);					
					pointer.toMyInterface = nullptr;
				}

				return *this;
			}
		
			// Gibt die Schnittstelle zurück damit diese als Argument verwendet werden kann.	
			operator InterfaceType * () const
			{
				return toMyInterface;
			}

			// Gibt die Adresse der Schnittstelle zurück damit diese initialisiert werden kann.	
			InterfaceType ** operator & ()
			{
				assert(toMyInterface == nullptr); // Muss uninitialisiert sein.
				return &toMyInterface;
			}
		
			// Dereferenziert den Zeiger.	
			InterfaceType & operator * () const
			{ 
				assert(toMyInterface != nullptr); // Muss initialisiert sein.
				return *toMyInterface;
			}

			// Erlaubt den Zugriff auf die Schnittstellenimplementation.	
			InterfaceType * operator -> () const
			{
				assert(toMyInterface != nullptr); // Muss initialisiert sein.
				return toMyInterface;
			}

			// Gibt die Schnittstelle zurück.	
			InterfaceType * Get() const
			{
				return toMyInterface;
			}

			// Weist eine neue Schnittstelle zu.
			// Dekrementiert zuerst den Referenzzähler der bestehenden Schnittstelle.	
			void Reset(InterfaceType * toInterface)
			{
				if (toMyInterface != toInterface) // Selbstzuweisung verhindern.
				{
					ReleaseReference(toMyInterface);
					toMyInterface = toInterface;
				}
			}

		private:

			InterfaceType * toMyInterface; // Kann auch uninitialisiert sein.
		};
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// D3D11.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace D3D11
	{
		// ::D3D11CreateDevice()
		::ID3D11Device * CreateDevice(
			::IDXGIAdapter * toAdapter, 
			::D3D_DRIVER_TYPE driverType, 
			::HMODULE software, 
			::UINT flags, 
			::D3D_FEATURE_LEVEL const * toFeatureLevels,
			::UINT featureLevelCount,
			::UINT sdkVersion,				
			::D3D_FEATURE_LEVEL * toFeatureLevel,
			::ID3D11DeviceContext ** toImmediateContext)			
		{
			::ID3D11Device * toDevice(nullptr);			
			auto const result(::D3D11CreateDevice(
				toAdapter,
				driverType,
				software,
				flags,
				toFeatureLevels,
				featureLevelCount,
				sdkVersion,
				&toDevice,
				toFeatureLevel,
				toImmediateContext));

			if (FAILED(result))
			{
				ThrowDirectXException("::D3D11CreateDevice() failed.", result);
			}

			return toDevice;
		}

		namespace Device
		{		
			// ID3D11Device::CheckMultisampleQualityLevels()
			::UINT CheckMultisampleQualityLevels(::ID3D11Device * toDevice, ::DXGI_FORMAT format, ::UINT sampleCount)
			{
				assert(toDevice != nullptr);

				::UINT qualityLevels(0);
				auto const result(toDevice->CheckMultisampleQualityLevels(format, sampleCount, &qualityLevels));

				if (FAILED(result))
				{
					ThrowDirectXException("ID3D11Device::CheckMultisampleQualityLevels() failed.", result);
				}

				return qualityLevels;
			}

			// ID3D11Device::CreateTexture2D()
			::ID3D11Texture2D * CreateTexture2D(
				::ID3D11Device * toDevice, 
				::D3D11_TEXTURE2D_DESC const * toDescription,
				::D3D11_SUBRESOURCE_DATA const * toInitialData)
			{
				assert(toDevice != nullptr);

				::ID3D11Texture2D * toTexture(nullptr);
				auto const result(toDevice->CreateTexture2D(toDescription, toInitialData, &toTexture));

				if (FAILED(result))
				{
					ThrowDirectXException("ID3D11Device::CreateTexture2D() failed.", result);
				}

				return toTexture;
			}

			// ID3D11Device::CreateRenderTargetView()
			::ID3D11RenderTargetView * CreateRenderTargetView(
				::ID3D11Device * toDevice, 
				::ID3D11Resource * toResource, 
				::D3D11_RENDER_TARGET_VIEW_DESC const * toDescription)
			{
				assert(toDevice != nullptr);

				::ID3D11RenderTargetView * toView(nullptr);
				auto const result(toDevice->CreateRenderTargetView(toResource, toDescription, &toView));

				if (FAILED(result))
				{
					ThrowDirectXException("ID3D11Device::CreateRenderTargetView() failed.", result);
				}

				return toView;
			}

			// ID3D11Device::CreateDepthStencilView()
			::ID3D11DepthStencilView * CreateDepthStencilView(
				::ID3D11Device * toDevice, 
				::ID3D11Resource * toResource, 
				::D3D11_DEPTH_STENCIL_VIEW_DESC const * toDescription)
			{
				assert(toDevice != nullptr);

				::ID3D11DepthStencilView * toView(nullptr);
				auto const result(toDevice->CreateDepthStencilView(toResource, toDescription, &toView));

				if (FAILED(result))
				{
					ThrowDirectXException("ID3D11Device::CreateDepthStencilView() failed.", result);
				}

				return toView;
			}
		}

		namespace Texture2D
		{
			// ID3D11Texture2D::GetDesc()
			::D3D11_TEXTURE2D_DESC GetDescription(::ID3D11Texture2D * toTexture)
			{
				assert(toTexture != nullptr);

				::D3D11_TEXTURE2D_DESC description;
				toTexture->GetDesc(&description);

				return description; // Sollte zu keiner Kopie führen.
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DXGI.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	namespace DXGI
	{
		// ::CreateDXGIFactory1()
		::IDXGIFactory1 * CreateFactory1()
		{
			::IDXGIFactory1 * toFactory(nullptr);			
			auto const result(::CreateDXGIFactory1(
				__uuidof(::IDXGIFactory1), 
				reinterpret_cast<void **>(&toFactory)));

			if (FAILED(result))
			{
				ThrowDirectXException("::CreateDXGIFactory1() failed.", result);
			}

			return toFactory;
		}
		
		namespace Factory1
		{	
			// IDXGIFactory1::EnumAdapters1()
			::IDXGIAdapter1 * EnumerateAdapters(::IDXGIFactory1 * toFactory, ::UINT adapterOrdinal)
			{
				assert(toFactory != nullptr);

				::IDXGIAdapter1 * toAdapter(nullptr);
				auto const result(toFactory->EnumAdapters1(adapterOrdinal, &toAdapter));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGIFactory1::EnumAdapters1() failed.", result);
				}

				return toAdapter;
			}

			// IDXGIFactory1::CreateSwapChain()
			::IDXGISwapChain * CreateSwapChain(
				::IDXGIFactory1 * toFactory, 
				::IUnknown * toDevice, 
				::DXGI_SWAP_CHAIN_DESC /* const */ & description)
			{
				assert(toFactory != nullptr);
				assert(toDevice != nullptr);

				::IDXGISwapChain * toSwapChain(nullptr);
				auto const result(toFactory->CreateSwapChain(toDevice, &description, &toSwapChain));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGIFactory1::CreateSwapChain() failed.", result);
				}

				return toSwapChain;
			}
		
			// IDXGIFactory1::MakeWindowAssociation()
			void MakeWindowAssociation(::IDXGIFactory1 * toFactory, ::HWND const window, ::UINT flags)
			{
				assert(toFactory != nullptr);
				assert(window != nullptr);

				auto const result(toFactory->MakeWindowAssociation(window, flags));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGIFactory1::MakeWindowAssociation() failed.", result);
				}
			}	
		}

		namespace Adapter1
		{		
			// IDXGIAdapter1::GetDesc1()
			::DXGI_ADAPTER_DESC1 GetDescription(::IDXGIAdapter1 * toAdapter)
			{
				assert(toAdapter != nullptr);

				::DXGI_ADAPTER_DESC1 description;
				auto const result(toAdapter->GetDesc1(&description));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGIAdapter1::GetDesc1() failed.", result);
				}

				return description; // Sollte zu keiner Kopie führen.
			}

			// IDXGIAdapter1::EnumOutputs()
			::IDXGIOutput * EnumerateOutputs(::IDXGIAdapter1 * toAdapter, ::UINT outputOrdinal)
			{
				assert(toAdapter != nullptr);

				::IDXGIOutput * toOutput(nullptr);
				auto const result(toAdapter->EnumOutputs(outputOrdinal, &toOutput));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGIAdapter1::EnumOutputs() failed.", result);
				}

				return toOutput;
			}
		}

		namespace Output 
		{
			// IDXGIOutput::GetDesc()
			::DXGI_OUTPUT_DESC GetDescription(::IDXGIOutput * toOutput)
			{
				assert(toOutput != nullptr);

				::DXGI_OUTPUT_DESC description;
				auto const result(toOutput->GetDesc(&description));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGIOutput::GetDesc() failed.", result);
				}

				return description; // Sollte zu keiner Kopie führen.
			}
		}

		namespace SwapChain
		{
			// IDXGISwapChain::GetContainingOutput()
			::IDXGIOutput * GetContainingOutput(::IDXGISwapChain * toSwapChain)
			{
				assert(toSwapChain != nullptr);

				::IDXGIOutput * toOutput(nullptr);
				auto const result(toSwapChain->GetContainingOutput(&toOutput));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGISwapChain::GetContainingOutput() failed.", result);
				}

				return toOutput;
			}

			// IDXGISwapChain::GetBuffer()
			void * GetBuffer(::IDXGISwapChain * toSwapChain, ::UINT bufferID, ::IID interfaceID)
			{
				assert(toSwapChain != nullptr);

				void * toSurface(nullptr);
				auto const result(toSwapChain->GetBuffer(bufferID, interfaceID, &toSurface));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGISwapChain::GetBuffer() failed.", result);
				}

				return toSurface;
			}

			// IDXGISwapChain::ResizeBuffers()
			void ResizeBuffers(
				::IDXGISwapChain * toSwapChain,
				::UINT bufferCount,
				::UINT width,
				::UINT height,
				::DXGI_FORMAT format,
				::UINT swapChainFlags)
			{
				assert(toSwapChain != nullptr);
				auto const result(toSwapChain->ResizeBuffers(bufferCount, width, height, format, swapChainFlags));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGISwapChain::ResizeBuffers() failed.", result);
				}
			}

			// IDXGISwapChain::ResizeTarget()
			void ResizeTarget(::IDXGISwapChain * toSwapChain, ::DXGI_MODE_DESC const & description)
			{
				assert(toSwapChain != nullptr);
				auto const result(toSwapChain->ResizeTarget(&description));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGISwapChain::ResizeTarget() failed.", result);
				}
			}

			// IDXGISwapChain::GetFullscreenState()
			bool IsFullscreen(::IDXGISwapChain * toSwapChain)
			{
				assert(toSwapChain != nullptr);
				
				::BOOL fullscreen;
				toSwapChain->GetFullscreenState(&fullscreen, nullptr);

				return fullscreen == TRUE;
			}

			// IDXGISwapChain::GetDesc()
			::DXGI_SWAP_CHAIN_DESC GetDescription(::IDXGISwapChain * toSwapChain)
			{
				assert(toSwapChain != nullptr);

				::DXGI_SWAP_CHAIN_DESC description;
				auto const result(toSwapChain->GetDesc(&description));

				if (FAILED(result))
				{
					ThrowDirectXException("IDXGISwapChain::GetDesc() failed.", result);
				}

				return description; // Sollte zu keiner Kopie führen.
			}

			// IDXGISwapChain::SetFullscreenState()
			void SetFullscreenState(::IDXGISwapChain * toSwapChain, bool fullscreen, ::IDXGIOutput * toOutput)
			{
				assert(toSwapChain != nullptr);

				if (fullscreen)
				{
					auto const result(toSwapChain->SetFullscreenState(TRUE, toOutput));

					if (FAILED(result))
					{
						ThrowDirectXException("IDXGISwapChain::SetFullscreenState() failed.", result);
					}
				}
				else
				{
					toSwapChain->SetFullscreenState(FALSE, nullptr);
				}
			}

			// IDXGISwapChain::Present()
			bool Present(::IDXGISwapChain * toSwapChain, ::UINT interval, ::UINT flags)
			{
				assert(toSwapChain != nullptr);

				auto const result(toSwapChain->Present(interval, flags));

				switch (result)
				{
					case S_OK:
					{
						break;
					}
					case DXGI_STATUS_OCCLUDED:
					{
						return false;
					}
					default:
					{
						ThrowDirectXException("IDXGISwapChain::Present() failed.", result);						
						break; // Wird nie erreicht.
					}
				}

				return true;
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Application.hpp/cpp
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Test
{
	class Application : protected Windows::IListener, protected INonCopyAssignable
	{
	public:

		explicit Application(::HINSTANCE const moduleInstance) :
			
			// Die Fensterklasse initialisieren.
			myWindowClass(Windows::CreateWindowClassDescription(
				sizeof(::WNDCLASSEXW),
				CS_DBLCLKS,				
				0,
				0,
				moduleInstance,
				::LoadIconW(nullptr, IDI_APPLICATION), // moduleInstance, MAKEINTRESOURCE(...))
				::LoadIconW(nullptr, IDI_APPLICATION), // moduleInstance, MAKEINTRESOURCE(...))
				::LoadCursorW(nullptr, IDC_ARROW),
				reinterpret_cast<::HBRUSH>(::GetStockObject(BLACK_BRUSH)),
				nullptr,
				L"TestWindow")),

			myValidState(true),
			myRunningState(true),
			myFocusState(false),
			myOccludedState(false),
			myLostFullscreenState(false),

			myTime(0.0f),
			myTick(1.0f / 60.0f), // -> 60 mal pro Sekunde ticken.
			myTickBuffer(0.0f)
		{
			InitializeRenderDevice();

			std::wstringstream windowText;
			windowText << L"Test - " << DXGI::Adapter1::GetDescription(myGraphicsAdapter).Description;

			// Das Fenster kann nicht in der Initialisierungsliste initialisiert werden da während dem Erstellen
			// verschiedene Nachrichten gesendet werden und zu diesem Zeitpunkt die Anwendungsinstanz noch nicht
			// fertig konstruiert ist.
			myWindow.reset(new Windows::Window(
				WS_OVERLAPPEDWINDOW,
				WS_EX_APPWINDOW,
				L"TestWindow",
				windowText.str().c_str(),
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				CW_USEDEFAULT,
				nullptr,
				nullptr,
				moduleInstance,								
				this)); // -> Windows::IListener.

			Windows::SetWindowClientSize(*myWindow, 1280, 720);
			Windows::CenterWindowAtNearestArea(*myWindow, Windows::Area::Workspace);

			::ShowWindow(*myWindow, SW_SHOWNORMAL);
			::SetForegroundWindow(*myWindow);

			// Nicht minimiert starten.
			if (::IsIconic(*myWindow))
			{
				::ShowWindow(*myWindow, SW_RESTORE);
			}

			myClientSize = Windows::GetWindowClientSize(*myWindow);
						
			InitializeSwapChain();
			InitializeSwapChainDepencies();
		}

		bool IsRunning() const
		{
			return myValidState && myRunningState;
		}

		void Update()
		{
			if (!myValidState || !myRunningState)
			{
				return;
			}

			myTimer.Update();

			// Da nicht bei jedem Vollbildstatusverlust eine WA_ACTIVATE oder WA_ACTIVATEAPP Nachricht gesendet
			// wird, vor jedem Aktualisierungsvorgang einen Test durchführen.
			HandlePossibleLostFullscreenState();

			if (::IsIconic(*myWindow))
			{
				return;
			}

			myValidState = false;

			if (myLostFullscreenState)
			{
				myLostFullscreenState = false;
				ToggleScreen();
			}
			else if (HandlePossibleClientSizeChange())
			{
				ReleaseSwapChainDepencies();

				// Die neue Buffergrösse automatisch ermitteln.
				DXGI::SwapChain::ResizeBuffers(
					mySwapChain,
					0,
					0,
					0,
					::DXGI_FORMAT_UNKNOWN,
					::DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);

				InitializeSwapChainDepencies();
			}				
			
			// Alle Ressourcen sind korrekt initialisiert.
			myValidState = true;

			if (myOccludedState)
			{	
				// Prüfen, ob der Renderbereich wieder bereit ist.
				myOccludedState = DXGI::SwapChain::Present(mySwapChain, 0, DXGI_PRESENT_TEST);
			}
			else
			{
				if (myFocusState)
				{					
					myTickBuffer += myTimer.GetElapsedTime();

					// Die Anwendung unabhängig der Framerate ticken lassen.
					while (myTickBuffer >= myTick)
					{
						myTime += myTick;
						myTickBuffer -= myTick;
						Tick();
					}
				}

				Render();
			}
		}

	private:

		// Darf keine Ausnahme auslösen.
		bool MessageHandler(::UINT message, ::WPARAM firstParameter, ::LPARAM secondParameter)
		{
			switch (message)
			{
				case WM_ACTIVATE:
				case WM_ACTIVATEAPP:
				{
					myFocusState = firstParameter != WA_INACTIVE;

					// Prüfen, ob der Vollbildstatus verloren gegangen ist.
					if (myRunningState && mySwapChain.Get())
					{								
						HandlePossibleLostFullscreenState();
					}

					break;
				}
				case WM_GETMINMAXINFO:
				{
					// Die minimale Fenstergrösse (nicht Klientgrösse) festlegen.
					reinterpret_cast<::MINMAXINFO *>(secondParameter)->ptMinTrackSize.x = 400;
					reinterpret_cast<::MINMAXINFO *>(secondParameter)->ptMinTrackSize.y = 400;
					break;
				}
				case WM_KEYDOWN:
				case WM_SYSKEYDOWN:
				case WM_KEYUP:
				case WM_SYSKEYUP:
				{
					myKeyboard.MessageHandler(message, firstParameter, secondParameter);
					break;
				}
				case WM_CLOSE:
				case WM_DESTROY:
				{
					// WM_CLOSE wird nicht weiterverarbeitet.
					myRunningState = false;
					break;
				}
				case WM_SYSCOMMAND:
				{
					switch (firstParameter & 0xFFF0)
					{
						case SC_KEYMENU:
						{
							// Nicht verwendete Menüverknüpfungen deaktivieren.							
							return false;
						}
						case SC_MONITORPOWER:
						case SC_SCREENSAVE:
						{
							// Wenn das Fenster fokusiert ist, den Energiesparmodus und den Bildschirmschoner
							// daktivieren.							
							return !myFocusState;
						}
					}

					break;
				}
			}

			return true;
		}

		void InitializeRenderDevice()
		{
			// Den Standardadapter verwenden.
			myGraphicsFactory.Reset(DXGI::CreateFactory1());
			myGraphicsAdapter.Reset(DXGI::Factory1::EnumerateAdapters(myGraphicsFactory, 0));
					
			::D3D_FEATURE_LEVEL featureLevel(::D3D_FEATURE_LEVEL_9_1);
			::UINT createFlags(0);

#if defined(DEBUG) || defined(_DEBUG)
			createFlags |= ::D3D11_CREATE_DEVICE_DEBUG;
#endif
			// Ein Renderdevice anhand des ermittelten Adapters erstellen.
			myRenderDevice.Reset(D3D11::CreateDevice(
				myGraphicsAdapter,
				::D3D_DRIVER_TYPE_UNKNOWN,
				nullptr,
				createFlags,
				nullptr,
				0,
				D3D11_SDK_VERSION,
				&featureLevel,
				&myRenderContext));

			if (featureLevel < ::D3D_FEATURE_LEVEL_11_0)
			{
				// TODO: Kein Shadermodel 5.0.
			}
		}

		void InitializeSwapChain()
		{
			// Die Buffergrösse sowie die Bildwiederholfrequenz automatisch ermitteln.
			::DXGI_SWAP_CHAIN_DESC swapChain;
			swapChain.BufferDesc.Format = ::DXGI_FORMAT_R8G8B8A8_UNORM;
			swapChain.BufferDesc.Width = 0;
			swapChain.BufferDesc.Height = 0;
			swapChain.BufferDesc.RefreshRate.Numerator = 0;
			swapChain.BufferDesc.RefreshRate.Denominator = 0;
			swapChain.BufferDesc.ScanlineOrdering = ::DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
			swapChain.BufferDesc.Scaling = ::DXGI_MODE_SCALING_UNSPECIFIED;
			swapChain.BufferCount = 2;			
			swapChain.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
			swapChain.Flags = ::DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
			swapChain.OutputWindow = *myWindow;
			swapChain.SwapEffect = ::DXGI_SWAP_EFFECT_DISCARD;
			swapChain.Windowed = TRUE;
			swapChain.SampleDesc.Count = 4;
			
			// Nach der bestmöglichen Qualität suchen. Diese entspricht dem ermittelten Wert minus 1.

			auto const sampleQuality(D3D11::Device::CheckMultisampleQualityLevels(
				myRenderDevice, 
				swapChain.BufferDesc.Format,
				swapChain.SampleDesc.Count));
				
			assert(sampleQuality != 0); // Direct3D 11 muss 4 x MSAA unterstützen.
			swapChain.SampleDesc.Quality = sampleQuality - 1;
										
			mySwapChain.Reset(DXGI::Factory1::CreateSwapChain(myGraphicsFactory, myRenderDevice, swapChain));

			// Die Fensterverwaltung selber übernehmen.
			DXGI::Factory1::MakeWindowAssociation(
				myGraphicsFactory, 
				*myWindow, 
				DXGI_MWA_NO_WINDOW_CHANGES | 
				DXGI_MWA_NO_ALT_ENTER | 
				DXGI_MWA_NO_PRINT_SCREEN);
		}

		void InitializeSwapChainDepencies()
		{
			myBackBuffer.Reset(reinterpret_cast<::ID3D11Texture2D *>(
				DXGI::SwapChain::GetBuffer(mySwapChain, 0, __uuidof(::ID3D11Texture2D))));

			myBackBufferView.Reset(D3D11::Device::CreateRenderTargetView(myRenderDevice, myBackBuffer, nullptr));
			
			// Den Depthstencilbuffer dem Backbuffer anpassen.
			auto const backBuffer(D3D11::Texture2D::GetDescription(myBackBuffer));
						
			::D3D11_TEXTURE2D_DESC depthStencil;
			depthStencil.Width = backBuffer.Width;
			depthStencil.Height = backBuffer.Height;
			depthStencil.MipLevels = 1;
			depthStencil.ArraySize = 1;
			depthStencil.Format = ::DXGI_FORMAT_D24_UNORM_S8_UINT;
			depthStencil.SampleDesc = backBuffer.SampleDesc;
			depthStencil.Usage = D3D11_USAGE_DEFAULT;
			depthStencil.BindFlags = D3D11_BIND_DEPTH_STENCIL;
			depthStencil.CPUAccessFlags = 0;
			depthStencil.MiscFlags = 0;
    
			myDepthStencil.Reset(D3D11::Device::CreateTexture2D(myRenderDevice, &depthStencil, nullptr));
			
			::D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilView;
			depthStencilView.Format = depthStencil.Format;
			depthStencilView.Flags = 0;
			depthStencilView.Texture2D.MipSlice = 0;
			
			// Prüfen, ob eine Multisampletextur verwendet werden muss.
			depthStencilView.ViewDimension = backBuffer.SampleDesc.Count > 1 ? 
				::D3D11_DSV_DIMENSION_TEXTURE2DMS :
				::D3D11_DSV_DIMENSION_TEXTURE2D;

			myDepthStencilView.Reset(D3D11::Device::CreateDepthStencilView(
				myRenderDevice, 
				myDepthStencil, 
				&depthStencilView));
		}

		void ReleaseSwapChainDepencies()
		{
			myRenderContext->OMSetRenderTargets(0, nullptr, nullptr);

			myBackBuffer.Reset(nullptr);
			myBackBufferView.Reset(nullptr);
			myDepthStencil.Reset(nullptr);
			myDepthStencilView.Reset(nullptr);
		}

		bool HandlePossibleClientSizeChange()
		{
			auto const clientSize(Windows::GetWindowClientSize(*myWindow));

			if (myClientSize != clientSize)
			{
				myClientSize = clientSize;
				return true;
			}

			return false;
		}

		// Darf keine Ausnahme auslösen.
		void HandlePossibleLostFullscreenState()
		{
			if (::IsIconic(*myWindow) || myLostFullscreenState)
			{
				return;
			}

			// Den Mauszeiger während der Aktion ausblenden.
			Windows::HiddenCursor const hiddenCursor;

			// Wenn im Vollbildmodus der Fensterfokus verloren geht, den Fensterstatus setzen und das Fenster
			// minimieren. Der Vollbildmodus später wiederherstellen, sobald das Fenster den minimierten Status
			// wieder verlässt.
			auto const isFullscreen(DXGI::SwapChain::IsFullscreen(mySwapChain));

			if ((isFullscreen && !myFocusState) || 
				(!isFullscreen && Windows::GetWindowStyle(*myWindow).second == 0))
			{
				mySwapChain->SetFullscreenState(FALSE, nullptr);
				myLostFullscreenState = true;				
						
				::ShowWindow(*myWindow, SW_MINIMIZE);
			}
		}

		void ToggleScreen()
		{
			// Den Mauszeiger während der Aktion ausblenden.
			Windows::HiddenCursor const hiddenCursor;

			if (DXGI::SwapChain::IsFullscreen(mySwapChain))
			{
				// Den vorherigen Fenstermodus wiederherstellen.
				mySwapChain->SetFullscreenState(FALSE, nullptr);								
				Windows::SetWindowStyle(*myWindow, WS_OVERLAPPEDWINDOW, WS_EX_APPWINDOW);
				myWindowPlacement.Set(*myWindow);				
				
				if (HandlePossibleClientSizeChange())
				{
					ReleaseSwapChainDepencies();

					// Die neue Buffergrösse automatisch ermitteln.
					DXGI::SwapChain::ResizeBuffers(
						mySwapChain,
						0,
						0,
						0,
						::DXGI_FORMAT_UNKNOWN,
						::DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);

					InitializeSwapChainDepencies();
				}
			}
			else
			{
				DirectX::Pointer<::IDXGIOutput> const output(DXGI::SwapChain::GetContainingOutput(mySwapChain));

				// Die Vollbildauflösung dem Desktop anpassen auf welchem sich das Fenster gerade befindet.						
				auto const desktop(DXGI::Output::GetDescription(output).DesktopCoordinates);			
				auto const width(static_cast<::UINT>(desktop.right));				
				auto const height(static_cast<::UINT>(desktop.bottom));
												
				// Den Fensterrahmen entfernen damit eine beliebige Klientgrösse gesetzt werden kann.
				// Zuvor den Fensterstatus speichern (Position, Grösse, minimiert, maximiert) damit dieser später
				// wiederhergestellt werden kann.								
				if (Windows::GetWindowStyle(*myWindow).second != 0)
				{
					myWindowPlacement.Get(*myWindow);

					if (::IsIconic(*myWindow) || ::IsZoomed(*myWindow))
					{
						::ShowWindow(*myWindow, SW_RESTORE);
					}
										
					Windows::SetWindowStyle(*myWindow, WS_POPUP, 0);
				}

				Windows::SetWindowClientSize(*myWindow, width, height);
				Windows::CenterWindowAtNearestArea(*myWindow, Windows::Area::Desktop);
				
				// Die Ressourcen müssen auf jeden Fall neu erstellt werden.
				ReleaseSwapChainDepencies();

				if (HandlePossibleClientSizeChange())
				{					
					// Die neue Buffergrösse automatisch ermitteln.
					DXGI::SwapChain::ResizeBuffers(
						mySwapChain,
						0,
						0,
						0,
						::DXGI_FORMAT_UNKNOWN,
						::DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);
				}

				// Bei einem Wechsel in den Vollbildstatus muss IDXGISwapChain::ResizeBuffers() gleich nach
				// IDXGISwapChain::SetFullscreenState() aufgerufen werden auch wenn keine Grössenänderung
				// vorgekommen ist. Ansonsten wird eine IDXGISwapChain::Present() Performancewarnung ausgegeben.				
				DXGI::SwapChain::SetFullscreenState(mySwapChain, true, output);								
				DXGI::SwapChain::ResizeBuffers(
					mySwapChain,
					0,
					0,
					0,
					::DXGI_FORMAT_UNKNOWN,
					::DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);

				InitializeSwapChainDepencies();
			}
		}

		void Tick()
		{
			myKeyboard.Update();

			if (myKeyboard.IsToggled(VK_ESCAPE))
			{
				myRunningState = false;
			}

			// Alt + Enter -> Fenster- Vollbildmoduswechsel.
			if (myKeyboard.IsDown(VK_MENU) && myKeyboard.IsToggled(VK_RETURN))
			{
				ToggleScreen();
			}
		}

		void Render()
		{
			::FLOAT const clearColor[4] = { 0.0f, 0.125f, 0.3f, 0.0f };
			::UINT const clearFlags(::D3D11_CLEAR_DEPTH | ::D3D11_CLEAR_STENCIL);

			::ID3D11RenderTargetView * const renderTargetViews[1] =
			{
				myBackBufferView.Get()
			};

			myRenderContext->OMSetRenderTargets(1, renderTargetViews, myDepthStencilView);
			myRenderContext->ClearRenderTargetView(myBackBufferView, clearColor);
			myRenderContext->ClearDepthStencilView(myDepthStencilView, clearFlags, 1.0f, 0);

			// Vertikale Synchronisation verwenden.
			myOccludedState = !DXGI::SwapChain::Present(mySwapChain, 1, 0);
		}

	private:
		
		Windows::WindowClass const myWindowClass;
		std::unique_ptr<Windows::Window> myWindow;
		Windows::SizeType myClientSize;
		Windows::WindowPlacement myWindowPlacement;
		Windows::Timer myTimer;
		Windows::Keyboard myKeyboard;

		DirectX::Pointer<::IDXGIFactory1> myGraphicsFactory;
		DirectX::Pointer<::IDXGIAdapter1> myGraphicsAdapter;
		DirectX::Pointer<::ID3D11Device> myRenderDevice;
		DirectX::Pointer<::ID3D11DeviceContext> myRenderContext;
		DirectX::Pointer<::IDXGISwapChain> mySwapChain;
		DirectX::Pointer<::ID3D11Texture2D> myBackBuffer;
		DirectX::Pointer<::ID3D11RenderTargetView> myBackBufferView;
		DirectX::Pointer<::ID3D11Texture2D> myDepthStencil;
		DirectX::Pointer<::ID3D11DepthStencilView> myDepthStencilView;

		bool myValidState;
		bool myRunningState;
		bool myFocusState;
		bool myOccludedState;
		bool myLostFullscreenState;

		float myTime;
		float const myTick;
		float myTickBuffer;
	};
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int WINAPI WinMain(::HINSTANCE moduleInstance, ::HINSTANCE /* unusedInstance */, ::LPSTR /* toCommandLine */, int /* showCommand */)
{
	try
	{
		Test::Application application(moduleInstance);
		::MSG message = { 0 };

		while (application.IsRunning())
		{
			if (::PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE))
			{
				::TranslateMessage(&message);
				::DispatchMessageW(&message);
			}
			else
			{
				application.Update();
			}
		}
	}
	catch (std::exception const & exception)
	{
		::MessageBoxA(
			nullptr,
			exception.what(),
			"Test - Unhandled Exception",
			MB_ICONERROR | 
			MB_SETFOREGROUND);
	}

	return 0;
}
Ich habe festgestellt, dass viele meiner Komponenten die Windows.h brauchen. Um diese möglichst schlank zu halten, verwende ich verschiedene #defines um Komponenten zu verwerfen. Muss ich diese nun vor jedem Windows.h platzieren oder reicht es, diese einmalig in WinMain.cpp zu definieren?
Denn das würde bedeuten, dass ich die #defines auch vor jedem Header, der intern die Windows.h einbinden, diese platzieren müsste. Also z.B. vor DXGI.h oder D3D11.h.

Ich habe im Internet geforscht, aber konnte keine befriedigende Antwort finden.
Besten Dank.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Gültigkeitsbereich von #define

Beitrag von CodingCat »

Ja, #defines gelten immer nur in dem Modul, in dem sie definiert wurden, und dort auch nur für nachfolgenden Code. Damit müssen deine #defines tatsächlich noch vor der ersten (direkten oder indirekten) Einbindung von Windows.h definiert worden sein. Hierzu empfiehlt es sich, einen eigenen Header anzulegen, der nur diese Definitionen enthält, und am Anfang einer jeden Datei, die Windows.h direkt oder indirekt einbindet, noch zuvor eingebunden werden kann. Wenn du ohnehin einen zentralen Konfigurations-Header hast, kannst du diesen neuen Header zur WinAPI-Konfiguration eventuell sogar gleich dort mit einbinden. Ob sich das lohnt, hängt davon ab, wie häufig du in deinem Projekt Windows.h, D3D11.h etc. einbinden wirst.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Fitim
Beiträge: 12
Registriert: 27.05.2012, 19:29

Re: Gültigkeitsbereich von #define

Beitrag von Fitim »

Ahh, besten Dank.
Antworten