#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DIRECTINPUT_VERSION 0x0800 #include #include #include #include #include #include #include #include #if defined(KINC_VTUNE) #include __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; }