Skip to content
Snippets Groups Projects
win_main.c 16.3 KiB
Newer Older
Alam Ed Arias's avatar
Alam Ed Arias committed
// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// Copyright (C) 1998-2000 by DooM Legacy Team.
//
// 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 2
// 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.
//-----------------------------------------------------------------------------
/// \file
/// \brief Win32 WinMain Entry Point
///
///	Win32 Sonic Robo Blast 2
///
///	NOTE:
///		To compile WINDOWS SRB2 version : define a '_WINDOWS' symbol.
///		to do this go to Project/Settings/ menu, click C/C++ tab, in
///		'Preprocessor definitions:' add '_WINDOWS'

#include "../doomdef.h"
#include <stdio.h>

#ifdef _WINDOWS

#include "../doomstat.h"  // netgame
#include "resource.h"

#include "../m_argv.h"
#include "../d_main.h"
#include "../i_system.h"

#include "../keys.h"    //hack quick test

#include "../console.h"

#include "fabdxlib.h"
#include "win_main.h"
#include "win_dbg.h"
#include "../i_sound.h" // midi pause/unpause
#include "../g_input.h" // KEY_MOUSEWHEELxxx
#include "../screen.h" // for BASEVID*

// MSWheel support for Win95/NT3.51
#include <zmouse.h>

#ifndef WM_XBUTTONDOWN
#define WM_XBUTTONDOWN 523
#endif
#ifndef WM_XBUTTONUP
#define WM_XBUTTONUP 524
#endif
#ifndef MK_XBUTTON1
#define MK_XBUTTON1 32
#endif
#ifndef MK_XBUTTON2
#define MK_XBUTTON2 64
#endif

typedef BOOL (WINAPI *p_IsDebuggerPresent)(VOID);

HWND hWndMain = NULL;
static HCURSOR windowCursor = NULL; // main window cursor

static LPCSTR wClassName = "SRB2WC";

INT appActive = false; // app window is active
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef LOGMESSAGES
FILE *logstream;
#endif

BOOL nodinput = FALSE;

