1479 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			1479 lines
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | #include <kinc/graphics4/graphics.h>
 | ||
|  | 
 | ||
|  | #include <kinc/input/gamepad.h>
 | ||
|  | 
 | ||
|  | #include <kinc/backend/SystemMicrosoft.h>
 | ||
|  | #include <kinc/backend/Windows.h>
 | ||
|  | 
 | ||
|  | #include <kinc/display.h>
 | ||
|  | #include <kinc/input/keyboard.h>
 | ||
|  | #include <kinc/input/mouse.h>
 | ||
|  | #include <kinc/input/pen.h>
 | ||
|  | #include <kinc/input/surface.h>
 | ||
|  | #include <kinc/log.h>
 | ||
|  | #include <kinc/system.h>
 | ||
|  | #include <kinc/threads/thread.h>
 | ||
|  | #include <kinc/video.h>
 | ||
|  | #include <kinc/window.h>
 | ||
|  | 
 | ||
|  | #define DIRECTINPUT_VERSION 0x0800
 | ||
|  | #include <dinput.h>
 | ||
|  | #include <oleauto.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <wbemidl.h>
 | ||
|  | 
 | ||
|  | #include <XInput.h>
 | ||
|  | #include <dbghelp.h>
 | ||
|  | #include <shellapi.h>
 | ||
|  | #include <shlobj.h>
 | ||
|  | 
 | ||
|  | #if defined(KINC_VTUNE)
 | ||
|  | #include <ittnotify.h>
 | ||
|  | 
 | ||
|  | __itt_domain *kinc_itt_domain; | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #ifdef KINC_G4ONG5
 | ||
|  | #define Graphics Graphics5
 | ||
|  | #elif KINC_G4
 | ||
|  | #define Graphics Graphics4
 | ||
|  | #else
 | ||
|  | #define Graphics Graphics3
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; | ||
|  | __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | ||
|  | void kinc_internal_resize(int window, int width, int height); | ||
|  | 
 | ||
|  | typedef BOOL(WINAPI *GetPointerInfoType)(UINT32 pointerId, POINTER_INFO *pointerInfo); | ||
|  | static GetPointerInfoType MyGetPointerInfo = NULL; | ||
|  | typedef BOOL(WINAPI *GetPointerPenInfoType)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); | ||
|  | static GetPointerPenInfoType MyGetPointerPenInfo = NULL; | ||
|  | typedef BOOL(WINAPI *EnableNonClientDpiScalingType)(HWND hwnd); | ||
|  | static EnableNonClientDpiScalingType MyEnableNonClientDpiScaling = NULL; | ||
|  | 
 | ||
|  | #define MAX_TOUCH_POINTS 10
 | ||
|  | 
 | ||
|  | #define KINC_DINPUT_MAX_COUNT 8
 | ||
|  | 
 | ||
|  | struct touchpoint { | ||
|  | 	int sysID; | ||
|  | 	int x; | ||
|  | 	int y; | ||
|  | }; | ||
|  | 
 | ||
|  | static struct touchpoint touchPoints[MAX_TOUCH_POINTS]; | ||
|  | static int mouseX, mouseY; | ||
|  | static bool keyPressed[256]; | ||
|  | static int keyTranslated[256]; // http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
 | ||
|  | 
 | ||
