1
0
mirror of synced 2026-01-26 19:21:32 +03:00
Files
bkbtl/emulator/ScreenView.cpp
2024-06-12 14:20:39 +03:00

482 lines
17 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 <http://www.gnu.org/licenses/>. */
// ScreenView.cpp
#include "stdafx.h"
#include <windowsx.h>
#include <mmintrin.h>
#include <Vfw.h>
#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;
}
//////////////////////////////////////////////////////////////////////