static LRESULT CALLBACK MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	event_t ev; //Doom input event
	int mouse_keys;

	// judgecutor:
	// Response MSH Mouse Wheel event

	if (message == MSHWheelMessage)
	{
		message = WM_MOUSEWHEEL;
		wParam <<= 16;
Alam Ed Arias's avatar
Alam Ed Arias committed
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
	}

	//I_OutputMsg("MainWndproc: %p,%i,%i,%i",hWnd, message, wParam, (UINT)lParam);

	switch (message)
	{
		case WM_CREATE:
			nodinput = M_CheckParm("-nodinput");
			if (!nodinput && !LoadDirectInput())
				nodinput = true;
			break;

		case WM_ACTIVATEAPP:           // Handle task switching
			appActive = (int)wParam;

			//coming back from alt-tab?  reset the palette.
			if (appActive)
				vid.recalc = true;

			// pause music when alt-tab
			if (appActive  && !paused)
				I_ResumeSong(0);
			else if (!paused)
				I_PauseSong(0);
			{
				HANDLE ci = GetStdHandle(STD_INPUT_HANDLE);
				DWORD mode;
				if (ci != INVALID_HANDLE_VALUE && GetFileType(ci) == FILE_TYPE_CHAR && GetConsoleMode(ci, &mode))
					appActive = true;
			}
			InvalidateRect (hWnd, NULL, TRUE);
			break;

		case WM_PAINT:
			if (!appActive && !bAppFullScreen && !netgame)
				// app becomes inactive (if windowed)
			{
				// Paint "Game Paused" in the middle of the screen
				PAINTSTRUCT ps;
				RECT        rect;
				HDC hdc = BeginPaint (hWnd, &ps);
				GetClientRect (hWnd, &rect);
				DrawText (hdc, TEXT("Game Paused"), -1, &rect,
					DT_SINGLELINE | DT_CENTER | DT_VCENTER);
				EndPaint (hWnd, &ps);
				return 0;
			}

			break;

		case WM_QUERYNEWPALETTE:
			RestoreDDPalette();
			return TRUE;

		case WM_PALETTECHANGED:
			if((HWND)wParam != hWnd)
				RestoreDDPalette();
			break;

		//case WM_RBUTTONDOWN:
		//case WM_LBUTTONDOWN:

		case WM_MOVE:
			if (bAppFullScreen)
			{
				SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
				return 0;
			}
			else
			{
				windowPosX = (SHORT) LOWORD(lParam);    // horizontal position
				windowPosY = (SHORT) HIWORD(lParam);    // vertical position
				break;
			}
			break;

			// This is where switching windowed/fullscreen is handled. DirectDraw
			// objects must be destroyed, recreated, and artwork reloaded.

		case WM_DISPLAYCHANGE:
		case WM_SIZE:
			break;

		case WM_SETCURSOR:
			if (bAppFullScreen)
				SetCursor(NULL);
			else
				SetCursor(windowCursor);
			return TRUE;

		case WM_KEYUP:
			ev.type = ev_keyup;
			goto handleKeyDoom;
			break;

		case WM_KEYDOWN:
			ev.type = ev_keydown;

	handleKeyDoom:
			ev.data1 = 0;
			if (wParam == VK_PAUSE)
			// intercept PAUSE key
			{
				ev.data1 = KEY_PAUSE;
			}
			else if (!keyboard_started)
			// post some keys during the game startup
			// (allow escaping from network synchronization, or pressing enter after
			//  an error message in the console)
			{
				switch (wParam)
				{
					case VK_ESCAPE: ev.data1 = KEY_ESCAPE;  break;
					case VK_RETURN: ev.data1 = KEY_ENTER;   break;
					case VK_SHIFT:  ev.data1 = KEY_LSHIFT;  break;
					default: ev.data1 = MapVirtualKey((DWORD)wParam,2); // convert in to char
				}
			}

			if (ev.data1)
				D_PostEvent (&ev);

			return 0;
			break;

		// judgecutor:
		// Handle mouse events
		case WM_LBUTTONDOWN:
		case WM_LBUTTONUP:
		case WM_RBUTTONDOWN:
		case WM_RBUTTONUP:
		case WM_MBUTTONDOWN:
		case WM_MBUTTONUP:
		case WM_MOUSEMOVE:
			if (nodinput)
			{
				mouse_keys = 0;
				if (wParam & MK_LBUTTON)
					mouse_keys |= 1;
				if (wParam & MK_RBUTTON)
					mouse_keys |= 2;
				if (wParam & MK_MBUTTON)
					mouse_keys |= 4;
				I_GetSysMouseEvents(mouse_keys);
			}
			break;

		case WM_XBUTTONUP:
			if (nodinput)
			{
				ev.type = ev_keyup;
				ev.data1 = KEY_MOUSE1 + 3 + HIWORD(wParam);
				D_PostEvent(&ev);
				return TRUE;
			}

		case WM_XBUTTONDOWN:
			if (nodinput)
			{
				ev.type = ev_keydown;
				ev.data1 = KEY_MOUSE1 + 3 + HIWORD(wParam);
				D_PostEvent(&ev);
				return TRUE;
			}

		case WM_MOUSEWHEEL:
			//I_OutputMsg("MW_WHEEL dispatched.\n");
			ev.type = ev_keydown;
			if ((INT16)HIWORD(wParam) > 0)
				ev.data1 = KEY_MOUSEWHEELUP;
			else
				ev.data1 = KEY_MOUSEWHEELDOWN;
			D_PostEvent(&ev);
			break;

		case WM_SETTEXT:
			COM_BufAddText((LPCSTR)lParam);
			return TRUE;
			break;

		case WM_CLOSE:
			PostQuitMessage(0);         //to quit while in-game
			ev.data1 = KEY_ESCAPE;      //to exit network synchronization
			ev.type = ev_keydown;
			D_PostEvent (&ev);
			return 0;
		case WM_DESTROY:
			//faB: main app loop will exit the loop and proceed with I_Quit()
			PostQuitMessage(0);
			break;

		case WM_SYSCOMMAND:
			// Don't allow the keyboard to activate the menu.
			if(wParam == SC_KEYMENU)
				return 0;

			break;

		default:
			break;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}