|  | static int GetTouchIndex(int dwID) { | ||
|  | 	for (int i = 0; i < MAX_TOUCH_POINTS; i++) { | ||
|  | 		if (touchPoints[i].sysID == dwID) { | ||
|  | 			return i; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return -1; | ||
|  | } | ||
|  | 
 | ||
|  | static int GetAddTouchIndex(int dwID) { | ||
|  | 	for (int i = 0; i < MAX_TOUCH_POINTS; i++) { | ||
|  | 		if (touchPoints[i].sysID == dwID) { | ||
|  | 			return i; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	for (int i = 0; i < MAX_TOUCH_POINTS; i++) { | ||
|  | 		if (touchPoints[i].sysID == -1) { | ||
|  | 			touchPoints[i].sysID = dwID; | ||
|  | 			return i; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	return -1; | ||
|  | } | ||
|  | 
 | ||
|  | static void ReleaseTouchIndex(int dwID) { | ||
|  | 	for (int i = 0; i < MAX_TOUCH_POINTS; i++) { | ||
|  | 		if (touchPoints[i].sysID == dwID) { | ||
|  | 			touchPoints[i].sysID = -1; | ||
|  | 			touchPoints[i].x = -1; | ||
|  | 			touchPoints[i].y = -1; | ||
|  | 		} | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static void initKeyTranslation() { | ||
|  | 	for (int i = 0; i < 256; ++i) | ||
|  | 		keyTranslated[i] = KINC_KEY_UNKNOWN; | ||
|  | 
 | ||
|  | 	keyTranslated[VK_BACK] = KINC_KEY_BACKSPACE; | ||
|  | 	keyTranslated[VK_TAB] = KINC_KEY_TAB; | ||
|  | 	keyTranslated[VK_CLEAR] = KINC_KEY_CLEAR; | ||
|  | 	keyTranslated[VK_RETURN] = KINC_KEY_RETURN; | ||
|  | 	keyTranslated[VK_SHIFT] = KINC_KEY_SHIFT; | ||
|  | 	keyTranslated[VK_CONTROL] = KINC_KEY_CONTROL; | ||
|  | 	keyTranslated[VK_MENU] = KINC_KEY_ALT; | ||
|  | 	keyTranslated[VK_PAUSE] = KINC_KEY_PAUSE; | ||
|  | 	keyTranslated[VK_CAPITAL] = KINC_KEY_CAPS_LOCK; | ||
|  | 	keyTranslated[VK_KANA] = KINC_KEY_KANA; | ||
|  | 	// keyTranslated[VK_HANGUEL]
 | ||
|  | 	keyTranslated[VK_HANGUL] = KINC_KEY_HANGUL; | ||
|  | 	keyTranslated[VK_JUNJA] = KINC_KEY_JUNJA; | ||
|  | 	keyTranslated[VK_FINAL] = KINC_KEY_FINAL; | ||
|  | 	keyTranslated[VK_HANJA] = KINC_KEY_HANJA; | ||
|  | 	keyTranslated[VK_KANJI] = KINC_KEY_KANJI; | ||
|  | 	keyTranslated[VK_ESCAPE] = KINC_KEY_ESCAPE; | ||
|  | 	// keyTranslated[VK_CONVERT]
 | ||
|  | 	// keyTranslated[VK_NONCONVERT
 | ||
|  | 	// keyTranslated[VK_ACCEPT
 | ||
|  | 	// keyTranslated[VK_MODECHANGE
 | ||
|  | 	keyTranslated[VK_SPACE] = KINC_KEY_SPACE; | ||
|  | 	keyTranslated[VK_PRIOR] = KINC_KEY_PAGE_UP; | ||
|  | 	keyTranslated[VK_NEXT] = KINC_KEY_PAGE_DOWN; | ||
|  | 	keyTranslated[VK_END] = KINC_KEY_END; | ||
|  | 	keyTranslated[VK_HOME] = KINC_KEY_HOME; | ||
|  | 	keyTranslated[VK_LEFT] = KINC_KEY_LEFT; | ||
|  | 	keyTranslated[VK_UP] = KINC_KEY_UP; | ||
|  | 	keyTranslated[VK_RIGHT] = KINC_KEY_RIGHT; | ||
|  | 	keyTranslated[VK_DOWN] = KINC_KEY_DOWN; | ||
|  | 	// keyTranslated[VK_SELECT
 | ||
|  | 	keyTranslated[VK_PRINT] = KINC_KEY_PRINT; | ||
|  | 	// keyTranslated[VK_EXECUTE
 | ||
|  | 	// keyTranslated[VK_SNAPSHOT
 | ||
|  | 	keyTranslated[VK_INSERT] = KINC_KEY_INSERT; | ||
|  | 	keyTranslated[VK_DELETE] = KINC_KEY_DELETE; | ||
|  | 	keyTranslated[VK_HELP] = KINC_KEY_HELP; | ||
|  | 	keyTranslated[0x30] = KINC_KEY_0; | ||
|  | 	keyTranslated[0x31] = KINC_KEY_1; | ||
|  | 	keyTranslated[0x32] = KINC_KEY_2; | ||
|  | 	keyTranslated[0x33] = KINC_KEY_3; | ||
|  | 	keyTranslated[0x34] = KINC_KEY_4; | ||
|  | 	keyTranslated[0x35] = KINC_KEY_5; | ||
|  | 	keyTranslated[0x36] = KINC_KEY_6; | ||
|  | 	keyTranslated[0x37] = KINC_KEY_7; | ||
|  | 	keyTranslated[0x38] = KINC_KEY_8; | ||
|  | 	keyTranslated[0x39] = KINC_KEY_9; | ||
|  | 	keyTranslated[0x41] = KINC_KEY_A; | ||
|  | 	keyTranslated[0x42] = KINC_KEY_B; | ||
|  | 	keyTranslated[0x43] = KINC_KEY_C; | ||
|  | 	keyTranslated[0x44] = KINC_KEY_D; | ||
|  | 	keyTranslated[0x45] = KINC_KEY_E; | ||
|  | 	keyTranslated[0x46] = KINC_KEY_F; | ||
|  | 	keyTranslated[0x47] = KINC_KEY_G; | ||
|  | 	keyTranslated[0x48] = KINC_KEY_H; | ||
|  | 	keyTranslated[0x49] = KINC_KEY_I; | ||
|  | 	keyTranslated[0x4A] = KINC_KEY_J; | ||
|  | 	keyTranslated[0x4B] = KINC_KEY_K; | ||
|  | 	keyTranslated[0x4C] = KINC_KEY_L; | ||
|  | 	keyTranslated[0x4D] = KINC_KEY_M; | ||
|  | 	keyTranslated[0x4E] = KINC_KEY_N; | ||
|  | 	keyTranslated[0x4F] = KINC_KEY_O; | ||
|  | 	keyTranslated[0x50] = KINC_KEY_P; | ||
|  | 	keyTranslated[0x51] = KINC_KEY_Q; | ||
|  | 	keyTranslated[0x52] = KINC_KEY_R; | ||
|  | 	keyTranslated[0x53] = KINC_KEY_S; | ||
|  | 	keyTranslated[0x54] = KINC_KEY_T; | ||
|  | 	keyTranslated[0x55] = KINC_KEY_U; | ||
|  | 	keyTranslated[0x56] = KINC_KEY_V; | ||
|  | 	keyTranslated[0x57] = KINC_KEY_W; | ||
|  | 	keyTranslated[0x58] = KINC_KEY_X; | ||
|  | 	keyTranslated[0x59] = KINC_KEY_Y; | ||
|  | 	keyTranslated[0x5A] = KINC_KEY_Z; | ||
|  | 	keyTranslated[VK_LWIN] = KINC_KEY_WIN; | ||
|  | 	keyTranslated[VK_RWIN] = KINC_KEY_WIN; | ||
|  | 	keyTranslated[VK_APPS] = KINC_KEY_CONTEXT_MENU; | ||
|  | 	// keyTranslated[VK_SLEEP
 | ||
|  | 	keyTranslated[VK_NUMPAD0] = KINC_KEY_NUMPAD_0; | ||
|  | 	keyTranslated[VK_NUMPAD1] = KINC_KEY_NUMPAD_1; | ||
|  | 	keyTranslated[VK_NUMPAD2] = KINC_KEY_NUMPAD_2; | ||
|  | 	keyTranslated[VK_NUMPAD3] = KINC_KEY_NUMPAD_3; | ||
|  | 	keyTranslated[VK_NUMPAD4] = KINC_KEY_NUMPAD_4; | ||
|  | 	keyTranslated[VK_NUMPAD5] = KINC_KEY_NUMPAD_5; | ||
|  | 	keyTranslated[VK_NUMPAD6] = KINC_KEY_NUMPAD_6; | ||
|  | 	keyTranslated[VK_NUMPAD7] = KINC_KEY_NUMPAD_7; | ||
|  | 	keyTranslated[VK_NUMPAD8] = KINC_KEY_NUMPAD_8; | ||
|  | 	keyTranslated[VK_NUMPAD9] = KINC_KEY_NUMPAD_9; | ||
|  | 	keyTranslated[VK_MULTIPLY] = KINC_KEY_MULTIPLY; | ||
|  | 	keyTranslated[VK_ADD] = KINC_KEY_ADD; | ||
|  | 	// keyTranslated[VK_SEPARATOR
 | ||
|  | 	keyTranslated[VK_SUBTRACT] = KINC_KEY_SUBTRACT; | ||
|  | 	keyTranslated[VK_DECIMAL] = KINC_KEY_DECIMAL; | ||
|  | 	keyTranslated[VK_DIVIDE] = KINC_KEY_DIVIDE; | ||
|  | 	keyTranslated[VK_F1] = KINC_KEY_F1; | ||
|  | 	keyTranslated[VK_F2] = KINC_KEY_F2; | ||
|  | 	keyTranslated[VK_F3] = KINC_KEY_F3; | ||
|  | 	keyTranslated[VK_F4] = KINC_KEY_F4; | ||
|  | 	keyTranslated[VK_F5] = KINC_KEY_F5; | ||
|  | 	keyTranslated[VK_F6] = KINC_KEY_F6; | ||
|  | 	keyTranslated[VK_F7] = KINC_KEY_F7; | ||
|  | 	keyTranslated[VK_F8] = KINC_KEY_F8; | ||
|  | 	keyTranslated[VK_F9] = KINC_KEY_F9; | ||
|  | 	keyTranslated[VK_F10] = KINC_KEY_F10; | ||
|  | 	keyTranslated[VK_F11] = KINC_KEY_F11; | ||
|  | 	keyTranslated[VK_F12] = KINC_KEY_F12; | ||
|  | 	keyTranslated[VK_F13] = KINC_KEY_F13; | ||
|  | 	keyTranslated[VK_F14] = KINC_KEY_F14; | ||
|  | 	keyTranslated[VK_F15] = KINC_KEY_F15; | ||
|  | 	keyTranslated[VK_F16] = KINC_KEY_F16; | ||
|  | 	keyTranslated[VK_F17] = KINC_KEY_F17; | ||
|  | 	keyTranslated[VK_F18] = KINC_KEY_F18; | ||
|  | 	keyTranslated[VK_F19] = KINC_KEY_F19; | ||
|  | 	keyTranslated[VK_F20] = KINC_KEY_F20; | ||
|  | 	keyTranslated[VK_F21] = KINC_KEY_F21; | ||
|  | 	keyTranslated[VK_F22] = KINC_KEY_F22; | ||
|  | 	keyTranslated[VK_F23] = KINC_KEY_F23; | ||
|  | 	keyTranslated[VK_F24] = KINC_KEY_F24; | ||
|  | 	keyTranslated[VK_NUMLOCK] = KINC_KEY_NUM_LOCK; | ||
|  | 	keyTranslated[VK_SCROLL] = KINC_KEY_SCROLL_LOCK; | ||
|  | 	// 0x92-96 //OEM specific
 | ||
|  | 	keyTranslated[VK_LSHIFT] = KINC_KEY_SHIFT; | ||
|  | 	keyTranslated[VK_RSHIFT] = KINC_KEY_SHIFT; | ||
|  | 	keyTranslated[VK_LCONTROL] = KINC_KEY_CONTROL; | ||
|  | 	keyTranslated[VK_RCONTROL] = KINC_KEY_CONTROL; | ||
|  | 	// keyTranslated[VK_LMENU
 | ||
|  | 	// keyTranslated[VK_RMENU
 | ||
|  | 	// keyTranslated[VK_BROWSER_BACK
 | ||
|  | 	// keyTranslated[VK_BROWSER_FORWARD
 | ||
|  | 	// keyTranslated[VK_BROWSER_REFRESH
 | ||
|  | 	// keyTranslated[VK_BROWSER_STOP
 | ||
|  | 	// keyTranslated[VK_BROWSER_SEARCH
 | ||
|  | 	// keyTranslated[VK_BROWSER_FAVORITES
 | ||
|  | 	// keyTranslated[VK_BROWSER_HOME
 | ||
|  | 	// keyTranslated[VK_VOLUME_MUTE
 | ||
|  | 	// keyTranslated[VK_VOLUME_DOWN
 | ||
|  | 	// keyTranslated[VK_VOLUME_UP
 | ||
|  | 	// keyTranslated[VK_MEDIA_NEXT_TRACK
 | ||
|  | 	// keyTranslated[VK_MEDIA_PREV_TRACK
 | ||
|  | 	// keyTranslated[VK_MEDIA_STOP
 | ||
|  | 	// keyTranslated[VK_MEDIA_PLAY_PAUSE
 | ||
|  | 	// keyTranslated[VK_LAUNCH_MAIL
 | ||
|  | 	// keyTranslated[VK_LAUNCH_MEDIA_SELECT
 | ||
|  | 	// keyTranslated[VK_LAUNCH_APP1
 | ||
|  | 	// keyTranslated[VK_LAUNCH_APP2
 | ||
|  | 	keyTranslated[VK_OEM_1] = KINC_KEY_SEMICOLON; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key
 | ||
|  | 	keyTranslated[VK_OEM_PLUS] = KINC_KEY_PLUS; | ||
|  | 	keyTranslated[VK_OEM_COMMA] = KINC_KEY_COMMA; | ||
|  | 	keyTranslated[VK_OEM_MINUS] = KINC_KEY_HYPHEN_MINUS; | ||
|  | 	keyTranslated[VK_OEM_PERIOD] = KINC_KEY_PERIOD; | ||
|  | 	keyTranslated[VK_OEM_2] = KINC_KEY_SLASH;         // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key
 | ||
|  | 	keyTranslated[VK_OEM_3] = KINC_KEY_BACK_QUOTE;    // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key
 | ||
|  | 	keyTranslated[VK_OEM_4] = KINC_KEY_OPEN_BRACKET;  // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key
 | ||
|  | 	keyTranslated[VK_OEM_5] = KINC_KEY_BACK_SLASH;    // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key
 | ||
|  | 	keyTranslated[VK_OEM_6] = KINC_KEY_CLOSE_BRACKET; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key
 | ||
|  | 	keyTranslated[VK_OEM_7] = KINC_KEY_QUOTE;         // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the
 | ||
|  | 	                                          // 'single-quote/double-quote' key keyTranslated[VK_OEM_8 //Used for miscellaneous characters; it can vary by
 | ||
|  | 	                                          // keyboard. keyTranslated[0xE1 //OEM specific keyTranslated[VK_OEM_102 //Either the angle bracket key or the
 | ||
|  | 	                                          // backslash key on the RT 102-key keyboard 0xE3-E4 //OEM specific keyTranslated[VK_PROCESSKEY 0xE6 //OEM specific
 | ||
|  | 	// keyTranslated[VK_PACKET //Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value
 | ||
|  | 	// used for non-keyboard input methods.
 | ||
|  | 	// 0xE9-F5 //OEM specific
 | ||
|  | 	// keyTranslated[VK_ATTN
 | ||
|  | 	// keyTranslated[VK_CRSEL
 | ||
|  | 	// keyTranslated[VK_EXSEL
 | ||
|  | 	// keyTranslated[VK_EREOF
 | ||
|  | 	// keyTranslated[VK_PLAY
 | ||
|  | 	// keyTranslated[VK_ZOOM
 | ||
|  | 	// keyTranslated[VK_NONAME
 | ||
|  | 	// keyTranslated[VK_PA1
 | ||
|  | 	// keyTranslated[PA1 key
 | ||
|  | 	// keyTranslated[VK_OEM_CLEAR
 | ||
|  | } | ||
|  | 
 | ||
|  | static bool detectGamepad = true; | ||
|  | static bool gamepadFound = false; | ||
|  | static unsigned r = 0; | ||
|  | 
 | ||
|  | static wchar_t toUnicode(WPARAM wParam, LPARAM lParam) { | ||
|  | 	wchar_t buffer[11]; | ||
|  | 	BYTE state[256]; | ||
|  | 	GetKeyboardState(state); | ||
|  | 	ToUnicode((UINT)wParam, (lParam >> 8) & 0xFFFFFF00, state, buffer, 10, 0); | ||
|  | 	return buffer[0]; | ||
|  | } | ||
|  | 
 | ||
|  | #if !defined(KINC_DIRECT3D9) && !defined(KINC_DIRECT3D11) && !defined(KINC_DIRECT3D12)
 | ||
|  | #define HANDLE_ALT_ENTER
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | static bool cursors_initialized = false; | ||
|  | static int cursor = 0; | ||
|  | #define NUM_CURSORS 14
 | ||
|  | static HCURSOR cursors[NUM_CURSORS]; | ||
|  | 
 | ||
|  | void kinc_mouse_set_cursor(int set_cursor) { | ||
|  | 	cursor = set_cursor >= NUM_CURSORS ? 0 : set_cursor; | ||
|  | 	if (cursors_initialized) { | ||
|  | 		SetCursor(cursors[cursor]); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | LRESULT WINAPI KoreWindowsMessageProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { | ||
|  | 	int windowId; | ||
|  | 	DWORD pointerId; | ||
|  | 	POINTER_INFO pointerInfo = {0}; | ||
|  | 	POINTER_PEN_INFO penInfo = {0}; | ||
|  | 	static bool controlDown = false; | ||
|  | #ifdef HANDLE_ALT_ENTER
 | ||
|  | 	static bool altDown = false; | ||
|  | #endif
 | ||
|  | 	static int last_window_width = -1; | ||
|  | 	static int last_window_height = -1; | ||
|  | 	static int last_window_x = INT_MIN; | ||
|  | 	static int last_window_y = INT_MIN; | ||
|  | 
 | ||
|  | 	switch (msg) { | ||
|  | 	case WM_NCCREATE: | ||
|  | 		if (MyEnableNonClientDpiScaling != NULL) { | ||
|  | 			MyEnableNonClientDpiScaling(hWnd); | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	case WM_DPICHANGED: { | ||
|  | 		int window = kinc_windows_window_index_from_hwnd(hWnd); | ||
|  | 		if (window >= 0) { | ||
|  | 			kinc_internal_call_ppi_changed_callback(window, LOWORD(wParam)); | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	case WM_MOVE: | ||
|  | 	case WM_MOVING: | ||
|  | 	case WM_SIZING: | ||
|  | 		// Scheduler::breakTime();
 | ||
|  | 		break; | ||
|  | 	case WM_SIZE: { | ||
|  | 		int window = kinc_windows_window_index_from_hwnd(hWnd); | ||
|  | 		if (window >= 0) { | ||
|  | 			int width = LOWORD(lParam); | ||
|  | 			int height = HIWORD(lParam); | ||
|  | 			kinc_internal_resize(window, width, height); | ||
|  | 			kinc_internal_call_resize_callback(window, width, height); | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	case WM_CLOSE: { | ||
|  | 		int window_index = kinc_windows_window_index_from_hwnd(hWnd); | ||
|  | 		if (kinc_internal_call_close_callback(window_index)) { | ||
|  | 			kinc_window_destroy(window_index); | ||
|  | 			if (kinc_count_windows() <= 0) { | ||
|  | 				kinc_stop(); | ||
|  | 				return 0; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		return 0; | ||
|  | 	} | ||
|  | 	case WM_ERASEBKGND: | ||
|  | 		return 1; | ||
|  | 	case WM_ACTIVATE: | ||
|  | 		if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { | ||
|  | 			kinc_internal_mouse_window_activated(kinc_windows_window_index_from_hwnd(hWnd)); | ||
|  | 			kinc_internal_foreground_callback(); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			kinc_internal_mouse_window_deactivated(kinc_windows_window_index_from_hwnd(hWnd)); | ||
|  | 			kinc_internal_background_callback(); | ||
|  | #ifdef HANDLE_ALT_ENTER
 | ||
|  | 			altDown = false; | ||
|  | #endif
 | ||
|  | 		} | ||
|  | 		RegisterTouchWindow(hWnd, 0); | ||
|  | 		break; | ||
|  | 	case WM_MOUSELEAVE: | ||
|  | 		windowId = kinc_windows_window_index_from_hwnd(hWnd); | ||
|  | 		//**windows[windowId]->isMouseInside = false;
 | ||
|  | 		kinc_internal_mouse_trigger_leave_window(windowId); | ||
|  | 		break; | ||
|  | 	case WM_MOUSEMOVE: | ||
|  | 		windowId = kinc_windows_window_index_from_hwnd(hWnd); | ||
|  | 		/*if (!windows[windowId]->isMouseInside) {
 | ||
|  | 		    windows[windowId]->isMouseInside = true; | ||
|  | 		    TRACKMOUSEEVENT tme; | ||
|  | 		    tme.cbSize = sizeof(TRACKMOUSEEVENT); | ||
|  | 		    tme.dwFlags = TME_LEAVE; | ||
|  | 		    tme.hwndTrack = hWnd; | ||
|  | 		    TrackMouseEvent(&tme); | ||
|  | 		}*/ | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_move(windowId, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_CREATE: | ||
|  | 		cursors[0] = LoadCursor(0, IDC_ARROW); | ||
|  | 		cursors[1] = LoadCursor(0, IDC_HAND); | ||
|  | 		cursors[2] = LoadCursor(0, IDC_IBEAM); | ||
|  | 		cursors[3] = LoadCursor(0, IDC_SIZEWE); | ||
|  | 		cursors[4] = LoadCursor(0, IDC_SIZENS); | ||
|  | 		cursors[5] = LoadCursor(0, IDC_SIZENESW); | ||
|  | 		cursors[6] = LoadCursor(0, IDC_SIZENWSE); | ||
|  | 		cursors[7] = LoadCursor(0, IDC_SIZENWSE); | ||
|  | 		cursors[8] = LoadCursor(0, IDC_SIZENESW); | ||
|  | 		cursors[9] = LoadCursor(0, IDC_SIZEALL); | ||
|  | 		cursors[10] = LoadCursor(0, IDC_SIZEALL); | ||
|  | 		cursors[11] = LoadCursor(0, IDC_NO); | ||
|  | 		cursors[12] = LoadCursor(0, IDC_WAIT); | ||
|  | 		cursors[13] = LoadCursor(0, IDC_CROSS); | ||
|  | 		cursors_initialized = true; | ||
|  | 		if (cursor != 0) { | ||
|  | 			SetCursor(cursors[cursor]); | ||
|  | 		} | ||
|  | 		return TRUE; | ||
|  | 	case WM_SETCURSOR: | ||
|  | 		if (LOWORD(lParam) == HTCLIENT) { | ||
|  | 			SetCursor(cursors[cursor]); | ||
|  | 			return TRUE; | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	case WM_LBUTTONDOWN: | ||
|  | 		if (!kinc_mouse_is_locked()) | ||
|  | 			SetCapture(hWnd); | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), 0, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_LBUTTONUP: | ||
|  | 		if (!kinc_mouse_is_locked()) | ||
|  | 			ReleaseCapture(); | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), 0, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_RBUTTONDOWN: | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), 1, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_RBUTTONUP: | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), 1, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_MBUTTONDOWN: | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), 2, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_MBUTTONUP: | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), 2, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_XBUTTONDOWN: | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), HIWORD(wParam) + 2, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_XBUTTONUP: | ||
|  | 		mouseX = GET_X_LPARAM(lParam); | ||
|  | 		mouseY = GET_Y_LPARAM(lParam); | ||
|  | 		kinc_internal_mouse_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), HIWORD(wParam) + 2, mouseX, mouseY); | ||
|  | 		break; | ||
|  | 	case WM_MOUSEWHEEL: | ||
|  | 		kinc_internal_mouse_trigger_scroll(kinc_windows_window_index_from_hwnd(hWnd), GET_WHEEL_DELTA_WPARAM(wParam) / -120); | ||
|  | 		break; | ||
|  | 	case WM_POINTERDOWN: | ||
|  | 		pointerId = GET_POINTERID_WPARAM(wParam); | ||
|  | 		MyGetPointerInfo(pointerId, &pointerInfo); | ||
|  | 		if (pointerInfo.pointerType == PT_PEN) { | ||
|  | 			MyGetPointerPenInfo(pointerId, &penInfo); | ||
|  | 			ScreenToClient(hWnd, &pointerInfo.ptPixelLocation); | ||
|  | 			kinc_internal_pen_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, | ||
|  | 			                                penInfo.pressure / 1024.0f); | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	case WM_POINTERUP: | ||
|  | 		pointerId = GET_POINTERID_WPARAM(wParam); | ||
|  | 		MyGetPointerInfo(pointerId, &pointerInfo); | ||
|  | 		if (pointerInfo.pointerType == PT_PEN) { | ||
|  | 			MyGetPointerPenInfo(pointerId, &penInfo); | ||
|  | 			ScreenToClient(hWnd, &pointerInfo.ptPixelLocation); | ||
|  | 			kinc_internal_pen_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, | ||
|  | 			                                  penInfo.pressure / 1024.0f); | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	case WM_POINTERUPDATE: | ||
|  | 		pointerId = GET_POINTERID_WPARAM(wParam); | ||
|  | 		MyGetPointerInfo(pointerId, &pointerInfo); | ||
|  | 		if (pointerInfo.pointerType == PT_PEN) { | ||
|  | 			MyGetPointerPenInfo(pointerId, &penInfo); | ||
|  | 			ScreenToClient(hWnd, &pointerInfo.ptPixelLocation); | ||
|  | 			kinc_internal_pen_trigger_move(kinc_windows_window_index_from_hwnd(hWnd), pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, | ||
|  | 			                               penInfo.pressure / 1024.0f); | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	case WM_TOUCH: { | ||
|  | 		BOOL bHandled = FALSE; | ||
|  | 		UINT cInputs = LOWORD(wParam); | ||
|  | 		PTOUCHINPUT pInputs = _alloca(cInputs * sizeof(TOUCHINPUT)); | ||
|  | 		POINT ptInput; | ||
|  | 		int tindex; | ||
|  | 		if (pInputs) { | ||
|  | 			if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) { | ||
|  | 				for (int i = 0; i < (int)cInputs; i++) { | ||
|  | 					TOUCHINPUT ti = pInputs[i]; | ||
|  | 					if (ti.dwID != 0) { | ||
|  | 						ptInput.x = TOUCH_COORD_TO_PIXEL(ti.x); | ||
|  | 						ptInput.y = TOUCH_COORD_TO_PIXEL(ti.y); | ||
|  | 						ScreenToClient(hWnd, &ptInput); | ||
|  | 
 | ||
|  | 						if (ti.dwFlags & TOUCHEVENTF_UP) { | ||
|  | 							tindex = GetTouchIndex(ti.dwID); | ||
|  | 							ReleaseTouchIndex(ti.dwID); | ||
|  | 							kinc_internal_surface_trigger_touch_end(tindex, ptInput.x, ptInput.y); | ||
|  | 						} | ||
|  | 						else { | ||
|  | 							bool touchExisits = GetTouchIndex(ti.dwID) != -1; | ||
|  | 							tindex = GetAddTouchIndex(ti.dwID); | ||
|  | 							if (tindex >= 0) { | ||
|  | 								if (touchExisits) { | ||
|  | 									if (touchPoints[tindex].x != ptInput.x || touchPoints[tindex].y != ptInput.y) { | ||
|  | 										touchPoints[tindex].x = ptInput.x; | ||
|  | 										touchPoints[tindex].y = ptInput.y; | ||
|  | 										kinc_internal_surface_trigger_move(tindex, ptInput.x, ptInput.y); | ||
|  | 									} | ||
|  | 								} | ||
|  | 								else { | ||
|  | 									touchPoints[tindex].x = ptInput.x; | ||
|  | 									touchPoints[tindex].y = ptInput.y; | ||
|  | 									kinc_internal_surface_trigger_touch_start(tindex, ptInput.x, ptInput.y); | ||
|  | 								} | ||
|  | 							} | ||
|  | 						} | ||
|  | 					} | ||
|  | 					bHandled = TRUE; | ||
|  | 
 | ||
|  | 					if (!CloseTouchInputHandle((HTOUCHINPUT)lParam)) { | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 		if (bHandled) | ||
|  | 			CloseTouchInputHandle((HTOUCHINPUT)lParam); | ||
|  | 		else | ||
|  | 			DefWindowProcW(hWnd, WM_TOUCH, wParam, lParam); | ||
|  | 
 | ||
|  | 		InvalidateRect(hWnd, NULL, FALSE); | ||
|  | 	} break; | ||
|  | 	case WM_KEYDOWN: | ||
|  | 	case WM_SYSKEYDOWN: | ||
|  | 		if (!keyPressed[wParam]) { | ||
|  | 			keyPressed[wParam] = true; | ||
|  | 
 | ||
|  | 			if (keyTranslated[wParam] == KINC_KEY_CONTROL) { | ||
|  | 				controlDown = true; | ||
|  | 			} | ||
|  | #ifdef HANDLE_ALT_ENTER
 | ||
|  | 			else if (keyTranslated[wParam] == KINC_KEY_ALT) { | ||
|  | 				altDown = true; | ||
|  | 			} | ||
|  | #endif
 | ||
|  | 			else { | ||
|  | 				if (controlDown && keyTranslated[wParam] == KINC_KEY_X) { | ||
|  | 					char *text = kinc_internal_cut_callback(); | ||
|  | 					if (text != NULL) { | ||
|  | 						wchar_t wtext[4096]; | ||
|  | 						MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096); | ||
|  | 						OpenClipboard(hWnd); | ||
|  | 						EmptyClipboard(); | ||
|  | 						size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t); | ||
|  | 						HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size); | ||
|  | 						void *data = GlobalLock(handle); | ||
|  | 						memcpy(data, wtext, size); | ||
|  | 						GlobalUnlock(handle); | ||
|  | 						SetClipboardData(CF_UNICODETEXT, handle); | ||
|  | 						CloseClipboard(); | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				if (controlDown && keyTranslated[wParam] == KINC_KEY_C) { | ||
|  | 					char *text = kinc_internal_copy_callback(); | ||
|  | 					if (text != NULL) { | ||
|  | 						wchar_t wtext[4096]; | ||
|  | 						MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096); | ||
|  | 						OpenClipboard(hWnd); | ||
|  | 						EmptyClipboard(); | ||
|  | 						size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t); | ||
|  | 						HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size); | ||
|  | 						void *data = GlobalLock(handle); | ||
|  | 						memcpy(data, wtext, size); | ||
|  | 						GlobalUnlock(handle); | ||
|  | 						SetClipboardData(CF_UNICODETEXT, handle); | ||
|  | 						CloseClipboard(); | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				if (controlDown && keyTranslated[wParam] == KINC_KEY_V) { | ||
|  | 					if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { | ||
|  | 						OpenClipboard(hWnd); | ||
|  | 						HANDLE handle = GetClipboardData(CF_UNICODETEXT); | ||
|  | 						if (handle != NULL) { | ||
|  | 							wchar_t *wtext = (wchar_t *)GlobalLock(handle); | ||
|  | 							if (wtext != NULL) { | ||
|  | 								char text[4096]; | ||
|  | 								WideCharToMultiByte(CP_UTF8, 0, wtext, -1, text, 4096, NULL, NULL); | ||
|  | 								kinc_internal_paste_callback(text); | ||
|  | 								GlobalUnlock(handle); | ||
|  | 							} | ||
|  | 						} | ||
|  | 						CloseClipboard(); | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | #ifdef HANDLE_ALT_ENTER
 | ||
|  | 				if (altDown && keyTranslated[wParam] == KINC_KEY_RETURN) { | ||
|  | 					if (kinc_window_get_mode(0) == KINC_WINDOW_MODE_WINDOW) { | ||
|  | 						last_window_width = kinc_window_width(0); | ||
|  | 						last_window_height = kinc_window_height(0); | ||
|  | 						last_window_x = kinc_window_x(0); | ||
|  | 						last_window_y = kinc_window_y(0); | ||
|  | 						kinc_window_change_mode(0, KINC_WINDOW_MODE_FULLSCREEN); | ||
|  | 					} | ||
|  | 					else { | ||
|  | 						kinc_window_change_mode(0, KINC_WINDOW_MODE_WINDOW); | ||
|  | 						if (last_window_width > 0 && last_window_height > 0) { | ||
|  | 							kinc_window_resize(0, last_window_width, last_window_height); | ||
|  | 						} | ||
|  | 						if (last_window_x > INT_MIN && last_window_y > INT_MIN) { | ||
|  | 							kinc_window_move(0, last_window_x, last_window_y); | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | #endif
 | ||
|  | 			} | ||
|  | 		} | ||
|  | 		kinc_internal_keyboard_trigger_key_down(keyTranslated[wParam]); | ||
|  | 		break; | ||
|  | 	case WM_KEYUP: | ||
|  | 	case WM_SYSKEYUP: | ||
|  | 		keyPressed[wParam] = false; | ||
|  | 
 | ||
|  | 		if (keyTranslated[wParam] == KINC_KEY_CONTROL) { | ||
|  | 			controlDown = false; | ||
|  | 		} | ||
|  | #ifdef HANDLE_ALT_ENTER
 | ||
|  | 		if (keyTranslated[wParam] == KINC_KEY_ALT) { | ||
|  | 			altDown = false; | ||
|  | 		} | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 		kinc_internal_keyboard_trigger_key_up(keyTranslated[wParam]); | ||
|  | 		break; | ||
|  | 	case WM_CHAR: | ||
|  | 		switch (wParam) { | ||
|  | 		case 0x1B: // escape
 | ||
|  | 			break; | ||
|  | 		default: | ||
|  | 			kinc_internal_keyboard_trigger_key_press((unsigned)wParam); | ||
|  | 			break; | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	case WM_SYSCOMMAND: | ||
|  | 		switch (wParam) { | ||
|  | 		case SC_KEYMENU: | ||
|  | 			return 0; // Prevent from happening
 | ||
|  | 		case SC_SCREENSAVE: | ||
|  | 		case SC_MONITORPOWER: | ||
|  | 			return 0; // Prevent from happening
 | ||
|  | 
 | ||
|  | 		// Pause game when window is minimized, continue when it's restored or maximized.
 | ||
|  | 		//
 | ||
|  | 		// Unfortunately, if the game would continue to run when minimized, the graphics in
 | ||
|  | 		// the Windows Vista/7 taskbar would not be updated, even when Direct3DDevice::Present()
 | ||
|  | 		// is called without error. I do not know why.
 | ||
|  | 		case SC_MINIMIZE: | ||
|  | 			// Scheduler::haltTime(); // haltTime()/unhaltTime() is incremental, meaning that this doesn't interfere with when the game itself calls these
 | ||
|  | 			// functions
 | ||
|  | 			break; | ||
|  | 		case SC_RESTORE: | ||
|  | 		case SC_MAXIMIZE: | ||
|  | 			// Scheduler::unhaltTime();
 | ||
|  | 			break; | ||
|  | 		} | ||
|  | 		break; | ||
|  | 	case WM_DEVICECHANGE: | ||
|  | 		detectGamepad = true; | ||
|  | 		break; | ||
|  | 	case WM_DROPFILES: { | ||
|  | 		HDROP hDrop = (HDROP)wParam; | ||
|  | 		unsigned count = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0); | ||
|  | 		for (unsigned i = 0; i < count; ++i) { | ||
|  | 			wchar_t filePath[260]; | ||
|  | 			if (DragQueryFileW(hDrop, i, filePath, 260)) { | ||
|  | 				kinc_internal_drop_files_callback(filePath); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		DragFinish(hDrop); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	} | ||
|  | 	return DefWindowProcW(hWnd, msg, wParam, lParam); | ||
|  | } | ||
|  | 
 | ||
|  | static float axes[12 * 6]; | ||
|  | static float buttons[12 * 16]; | ||
|  | 
 | ||
|  | typedef DWORD(WINAPI *XInputGetStateType)(DWORD dwUserIndex, XINPUT_STATE *pState); | ||
|  | typedef DWORD(WINAPI *XInputSetStateType)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration); | ||
|  | static XInputGetStateType InputGetState = NULL; | ||
|  | static XInputSetStateType InputSetState = NULL; | ||
|  | 
 | ||
|  | void loadXInput() { | ||
|  | 	HMODULE lib = LoadLibraryA("xinput1_4.dll"); | ||
|  | 	if (lib == NULL) { | ||
|  | 		lib = LoadLibraryA("xinput1_3.dll"); | ||
|  | 	} | ||
|  | 	if (lib == NULL) { | ||
|  | 		lib = LoadLibraryA("xinput9_1_0.dll"); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (lib != NULL) { | ||
|  | 		InputGetState = (XInputGetStateType)GetProcAddress(lib, "XInputGetState"); | ||
|  | 		InputSetState = (XInputSetStateType)GetProcAddress(lib, "XInputSetState"); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static IDirectInput8W *di_instance = NULL; | ||
|  | static IDirectInputDevice8W *di_pads[KINC_DINPUT_MAX_COUNT]; | ||
|  | static DIJOYSTATE2 di_padState[KINC_DINPUT_MAX_COUNT]; | ||
|  | static DIJOYSTATE2 di_lastPadState[KINC_DINPUT_MAX_COUNT]; | ||
|  | static DIDEVCAPS di_deviceCaps[KINC_DINPUT_MAX_COUNT]; | ||
|  | static int padCount = 0; | ||
|  | 
 | ||
|  | static void cleanupPad(int padIndex) { | ||
|  | 	if (di_pads[padIndex] != NULL) { | ||
|  | 		di_pads[padIndex]->lpVtbl->Unacquire(di_pads[padIndex]); | ||
|  | 		di_pads[padIndex]->lpVtbl->Release(di_pads[padIndex]); | ||
|  | 		di_pads[padIndex] = 0; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | #ifndef SAFE_RELEASE
 | ||
|  | #define SAFE_RELEASE(x)                                                                                                                                        \
 | ||
|  | 	if (x != NULL) {                                                                                                                                           \ | ||
|  | 		x->lpVtbl->Release(x);                                                                                                                                 \ | ||
|  | 		x = NULL;                                                                                                                                              \ | ||
|  | 	} | ||
|  | #endif
 | ||
|  | 
 | ||
|  | // From
 | ||
|  | //-----------------------------------------------------------------------------
 | ||
|  | // Enum each PNP device using WMI and check each device ID to see if it contains
 | ||
|  | // "IG_" (ex. "VID_045E&PID_028E&IG_00").  If it does, then it's an XInput device
 | ||
|  | // Unfortunately this information can not be found by just using DirectInput
 | ||
|  | //-----------------------------------------------------------------------------
 | ||
|  | static BOOL IsXInputDevice(const GUID *pGuidProductFromDirectInput) { | ||
|  | 	IWbemLocator *pIWbemLocator = NULL; | ||
|  | 	IEnumWbemClassObject *pEnumDevices = NULL; | ||
|  | 	IWbemClassObject *pDevices[20] = {0}; | ||
|  | 	IWbemServices *pIWbemServices = NULL; | ||
|  | 	BSTR bstrNamespace = NULL; | ||
|  | 	BSTR bstrDeviceID = NULL; | ||
|  | 	BSTR bstrClassName = NULL; | ||
|  | 	DWORD uReturned = 0; | ||
|  | 	bool bIsXinputDevice = false; | ||
|  | 	UINT iDevice = 0; | ||
|  | 	VARIANT var; | ||
|  | 	HRESULT hr; | ||
|  | 
 | ||
|  | 	// CoInit if needed
 | ||
|  | 	hr = CoInitialize(NULL); | ||
|  | 	bool bCleanupCOM = SUCCEEDED(hr); | ||
|  | 
 | ||
|  | 	// Create WMI
 | ||
|  | 	hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *)&pIWbemLocator); | ||
|  | 	if (FAILED(hr) || pIWbemLocator == NULL) | ||
|  | 		goto LCleanup; | ||
|  | 
 | ||
|  | 	bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); | ||
|  | 	if (bstrNamespace == NULL) | ||
|  | 		goto LCleanup; | ||
|  | 	bstrClassName = SysAllocString(L"Win32_PNPEntity"); | ||
|  | 	if (bstrClassName == NULL) | ||
|  | 		goto LCleanup; | ||
|  | 	bstrDeviceID = SysAllocString(L"DeviceID"); | ||
|  | 	if (bstrDeviceID == NULL) | ||
|  | 		goto LCleanup; | ||
|  | 
 | ||
|  | 	// Connect to WMI
 | ||
|  | 	hr = pIWbemLocator->lpVtbl->ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices); | ||
|  | 	if (FAILED(hr) || pIWbemServices == NULL) | ||
|  | 		goto LCleanup; | ||
|  | 
 | ||
|  | 	// Switch security level to IMPERSONATE.
 | ||
|  | 	CoSetProxyBlanket((IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, | ||
|  | 	                  EOAC_NONE); | ||
|  | 
 | ||
|  | 	hr = pIWbemServices->lpVtbl->CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices); | ||
|  | 	if (FAILED(hr) || pEnumDevices == NULL) | ||
|  | 		goto LCleanup; | ||
|  | 
 | ||
|  | 	// Loop over all devices
 | ||
|  | 	for (;;) { | ||
|  | 		// Get 20 at a time
 | ||
|  | 		hr = pEnumDevices->lpVtbl->Next(pEnumDevices, 10000, 20, pDevices, &uReturned); | ||
|  | 		if (FAILED(hr)) | ||
|  | 			goto LCleanup; | ||
|  | 		if (uReturned == 0) | ||
|  | 			break; | ||
|  | 
 | ||
|  | 		for (iDevice = 0; iDevice < uReturned; iDevice++) { | ||
|  | 			// For each device, get its device ID
 | ||
|  | 			hr = pDevices[iDevice]->lpVtbl->Get(pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL); | ||
|  | 			if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) { | ||
|  | 				// Check if the device ID contains "IG_".  If it does, then it's an XInput device
 | ||
|  | 				// This information can not be found from DirectInput
 | ||
|  | 				// TODO: Doesn't work with an Xbox Series X|S controller
 | ||
|  | 				if (wcsstr(var.bstrVal, L"IG_")) { | ||
|  | 					// If it does, then get the VID/PID from var.bstrVal
 | ||
|  | 					DWORD dwPid = 0, dwVid = 0; | ||
|  | 					WCHAR *strVid = wcsstr(var.bstrVal, L"VID_"); | ||
|  | #ifndef KINC_NO_CLIB
 | ||
|  | 					if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) { | ||
|  | 						dwVid = 0; | ||
|  | 					} | ||
|  | 					WCHAR *strPid = wcsstr(var.bstrVal, L"PID_"); | ||
|  | 					if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) { | ||
|  | 						dwPid = 0; | ||
|  | 					} | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 					// Compare the VID/PID to the DInput device
 | ||
|  | 					DWORD dwVidPid = MAKELONG(dwVid, dwPid); | ||
|  | 					if (dwVidPid == pGuidProductFromDirectInput->Data1) { | ||
|  | 						bIsXinputDevice = true; | ||
|  | 						goto LCleanup; | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 			SAFE_RELEASE(pDevices[iDevice]); | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | LCleanup: | ||
|  | 	if (bstrNamespace) | ||
|  | 		SysFreeString(bstrNamespace); | ||
|  | 	if (bstrDeviceID) | ||
|  | 		SysFreeString(bstrDeviceID); | ||
|  | 	if (bstrClassName) | ||
|  | 		SysFreeString(bstrClassName); | ||
|  | 	for (iDevice = 0; iDevice < 20; iDevice++) | ||
|  | 		SAFE_RELEASE(pDevices[iDevice]); | ||
|  | 	SAFE_RELEASE(pEnumDevices); | ||
|  | 	SAFE_RELEASE(pIWbemLocator); | ||
|  | 	SAFE_RELEASE(pIWbemServices); | ||
|  | 
 | ||
|  | 	if (bCleanupCOM) | ||
|  | 		CoUninitialize(); | ||
|  | 
 | ||
|  | 	return bIsXinputDevice; | ||
|  | } | ||
|  | 
 | ||
|  | // TODO (DK) this should probably be called from somewhere?
 | ||
|  | static void cleanupDirectInput() { | ||
|  | 	for (int padIndex = 0; padIndex < KINC_DINPUT_MAX_COUNT; ++padIndex) { | ||
|  | 		cleanupPad(padIndex); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (di_instance != NULL) { | ||
|  | 		di_instance->lpVtbl->Release(di_instance); | ||
|  | 		di_instance = NULL; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static BOOL CALLBACK enumerateJoystickAxesCallback(LPCDIDEVICEOBJECTINSTANCEW ddoi, LPVOID context) { | ||
|  | 	HWND hwnd = (HWND)context; | ||
|  | 
 | ||
|  | 	DIPROPRANGE propertyRange; | ||
|  | 	propertyRange.diph.dwSize = sizeof(DIPROPRANGE); | ||
|  | 	propertyRange.diph.dwHeaderSize = sizeof(DIPROPHEADER); | ||
|  | 	propertyRange.diph.dwHow = DIPH_BYID; | ||
|  | 	propertyRange.diph.dwObj = ddoi->dwType; | ||
|  | 	propertyRange.lMin = -32768; | ||
|  | 	propertyRange.lMax = 32768; | ||
|  | 
 | ||
|  | 	HRESULT hr = di_pads[padCount]->lpVtbl->SetProperty(di_pads[padCount], DIPROP_RANGE, &propertyRange.diph); | ||
|  | 
 | ||
|  | 	if (FAILED(hr)) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / SetProperty() failed (HRESULT=0x%x)", padCount, hr); | ||
|  | 
 | ||
|  | 		// TODO (DK) cleanup?
 | ||
|  | 		// cleanupPad(padCount);
 | ||
|  | 
 | ||
|  | 		return DIENUM_STOP; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return DIENUM_CONTINUE; | ||
|  | } | ||
|  | 
 | ||
|  | static BOOL CALLBACK enumerateJoysticksCallback(LPCDIDEVICEINSTANCEW ddi, LPVOID context) { | ||
|  | 	if (padCount < XUSER_MAX_COUNT && IsXInputDevice(&ddi->guidProduct)) { | ||
|  | 		++padCount; | ||
|  | 		return DIENUM_CONTINUE; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	HRESULT hr = di_instance->lpVtbl->CreateDevice(di_instance, &ddi->guidInstance, &di_pads[padCount], NULL); | ||
|  | 
 | ||
|  | 	if (SUCCEEDED(hr)) { | ||
|  | 		hr = di_pads[padCount]->lpVtbl->SetDataFormat(di_pads[padCount], &c_dfDIJoystick2); | ||
|  | 
 | ||
|  | 		// TODO (DK) required?
 | ||
|  | 		// hr = di_pads[padCount]->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
 | ||
|  | 
 | ||
|  | 		if (SUCCEEDED(hr)) { | ||
|  | 			di_deviceCaps[padCount].dwSize = sizeof(DIDEVCAPS); | ||
|  | 			hr = di_pads[padCount]->lpVtbl->GetCapabilities(di_pads[padCount], &di_deviceCaps[padCount]); | ||
|  | 
 | ||
|  | 			if (SUCCEEDED(hr)) { | ||
|  | 				hr = di_pads[padCount]->lpVtbl->EnumObjects(di_pads[padCount], enumerateJoystickAxesCallback, NULL, DIDFT_AXIS); | ||
|  | 
 | ||
|  | 				if (SUCCEEDED(hr)) { | ||
|  | 					hr = di_pads[padCount]->lpVtbl->Acquire(di_pads[padCount]); | ||
|  | 
 | ||
|  | 					if (SUCCEEDED(hr)) { | ||
|  | 						memset(&di_padState[padCount], 0, sizeof(DIJOYSTATE2)); | ||
|  | 						hr = di_pads[padCount]->lpVtbl->GetDeviceState(di_pads[padCount], sizeof(DIJOYSTATE2), &di_padState[padCount]); | ||
|  | 
 | ||
|  | 						if (SUCCEEDED(hr)) { | ||
|  | 							kinc_log(KINC_LOG_LEVEL_INFO, "DirectInput8 / Pad%i / initialized", padCount); | ||
|  | 						} | ||
|  | 						else { | ||
|  | 							kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / GetDeviceState() failed (HRESULT=0x%x)", padCount, hr); | ||
|  | 							// cleanupPad(padCount); // (DK) don't kill it, we try again in handleDirectInputPad()
 | ||
|  | 						} | ||
|  | 					} | ||
|  | 					else { | ||
|  | 						kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / Acquire() failed (HRESULT=0x%x)", padCount, hr); | ||
|  | 						cleanupPad(padCount); | ||
|  | 					} | ||
|  | 				} | ||
|  | 				else { | ||
|  | 					kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / EnumObjects(DIDFT_AXIS) failed (HRESULT=0x%x)", padCount, hr); | ||
|  | 					cleanupPad(padCount); | ||
|  | 				} | ||
|  | 			} | ||
|  | 			else { | ||
|  | 				kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / GetCapabilities() failed (HRESULT=0x%x)", padCount, hr); | ||
|  | 				cleanupPad(padCount); | ||
|  | 			} | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / SetDataFormat() failed (HRESULT=0x%x)", padCount, hr); | ||
|  | 			cleanupPad(padCount); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		++padCount; | ||
|  | 
 | ||
|  | 		if (padCount >= KINC_DINPUT_MAX_COUNT) { | ||
|  | 			return DIENUM_STOP; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return DIENUM_CONTINUE; | ||
|  | } | ||
|  | 
 | ||
|  | static void initializeDirectInput() { | ||
|  | 	HINSTANCE hinstance = GetModuleHandleW(NULL); | ||
|  | 
 | ||
|  | 	memset(&di_pads, 0, sizeof(IDirectInputDevice8) * KINC_DINPUT_MAX_COUNT); | ||
|  | 	memset(&di_padState, 0, sizeof(DIJOYSTATE2) * KINC_DINPUT_MAX_COUNT); | ||
|  | 	memset(&di_lastPadState, 0, sizeof(DIJOYSTATE2) * KINC_DINPUT_MAX_COUNT); | ||
|  | 	memset(&di_deviceCaps, 0, sizeof(DIDEVCAPS) * KINC_DINPUT_MAX_COUNT); | ||
|  | 
 | ||
|  | 	HRESULT hr = DirectInput8Create(hinstance, DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void **)&di_instance, NULL); | ||
|  | 
 | ||
|  | 	if (SUCCEEDED(hr)) { | ||
|  | 		hr = di_instance->lpVtbl->EnumDevices(di_instance, DI8DEVCLASS_GAMECTRL, enumerateJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY); | ||
|  | 
 | ||
|  | 		if (SUCCEEDED(hr)) { | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			cleanupDirectInput(); | ||
|  | 		} | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8Create failed (HRESULT=0x%x)", hr); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | bool handleDirectInputPad(int padIndex) { | ||
|  | 	if (di_pads[padIndex] == NULL) { | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	HRESULT hr = di_pads[padIndex]->lpVtbl->GetDeviceState(di_pads[padIndex], sizeof(DIJOYSTATE2), &di_padState[padIndex]); | ||
|  | 
 | ||
|  | 	switch (hr) { | ||
|  | 	case S_OK: { | ||
|  | 		// TODO (DK) there is a lot more to handle
 | ||
|  | 		for (int axisIndex = 0; axisIndex < 2; ++axisIndex) { | ||
|  | 			LONG *now = NULL; | ||
|  | 			LONG *last = NULL; | ||
|  | 
 | ||
|  | 			switch (axisIndex) { | ||
|  | 			case 0: { | ||
|  | 				now = &di_padState[padIndex].lX; | ||
|  | 				last = &di_lastPadState[padIndex].lX; | ||
|  | 			} break; | ||
|  | 			case 1: { | ||
|  | 				now = &di_padState[padIndex].lY; | ||
|  | 				last = &di_lastPadState[padIndex].lY; | ||
|  | 			} break; | ||
|  | 			case 2: { | ||
|  | 				now = &di_padState[padIndex].lZ; | ||
|  | 				last = &di_lastPadState[padIndex].lZ; | ||
|  | 			} break; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (*now != *last) { | ||
|  | 				kinc_internal_gamepad_trigger_axis(padIndex, axisIndex, *now / 32768.0f); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		for (int buttonIndex = 0; buttonIndex < 128; ++buttonIndex) { | ||
|  | 			BYTE *now = &di_padState[padIndex].rgbButtons[buttonIndex]; | ||
|  | 			BYTE *last = &di_lastPadState[padIndex].rgbButtons[buttonIndex]; | ||
|  | 
 | ||
|  | 			if (*now != *last) { | ||
|  | 				kinc_internal_gamepad_trigger_button(padIndex, buttonIndex, *now / 255.0f); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		for (int povIndex = 0; povIndex < 4; ++povIndex) { | ||
|  | 			DWORD *now = &di_padState[padIndex].rgdwPOV[povIndex]; | ||
|  | 			DWORD *last = &di_lastPadState[padIndex].rgdwPOV[povIndex]; | ||
|  | 
 | ||
|  | 			bool up = (*now == 0 || *now == 31500 || *now == 4500); | ||
|  | 			bool down = (*now == 18000 || *now == 13500 || *now == 22500); | ||
|  | 			bool left = (*now == 27000 || *now == 22500 || *now == 31500); | ||
|  | 			bool right = (*now == 9000 || *now == 4500 || *now == 13500); | ||
|  | 
 | ||
|  | 			bool lastUp = (*last == 0 || *last == 31500 || *last == 4500); | ||
|  | 			bool lastDown = (*last == 18000 || *last == 13500 || *last == 22500); | ||
|  | 			bool lastLeft = (*last == 27000 || *last == 22500 || *last == 31500); | ||
|  | 			bool lastRight = (*last == 9000 || *last == 4500 || *last == 13500); | ||
|  | 
 | ||
|  | 			if (up != lastUp) { | ||
|  | 				kinc_internal_gamepad_trigger_button(padIndex, 12, up ? 1.0f : 0.0f); | ||
|  | 			} | ||
|  | 			if (down != lastDown) { | ||
|  | 				kinc_internal_gamepad_trigger_button(padIndex, 13, down ? 1.0f : 0.0f); | ||
|  | 			} | ||
|  | 			if (left != lastLeft) { | ||
|  | 				kinc_internal_gamepad_trigger_button(padIndex, 14, left ? 1.0f : 0.0f); | ||
|  | 			} | ||
|  | 			if (right != lastRight) { | ||
|  | 				kinc_internal_gamepad_trigger_button(padIndex, 15, right ? 1.0f : 0.0f); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		memcpy(&di_lastPadState[padIndex], &di_padState[padIndex], sizeof(DIJOYSTATE2)); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	case DIERR_INPUTLOST: // fall through
 | ||
|  | 	case DIERR_NOTACQUIRED: { | ||
|  | 		hr = di_pads[padIndex]->lpVtbl->Acquire(di_pads[padIndex]); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return hr == S_OK; | ||
|  | } | ||
|  | 
 | ||
|  | static bool isXInputGamepad(int gamepad) { | ||
|  | 	//if gamepad is greater than XInput max, treat it as DINPUT.
 | ||
|  | 	if (gamepad >= XUSER_MAX_COUNT) | ||
|  | 	{ | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 	XINPUT_STATE state; | ||
|  | 	memset(&state, 0, sizeof(XINPUT_STATE)); | ||
|  | 	DWORD dwResult = InputGetState(gamepad, &state); | ||
|  | 	return dwResult == ERROR_SUCCESS; | ||
|  | } | ||
|  | 
 | ||
|  | static bool isDirectInputGamepad(int gamepad) { | ||
|  | 	if (di_pads[gamepad] == NULL) { | ||
|  | 		return false; | ||
|  | 	} | ||
|  | 	HRESULT hr = di_pads[gamepad]->lpVtbl->GetDeviceState(di_pads[gamepad], sizeof(DIJOYSTATE2), &di_padState[gamepad]); | ||
|  | 	return hr == S_OK; | ||
|  | } | ||
|  | 
 | ||
|  | const char *kinc_gamepad_vendor(int gamepad) { | ||
|  | 	if (isXInputGamepad(gamepad)) { | ||
|  | 		return "Microsoft"; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		return "DirectInput8"; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | const char *kinc_gamepad_product_name(int gamepad) { | ||
|  | 	if (isXInputGamepad(gamepad)) { | ||
|  | 		return "Xbox 360 Controller"; | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		return "Generic Gamepad"; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | bool kinc_internal_handle_messages() { | ||
|  | 	MSG message; | ||
|  | 
 | ||
|  | 	while (PeekMessageW(&message, 0, 0, 0, PM_REMOVE)) { | ||
|  | 		TranslateMessage(&message); | ||
|  | 		DispatchMessageW(&message); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (InputGetState != NULL && (detectGamepad || gamepadFound)) { | ||
|  | 		detectGamepad = false; | ||
|  | 		for (DWORD i = 0; i < KINC_DINPUT_MAX_COUNT; ++i) { | ||
|  | 			XINPUT_STATE state; | ||
|  | 			memset(&state, 0, sizeof(XINPUT_STATE)); | ||
|  | 			DWORD dwResult = InputGetState(i, &state); | ||
|  | 
 | ||
|  | 			if (dwResult == ERROR_SUCCESS) { | ||
|  | 				gamepadFound = true; | ||
|  | 
 | ||
|  | 				float newaxes[6]; | ||
|  | 				newaxes[0] = state.Gamepad.sThumbLX / 32768.0f; | ||
|  | 				newaxes[1] = state.Gamepad.sThumbLY / 32768.0f; | ||
|  | 				newaxes[2] = state.Gamepad.sThumbRX / 32768.0f; | ||
|  | 				newaxes[3] = state.Gamepad.sThumbRY / 32768.0f; | ||
|  | 				newaxes[4] = state.Gamepad.bLeftTrigger / 255.0f; | ||
|  | 				newaxes[5] = state.Gamepad.bRightTrigger / 255.0f; | ||
|  | 				for (int i2 = 0; i2 < 6; ++i2) { | ||
|  | 					if (axes[i * 6 + i2] != newaxes[i2]) { | ||
|  | 						kinc_internal_gamepad_trigger_axis(i, i2, newaxes[i2]); | ||
|  | 						axes[i * 6 + i2] = newaxes[i2]; | ||
|  | 					} | ||
|  | 				} | ||
|  | 				float newbuttons[16]; | ||
|  | 				newbuttons[0] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[1] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[2] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[3] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[4] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[5] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[6] = state.Gamepad.bLeftTrigger / 255.0f; | ||
|  | 				newbuttons[7] = state.Gamepad.bRightTrigger / 255.0f; | ||
|  | 				newbuttons[8] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[9] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[10] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[11] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[12] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[13] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[14] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 1.0f : 0.0f; | ||
|  | 				newbuttons[15] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 1.0f : 0.0f; | ||
|  | 				for (int i2 = 0; i2 < 16; ++i2) { | ||
|  | 					if (buttons[i * 16 + i2] != newbuttons[i2]) { | ||
|  | 						kinc_internal_gamepad_trigger_button(i, i2, newbuttons[i2]); | ||
|  | 						buttons[i * 16 + i2] = newbuttons[i2]; | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 			else { | ||
|  | 					if (handleDirectInputPad(i)) { | ||
|  | 						gamepadFound = true; | ||
|  | 					} | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return true; | ||
|  | } | ||
|  | 
 | ||
|  | //**vec2i Kore::System::mousePos() {
 | ||
|  | //**	return vec2i(mouseX, mouseY);
 | ||
|  | //**}
 | ||
|  | 
 | ||
|  | static bool keyboardshown = false; | ||
|  | static char language[3] = {0}; | ||
|  | 
 | ||
|  | void kinc_keyboard_show() { | ||
|  | 	keyboardshown = true; | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_keyboard_hide() { | ||
|  | 	keyboardshown = false; | ||
|  | } | ||
|  | 
 | ||
|  | bool kinc_keyboard_active() { | ||
|  | 	return keyboardshown; | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_load_url(const char *url) { | ||
|  | #define WURL_SIZE 1024
 | ||
|  | #define HTTP "http://"
 | ||
|  | #define HTTPS "https://"
 | ||
|  | 	if (strncmp(url, HTTP, sizeof(HTTP) - 1) == 0 || strncmp(url, HTTPS, sizeof(HTTPS) - 1) == 0) { | ||
|  | 		wchar_t wurl[WURL_SIZE]; | ||
|  | 		MultiByteToWideChar(CP_UTF8, 0, url, -1, wurl, WURL_SIZE); | ||
|  | 		INT_PTR ret = (INT_PTR)ShellExecuteW(NULL, L"open", wurl, NULL, NULL, SW_SHOWNORMAL); | ||
|  | 		// According to ShellExecuteW's documentation return values > 32 indicate a success.
 | ||
|  | 		if (ret <= 32) { | ||
|  | 			kinc_log(KINC_LOG_LEVEL_WARNING, "Error opening url %s", url); | ||
|  | 		} | ||
|  | 	} | ||
|  | #undef HTTPS
 | ||
|  | #undef HTTP
 | ||
|  | #undef WURL_SIZE
 | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_set_keep_screen_on(bool on) {} | ||
|  | void kinc_vibrate(int ms) {} | ||
|  | 
 | ||
|  | const char *kinc_language() { | ||
|  | 	wchar_t wlanguage[3] = {0}; | ||
|  | 
 | ||
|  | 	if (GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, wlanguage, 3)) { | ||
|  | 		WideCharToMultiByte(CP_UTF8, 0, wlanguage, -1, language, 3, NULL, NULL); | ||
|  | 		return language; | ||
|  | 	} | ||
|  | 	return "en"; | ||
|  | } | ||
|  | 
 | ||
|  | const char *kinc_system_id() { | ||
|  | 	return "Windows"; | ||
|  | } | ||
|  | 
 | ||
|  | static bool co_initialized = false; | ||
|  | 
 | ||
|  | void kinc_windows_co_initialize(void) { | ||
|  | 	if (!co_initialized) { | ||
|  | 		kinc_microsoft_affirm(CoInitializeEx(0, COINIT_MULTITHREADED)); | ||
|  | 		co_initialized = true; | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static wchar_t savePathw[2048] = {0}; | ||
|  | static char savePath[2048] = {0}; | ||
|  | 
 | ||
|  | static void findSavePath() { | ||
|  | 	kinc_windows_co_initialize(); | ||
|  | 	IKnownFolderManager *folders = NULL; | ||
|  | 	CoCreateInstance(&CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, &IID_IKnownFolderManager, (LPVOID *)&folders); | ||
|  | 	IKnownFolder *folder = NULL; | ||
|  | 	folders->lpVtbl->GetFolder(folders, &FOLDERID_SavedGames, &folder); | ||
|  | 
 | ||
|  | 	LPWSTR path; | ||
|  | 	folder->lpVtbl->GetPath(folder, 0, &path); | ||
|  | 
 | ||
|  | 	wcscpy(savePathw, path); | ||
|  | 	wcscat(savePathw, L"\\"); | ||
|  | 	wchar_t name[1024]; | ||
|  | 	MultiByteToWideChar(CP_UTF8, 0, kinc_application_name(), -1, name, 1024); | ||
|  | 	wcscat(savePathw, name); | ||
|  | 	wcscat(savePathw, L"\\"); | ||
|  | 
 | ||
|  | 	SHCreateDirectoryExW(NULL, savePathw, NULL); | ||
|  | 	WideCharToMultiByte(CP_UTF8, 0, savePathw, -1, savePath, 1024, NULL, NULL); | ||
|  | 
 | ||
|  | 	CoTaskMemFree(path); | ||
|  | 	folder->lpVtbl->Release(folder); | ||
|  | 	folders->lpVtbl->Release(folders); | ||
|  | 	// CoUninitialize();
 | ||
|  | } | ||
|  | 
 | ||
|  | const char *kinc_internal_save_path() { | ||
|  | 	if (savePath[0] == 0) | ||
|  | 		findSavePath(); | ||
|  | 	return savePath; | ||
|  | } | ||
|  | 
 | ||
|  | static const char *videoFormats[] = {"ogv", NULL}; | ||
|  | static LARGE_INTEGER frequency; | ||
|  | static LARGE_INTEGER startCount; | ||
|  | 
 | ||
|  | const char **kinc_video_formats() { | ||
|  | 	return videoFormats; | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_login(void) {} | ||
|  | 
 | ||
|  | void kinc_unlock_achievement(int id) {} | ||
|  | 
 | ||
|  | bool kinc_gamepad_connected(int num) { | ||
|  | 	return isXInputGamepad(num) || isDirectInputGamepad(num); | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_gamepad_rumble(int gamepad, float left, float right) { | ||
|  | 	if (isXInputGamepad(gamepad)) { | ||
|  | 		XINPUT_VIBRATION vibration; | ||
|  | 		memset(&vibration, 0, sizeof(XINPUT_VIBRATION)); | ||
|  | 		vibration.wLeftMotorSpeed = (WORD)(65535.f * left); | ||
|  | 		vibration.wRightMotorSpeed = (WORD)(65535.f * right); | ||
|  | 		InputSetState(gamepad, &vibration); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | double kinc_frequency() { | ||
|  | 	return (double)frequency.QuadPart; | ||
|  | } | ||
|  | 
 | ||
|  | kinc_ticks_t kinc_timestamp(void) { | ||
|  | 	LARGE_INTEGER stamp; | ||
|  | 	QueryPerformanceCounter(&stamp); | ||
|  | 	return stamp.QuadPart - startCount.QuadPart; | ||
|  | } | ||
|  | 
 | ||
|  | double kinc_time(void) { | ||
|  | 	LARGE_INTEGER stamp; | ||
|  | 	QueryPerformanceCounter(&stamp); | ||
|  | 	return (double)(stamp.QuadPart - startCount.QuadPart) / (double)frequency.QuadPart; | ||
|  | } | ||
|  | 
 | ||
|  | #if !defined(KINC_NO_MAIN) && !defined(KINC_NO_CLIB)
 | ||
|  | int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { | ||
|  | 	int ret = kickstart(__argc, __argv); | ||
|  | 	if (ret != 0) { | ||
|  | #ifdef NDEBUG
 | ||
|  | 		MessageBox(0, L"Unknown Error", L"Error", MB_OK); | ||
|  | #else
 | ||
|  | 		__debugbreak(); | ||
|  | #endif
 | ||
|  | 	} | ||
|  | 	return ret; | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | typedef BOOL(__stdcall *MiniDumpWriteDumpType)(IN HANDLE hProcess, IN DWORD ProcessId, IN HANDLE hFile, IN MINIDUMP_TYPE DumpType, | ||
|  |                                                IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, | ||
|  |                                                OPTIONAL IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, | ||
|  |                                                OPTIONAL IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL); | ||
|  | 
 | ||
|  | static MiniDumpWriteDumpType MyMiniDumpWriteDump; | ||
|  | 
 | ||
|  | static LONG __stdcall MyCrashHandlerExceptionFilter(EXCEPTION_POINTERS *pEx) { | ||
|  | 	HANDLE file = CreateFileA("kinc.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | ||
|  | 	if (file != INVALID_HANDLE_VALUE) { | ||
|  | 		MINIDUMP_EXCEPTION_INFORMATION stMDEI; | ||
|  | 		stMDEI.ThreadId = GetCurrentThreadId(); | ||
|  | 		stMDEI.ExceptionPointers = pEx; | ||
|  | 		stMDEI.ClientPointers = TRUE; | ||
|  | 		MyMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpNormal, &stMDEI, NULL, NULL); | ||
|  | 		CloseHandle(file); | ||
|  | 	} | ||
|  | 	return EXCEPTION_CONTINUE_SEARCH; | ||
|  | } | ||
|  | 
 | ||
|  | static void init_crash_handler() { | ||
|  | 	HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); | ||
|  | 	if (dbghelp != NULL) { | ||
|  | 		MyMiniDumpWriteDump = (MiniDumpWriteDumpType)GetProcAddress(dbghelp, "MiniDumpWriteDump"); | ||
|  | 		SetUnhandledExceptionFilter(MyCrashHandlerExceptionFilter); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | #ifdef KINC_KONG
 | ||
|  | void kong_init(void); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | int kinc_init(const char *name, int width, int height, kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { | ||
|  | 	init_crash_handler(); | ||
|  | 
 | ||
|  | #if defined(KINC_VTUNE)
 | ||
|  | 	kinc_itt_domain = __itt_domain_create(name); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	// Pen functions are only in Windows 8 and later, so load them dynamically
 | ||
|  | 	HMODULE user32 = LoadLibraryA("user32.dll"); | ||
|  | 	MyGetPointerInfo = (GetPointerInfoType)GetProcAddress(user32, "GetPointerInfo"); | ||
|  | 	MyGetPointerPenInfo = (GetPointerPenInfoType)GetProcAddress(user32, "GetPointerPenInfo"); | ||
|  | 	MyEnableNonClientDpiScaling = (EnableNonClientDpiScalingType)GetProcAddress(user32, "EnableNonClientDpiScaling"); | ||
|  | 	initKeyTranslation(); | ||
|  | 	for (int i = 0; i < 256; ++i) { | ||
|  | 		keyPressed[i] = false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for (int i = 0; i < MAX_TOUCH_POINTS; i++) { | ||
|  | 		touchPoints[i].sysID = -1; | ||
|  | 		touchPoints[i].x = -1; | ||
|  | 		touchPoints[i].y = -1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	kinc_display_init(); | ||
|  | 
 | ||
|  | 	QueryPerformanceCounter(&startCount); | ||
|  | 	QueryPerformanceFrequency(&frequency); | ||
|  | 
 | ||
|  | 	for (int i = 0; i < 256; ++i) { | ||
|  | 		keyPressed[i] = false; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Kore::System::_init(name, width, height, &win, &frame);
 | ||
|  | 	kinc_set_application_name(name); | ||
|  | 	kinc_window_options_t defaultWin; | ||
|  | 	if (win == NULL) { | ||
|  | 		kinc_window_options_set_defaults(&defaultWin); | ||
|  | 		win = &defaultWin; | ||
|  | 	} | ||
|  | 	win->width = width; | ||
|  | 	win->height = height; | ||
|  | 	if (win->title == NULL) { | ||
|  | 		win->title = name; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	kinc_g4_internal_init(); | ||
|  | 
 | ||
|  | 	int window = kinc_window_create(win, frame); | ||
|  | 	loadXInput(); | ||
|  | 	initializeDirectInput(); | ||
|  | 
 | ||
|  | #ifdef KINC_KONG
 | ||
|  | 	kong_init(); | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 	return window; | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_internal_shutdown() { | ||
|  | 	kinc_windows_hide_windows(); | ||
|  | 	kinc_internal_shutdown_callback(); | ||
|  | 	kinc_windows_destroy_windows(); | ||
|  | 	kinc_g4_internal_destroy(); | ||
|  | 	kinc_windows_restore_displays(); | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_copy_to_clipboard(const char *text) { | ||
|  | 	wchar_t wtext[4096]; | ||
|  | 	MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096); | ||
|  | 	OpenClipboard(kinc_windows_window_handle(0)); | ||
|  | 	EmptyClipboard(); | ||
|  | 	size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t); | ||
|  | 	HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size); | ||
|  | 	void *data = GlobalLock(handle); | ||
|  | 	memcpy(data, wtext, size); | ||
|  | 	GlobalUnlock(handle); | ||
|  | 	SetClipboardData(CF_UNICODETEXT, handle); | ||
|  | 	CloseClipboard(); | ||
|  | } | ||
|  | 
 | ||
|  | int kinc_cpu_cores(void) { | ||
|  | 	SYSTEM_LOGICAL_PROCESSOR_INFORMATION info[1024]; | ||
|  | 	DWORD returnLength = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) * 1024; | ||
|  | 	BOOL success = GetLogicalProcessorInformation(&info[0], &returnLength); | ||
|  | 
 | ||
|  | 	int proper_cpu_count = 0; | ||
|  | 
 | ||
|  | 	if (success) { | ||
|  | 		DWORD byteOffset = 0; | ||
|  | 		PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = &info[0]; | ||
|  | 		while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) { | ||
|  | 			if (ptr->Relationship == RelationProcessorCore) { | ||
|  | 				++proper_cpu_count; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); | ||
|  | 			++ptr; | ||
|  | 		} | ||
|  | 	} | ||
|  | 	else { | ||
|  | 		proper_cpu_count = 1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return proper_cpu_count; | ||
|  | } | ||
|  | 
 | ||
|  | int kinc_hardware_threads(void) { | ||
|  | 	SYSTEM_INFO sysinfo; | ||
|  | 	GetSystemInfo(&sysinfo); | ||
|  | 	return sysinfo.dwNumberOfProcessors; | ||
|  | } |