/* This file is part of BKBTL. BKBTL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. BKBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with BKBTL. If not, see . */ // ScreenView.cpp #include "stdafx.h" #include #include #include #include "Main.h" #include "Views.h" #include "Emulator.h" #include "util/BitmapFile.h" ////////////////////////////////////////////////////////////////////// #define COLOR_BK_BACKGROUND RGB(115,115,115) HWND g_hwndScreen = NULL; // Screen View window handle HDRAWDIB m_hdd = NULL; BITMAPINFO m_bmpinfo; HBITMAP m_hbmp = NULL; DWORD * m_bits = NULL; int m_cxScreenWidth = BK_SCREEN_WIDTH; int m_cyScreenHeight = BK_SCREEN_HEIGHT; int m_xScreenOffset = 0; int m_yScreenOffset = 0; BYTE m_ScreenKeyState[256]; int m_ScreenMode = 0; void ScreenView_CreateDisplay(); void ScreenView_OnDraw(HDC hdc); //BOOL ScreenView_OnKeyEvent(WPARAM vkey, BOOL okExtKey, BOOL okPressed); void ScreenView_OnRButtonDown(int mousex, int mousey); const int KEYEVENT_QUEUE_SIZE = 32; WORD m_ScreenKeyQueue[KEYEVENT_QUEUE_SIZE]; int m_ScreenKeyQueueTop = 0; int m_ScreenKeyQueueBottom = 0; int m_ScreenKeyQueueCount = 0; void ScreenView_PutKeyEventToQueue(WORD keyevent); WORD ScreenView_GetKeyEventFromQueue(); ////////////////////////////////////////////////////////////////////// void ScreenView_RegisterClass() { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = ScreenViewWndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = g_hInst; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = NULL; //(HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; wcex.lpszClassName = CLASSNAME_SCREENVIEW; wcex.hIconSm = NULL; RegisterClassEx(&wcex); } void ScreenView_Init() { m_hdd = DrawDibOpen(); ScreenView_CreateDisplay(); } void ScreenView_Done() { if (m_hbmp != NULL) { VERIFY(::DeleteObject(m_hbmp)); m_hbmp = NULL; } DrawDibClose( m_hdd ); } void ScreenView_CreateDisplay() { ASSERT(g_hwnd != NULL); if (m_hbmp != NULL) { VERIFY(::DeleteObject(m_hbmp)); m_hbmp = NULL; } HDC hdc = ::GetDC(g_hwnd); m_bmpinfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); m_bmpinfo.bmiHeader.biWidth = m_cxScreenWidth; m_bmpinfo.bmiHeader.biHeight = m_cyScreenHeight; m_bmpinfo.bmiHeader.biPlanes = 1; m_bmpinfo.bmiHeader.biBitCount = 32; m_bmpinfo.bmiHeader.biCompression = BI_RGB; m_bmpinfo.bmiHeader.biSizeImage = 0; m_bmpinfo.bmiHeader.biXPelsPerMeter = 0; m_bmpinfo.bmiHeader.biYPelsPerMeter = 0; m_bmpinfo.bmiHeader.biClrUsed = 0; m_bmpinfo.bmiHeader.biClrImportant = 0; m_hbmp = CreateDIBSection( hdc, &m_bmpinfo, DIB_RGB_COLORS, (void **)&m_bits, NULL, 0 ); VERIFY(::ReleaseDC(g_hwnd, hdc)); } // Create Screen View as child of Main Window void ScreenView_Create(HWND hwndParent, int x, int y) { ASSERT(hwndParent != NULL); int xLeft = x; int yTop = y; int cyScreenHeight = 4 + BK_SCREEN_HEIGHT + 4; int cyHeight = cyScreenHeight; int cxWidth = 4 + m_cxScreenWidth + 4; g_hwndScreen = CreateWindow( CLASSNAME_SCREENVIEW, NULL, WS_CHILD | WS_VISIBLE, xLeft, yTop, cxWidth, cyHeight, hwndParent, NULL, g_hInst, NULL); // Initialize m_ScreenKeyState VERIFY(::GetKeyboardState(m_ScreenKeyState)); } LRESULT CALLBACK ScreenViewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_COMMAND: ::PostMessage(g_hwnd, WM_COMMAND, wParam, lParam); break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); ScreenView_PrepareScreen(); //DEBUG ScreenView_OnDraw(hdc); EndPaint(hWnd, &ps); } break; case WM_LBUTTONDOWN: SetFocus(hWnd); break; case WM_RBUTTONDOWN: ScreenView_OnRButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); break; //case WM_KEYDOWN: // //if ((lParam & (1 << 30)) != 0) // Auto-repeats should be ignored // // return (LRESULT) TRUE; // //return (LRESULT) ScreenView_OnKeyEvent(wParam, (lParam & (1 << 24)) != 0, TRUE); // return (LRESULT) TRUE; //case WM_KEYUP: // //return (LRESULT) ScreenView_OnKeyEvent(wParam, (lParam & (1 << 24)) != 0, FALSE); // return (LRESULT) TRUE; case WM_SETCURSOR: if (::GetFocus() == g_hwndScreen) { SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); return (LRESULT) TRUE; } else return DefWindowProc(hWnd, message, wParam, lParam); default: return DefWindowProc(hWnd, message, wParam, lParam); } return (LRESULT)FALSE; } void ScreenView_OnRButtonDown(int mousex, int mousey) { ::SetFocus(g_hwndScreen); HMENU hMenu = ::CreatePopupMenu(); ::AppendMenu(hMenu, 0, ID_FILE_SCREENSHOT, _T("Screenshot")); ::AppendMenu(hMenu, 0, ID_FILE_SCREENSHOTTOCLIPBOARD, _T("Screenshot to Clipboard")); POINT pt = { mousex, mousey }; ::ClientToScreen(g_hwndScreen, &pt); ::TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, g_hwndScreen, NULL); VERIFY(::DestroyMenu(hMenu)); } int ScreenView_GetScreenMode() { return m_ScreenMode; } void ScreenView_SetScreenMode(int newMode) { if (m_ScreenMode == newMode) return; m_ScreenMode = newMode; // Ask Emulator module for screen width and height int cxWidth, cyHeight; Emulator_GetScreenSize(newMode, &cxWidth, &cyHeight); m_cxScreenWidth = cxWidth; m_cyScreenHeight = cyHeight; ScreenView_CreateDisplay(); ScreenView_RedrawScreen(); } void ScreenView_OnDraw(HDC hdc) { if (m_bits == NULL) return; HBRUSH hBrush = ::CreateSolidBrush(COLOR_BK_BACKGROUND); HGDIOBJ hOldBrush = ::SelectObject(hdc, hBrush); RECT rc; ::GetClientRect(g_hwndScreen, &rc); m_xScreenOffset = 0; m_yScreenOffset = 0; if (rc.right > m_cxScreenWidth) { m_xScreenOffset = (rc.right - m_cxScreenWidth) / 2; ::PatBlt(hdc, 0, 0, m_xScreenOffset, rc.bottom, PATCOPY); ::PatBlt(hdc, rc.right, 0, m_cxScreenWidth + m_xScreenOffset - rc.right, rc.bottom, PATCOPY); } if (rc.bottom > m_cyScreenHeight) { m_yScreenOffset = (rc.bottom - m_cyScreenHeight) / 2; ::PatBlt(hdc, m_xScreenOffset, 0, m_cxScreenWidth, m_yScreenOffset, PATCOPY); int frombottom = rc.bottom - m_yScreenOffset - m_cyScreenHeight; ::PatBlt(hdc, m_xScreenOffset, rc.bottom, m_cxScreenWidth, -frombottom, PATCOPY); } ::SelectObject(hdc, hOldBrush); VERIFY(::DeleteObject(hBrush)); DrawDibDraw(m_hdd, hdc, m_xScreenOffset, m_yScreenOffset, -1, -1, &m_bmpinfo.bmiHeader, m_bits, 0, 0, m_cxScreenWidth, m_cyScreenHeight, 0); } void ScreenView_RedrawScreen() { ScreenView_PrepareScreen(); HDC hdc = ::GetDC(g_hwndScreen); ScreenView_OnDraw(hdc); VERIFY(::ReleaseDC(g_hwndScreen, hdc)); } void ScreenView_PrepareScreen() { if (m_bits == NULL) return; Emulator_PrepareScreenRGB32(m_bits, m_ScreenMode); } void ScreenView_PutKeyEventToQueue(WORD keyevent) { // DebugPrintFormat(_T("Scan: 0x%0x\r\n"), keyevent & 127); if (m_ScreenKeyQueueCount == KEYEVENT_QUEUE_SIZE) return; // Full queue m_ScreenKeyQueue[m_ScreenKeyQueueTop] = keyevent; m_ScreenKeyQueueTop++; if (m_ScreenKeyQueueTop >= KEYEVENT_QUEUE_SIZE) m_ScreenKeyQueueTop = 0; m_ScreenKeyQueueCount++; } WORD ScreenView_GetKeyEventFromQueue() { if (m_ScreenKeyQueueCount == 0) return 0; // Empty queue WORD keyevent = m_ScreenKeyQueue[m_ScreenKeyQueueBottom]; m_ScreenKeyQueueBottom++; if (m_ScreenKeyQueueBottom >= KEYEVENT_QUEUE_SIZE) m_ScreenKeyQueueBottom = 0; m_ScreenKeyQueueCount--; return keyevent; } // АР2 = Ctrl; // Ins = ВС; Tab = ТАБ; // РУС = End, 0x23; ЛАТ = Home, 0x24; // джойстик = NumPad; //TODO: СТОП = Break, 0x13; const BYTE arrPcscan2BkscanRus[256] = // BK keys from PC keys, РУС { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /*0*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0030, 0015, 0000, 0000, 0000, 0012, 0000, 0000, /*1*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*2*/ 0040, 0000, 0000, 0016, 0017, 0010, 0032, 0031, 0033, 0000, 0000, 0000, 0000, 0023, 0000, 0000, /*3*/ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, 0070, 0071, 0000, 0000, 0000, 0000, 0000, 0000, /*4*/ 0000, 0106, 0111, 0123, 0127, 0125, 0101, 0120, 0122, 0133, 0117, 0114, 0104, 0120, 0124, 0135, /*5*/ 0132, 0112, 0113, 0131, 0105, 0107, 0115, 0103, 0136, 0116, 0121, 0000, 0000, 0000, 0000, 0000, /*6*/ 0000, 0211, 0215, 0213, 0216, 0000, 0214, 0210, 0217, 0212, 0000, 0000, 0000, 0000, 0000, 0000, /*7*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*8*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*9*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*a*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*b*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0126, 0000, 0102, 0055, 0100, 0000, /*c*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*d*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0110, 0000, 0137, 0134, 0000, /*e*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*f*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, }; const BYTE arrPcscan2BkscanLat[256] = // BK keys from PC keys, ЛАТ { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /*0*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0030, 0015, 0000, 0000, 0000, 0012, 0000, 0000, /*1*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*2*/ 0040, 0000, 0000, 0016, 0017, 0010, 0032, 0031, 0033, 0000, 0000, 0000, 0000, 0023, 0000, 0000, /*3*/ 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, 0070, 0071, 0000, 0000, 0000, 0000, 0000, 0000, /*4*/ 0000, 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117, /*5*/ 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127, 0130, 0131, 0132, 0000, 0000, 0000, 0000, 0000, /*6*/ 0000, 0211, 0215, 0213, 0216, 0000, 0214, 0210, 0217, 0212, 0000, 0000, 0000, 0000, 0000, 0000, /*7*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*8*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*9*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*a*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*b*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0052, 0000, 0074, 0055, 0076, 0000, /*c*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*d*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0133, 0000, 0135, 0134, 0000, /*e*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*f*/ 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, }; void ScreenView_ScanKeyboard() { if (! g_okEmulatorRunning) return; if (::GetFocus() == g_hwndScreen) { // Read current keyboard state BYTE keys[256]; VERIFY(::GetKeyboardState(keys)); BOOL okShift = ((keys[VK_SHIFT] & 128) != 0); BOOL okCtrl = ((keys[VK_CONTROL] & 128) != 0); WORD bkregister = g_pBoard->GetKeyboardRegister(); // Выбираем таблицу маппинга в зависимости от флага РУС/ЛАТ в БК const BYTE* pTable = ((bkregister & KEYB_LAT) == 0) ? arrPcscan2BkscanRus : arrPcscan2BkscanLat; // Check every key for state change for (int scan = 0; scan < 256; scan++) { BYTE newstate = keys[scan]; BYTE oldstate = m_ScreenKeyState[scan]; if ((newstate & 128) != (oldstate & 128)) // Key state changed - key pressed or released { BYTE pcscan = (BYTE)scan; // DebugPrintFormat(_T("Key PC: 0x%0x 0x%0x 0x%0x\r\n"), scan, keys[VK_SHIFT], keys[VK_CONTROL]); BYTE bkscan = pTable[pcscan]; if (bkscan != 0) { if (okShift && bkscan >= 0100 && bkscan <= 0137) bkscan += 040; else if (okShift && bkscan >= 0060 && bkscan <= 0077) bkscan -= 020; BYTE pressed = (newstate & 128) | (okCtrl ? 64 : 0); WORD keyevent = MAKEWORD(bkscan, pressed); ScreenView_PutKeyEventToQueue(keyevent); KeyboardView_KeyEvent(bkscan, pressed); } } } // Save keyboard state ::memcpy(m_ScreenKeyState, keys, 256); } } void ScreenView_ProcessKeyboard() { // Process next event in the keyboard queue WORD keyevent = ScreenView_GetKeyEventFromQueue(); if (keyevent != 0) { bool pressed = ((keyevent & 0x8000) != 0); bool ctrl = ((keyevent & 0x4000) != 0); BYTE bkscan = LOBYTE(keyevent); if (((bkscan & 0370) == 0260) && Settings_GetJoystick() != 0) // Skip joystick events if NumPad joystick is off return; // TCHAR bufoct[7]; PrintOctalValue(bufoct, bkscan); // DebugPrintFormat(_T("KeyEvent: %s %d %d\r\n"), bufoct, pressed, ctrl); g_pBoard->KeyboardEvent(bkscan, pressed, ctrl); } } // External key event - e.g. from KeyboardView void ScreenView_KeyEvent(BYTE keyscan, BOOL pressed) { ScreenView_PutKeyEventToQueue(MAKEWORD(keyscan, pressed ? 128 : 0)); } BOOL ScreenView_SaveScreenshot(LPCTSTR sFileName) { ASSERT(sFileName != NULL); ASSERT(m_bits != NULL); DWORD* pBits = (DWORD*)::calloc(m_cxScreenWidth * m_cyScreenHeight, sizeof(uint32_t)); Emulator_PrepareScreenRGB32(pBits, m_ScreenMode); LPCTSTR sFileNameExt = _tcsrchr(sFileName, _T('.')); BitmapFileFormat format = BitmapFileFormatPng; if (sFileNameExt != NULL && _tcsicmp(sFileNameExt, _T(".bmp")) == 0) format = BitmapFileFormatBmp; else if (sFileNameExt != NULL && (_tcsicmp(sFileNameExt, _T(".tif")) == 0 || _tcsicmp(sFileNameExt, _T(".tiff")) == 0)) format = BitmapFileFormatTiff; bool result = BitmapFile_SaveImageFile( (const uint32_t *)pBits, sFileName, format, m_cxScreenWidth, m_cyScreenHeight); ::free(pBits); return result; } HGLOBAL ScreenView_GetScreenshotAsDIB() { void* pBits = ::calloc(m_cxScreenWidth * m_cyScreenHeight, 4); Emulator_PrepareScreenRGB32(pBits, m_ScreenMode); BITMAPINFOHEADER bi; ::ZeroMemory(&bi, sizeof(BITMAPINFOHEADER)); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = m_cxScreenWidth; bi.biHeight = m_cyScreenHeight; bi.biPlanes = 1; bi.biBitCount = 32; bi.biCompression = BI_RGB; bi.biSizeImage = bi.biWidth * bi.biHeight * 4; HGLOBAL hDIB = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(BITMAPINFOHEADER) + bi.biSizeImage); if (hDIB == NULL) { ::free(pBits); return NULL; } LPBYTE p = (LPBYTE)::GlobalLock(hDIB); ::CopyMemory(p, &bi, sizeof(BITMAPINFOHEADER)); p += sizeof(BITMAPINFOHEADER); for (int line = 0; line < m_cyScreenHeight; line++) { LPBYTE psrc = (LPBYTE)pBits + line * m_cxScreenWidth * 4; ::CopyMemory(p, psrc, m_cxScreenWidth * 4); p += m_cxScreenWidth * 4; } ::GlobalUnlock(hDIB); ::free(pBits); return hDIB; } //////////////////////////////////////////////////////////////////////