static inline VOID OpenTextConsole(VOID)
{
	HANDLE ci, co;
	BOOL console;

#ifdef _DEBUG
	console = M_CheckParm("-noconsole") == 0;
#else
	console = M_CheckParm("-console") != 0;
#endif

	dedicated = M_CheckParm("-dedicated") != 0;

	if (M_CheckParm("-detachconsole"))
	{
		if (FreeConsole())
		{
			I_OutputMsg("Detatched console.\n");
			console = TRUE; //lets get back a console
		}
		else
		{
			I_OutputMsg("No console to detatch.\n");
			I_ShowLastError(FALSE);
		}
	}

	if (dedicated || console)
	{
		if (AllocConsole()) //Let get the real console HANDLEs, because Mingw's Bash is bad!
		{
			SetConsoleTitleA("SRB2 Console");
			CONS_Printf(M_GetText("Hello, it's me, SRB2's Console Window\n"));
		}
		else
		{
			I_OutputMsg("We have a console already.\n");
			I_ShowLastError(FALSE);
			return;
		}
	}
	else
		return;

	ci = CreateFile(TEXT("CONIN$") ,               GENERIC_READ, FILE_SHARE_READ,  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (ci != INVALID_HANDLE_VALUE)
	{
		HANDLE sih = GetStdHandle(STD_INPUT_HANDLE);
		if (sih != ci)
		{
			I_OutputMsg("Old STD_INPUT_HANDLE: %p\nNew STD_INPUT_HANDLE: %p\n", sih, ci);
			SetStdHandle(STD_INPUT_HANDLE,ci);
		}
		else
			I_OutputMsg("STD_INPUT_HANDLE already set at %p\n", ci);

		if (GetFileType(ci) == FILE_TYPE_CHAR)
		{
#if 0
			const DWORD CM = ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT; //default mode but no ENABLE_MOUSE_INPUT
			if (SetConsoleMode(ci,CM))
			{
				I_OutputMsg("Disabled mouse input on the console\n");
			}
			else
			{
				I_OutputMsg("Could not disable mouse input on the console\n");
				I_ShowLastError(FALSE);
			}
#endif
		}
		else
			I_OutputMsg("Handle CONIN$ in not a Console HANDLE\n");
	}
	else
	{
		I_OutputMsg("Could not get a CONIN$ HANDLE\n");
		I_ShowLastError(FALSE);
	}

	co = CreateFile(TEXT("CONOUT$"), GENERIC_WRITE|GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (co != INVALID_HANDLE_VALUE)
	{
		HANDLE soh = GetStdHandle(STD_OUTPUT_HANDLE);
		HANDLE seh = GetStdHandle(STD_ERROR_HANDLE);
		if (soh != co)
		{
			I_OutputMsg("Old STD_OUTPUT_HANDLE: %p\nNew STD_OUTPUT_HANDLE: %p\n", soh, co);
			SetStdHandle(STD_OUTPUT_HANDLE,co);
		}
		else
			I_OutputMsg("STD_OUTPUT_HANDLE already set at %p\n", co);
		if (seh != co)
		{
			I_OutputMsg("Old STD_ERROR_HANDLE: %p\nNew STD_ERROR_HANDLE: %p\n", seh, co);
			SetStdHandle(STD_ERROR_HANDLE,co);
		}
		else
			I_OutputMsg("STD_ERROR_HANDLE already set at %p\n", co);
	}
	else
		I_OutputMsg("Could not get a CONOUT$ HANDLE\n");
}


//
// Do that Windows initialization stuff...
//
static HWND OpenMainWindow (HINSTANCE hInstance, LPSTR wTitle)
{
	const LONG	styles = WS_CAPTION|WS_POPUP|WS_SYSMENU, exstyles = 0;
	HWND        hWnd;
	WNDCLASSEXA wc;
	RECT		bounds;

	// Set up and register window class
	ZeroMemory(&wc, sizeof(wc));
	wc.cbSize        = sizeof(wc);
	wc.style         = CS_HREDRAW | CS_VREDRAW /*| CS_DBLCLKS*/;
	wc.lpfnWndProc   = MainWndproc;
	wc.hInstance     = hInstance;
	wc.hIcon         = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DLICON1));
	windowCursor     = LoadCursor(NULL, IDC_WAIT); //LoadCursor(hInstance, MAKEINTRESOURCE(IDC_DLCURSOR1));
	wc.hCursor       = windowCursor;
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszClassName = wClassName;

	if (!RegisterClassExA(&wc))
	{
		I_OutputMsg("Error doing RegisterClassExA\n");
		I_ShowLastError(TRUE);
		return INVALID_HANDLE_VALUE;
	}

	// Create a window
	// CreateWindowEx - seems to create just the interior, not the borders

	bounds.left = 0;
	bounds.right = dedicated ? 0 : specialmodes[0].width;
	bounds.top = 0;
	bounds.bottom = dedicated ? 0 : specialmodes[0].height;

	AdjustWindowRectEx(&bounds, styles, FALSE, exstyles);

	hWnd = CreateWindowExA(
	       exstyles,                                 //ExStyle
	       wClassName,                        //Classname
	       wTitle,                            //Windowname
	       styles,    //dwStyle       //WS_VISIBLE|WS_POPUP for bAppFullScreen
	       0,
	       0,
	       bounds.right - bounds.left,        //GetSystemMetrics(SM_CXSCREEN),
	       bounds.bottom - bounds.top,        //GetSystemMetrics(SM_CYSCREEN),
	       NULL,                              //hWnd Parent
	       NULL,                              //hMenu Menu
	       hInstance,
	       NULL);

	if (hWnd == INVALID_HANDLE_VALUE)
	{
		I_OutputMsg("Error doing CreateWindowExA\n");
		I_ShowLastError(TRUE);
	}

	return hWnd;
}


