From 783c002a64c2c1c20a308b7b574c15174660282e Mon Sep 17 00:00:00 2001 From: infvaL <38145742+infval@users.noreply.github.com> Date: Sun, 21 Oct 2018 20:51:11 +0300 Subject: [PATCH] Add Control Config (Win platform) --- platform/WIN/main.c | 532 +++++++++++++++++++++++++++----------- platform/WIN/resource.h | 3 +- platform/WIN/resources.rc | 7 +- 3 files changed, 385 insertions(+), 157 deletions(-) diff --git a/platform/WIN/main.c b/platform/WIN/main.c index 6b7b1e7..bcc8909 100644 --- a/platform/WIN/main.c +++ b/platform/WIN/main.c @@ -1,8 +1,14 @@ +#pragma comment(linker,"\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + #include //#include +//#pragma comment(lib, "Comctl32.lib") #include #include +// PathFindFileName() #include #pragma comment(lib, "Shlwapi.lib") @@ -17,11 +23,14 @@ #include "../../common/supervision.h" #include "../../common/sound.h" +#define SCREEN_W 160 +#define SCREEN_H 160 + #ifdef TERRIBLE_AUDIO_IMPLEMENTATION WAVEHDR whdr; HWAVEOUT hWaveOut; -// 44100 / 60 * 2 (channels) * 2 (16-bit) - 20 -#define BUFFER_SIZE 2920 +// 44100 / 60 * 2 (channels) * 2 (16-bit) +#define BUFFER_SIZE 2940 int8 audioBuffer[BUFFER_SIZE]; #endif @@ -32,8 +41,8 @@ LPCTSTR szClassName = _T("Potator"); #define WINDOW_STYLE (WS_SYSMENU | WS_MINIMIZEBOX | WS_CAPTION | WS_VISIBLE) #define WINDOW_EX_STYLE (WS_EX_CLIENTEDGE) +HWND hWindow; HMENU hMenu; -HWND hWnd; HDC hDC; DWORD threadID; @@ -43,12 +52,33 @@ char romName[MAX_PATH]; uint8 *buffer; uint32 bufferSize = 0; uint8 windowScale = 2; -uint16 screenBuffer[160 * 160]; +uint16 screenBuffer[SCREEN_W * SCREEN_H]; + +int keysMapping[] = { + VK_RIGHT + , VK_LEFT + , VK_DOWN + , VK_UP + , 'X' + , 'C' + , 'Z' + , VK_SPACE +}; +LPCTSTR keysNames[] = { + _T("Right") + , _T("Left") + , _T("Down") + , _T("Up") + , _T("B") + , _T("A") + , _T("Select") + , _T("Start") +}; #define UPDATE_RATE 60 - uint64 startCounter; uint64 freq; + void InitCounter(void) { QueryPerformanceFrequency((LARGE_INTEGER*)&freq); @@ -77,7 +107,7 @@ BOOL NeedUpdate(void) // FIXME: IPC (lock Load ROM before exit 'while (execute)') DWORD WINAPI run(LPVOID lpParameter) { - BITMAPV4HEADER bmi; + BITMAPV4HEADER bmi = { 0 }; TCHAR txt[64]; uint64 curticks = 0; uint64 fpsticks = 0; @@ -89,8 +119,6 @@ DWORD WINAPI run(LPVOID lpParameter) hTimer = CreateWaitableTimer(NULL, FALSE, NULL); SetWaitableTimer(hTimer, &dueTime, 1000 / UPDATE_RATE, NULL, NULL, TRUE); - //CreateBitmapIndirect(&bmi); - memset(&bmi, 0, sizeof(bmi)); bmi.bV4Size = sizeof(bmi); bmi.bV4Planes = 1; bmi.bV4BitCount = 16; @@ -98,22 +126,18 @@ DWORD WINAPI run(LPVOID lpParameter) bmi.bV4RedMask = 0x001F; bmi.bV4GreenMask = 0x03E0; bmi.bV4BlueMask = 0x7C00; - bmi.bV4Width = 160; - bmi.bV4Height = -160; + bmi.bV4Width = SCREEN_W; + bmi.bV4Height = -SCREEN_H; while (!finished) { InitCounter(); while (execute) { uint8 controls_state = 0; - if (GetAsyncKeyState(VK_RIGHT) & 0x8000) controls_state |= 0x01; - if (GetAsyncKeyState(VK_LEFT ) & 0x8000) controls_state |= 0x02; - if (GetAsyncKeyState(VK_DOWN ) & 0x8000) controls_state |= 0x04; - if (GetAsyncKeyState(VK_UP ) & 0x8000) controls_state |= 0x08; - if (GetAsyncKeyState('X' ) & 0x8000) controls_state |= 0x10; - if (GetAsyncKeyState('C' ) & 0x8000) controls_state |= 0x20; - if (GetAsyncKeyState('Z' ) & 0x8000) controls_state |= 0x40; - if (GetAsyncKeyState(VK_SPACE) & 0x8000) controls_state |= 0x80; + for (int i = 0; i < 8; i++) { + if (GetAsyncKeyState(keysMapping[i]) & 0x8000) + controls_state |= 1 << i; + } controls_state_write(0, controls_state); @@ -140,23 +164,22 @@ DWORD WINAPI run(LPVOID lpParameter) fpsticks = curticks; // Initial value if (curticks >= fpsticks) { _stprintf(txt, _T("%s (FPS: %d)"), szClassName, fpsframecount); - SetWindowText(hWnd, txt); + SetWindowText(hWindow, txt); fpsframecount = 0; fpsticks += freq; } RECT r; - GetClientRect(hWnd, &r); - LONG w = r.right - r.left; - LONG h = r.bottom - r.top; + GetClientRect(hWindow, &r); + LONG x = 0, w = r.right - r.left; + LONG y = 0, h = r.bottom - r.top; if (w != h) { // Center - LONG size = h / 160 * 160; - StretchDIBits(hDC, (w - size) / 2, (h - size) / 2, size, size, 0, 0, 160, 160, screenBuffer, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, SRCCOPY); - } - else { - StretchDIBits(hDC, 0, 0, w, h, 0, 0, 160, 160, screenBuffer, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, SRCCOPY); + LONG size = h / SCREEN_H * SCREEN_H; + x = (w - size) / 2; w = size; + y = (h - size) / 2; h = size; } + StretchDIBits(hDC, x, y, w, h, 0, 0, SCREEN_W, SCREEN_H, screenBuffer, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, SRCCOPY); } WaitForSingleObject(hTimer, INFINITE); @@ -200,15 +223,34 @@ int LoadROM(LPCTSTR fileName) return 0; } +void UpdateGhosting(void); +void UpdatePalette(void); + +void LoadBuffer(void) +{ + supervision_load(&buffer, bufferSize); + UpdateGhosting(); + UpdatePalette(); +} + +void StopEmulation(void) +{ + execute = FALSE; + Sleep(1000 / UPDATE_RATE * 4); // Wait for the end of execution +} + +void ResumeEmulation(void) +{ + execute = TRUE; +} + void UpdateWindowSize(void) { CheckMenuRadioItem(hMenu, IDM_SIZE1, IDM_SIZE6, IDM_SIZE1 + windowScale - 1, MF_BYCOMMAND); - - RECT r = { 0 }; - r.right = 160 * windowScale; - r.bottom = 160 * windowScale; + RECT r; + SetRect(&r, 0, 0, SCREEN_W * windowScale, SCREEN_H * windowScale); AdjustWindowRectEx(&r, WINDOW_STYLE, TRUE, WINDOW_EX_STYLE); - SetWindowPos(hWnd, NULL, 0, 0, r.right - r.left, r.bottom - r.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW); + SetWindowPos(hWindow, NULL, 0, 0, r.right - r.left, r.bottom - r.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW); } int currGhosting = 0; @@ -241,7 +283,7 @@ void InitMenu(void) AppendMenu(hMenuGhosting, MF_STRING, IDM_GHOSTING + i, buf); } HMENU hMenuOptions = GetSubMenu(hMenu, 1); - AppendMenu(hMenuOptions, MF_POPUP, (UINT_PTR)hMenuGhosting, _T("Ghosting")); + InsertMenu(hMenuOptions, 2, MF_POPUP | MF_BYPOSITION, (UINT_PTR)hMenuGhosting, _T("Ghosting")); UpdateGhosting(); HMENU hMenuPalette = CreateMenu(); @@ -250,144 +292,194 @@ void InitMenu(void) _stprintf(buf, _T("%d"), i); AppendMenu(hMenuPalette, MF_STRING, IDM_PALETTE + i, buf); } - AppendMenu(hMenuOptions, MF_POPUP, (UINT_PTR)hMenuPalette, _T("Palette")); + InsertMenu(hMenuOptions, 3, MF_POPUP | MF_BYPOSITION, (UINT_PTR)hMenuPalette, _T("Palette")); UpdatePalette(); } +BOOL showCursorFullscreen = FALSE; + +BOOL IsFullscreen(void) +{ + return GetWindowLongPtr(hWindow, GWL_STYLE) & WS_POPUP; +} + void ToggleFullscreen(void) { - static RECT lastPos = { 0 }; - if (GetWindowLongPtr(hWnd, GWL_STYLE) & WS_POPUP) { - SetWindowLongPtr(hWnd, GWL_STYLE, WINDOW_STYLE); - SetWindowLongPtr(hWnd, GWL_EXSTYLE, WINDOW_EX_STYLE); - SetWindowPos(hWnd, NULL, lastPos.left, lastPos.top, 0, 0, SWP_FRAMECHANGED); - UpdateWindowSize(); - SetMenu(hWnd, hMenu); + static WINDOWPLACEMENT wp; + if (IsFullscreen()) { + SetWindowLongPtr(hWindow, GWL_STYLE, WINDOW_STYLE); + SetWindowLongPtr(hWindow, GWL_EXSTYLE, WINDOW_EX_STYLE); + SetWindowPlacement(hWindow, &wp); + SetWindowPos(hWindow, NULL, 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); + SetMenu(hWindow, hMenu); + if (!showCursorFullscreen) + ShowCursor(TRUE); } else { + showCursorFullscreen = FALSE; + wp.length = sizeof(wp); + GetWindowPlacement(hWindow, &wp); + SetWindowLongPtr(hWindow, GWL_STYLE, WS_VISIBLE | WS_POPUP); + SetWindowLongPtr(hWindow, GWL_EXSTYLE, 0); int w = GetSystemMetrics(SM_CXSCREEN); int h = GetSystemMetrics(SM_CYSCREEN); - GetWindowRect(hWnd, &lastPos); - SetWindowLongPtr(hWnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); - SetWindowLongPtr(hWnd, GWL_EXSTYLE, 0); - SetWindowPos(hWnd, HWND_TOP, 0, 0, w, h, SWP_FRAMECHANGED); - SetMenu(hWnd, NULL); + // SWP_NOOWNERZORDER - prevent showing child windows on top + SetWindowPos(hWindow, HWND_TOP, 0, 0, w, h, SWP_FRAMECHANGED | SWP_NOOWNERZORDER); + SetMenu(hWindow, NULL); + ShowCursor(FALSE); RECT r = { 0, 0, w, h }; FillRect(hDC, &r, (HBRUSH)GetStockObject(BLACK_BRUSH)); } + InvalidateRect(hWindow, NULL, TRUE); } +void RegisterControlConfigClass(HWND); +void CreateControlConfigWindow(HWND); + LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - switch (msg) - { + static HFONT hFont; + switch (msg) { + case WM_CREATE: + hFont = CreateFont(0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, + ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Arial")); + RegisterControlConfigClass(hWnd); + break; case WM_COMMAND: - { - int wmId = LOWORD(wParam); - if (wmId >= IDM_GHOSTING && wmId <= IDM_GHOSTING + SV_GHOSTING_MAX) { - currGhosting = wmId - IDM_GHOSTING; - UpdateGhosting(); - break; - } - else if (wmId >= IDM_PALETTE && wmId <= IDM_PALETTE + SV_COLOR_SCHEME_COUNT - 1) { - currPalette = wmId - IDM_PALETTE; - UpdatePalette(); - break; - } - switch (wmId) - { - case IDM_OPEN: - { - execute = FALSE; // Stop emulation while opening new rom - TCHAR szFileName[MAX_PATH] = _T(""); - OPENFILENAME ofn; - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = hWnd; - ofn.lpstrFilter = _T("Supervision files (.sv, .ws, .bin)\0*.sv;*.ws;*.bin\0\0"); - ofn.nFilterIndex = 1; - ofn.lpstrFile = szFileName; - ofn.nMaxFile = MAX_PATH; - ofn.lpstrDefExt = _T("sv"); - ofn.Flags = OFN_NOCHANGEDIR; - - if (!GetOpenFileName(&ofn)) { - if (buffer) - execute = TRUE; - return 0; - } - - if (LoadROM(szFileName) == 0) { - supervision_load(&buffer, (uint32)bufferSize); - UpdateGhosting(); - UpdatePalette(); - execute = TRUE; - } - } - break; - case IDM_SAVE: - if (buffer) { - execute = FALSE; - Sleep(1000 / UPDATE_RATE * 4); // Wait for the end of execution - supervision_save_state(romName, 0); - execute = TRUE; - } - break; - case IDM_LOAD: - if (buffer) { - execute = FALSE; - Sleep(1000 / UPDATE_RATE * 4); // Wait for the end of execution - supervision_load_state(romName, 0); - execute = TRUE; - } - break; - case IDM_WEBSITE: - ShellExecute(NULL, _T("open"), _T("https://github.com/infval/potator"), NULL, NULL, SW_SHOWNORMAL); - break; - case IDM_ABOUT: - MessageBox(NULL, _T("Potator 1.0 (fork)"), szClassName, MB_ICONEXCLAMATION | MB_OK); - break; - case IDM_FULLSCREEN: - ToggleFullscreen(); - break; - case IDM_SIZE1: - case IDM_SIZE2: - case IDM_SIZE3: - case IDM_SIZE4: - case IDM_SIZE5: - case IDM_SIZE6: - windowScale = wmId - IDM_SIZE1 + 1; - UpdateWindowSize(); - break; - case IDM_EXIT: - DestroyWindow(hWnd); - break; - default: - return DefWindowProc(hWnd, msg, wParam, lParam); - } + { + int wmId = LOWORD(wParam); + if (wmId >= IDM_GHOSTING && wmId <= IDM_GHOSTING + SV_GHOSTING_MAX) { + currGhosting = wmId - IDM_GHOSTING; + UpdateGhosting(); + break; } + else if (wmId >= IDM_PALETTE && wmId <= IDM_PALETTE + SV_COLOR_SCHEME_COUNT - 1) { + currPalette = wmId - IDM_PALETTE; + UpdatePalette(); + break; + } + switch (wmId) { + case IDM_OPEN: + { + execute = FALSE; // Stop emulation while opening new rom + TCHAR szFileName[MAX_PATH] = _T(""); + OPENFILENAME ofn; + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hWnd; + ofn.lpstrFilter = _T("Supervision files (.sv, .ws, .bin)\0*.sv;*.ws;*.bin\0\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrDefExt = _T("sv"); + ofn.Flags = OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + if (!GetOpenFileName(&ofn)) { + if (buffer) + execute = TRUE; + return 0; + } + + if (LoadROM(szFileName) == 0) { + LoadBuffer(); + execute = TRUE; + } + } + break; + case IDM_SAVE: + if (buffer) { + StopEmulation(); + supervision_save_state(romName, 0); + ResumeEmulation(); + } + break; + case IDM_LOAD: + if (buffer) { + StopEmulation(); + supervision_load_state(romName, 0); + ResumeEmulation(); + } + break; + case IDM_WEBSITE: + ShellExecute(NULL, _T("open"), _T("https://github.com/infval/potator"), NULL, NULL, SW_SHOWNORMAL); + break; + case IDM_ABOUT: + MessageBox(NULL, _T("Potator 1.0 (fork)"), szClassName, MB_ICONEXCLAMATION | MB_OK); + break; + case IDM_FULLSCREEN: + ToggleFullscreen(); + break; + case IDM_SIZE1: + case IDM_SIZE2: + case IDM_SIZE3: + case IDM_SIZE4: + case IDM_SIZE5: + case IDM_SIZE6: + windowScale = wmId - IDM_SIZE1 + 1; + UpdateWindowSize(); + break; + case IDM_CONTROL_CONFIG: + CreateControlConfigWindow(hWnd); + break; + case IDM_EXIT: + DestroyWindow(hWnd); + break; + default: + return DefWindowProc(hWnd, msg, wParam, lParam); + } + } break; case WM_DROPFILES: - { - execute = FALSE; - Sleep(1000 / UPDATE_RATE * 4); // Wait for the end of execution - HDROP hDrop = (HDROP)wParam; - TCHAR szFileName[MAX_PATH] = _T(""); - DragQueryFile(hDrop, 0, szFileName, MAX_PATH); - DragFinish(hDrop); + { + StopEmulation(); + HDROP hDrop = (HDROP)wParam; + TCHAR szFileName[MAX_PATH] = _T(""); + DragQueryFile(hDrop, 0, szFileName, MAX_PATH); + DragFinish(hDrop); - if (LoadROM(szFileName) == 0) { - supervision_load(&buffer, (uint32)bufferSize); - UpdateGhosting(); - UpdatePalette(); - execute = TRUE; - } + if (LoadROM(szFileName) == 0) { + LoadBuffer(); + ResumeEmulation(); } + } break; case WM_LBUTTONDBLCLK: ToggleFullscreen(); break; + case WM_SYSCOMMAND: + // Alt or F10 show menu + if (wParam == SC_KEYMENU && IsFullscreen()) { + showCursorFullscreen = !showCursorFullscreen; + ShowCursor(showCursorFullscreen); + SetMenu(hWindow, showCursorFullscreen ? hMenu : NULL); + return DefWindowProc(hWnd, msg, wParam, lParam); // If break;, menu doesn't get focus + } + return DefWindowProc(hWnd, msg, wParam, lParam); + case WM_PAINT: + { + if (!buffer && !IsFullscreen()) { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + SelectObject(hdc, hFont); + TEXTMETRIC tm; + GetTextMetrics(hdc, &tm); + SetTextColor(hdc, RGB(255, 255, 255)); + SetBkColor(hdc, RGB(0, 0, 0)); +#define X(s) _T(s), _countof(s) + TextOut(hdc, 8, 8 , X("Open ROM:")); + TextOut(hdc, 8, 8 + tm.tmHeight * 1, X("Drag & Drop")); + TextOut(hdc, 8, 8 + tm.tmHeight * 3, X("Toggle Fullscreen:")); + TextOut(hdc, 8, 8 + tm.tmHeight * 4, X("Double-Click or Alt+Enter")); + TextOut(hdc, 8, 8 + tm.tmHeight * 6, X("Toggle Menu in Fullscreen:")); + TextOut(hdc, 8, 8 + tm.tmHeight * 7, X("Press Alt or F10")); +#undef X + EndPaint(hWnd, &ps); + } + } + break; case WM_CLOSE: + DeleteObject(hFont); DestroyWindow(hWnd); break; case WM_DESTROY: @@ -399,9 +491,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) return 0; } -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { - //InitCommonControlsEx + //INITCOMMONCONTROLSEX ccs = { 0 }; + //ccs.dwSize = sizeof(ccs); + //ccs.dwICC = ICC_STANDARD_CLASSES; + //InitCommonControlsEx(&ccs); supervision_init(); @@ -424,23 +519,23 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine return FALSE; } - hWnd = CreateWindowEx(WINDOW_EX_STYLE, szClassName, szClassName, WINDOW_STYLE, + hWindow = CreateWindowEx(WINDOW_EX_STYLE, szClassName, szClassName, WINDOW_STYLE, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); - if (!hWnd) { + if (!hWindow) { MessageBox(NULL, _T("Window creation failed!"), szClassName, MB_ICONEXCLAMATION | MB_OK); return FALSE; } - DragAcceptFiles(hWnd, TRUE); + DragAcceptFiles(hWindow, TRUE); - hDC = GetDC(hWnd); - hMenu = GetMenu(hWnd); + hDC = GetDC(hWindow); + hMenu = GetMenu(hWindow); UpdateWindowSize(); InitMenu(); - ShowWindow(hWnd, nCmdShow); - UpdateWindow(hWnd); + ShowWindow(hWindow, nCmdShow); + UpdateWindow(hWindow); #ifdef TERRIBLE_AUDIO_IMPLEMENTATION whdr.lpData = (LPSTR)audioBuffer; @@ -475,9 +570,138 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine } } - ReleaseDC(hWnd, hDC); + ReleaseDC(hWindow, hDC); UnregisterClass(wcex.lpszClassName, hInstance); return (int)msg.wParam; } + +// Control Config + +WPARAM MapLeftRightKeys(WPARAM vk, LPARAM lParam); +void VirtualKeyCodeToString(UINT virtualKey, LPTSTR szName, int size); + +HWND hButton; +int buttonId; + +LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam) +{ + if (code < 0) { + return CallNextHookEx(NULL, code, wParam, lParam); + } + MSG msg = *((MSG*)lParam); + if (msg.message == WM_KEYDOWN) { + if (hButton) { + TCHAR buf[32] = { 0 }; + GetKeyNameText(msg.lParam, buf, _countof(buf)); + SetWindowText(hButton, buf); + EnableWindow(hButton, TRUE); + hButton = NULL; + keysMapping[buttonId - 1] = (int)MapLeftRightKeys(msg.wParam, msg.lParam); + } + } + return CallNextHookEx(NULL, code, wParam, lParam); +} + +HWND hControlConfigWindow; + +LRESULT CALLBACK ControlConfigProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static HHOOK hHook; + static HFONT hFont; + switch (msg) { + case WM_CREATE: + hButton = NULL; + buttonId = 0; + hFont = CreateFont(0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, + ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Arial")); + for (int i = 0; i < 8; i++) { + HWND h = CreateWindow(_T("static"), keysNames[i], WS_VISIBLE | WS_CHILD, + 11, 11 + i * 30, 80, 25, hwnd, (HMENU)((UINT_PTR)i + 1 + 8), NULL, NULL); + SendMessage(h, WM_SETFONT, (WPARAM)hFont, TRUE); + TCHAR name[32]; + VirtualKeyCodeToString(keysMapping[i], name, _countof(name)); + h = CreateWindow(_T("button"), name, WS_VISIBLE | WS_CHILD, + 80, 8 + i * 30, 120, 25, hwnd, (HMENU)((UINT_PTR)i + 1), NULL, NULL); + SendMessage(h, WM_SETFONT, (WPARAM)hFont, TRUE); + } + hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, GetModuleHandle(NULL), GetCurrentThreadId()); + break; + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + if (wmId >= 1 && wmId <= 8) { + if (hButton) break; + hButton = (HWND)lParam; + buttonId = wmId; + EnableWindow(hButton, FALSE); + SetWindowText(hButton, _T("Press Key")); + SetFocus(hwnd); + } + } + break; + case WM_CLOSE: + hControlConfigWindow = NULL; + DeleteObject(hFont); + UnhookWindowsHookEx(hHook); + DestroyWindow(hwnd); + break; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +void RegisterControlConfigClass(HWND hwnd) +{ + WNDCLASSEX wc = { 0 }; + wc.cbSize = sizeof(WNDCLASSEX); + wc.hInstance = GetModuleHandle(NULL); + wc.lpszClassName = _T("ControlConfigClass"); + wc.lpfnWndProc = ControlConfigProc; + wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE); + RegisterClassEx(&wc); +} + +void CreateControlConfigWindow(HWND hwnd) +{ + if (hControlConfigWindow) + return; + RECT r; + GetWindowRect(hWindow, &r); + hControlConfigWindow = CreateWindowEx(WS_EX_DLGMODALFRAME, _T("ControlConfigClass"), _T("Control Config"), + WS_VISIBLE | WS_SYSMENU | WS_CAPTION, r.left + 64, r.top + 64, 228, 290, + hwnd, NULL, GetModuleHandle(NULL), NULL); +} + +// https://stackoverflow.com/a/15977613 +WPARAM MapLeftRightKeys(WPARAM virtualKey, LPARAM lParam) +{ + UINT scancode = (lParam & 0x00ff0000) >> 16; + int extended = (lParam & 0x01000000) != 0; + switch (virtualKey) { + case VK_SHIFT: + return MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX); + case VK_CONTROL: + return extended ? VK_RCONTROL : VK_LCONTROL; + case VK_MENU: + return extended ? VK_RMENU : VK_LMENU; + } + return virtualKey; +} + +// https://stackoverflow.com/a/38107083 +void VirtualKeyCodeToString(UINT virtualKey, LPTSTR szName, int size) +{ + UINT scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC); + switch (virtualKey) { + case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: + case VK_RCONTROL: case VK_RMENU: + case VK_LWIN: case VK_RWIN: case VK_APPS: + case VK_INSERT: case VK_DELETE: + case VK_HOME: case VK_END: + case VK_PRIOR: case VK_NEXT: + case VK_NUMLOCK: case VK_DIVIDE: + scanCode |= KF_EXTENDED; + } + GetKeyNameText(scanCode << 16, szName, size); +} diff --git a/platform/WIN/resource.h b/platform/WIN/resource.h index 2c7805d..47d5602 100644 --- a/platform/WIN/resource.h +++ b/platform/WIN/resource.h @@ -17,8 +17,9 @@ #define IDM_SIZE6 112 #define IDM_FULLSCREEN 113 +#define IDM_CONTROL_CONFIG 114 -#define IDM_GHOSTING 114 +#define IDM_GHOSTING 115 #define IDM_PALETTE (IDM_GHOSTING + SV_GHOSTING_MAX + 1) #endif diff --git a/platform/WIN/resources.rc b/platform/WIN/resources.rc index a966c46..a8fdde4 100644 --- a/platform/WIN/resources.rc +++ b/platform/WIN/resources.rc @@ -6,7 +6,7 @@ MENU_PRINCIPAL MENU BEGIN POPUP "&File" BEGIN - MENUITEM "&Open\tCtrl+O",IDM_OPEN + MENUITEM "&Open ROM...\tCtrl+O",IDM_OPEN MENUITEM "&Load State\t2", IDM_LOAD MENUITEM "&Save State\t1", IDM_SAVE MENUITEM SEPARATOR @@ -24,6 +24,7 @@ BEGIN MENUITEM "&5x",IDM_SIZE5 MENUITEM "&6x",IDM_SIZE6 END + MENUITEM "&Control Config...\tCtrl+I",IDM_CONTROL_CONFIG END POPUP "&Help" BEGIN @@ -35,8 +36,10 @@ END IDR_MAIN_ACCEL ACCELERATORS DISCARDABLE BEGIN - "O", IDM_OPEN, VIRTKEY, CONTROL, NOINVERT + "O", IDM_OPEN, VIRTKEY, CONTROL "2", IDM_LOAD, VIRTKEY "1", IDM_SAVE, VIRTKEY VK_F11, IDM_FULLSCREEN, VIRTKEY + VK_RETURN, IDM_FULLSCREEN, VIRTKEY, ALT + "I", IDM_CONTROL_CONFIG, VIRTKEY, CONTROL END