Split off the Windows message handler into a separate thread.

This commit is contained in:
Fatbag 2012-04-13 22:55:13 -05:00
parent d9afae7022
commit 4294cb41f7
11 changed files with 547 additions and 217 deletions

View file

@ -7,7 +7,6 @@ include_directories(${CMAKE_SOURCE_DIR}/Libraries/freetype/include)
if(WIN32)
set(NIOTSOCLIENT_SOURCES
Client.cpp
MessageHandler.cpp
Audio/Startup.cpp
Audio/windows/XAudio2.cpp
Graphics/Font.cpp
@ -15,7 +14,8 @@ if(WIN32)
Graphics/Viewport.cpp
resources/Resource.rc
System/System.cpp
Window/Window.cpp
)
add_executable(TSO WIN32 ${NIOTSOCLIENT_SOURCES})
add_executable(TSO ${NIOTSOCLIENT_SOURCES})
target_link_libraries(TSO FileHandler_shared freetype_shared ole32 opengl32)
endif()

View file

@ -17,15 +17,7 @@
#include "EngineInterface.hpp"
namespace Window {
unsigned Width, Height;
bool Fullscreen = false;
HWND hWnd = NULL;
}
int CreateWindowInvisible(HINSTANCE hInst, unsigned Width, unsigned Height, bool Fullscreen);
void Shutdown();
Scene * CurrentScene;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
@ -33,18 +25,22 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
int result;
//Disallow multiple instances of the game from running
CreateMutex(NULL, TRUE, "TSO_NIOTSO_MUTEX");
if(GetLastError() == ERROR_ALREADY_EXISTS){
if(CreateMutex(NULL, TRUE, "TSO_NIOTSO_MUTEX") == NULL){
HWND hWnd = FindWindow("TSO_NIOTSO", "The Sims Online");
if(hWnd != NULL){
ShowWindowAsync(hWnd, SW_RESTORE);
ShowWindow(hWnd, SW_RESTORE);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
return ERROR_INIT_ANOTHERINSTRUNNING;
}
Window::Width = 800;
Window::Height = 600;
Window::Fullscreen = false;
System::hInst = hInstance;
result = CreateWindowInvisible(hInstance, 800, 600, false);
result = Window::Initialize();
if(result != 0){
Shutdown();
return ERROR_INIT | ERROR_INIT_WINDOW | result;
@ -82,17 +78,15 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
QueryPerformanceCounter(&PreviousTime);
while(true){
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
LARGE_INTEGER CurrentTime;
QueryPerformanceCounter(&CurrentTime);
float TimeDelta = (float)(CurrentTime.QuadPart-PreviousTime.QuadPart)/System::ClockFreq.QuadPart;
if(TimeDelta < 0 || TimeDelta >= 5) //Invalid TimeDelta
if(TimeDelta < 0 || TimeDelta >= 5){ //Invalid TimeDelta
PreviousTime.QuadPart = CurrentTime.QuadPart;
continue;
}
memcpy(&System::UserInput, (const void*) &System::UserInput_v, sizeof(System::UserInput_t));
int result = CurrentScene->RunFor(TimeDelta);
if(result == System::SHUTDOWN)
@ -117,89 +111,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
return 0;
}
int CreateWindowInvisible(HINSTANCE hInst, unsigned Width, unsigned Height, bool Fullscreen)
{
const WNDCLASS wc = {
CS_OWNDC, //style
WndProc, //lpfnWndProc
0, //cbClsExtra
0, //cbWndExtra
hInst, //hInstance
(HICON) LoadImage(hInst, MAKEINTRESOURCE(IDI_TSO), //hIcon
IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE),
NULL, //hCursor
NULL, //hbrBackground
NULL, //lpszMenuName
"TSO_NIOTSO" //lpszClassName
};
if(!RegisterClass(&wc)){
MessageBox(NULL, "Failed to register the window class.", NULL, MB_OK | MB_ICONERROR);
return ERROR_REGISTER_CLASS;
}
SetCursor(NULL);
HWND hWnd = NULL;
if(Fullscreen){
DEVMODE dmScreenSettings;
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL;
dmScreenSettings.dmPelsWidth = Width;
dmScreenSettings.dmPelsHeight = Height;
dmScreenSettings.dmBitsPerPel = 32;
if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL){
MessageBox(NULL, "Fullscreen mode is not supported by your setup. It has been turned off.", NULL,
MB_OK | MB_ICONERROR);
Fullscreen = 0;
}else{
hWnd = CreateWindowEx(WS_EX_APPWINDOW, "TSO_NIOTSO", "The Sims Online", WS_POPUP,
0, 0, Width, Height, 0, 0, hInst, NULL);
}
}
if(hWnd == NULL){
Fullscreen = false;
RECT WindowRect = {0, 0, Width, Height};
//Use a style of WS_OVERLAPPEDWINDOW to allow resizing
AdjustWindowRectEx(&WindowRect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE,
WS_EX_APPWINDOW); //This finds the dimensions of a window with a client area of our specified dimensions
//Note: Windows can be positioned anywhere, even outside the visible workspace,
//but their sizes are limited to the size of the workspace on the primary display.
unsigned WindowWidth = WindowRect.right-WindowRect.left, WindowHeight = WindowRect.bottom-WindowRect.top;
RECT WorkspaceRect;
SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkspaceRect, 0);
hWnd = CreateWindowEx(WS_EX_APPWINDOW, "TSO_NIOTSO", "The Sims Online",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
((WorkspaceRect.right-WorkspaceRect.left - WindowWidth)>>1) + WorkspaceRect.left,
((WorkspaceRect.bottom-WorkspaceRect.top - WindowHeight)>>1) + WorkspaceRect.top,
WindowWidth, WindowHeight, 0, 0, hInst, NULL);
}
if(hWnd == NULL){
MessageBox(NULL, "Failed to create the window.", NULL, MB_OK | MB_ICONERROR);
return ERROR_CREATE_WINDOW;
}
Window::hWnd = hWnd;
Window::Width = Width;
Window::Height = Height;
Window::Fullscreen = Fullscreen;
System::hInst = hInst;
return 0;
}
void Shutdown()
{
Audio::Shutdown();
Graphics::Shutdown();
System::Shutdown();
if(Window::hWnd){
DestroyWindow(Window::hWnd);
Window::hWnd = NULL;
}
UnregisterClass("TSO_NIOTSO", System::hInst);
Window::Shutdown();
}

View file

@ -15,18 +15,25 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//Compiler/platform constants
#define WINVER 0x0502
#define _WIN32_WINNT 0x0502
#define _CRT_SECURE_NO_WARNINGS
//Standard libraries
#include <stdint.h>
#include <stdio.h>
#include <memory.h>
#include <windows.h>
#undef NULL
#define NULL 0
//Codebase libraries
#include "FileHandler.hpp"
#include "ft2build.h"
#include FT_FREETYPE_H
//Macro definitions
#ifndef min
#define min(x,y) ((x)<(y)?(x):(y))
#endif
@ -34,20 +41,14 @@
#define max(x,y) ((x)>(y)?(x):(y))
#endif
//IsometricEngine.cpp
namespace Window {
extern unsigned Width, Height;
extern bool Fullscreen;
extern HWND hWnd;
}
//MessageHandler.cpp
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//Codebase version, branding, linker resources
#include "version.h"
#include "System/System.hpp"
#include "Resources/Resource.h"
//Components
#include "Audio/Audio.hpp"
#include "Graphics/Graphics.hpp"
#include "System/System.hpp"
#include "Window/Window.hpp"
#include "Scene/Scene.hpp"

View file

@ -1,59 +0,0 @@
/*
Niotso - Copyright (C) 2012 Fatbag <X-Fi6@phppoll.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EngineInterface.hpp"
/****
For the sake of performance **all throughout the game**,
order the cases in order from most frequently occurred to less frequently occurred
except making adjustments where tiny response latency is essential
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_SETCURSOR: //Occurs often
SetCursor(NULL);
break;
case WM_SYSCOMMAND: //Occurs often
switch(wParam){
case SC_MONITORPOWER:
case SC_SCREENSAVE:
return 0;
}
break;
case WM_KEYDOWN:
case WM_KEYUP:
System::UserInput.Keys[wParam] = (uMsg == WM_KEYDOWN);
return 0;
case WM_CLOSE:
System::UserInput.CloseWindow = true;
PostQuitMessage(0);
return 0;
case WM_DEVMODECHANGE: {
DEVMODE dm;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
System::FramePeriod = 1.0f/dm.dmDisplayFrequency;
} return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

View file

@ -18,48 +18,51 @@
#include "../EngineInterface.hpp"
namespace System {
HINSTANCE hInst = NULL;
HANDLE Process;
HANDLE ProcessHeap;
LARGE_INTEGER ClockFreq;
float FramePeriod;
UserInput_t UserInput;
bool SceneFailed = false;
int Initialize(){
memset(&UserInput, 0, sizeof(UserInput));
QueryPerformanceFrequency(&ClockFreq);
DEVMODE dm;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
System::FramePeriod = 1.0f/dm.dmDisplayFrequency;
if(FT_Init_FreeType(&Graphics::FreeTypeLibrary)){
MessageBox(Window::hWnd, "Failed to initialize FreeType.", NULL, MB_OK | MB_ICONERROR);
Graphics::FreeTypeLibrary = NULL;
Shutdown();
return ERROR_SYSTEM_INIT_FREETYPE;
};
if(FT_New_Face(Graphics::FreeTypeLibrary, "simdialogue-uni-game.ttf", 0, &Graphics::FontFace)){
MessageBox(Window::hWnd, "simdialogue-uni-game.ttf does not exist or is corrupt or invalid.",
NULL, MB_OK | MB_ICONERROR);
Graphics::FontFace = NULL;
Shutdown();
return ERROR_SYSTEM_MISSING_FONT;
}
return 0;
}
HINSTANCE hInst = NULL;
HANDLE Process;
HANDLE ProcessHeap;
LARGE_INTEGER ClockFreq;
volatile float FramePeriod;
UserInput_t UserInput;
volatile UserInput_t UserInput_v;
bool SceneFailed = false;
int Initialize(){
memset(&UserInput, 0, sizeof(UserInput));
void Shutdown(){
if(Graphics::FontFace){
FT_Done_Face(Graphics::FontFace);
Graphics::FontFace = NULL;
}
if(Graphics::FreeTypeLibrary){
FT_Done_FreeType(Graphics::FreeTypeLibrary);
Graphics::FreeTypeLibrary = NULL;
}
QueryPerformanceFrequency(&ClockFreq);
DEVMODE dm;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
System::FramePeriod = 1.0f/dm.dmDisplayFrequency;
if(FT_Init_FreeType(&Graphics::FreeTypeLibrary)){
MessageBox(Window::hWnd, "Failed to initialize FreeType.", NULL, MB_OK | MB_ICONERROR);
Graphics::FreeTypeLibrary = NULL;
Shutdown();
return ERROR_SYSTEM_INIT_FREETYPE;
};
if(FT_New_Face(Graphics::FreeTypeLibrary, "simdialogue-uni-game.ttf", 0, &Graphics::FontFace)){
MessageBox(Window::hWnd, "simdialogue-uni-game.ttf does not exist or is corrupt or invalid.",
NULL, MB_OK | MB_ICONERROR);
Graphics::FontFace = NULL;
Shutdown();
return ERROR_SYSTEM_MISSING_FONT;
}
return 0;
}
void Shutdown(){
if(Graphics::FontFace){
FT_Done_Face(Graphics::FontFace);
Graphics::FontFace = NULL;
}
if(Graphics::FreeTypeLibrary){
FT_Done_FreeType(Graphics::FreeTypeLibrary);
Graphics::FreeTypeLibrary = NULL;
}
}
}

View file

@ -15,15 +15,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//System/System.cpp
namespace System {
int Initialize();
void Shutdown();
extern HINSTANCE hInst;
extern HANDLE Process;
extern HANDLE ProcessHeap;
extern LARGE_INTEGER ClockFreq;
extern float FramePeriod;
extern volatile float FramePeriod;
struct UserInput_t {
bool Keys[256];
@ -31,7 +31,8 @@ namespace System {
bool CloseWindow;
};
extern UserInput_t UserInput;
extern volatile UserInput_t UserInput_v;
extern bool SceneFailed;
//Constants
@ -52,8 +53,12 @@ namespace System {
//Use: return ERROR_INIT | result;
#define ERROR_INIT_WINDOW 0x0100
enum {
ERROR_REGISTER_CLASS = 1,
ERROR_CREATE_WINDOW
ERROR_WINDOW_SYNCOBJECT = 1,
ERROR_WINDOW_CREATE_THREAD,
ERROR_WINDOW_SYNCHRONIZE,
ERROR_WINDOW_REGISTER_CLASS,
ERROR_WINDOW_CREATE,
ERROR_WINDOW_HANDLE
};
#define ERROR_INIT_SYSTEM 0x0200
enum {

210
Client/Window/Window.cpp Normal file
View file

@ -0,0 +1,210 @@
/*
Niotso - Copyright (C) 2012 Fatbag <X-Fi6@phppoll.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../EngineInterface.hpp"
namespace Window {
unsigned Width, Height;
bool Fullscreen;
HWND hWnd;
static HANDLE Response = NULL;
static HANDLE Thread = NULL;
static volatile int Result;
static DWORD WINAPI Procedure(LPVOID);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static int CreateWindowInvisible(HINSTANCE hInst, unsigned Width, unsigned Height, bool Fullscreen);
int Initialize(){
Response = CreateEvent(NULL, FALSE, FALSE, NULL);
if(Response == NULL){
MessageBox(NULL, "Failed to create the message loop synchronization object.", NULL, MB_OK | MB_ICONERROR);
Shutdown();
return ERROR_WINDOW_SYNCOBJECT;
}
Thread = CreateThread(NULL, 1024 /* very tiny stack size is needed */, Window::Procedure, NULL, 0, NULL);
if(Thread == NULL){
MessageBox(NULL, "Failed to create the message loop thread.", NULL, MB_OK | MB_ICONERROR);
Shutdown();
return ERROR_WINDOW_CREATE_THREAD;
}
if(WaitForSingleObject(Window::Response, INFINITE) != WAIT_OBJECT_0){
MessageBox(NULL, "Failed to synchronize with the message loop thread.", NULL, MB_OK | MB_ICONERROR);
Shutdown();
return ERROR_WINDOW_SYNCHRONIZE;
}
if(Result != 0){
Shutdown();
return Result;
}
hWnd = FindWindow("TSO_NIOTSO", "The Sims Online");
if(hWnd == NULL){
MessageBox(NULL, "Failed to obtain a handle for the window.", NULL, MB_OK | MB_ICONERROR);
Shutdown();
return ERROR_WINDOW_HANDLE;
}
return 0;
}
void Shutdown(){
if(hWnd){
DestroyWindow(hWnd);
hWnd = NULL;
}
if(Thread){
DWORD value;
while(GetExitCodeThread(Thread, &value) && value){
//PostQuitMessage() doesn't reliably work from a separate thread
Sleep(1);
}
Thread = NULL;
}
if(Response){
CloseHandle(Response);
Response = NULL;
}
UnregisterClass("TSO_NIOTSO", System::hInst);
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch(uMsg){
case WM_SETCURSOR:
SetCursor(NULL);
break;
case WM_KEYDOWN:
case WM_KEYUP:
System::UserInput_v.Keys[wParam] = (uMsg == WM_KEYDOWN);
return 0;
case WM_CLOSE:
System::UserInput_v.CloseWindow = true;
return 0;
case WM_SHOWWINDOW:
//Once the window is hidden (we cannot safely destroy the window while we have the OpenGL context),
//WM_QUIT and WM_DESTROY won't show up when a separate thread calls DestroyWindow() or PostQuitMessage().
if(wParam == FALSE) PostQuitMessage(0);
return 0;
case WM_DEVMODECHANGE: {
DEVMODE dm;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
System::FramePeriod = 1.0f/dm.dmDisplayFrequency;
} return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static DWORD WINAPI Procedure(LPVOID){
int result = CreateWindowInvisible(System::hInst, Window::Width, Window::Height, Window::Fullscreen);
if(result != 0){
Window::Result = result;
SetEvent(Window::Response);
return 0;
}
SetEvent(Window::Response);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
static int CreateWindowInvisible(HINSTANCE hInst, unsigned Width, unsigned Height, bool Fullscreen)
{
const WNDCLASS wc = {
CS_OWNDC, //style
WndProc, //lpfnWndProc
0, //cbClsExtra
0, //cbWndExtra
hInst, //hInstance
(HICON) LoadImage(hInst, MAKEINTRESOURCE(IDI_TSO), //hIcon
IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE),
NULL, //hCursor
NULL, //hbrBackground
NULL, //lpszMenuName
"TSO_NIOTSO" //lpszClassName
};
if(!RegisterClass(&wc)){
MessageBox(NULL, "Failed to register the window class.", NULL, MB_OK | MB_ICONERROR);
return ERROR_WINDOW_REGISTER_CLASS;
}
SetCursor(NULL);
HWND hWnd = NULL;
if(Fullscreen){
DEVMODE dmScreenSettings;
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL;
dmScreenSettings.dmPelsWidth = Width;
dmScreenSettings.dmPelsHeight = Height;
dmScreenSettings.dmBitsPerPel = 32;
if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL){
MessageBox(NULL, "Fullscreen mode is not supported by your setup. It has been turned off.", NULL,
MB_OK | MB_ICONERROR);
Fullscreen = 0;
}else{
hWnd = CreateWindowEx(WS_EX_APPWINDOW, "TSO_NIOTSO", "The Sims Online", WS_POPUP,
0, 0, Width, Height, 0, 0, hInst, NULL);
}
}
if(hWnd == NULL){
Fullscreen = false;
RECT WindowRect = {0, 0, Width, Height};
//Use a style of WS_OVERLAPPEDWINDOW to allow resizing
AdjustWindowRectEx(&WindowRect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE,
WS_EX_APPWINDOW); //This finds the dimensions of a window with a client area of our specified dimensions
//Note: Windows can be positioned anywhere, even outside the visible workspace,
//but their sizes are limited to the size of the workspace on the primary display.
unsigned WindowWidth = WindowRect.right-WindowRect.left, WindowHeight = WindowRect.bottom-WindowRect.top;
RECT WorkspaceRect;
SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkspaceRect, 0);
hWnd = CreateWindowEx(WS_EX_APPWINDOW, "TSO_NIOTSO", "The Sims Online",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
((WorkspaceRect.right-WorkspaceRect.left - WindowWidth)>>1) + WorkspaceRect.left,
((WorkspaceRect.bottom-WorkspaceRect.top - WindowHeight)>>1) + WorkspaceRect.top,
WindowWidth, WindowHeight, 0, 0, hInst, NULL);
}
if(hWnd == NULL){
MessageBox(NULL, "Failed to create the window.", NULL, MB_OK | MB_ICONERROR);
return ERROR_WINDOW_CREATE;
}
return 0;
}
}

25
Client/Window/Window.hpp Normal file
View file

@ -0,0 +1,25 @@
/*
Niotso - Copyright (C) 2012 Fatbag <X-Fi6@phppoll.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Window {
int Initialize();
void Shutdown();
extern unsigned Width, Height;
extern bool Fullscreen;
extern HWND hWnd;
}