static inline BOOL tlErrorMessage(const TCHAR *err)
{
	/* make the cursor visible */
	SetCursor(LoadCursor(NULL, IDC_ARROW));

	//
	// warn user if there is one
	//
	printf("Error %Ts..\n", err);
Alam Ed Arias's avatar
Alam Ed Arias committed
	fflush(stdout);

	MessageBox(hWndMain, err, TEXT("ERROR"), MB_OK);
	return FALSE;
}


// ------------------
// Command line stuff
// ------------------
#define         MAXCMDLINEARGS          64
static  char *  myWargv[MAXCMDLINEARGS+1];
static  char    myCmdline[512];

static VOID     GetArgcArgv (LPSTR cmdline)
{
	LPSTR   tokenstr;
	size_t  i = 0, len;
	char    cSep = ' ';
	BOOL    bCvar = FALSE, prevCvar = FALSE;

	// split arguments of command line into argv
	strlcpy (myCmdline, cmdline, sizeof(myCmdline));      // in case window's cmdline is in protected memory..for strtok
	len = strlen (myCmdline);

	myargc = 0;
	while (myargc < MAXCMDLINEARGS)
	{
		// get token
		while (myCmdline[i] == cSep)
			i++;
		if (i >= len)
			break;
		tokenstr = myCmdline + i;
		if (myCmdline[i] == '"')
		{
			cSep = '"';
			i++;
			if (!prevCvar)    //cvar leave the "" in
				tokenstr++;
		}
		else
			cSep = ' ';

		//cvar
		if (myCmdline[i] == '+' && cSep == ' ')   //a + begins a cvarname, but not after quotes
			bCvar = TRUE;
		else
			bCvar = FALSE;

		while (myCmdline[i] &&
				myCmdline[i] != cSep)
			i++;

		if (myCmdline[i] == '"')
		{
			cSep = ' ';
			if (prevCvar)
				i++; // get ending " quote in arg
		}

		prevCvar = bCvar;

		if (myCmdline + i > tokenstr)
		{
			myWargv[myargc++] = tokenstr;
		}

		if (!myCmdline[i] || i >= len)
			break;

		myCmdline[i++] = '\0';
	}
	myWargv[myargc] = NULL;

	// m_argv.c uses myargv[], we used myWargv because we fill the arguments ourselves
	// and myargv is just a pointer, so we set it to point myWargv
	myargv = myWargv;
}

static inline VOID MakeCodeWritable(VOID)
{
#ifdef USEASM // Disable write-protection of code segment
	DWORD OldRights;
	const DWORD NewRights = PAGE_EXECUTE_READWRITE;
	PBYTE pBaseOfImage = (PBYTE)GetModuleHandle(NULL);
	PIMAGE_DOS_HEADER dosH =(PIMAGE_DOS_HEADER)pBaseOfImage;
	PIMAGE_NT_HEADERS ntH = (PIMAGE_NT_HEADERS)(pBaseOfImage + dosH->e_lfanew);
	PIMAGE_OPTIONAL_HEADER oH = (PIMAGE_OPTIONAL_HEADER)
		((PBYTE)ntH + sizeof (IMAGE_NT_SIGNATURE) + sizeof (IMAGE_FILE_HEADER));
	LPVOID pA = pBaseOfImage+oH->BaseOfCode;
	SIZE_T pS = oH->SizeOfCode;
#if 1 // try to find the text section
	PIMAGE_SECTION_HEADER ntS = IMAGE_FIRST_SECTION (ntH);
	WORD s;
	for (s = 0; s < ntH->FileHeader.NumberOfSections; s++)
	{
		if (memcmp (ntS[s].Name, ".text\0\0", 8) == 0)
		{
			pA = pBaseOfImage+ntS[s].VirtualAddress;
			pS = ntS[s].Misc.VirtualSize;
			break;
		}
	}
#endif

	if (!VirtualProtect(pA,pS,NewRights,&OldRights))
		I_Error("Could not make code writable\n");
#endif
}

// -----------------------------------------------------------------------------
// HandledWinMain : called by exception handler
// -----------------------------------------------------------------------------
static int WINAPI HandledWinMain(HINSTANCE hInstance)
{
	LPSTR          args;

#ifdef LOGMESSAGES
	// DEBUG!!! - set logstream to NULL to disable debug log
	// Replace WIN32 filehandle with standard C calls, because WIN32 doesn't handle \n properly.
	logstream = fopen(va("%s"PATHSEP"%s", srb2home, "log.txt"), "wt");
#endif

	// fill myargc,myargv for m_argv.c retrieval of cmdline arguments
	args = GetCommandLineA();
	CONS_Printf("Command line arguments: '%s'\n", args);
	GetArgcArgv(args);
	// Create a text console window
	OpenTextConsole();

#ifdef _DEBUG
	{
		int i;
		CONS_Printf("Myargc: %d\n", myargc);
		for (i = 0; i < myargc; i++)
			CONS_Printf("myargv[%d] : '%s'\n", i, myargv[i]);
	}
#endif

	// open a dummy window, both OpenGL and DirectX need one.
	if ((hWndMain = OpenMainWindow(hInstance, va("SRB2 "VERSIONSTRING))) == INVALID_HANDLE_VALUE)
	{
		tlErrorMessage(TEXT("Couldn't open window"));
		return FALSE;
	}

	// currently starts DirectInput
	CONS_Printf("I_StartupSystem() ...\n");
	I_StartupSystem();
	MakeCodeWritable();

	// startup SRB2
	CONS_Printf("Setting up SRB2...\n");
	D_SRB2Main();
	CONS_Printf("Entering main game loop...\n");
	// never return
	D_SRB2Loop();

	// back to Windoze
	return 0;
}

// -----------------------------------------------------------------------------
// Exception handler calls WinMain for catching exceptions
// -----------------------------------------------------------------------------
int WINAPI WinMain (HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR     lpCmdLine,
                    int       nCmdShow)
{
	int Result = -1;

Alam Ed Arias's avatar
Alam Ed Arias committed
	// Win95 and NT <4 don't have this, so link at runtime.
	p_IsDebuggerPresent pfnIsDebuggerPresent = (p_IsDebuggerPresent)GetProcAddress(GetModuleHandleA("kernel32.dll"),"IsDebuggerPresent");
Alam Ed Arias's avatar
Alam Ed Arias committed

	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);
	UNREFERENCED_PARAMETER(nCmdShow);

Alam Ed Arias's avatar
Alam Ed Arias committed
#ifdef BUGTRAP
	// Try BugTrap first.
	if((!pfnIsDebuggerPresent || !pfnIsDebuggerPresent()) && InitBugTrap())
		Result = HandledWinMain(hInstance);
	else
	{
#endif
		// Try Dr MinGW's exception handler.
		if (!pfnIsDebuggerPresent || !pfnIsDebuggerPresent())
Alam Ed Arias's avatar
Alam Ed Arias committed
			LoadLibraryA("exchndl.dll");

		prevExceptionFilter = SetUnhandledExceptionFilter(RecordExceptionInfo);

		Result = HandledWinMain(hInstance);
#ifdef BUGTRAP
	}	// BT failure clause.

	// This is safe even if BT didn't start.
	ShutdownBugTrap();
#endif

	return Result;
}
#endif //_WINDOWS