mirror of
https://github.com/AppleWin/AppleWin.git
synced 2026-10-25 14:03:18 +00:00
1. Registry functions The set of functions are changed to consistently use bool arguments. The macros are changed to inline functions to improve type checking. RegSaveValue() are overloaded with help of templates to reduce typecasting at call sites. For instance, bool value would be converted consistently instead of relying on call sites to convert bool to int values. 2. Memory functions BOOL usages were unnecessary. Mostly internal usages which can simply be bool. 3. FrameBase Some member variables are changed to bool. BOOL was used because of those Registry functions. Additional changes: - Some expressions are forced to be bool when assigning to bool variables.
3474 lines
106 KiB
C++
3474 lines
106 KiB
C++
/*
|
|
AppleWin : An Apple //e emulator for Windows
|
|
|
|
Copyright (C) 1994-1996, Michael O'Brien
|
|
Copyright (C) 1999-2001, Oliver Schmidt
|
|
Copyright (C) 2002-2005, Tom Charlesworth
|
|
Copyright (C) 2006-2014, Tom Charlesworth, Michael Pohoreski
|
|
|
|
AppleWin is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
AppleWin 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with AppleWin; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* Description: Frame
|
|
*
|
|
* Author: Various
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include <sstream>
|
|
#include <ctime>
|
|
#if _MSVC_LANG >= 201703L // Compiler option: /std:c++17
|
|
#define _HAS_CXX17 1
|
|
#include <filesystem>
|
|
#endif
|
|
#include "Windows/Win32Frame.h"
|
|
#include "Windows/AppleWin.h"
|
|
#include "CmdLine.h"
|
|
#include "Interface.h"
|
|
#include "Keyboard.h"
|
|
#include "Log.h"
|
|
#include "Memory.h"
|
|
#include "Mockingboard.h"
|
|
#include "MouseInterface.h"
|
|
#include "Windows/DirectInput.h"
|
|
#include "Windows/DXSoundBuffer.h"
|
|
#include "NTSC.h"
|
|
#include "ParallelPrinter.h"
|
|
#include "Pravets.h"
|
|
#include "Registry.h"
|
|
#include "SaveState.h"
|
|
#include "SerialComms.h"
|
|
#include "Uthernet1.h"
|
|
#include "Uthernet2.h"
|
|
#include "Speaker.h"
|
|
#include "Utilities.h"
|
|
#include "CardManager.h"
|
|
#include "Configuration/PropertySheet.h"
|
|
#include "Debugger/Debug.h"
|
|
#include "ProDOS_Utils.h"
|
|
#include "../resource/resource.h"
|
|
|
|
//#define ENABLE_MENU 0
|
|
#define DEBUG_KEY_MESSAGES 0
|
|
|
|
static bool FileExists(std::string strFilename);
|
|
|
|
// Must keep in sync with Disk_Status_e g_aDiskFullScreenColors
|
|
static const uint32_t g_aDiskFullScreenColorsLED[ NUM_DISK_STATUS ] =
|
|
{
|
|
RGB( 0, 0, 0), // DISK_STATUS_OFF BLACK
|
|
RGB( 0,255, 0), // DISK_STATUS_READ GREEN
|
|
RGB(255, 0, 0), // DISK_STATUS_WRITE RED
|
|
RGB(255,128, 0), // DISK_STATUS_PROT ORANGE
|
|
RGB( 0, 0,255), // DISK_STATUS_EMPTY -blue-
|
|
RGB( 0,128,128) // DISK_STATUS_SPIN -cyan-
|
|
};
|
|
|
|
static int Util_SelectDiskImage ( const HWND hwnd, const HINSTANCE hInstance, const char * pTitle, const bool bSave, char *pFilename, const char *pFilter )
|
|
{
|
|
OPENFILENAME ofn;
|
|
memset(&ofn, 0, sizeof(OPENFILENAME));
|
|
|
|
ofn.lStructSize = sizeof(OPENFILENAME);
|
|
ofn.hwndOwner = hwnd;
|
|
ofn.hInstance = hInstance;
|
|
ofn.lpstrFilter = pFilter;
|
|
ofn.lpstrFile = pFilename;
|
|
ofn.nMaxFile = MAX_PATH; // sizeof(szFilename);
|
|
ofn.lpstrInitialDir = g_sCurrentDir.c_str();
|
|
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
|
|
ofn.lpstrTitle = pTitle;
|
|
|
|
if( bSave )
|
|
ofn.Flags |= OFN_FILEMUSTEXIST;
|
|
|
|
int nRes = bSave ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
|
|
return nRes;
|
|
}
|
|
|
|
void Win32Frame::SetAltEnterToggleFullScreen(bool mode)
|
|
{
|
|
g_bAltEnter_ToggleFullScreen = mode;
|
|
}
|
|
|
|
// ==========================================================================
|
|
|
|
// Display construction:
|
|
// . Apple II video gets rendered to the framebuffer (maybe with some preliminary/final NTSC data in the border areas)
|
|
// . The *borderless* framebuffer is stretchblt() copied to the frame DC, in VideoRefreshScreen()
|
|
// . Draw cross-hairs (if using mouse as either a mouse or joystick) to frame DC
|
|
// . In Windowed mode:
|
|
// - Draw 3D border, 8x buttons, status area (disk LEDs, caps) to frame DC
|
|
// . In Fullscreen mode:
|
|
// - Optional: Draw status area to frame DC
|
|
//
|
|
|
|
UINT Win32Frame::Get3DBorderWidth(void)
|
|
{
|
|
return IsFullScreen() ? 0 : VIEWPORTX;
|
|
}
|
|
|
|
UINT Win32Frame::Get3DBorderHeight(void)
|
|
{
|
|
return IsFullScreen() ? 0 : VIEWPORTY;
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
void Win32Frame::FrameShowCursor(BOOL bShow)
|
|
{
|
|
int nCount;
|
|
|
|
if (bShow)
|
|
{
|
|
do
|
|
{
|
|
nCount = ShowCursor(bShow);
|
|
}
|
|
while(nCount < 0);
|
|
g_bShowingCursor = true;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
nCount = ShowCursor(bShow);
|
|
}
|
|
while(nCount >= 0);
|
|
g_bShowingCursor = false;
|
|
}
|
|
}
|
|
|
|
// Called when:
|
|
// . Ctrl-Left mouse button
|
|
// . PAUSE pressed (when MODE_RUNNING)
|
|
// . AppleWin's main window is activated/deactivated
|
|
void Win32Frame::RevealCursor()
|
|
{
|
|
CMouseInterface* pMouseCard = GetCardMgr().GetMouseCard();
|
|
|
|
if (!pMouseCard || !pMouseCard->IsActiveAndEnabled())
|
|
return;
|
|
|
|
pMouseCard->SetEnabled(false);
|
|
|
|
FrameShowCursor(TRUE);
|
|
|
|
if (GetPropertySheet().GetMouseShowCrosshair()) // Erase crosshairs if they are being drawn
|
|
DrawCrosshairs(0,0);
|
|
|
|
if (GetPropertySheet().GetMouseRestrictToWindow())
|
|
SetUsingCursor(FALSE);
|
|
|
|
g_bLastCursorInAppleViewport = false;
|
|
}
|
|
|
|
// Called when:
|
|
// . WM_MOUSEMOVE event
|
|
// . Switch from full-screen to normal (windowed) mode
|
|
// . AppleWin's main window is activated/deactivated
|
|
void Win32Frame::FullScreenRevealCursor(void)
|
|
{
|
|
if (!g_bIsFullScreen)
|
|
return;
|
|
|
|
if (GetCardMgr().IsMouseCardInstalled())
|
|
return;
|
|
|
|
if (!g_bUsingCursor && !g_bShowingCursor)
|
|
{
|
|
FrameShowCursor(TRUE);
|
|
g_uCount100msec = 0;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
#define LOADBUTTONBITMAP(bitmapname) LoadImage(g_hInstance,bitmapname, \
|
|
IMAGE_BITMAP,0,0, \
|
|
LR_CREATEDIBSECTION | \
|
|
LR_LOADMAP3DCOLORS | \
|
|
LR_LOADTRANSPARENT);
|
|
|
|
void Win32Frame::CreateGdiObjects(void)
|
|
{
|
|
memset(buttonbitmap, 0, BUTTONS*sizeof(HBITMAP));
|
|
|
|
buttonbitmap[BTN_HELP] = (HBITMAP)LOADBUTTONBITMAP("HELP_BUTTON");
|
|
|
|
switch (g_Apple2Type)
|
|
{
|
|
case A2TYPE_PRAVETS82:
|
|
case A2TYPE_PRAVETS8M:
|
|
case A2TYPE_PRAVETS8A:
|
|
buttonbitmap[BTN_RUN] = (HBITMAP)LOADBUTTONBITMAP("RUNP_BUTTON");
|
|
break;
|
|
case A2TYPE_TK30002E:
|
|
buttonbitmap[BTN_RUN] = (HBITMAP)LOADBUTTONBITMAP("RUN3000E_BUTTON");
|
|
break;
|
|
case A2TYPE_BASE64A:
|
|
buttonbitmap[BTN_RUN] = (HBITMAP)LOADBUTTONBITMAP("RUNBASE64A_BUTTON");
|
|
break;
|
|
default:
|
|
buttonbitmap[BTN_RUN] = (HBITMAP)LOADBUTTONBITMAP("RUN_BUTTON");
|
|
break;
|
|
}
|
|
|
|
buttonbitmap[BTN_DRIVE1 ] = (HBITMAP)LOADBUTTONBITMAP("DRIVE1_BUTTON");
|
|
buttonbitmap[BTN_DRIVE2 ] = (HBITMAP)LOADBUTTONBITMAP("DRIVE2_BUTTON");
|
|
buttonbitmap[BTN_DRIVESWAP] = (HBITMAP)LOADBUTTONBITMAP("DRIVESWAP_BUTTON");
|
|
buttonbitmap[BTN_FULLSCR ] = (HBITMAP)LOADBUTTONBITMAP("FULLSCR_BUTTON");
|
|
buttonbitmap[BTN_DEBUG ] = (HBITMAP)LOADBUTTONBITMAP("DEBUG_BUTTON");
|
|
buttonbitmap[BTN_SETUP ] = (HBITMAP)LOADBUTTONBITMAP("SETUP_BUTTON");
|
|
|
|
//
|
|
|
|
g_hCapsLockBitmap[0] = (HBITMAP)LOADBUTTONBITMAP("LED_CAPSOFF_BITMAP");
|
|
g_hCapsLockBitmap[1] = (HBITMAP)LOADBUTTONBITMAP("LED_CAPSON_BITMAP");
|
|
//Pravets8 only
|
|
g_hCapsBitmapP8[0] = (HBITMAP)LOADBUTTONBITMAP("LED_CAPSOFF_P8_BITMAP");
|
|
g_hCapsBitmapP8[1] = (HBITMAP)LOADBUTTONBITMAP("LED_CAPSON_P8_BITMAP");
|
|
g_hCapsBitmapLat[0] = (HBITMAP)LOADBUTTONBITMAP("LED_LATOFF_BITMAP");
|
|
g_hCapsBitmapLat[1] = (HBITMAP)LOADBUTTONBITMAP("LED_LATON_BITMAP");
|
|
|
|
/*charsetbitmap[0] = (HBITMAP)LOADBUTTONBITMAP("CHARSET_APPLE_BITMAP");
|
|
charsetbitmap[1] = (HBITMAP)LOADBUTTONBITMAP("CHARSET_82_BITMAP");
|
|
charsetbitmap[2] = (HBITMAP)LOADBUTTONBITMAP("CHARSET_8A_BITMAP");
|
|
charsetbitmap[3] = (HBITMAP)LOADBUTTONBITMAP("CHARSET_8M_BITMAP");
|
|
*/
|
|
//===========================
|
|
g_hDiskWindowedLED[ DISK_STATUS_OFF ] = (HBITMAP)LOADBUTTONBITMAP("DISKOFF_BITMAP");
|
|
g_hDiskWindowedLED[ DISK_STATUS_READ ] = (HBITMAP)LOADBUTTONBITMAP("DISKREAD_BITMAP");
|
|
g_hDiskWindowedLED[ DISK_STATUS_WRITE] = (HBITMAP)LOADBUTTONBITMAP("DISKWRITE_BITMAP");
|
|
g_hDiskWindowedLED[ DISK_STATUS_PROT ] = (HBITMAP)LOADBUTTONBITMAP("DISKPROT_BITMAP");
|
|
g_hDiskWindowedLED[ DISK_STATUS_EMPTY] = (HBITMAP)LOADBUTTONBITMAP("DISKOFF_BITMAP");
|
|
g_hDiskWindowedLED[ DISK_STATUS_SPIN ] = (HBITMAP)LOADBUTTONBITMAP("DISKREAD_BITMAP");
|
|
|
|
btnfacebrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
|
|
btnfacepen = CreatePen(PS_SOLID,1,GetSysColor(COLOR_BTNFACE));
|
|
btnhighlightpen = CreatePen(PS_SOLID,1,GetSysColor(COLOR_BTNHIGHLIGHT));
|
|
btnshadowpen = CreatePen(PS_SOLID,1,GetSysColor(COLOR_BTNSHADOW));
|
|
smallfont = CreateFont(smallfontHeight,6,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,
|
|
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
|
|
DEFAULT_QUALITY,VARIABLE_PITCH | FF_SWISS,
|
|
"Small Fonts");
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::DeleteGdiObjects(void)
|
|
{
|
|
for (int loop = 0; loop < BUTTONS; loop++)
|
|
_ASSERT(DeleteObject(buttonbitmap[loop]));
|
|
|
|
for (int loop = 0; loop < 2; loop++)
|
|
{
|
|
_ASSERT(DeleteObject(g_hCapsLockBitmap[loop]));
|
|
_ASSERT(DeleteObject(g_hCapsBitmapP8[loop]));
|
|
_ASSERT(DeleteObject(g_hCapsBitmapLat[loop]));
|
|
}
|
|
|
|
for (int loop = 0; loop < NUM_DISK_STATUS; loop++)
|
|
{
|
|
_ASSERT(DeleteObject(g_hDiskWindowedLED[loop]));
|
|
}
|
|
|
|
_ASSERT(DeleteObject(btnfacebrush));
|
|
_ASSERT(DeleteObject(btnfacepen));
|
|
_ASSERT(DeleteObject(btnhighlightpen));
|
|
_ASSERT(DeleteObject(btnshadowpen));
|
|
_ASSERT(DeleteObject(smallfont));
|
|
}
|
|
|
|
// Draws an 3D box around the main apple screen
|
|
//===========================================================================
|
|
void Win32Frame::Draw3dRect(HDC dc, int x1, int y1, int x2, int y2, BOOL out)
|
|
{
|
|
SelectObject(dc,GetStockObject(NULL_BRUSH));
|
|
SelectObject(dc,out ? btnshadowpen : btnhighlightpen);
|
|
POINT pt[3];
|
|
pt[0].x = x1; pt[0].y = y2-1;
|
|
pt[1].x = x2-1; pt[1].y = y2-1;
|
|
pt[2].x = x2-1; pt[2].y = y1;
|
|
Polyline(dc,(LPPOINT)&pt,3);
|
|
SelectObject(dc,(out == 1) ? btnhighlightpen : btnshadowpen);
|
|
pt[1].x = x1; pt[1].y = y1;
|
|
pt[2].x = x2; pt[2].y = y1;
|
|
Polyline(dc,(LPPOINT)&pt,3);
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::DrawBitmapRect (HDC dc, int x, int y, LPRECT rect, HBITMAP bitmap) {
|
|
HDC memdc = CreateCompatibleDC(dc);
|
|
SelectObject(memdc,bitmap);
|
|
BitBlt(dc,x,y,
|
|
rect->right + 1 - rect->left,
|
|
rect->bottom + 1 - rect->top,
|
|
memdc,
|
|
rect->left,
|
|
rect->top,
|
|
SRCCOPY);
|
|
DeleteDC(memdc);
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::DrawButton (HDC passdc, int number) {
|
|
FrameReleaseDC();
|
|
HDC dc = (passdc ? passdc : GetDC(g_hFrameWindow));
|
|
int x = buttonx;
|
|
int y = buttony+number*BUTTONCY;
|
|
if (number == buttondown) {
|
|
int loop = 0;
|
|
while (loop++ < 3)
|
|
Draw3dRect(dc,x+loop,y+loop,x+BUTTONCX,y+BUTTONCY,0);
|
|
RECT rect = {0,0,39,39};
|
|
DrawBitmapRect(dc,x+4,y+4,&rect,buttonbitmap[number]);
|
|
}
|
|
else {
|
|
Draw3dRect(dc,x+1,y+1,x+BUTTONCX,y+BUTTONCY,1);
|
|
Draw3dRect(dc,x+2,y+2,x+BUTTONCX-1,y+BUTTONCY-1,1);
|
|
RECT rect = {1,1,40,40};
|
|
DrawBitmapRect(dc,x+3,y+3,&rect,buttonbitmap[number]);
|
|
}
|
|
if ((number == BTN_DRIVE1) || (number == BTN_DRIVE2)) {
|
|
int offset = (number == buttondown) << 1;
|
|
RECT rect = {x+offset+3,
|
|
y+offset+31,
|
|
x+offset+42,
|
|
y+offset+42};
|
|
SelectObject(dc,smallfont);
|
|
SetTextColor(dc,RGB(0,0,0));
|
|
SetTextAlign(dc,TA_CENTER | TA_TOP);
|
|
SetBkMode(dc,TRANSPARENT);
|
|
|
|
LPCTSTR pszBaseName = (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
? dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6)).GetBaseName(number-BTN_DRIVE1).c_str()
|
|
: "";
|
|
|
|
ExtTextOut(dc,x+offset+22,rect.top,ETO_CLIPPED,&rect,
|
|
pszBaseName,
|
|
MIN(8,(UINT)strlen(pszBaseName)),
|
|
NULL);
|
|
}
|
|
if (!passdc)
|
|
ReleaseDC(g_hFrameWindow,dc);
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
// NB. x=y=0 means erase only
|
|
void Win32Frame::DrawCrosshairs (int x, int y) {
|
|
static int lastx = 0;
|
|
static int lasty = 0;
|
|
FrameReleaseDC();
|
|
HDC dc = GetDC(g_hFrameWindow);
|
|
#define LINE(x1,y1,x2,y2) MoveToEx(dc,x1,y1,NULL); LineTo(dc,x2,y2);
|
|
|
|
// ERASE THE OLD CROSSHAIRS
|
|
if (lastx && lasty)
|
|
if (g_bIsFullScreen)
|
|
{
|
|
int loop = 4;
|
|
while (loop--) {
|
|
RECT rect = {0,0,5,5};
|
|
switch (loop) {
|
|
case 0: OffsetRect(&rect, GetFullScreenOffsetX()+lastx-2, GetFullScreenOffsetY()+viewporty-5); break;
|
|
case 1: OffsetRect(&rect, GetFullScreenOffsetX()+lastx-2, GetFullScreenOffsetY()+viewporty+g_nViewportCY); break;
|
|
case 2: OffsetRect(&rect, GetFullScreenOffsetX()+viewportx-5, GetFullScreenOffsetY()+lasty-2); break;
|
|
case 3: OffsetRect(&rect, GetFullScreenOffsetX()+viewportx+g_nViewportCX, GetFullScreenOffsetY()+lasty-2); break;
|
|
}
|
|
FillRect(dc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int loop = 5;
|
|
while (loop--) {
|
|
switch (loop) {
|
|
case 0: SelectObject(dc,GetStockObject(BLACK_PEN)); break;
|
|
case 1: // fall through
|
|
case 2: SelectObject(dc,btnshadowpen); break;
|
|
case 3: // fall through
|
|
case 4: SelectObject(dc,btnfacepen); break;
|
|
}
|
|
LINE(lastx-2,VIEWPORTY-loop-1,
|
|
lastx+3,VIEWPORTY-loop-1);
|
|
LINE(VIEWPORTX-loop-1,lasty-2,
|
|
VIEWPORTX-loop-1,lasty+3);
|
|
if ((loop == 1) || (loop == 2))
|
|
SelectObject(dc,btnhighlightpen);
|
|
LINE(lastx-2,VIEWPORTY+g_nViewportCY+loop,
|
|
lastx+3,VIEWPORTY+g_nViewportCY+loop);
|
|
LINE(VIEWPORTX+g_nViewportCX+loop,lasty-2,
|
|
VIEWPORTX+g_nViewportCX+loop,lasty+3);
|
|
}
|
|
}
|
|
|
|
// DRAW THE NEW CROSSHAIRS
|
|
if (x && y) {
|
|
if (g_bIsFullScreen)
|
|
{
|
|
int loop = 4;
|
|
while (loop--) {
|
|
if ((loop == 1) || (loop == 2))
|
|
SelectObject(dc,GetStockObject(WHITE_PEN));
|
|
else
|
|
SelectObject(dc,GetStockObject(BLACK_PEN));
|
|
LINE(GetFullScreenOffsetX()+x+loop-2, GetFullScreenOffsetY()+viewporty-5,
|
|
GetFullScreenOffsetX()+x+loop-2, GetFullScreenOffsetY()+viewporty);
|
|
LINE(GetFullScreenOffsetX()+x+loop-2, GetFullScreenOffsetY()+viewporty+g_nViewportCY+4,
|
|
GetFullScreenOffsetX()+x+loop-2, GetFullScreenOffsetY()+viewporty+g_nViewportCY-1);
|
|
LINE(GetFullScreenOffsetX()+viewportx-5, GetFullScreenOffsetY()+y+loop-2,
|
|
GetFullScreenOffsetX()+viewportx, GetFullScreenOffsetY()+y+loop-2);
|
|
LINE(GetFullScreenOffsetX()+viewportx+g_nViewportCX+4, GetFullScreenOffsetY()+y+loop-2,
|
|
GetFullScreenOffsetX()+viewportx+g_nViewportCX-1, GetFullScreenOffsetY()+y+loop-2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int loop = 4;
|
|
while (loop--) {
|
|
if ((loop == 1) || (loop == 2))
|
|
SelectObject(dc,GetStockObject(WHITE_PEN));
|
|
else
|
|
SelectObject(dc,GetStockObject(BLACK_PEN));
|
|
LINE(x+loop-2,viewporty-5,
|
|
x+loop-2,viewporty);
|
|
LINE(x+loop-2,viewporty+g_nViewportCY+4,
|
|
x+loop-2,viewporty+g_nViewportCY-1);
|
|
LINE(viewportx-5, y+loop-2,
|
|
viewportx, y+loop-2);
|
|
LINE(viewportx+g_nViewportCX+4,y+loop-2,
|
|
viewportx+g_nViewportCX-1,y+loop-2);
|
|
}
|
|
}
|
|
}
|
|
#undef LINE
|
|
lastx = x;
|
|
lasty = y;
|
|
ReleaseDC(g_hFrameWindow,dc);
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::DrawFrameWindow (bool bPaintingWindow/*=false*/)
|
|
{
|
|
FrameReleaseDC();
|
|
PAINTSTRUCT ps;
|
|
HDC dc = bPaintingWindow
|
|
? BeginPaint(g_hFrameWindow,&ps)
|
|
: GetDC(g_hFrameWindow);
|
|
|
|
if (!g_bIsFullScreen)
|
|
{
|
|
// DRAW THE 3D BORDER AROUND THE EMULATED SCREEN
|
|
Draw3dRect(dc,
|
|
VIEWPORTX-2,VIEWPORTY-2,
|
|
VIEWPORTX+g_nViewportCX+2,VIEWPORTY+g_nViewportCY+2,
|
|
0);
|
|
Draw3dRect(dc,
|
|
VIEWPORTX-3,VIEWPORTY-3,
|
|
VIEWPORTX+g_nViewportCX+3,VIEWPORTY+g_nViewportCY+3,
|
|
0);
|
|
SelectObject(dc,btnfacepen);
|
|
Rectangle(dc,
|
|
VIEWPORTX-4,VIEWPORTY-4,
|
|
VIEWPORTX+g_nViewportCX+4,VIEWPORTY+g_nViewportCY+4);
|
|
Rectangle(dc,
|
|
VIEWPORTX-5,VIEWPORTY-5,
|
|
VIEWPORTX+g_nViewportCX+5,VIEWPORTY+g_nViewportCY+5);
|
|
|
|
// DRAW THE TOOLBAR BUTTONS
|
|
int iButton = BUTTONS;
|
|
while (iButton--)
|
|
{
|
|
DrawButton(dc,iButton);
|
|
}
|
|
|
|
if (g_nViewportScale == 2 || GetVideo().HasVidHD())
|
|
{
|
|
const int x = buttonx + 1;
|
|
const int y = buttony + BUTTONS * BUTTONCY + 36; // 36 = height of StatusArea
|
|
RECT rect = { x, y, x + BUTTONCX, y + BUTTONS * BUTTONCY + 22 };
|
|
|
|
if (GetVideo().HasVidHD())
|
|
{
|
|
if (g_nViewportScale == 1)
|
|
rect.bottom += 14;
|
|
else
|
|
rect.bottom += 32;
|
|
}
|
|
|
|
int res = FillRect(dc, &rect, btnfacebrush);
|
|
}
|
|
}
|
|
|
|
// DRAW THE STATUS AREA
|
|
DrawStatusArea(dc,DRAW_BACKGROUND | DRAW_LEDS | DRAW_DISK_STATUS);
|
|
|
|
// DRAW THE CONTENTS OF THE EMULATED SCREEN
|
|
if (g_nAppMode == MODE_LOGO)
|
|
DisplayLogo();
|
|
else if (g_nAppMode == MODE_DEBUG)
|
|
DebugDisplay();
|
|
else
|
|
VideoRedrawScreen();
|
|
|
|
if (bPaintingWindow)
|
|
EndPaint(g_hFrameWindow,&ps);
|
|
else
|
|
ReleaseDC(g_hFrameWindow,dc);
|
|
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
bool Win32Frame::IsFullScreen(void)
|
|
{
|
|
return g_bIsFullScreen;
|
|
}
|
|
|
|
bool Win32Frame::GetFullScreenShowSubunitStatus(void)
|
|
{
|
|
return g_bFullScreen_ShowSubunitStatus;
|
|
}
|
|
|
|
void Win32Frame::SetFullScreenShowSubunitStatus(bool bShow)
|
|
{
|
|
g_bFullScreen_ShowSubunitStatus = bShow;
|
|
}
|
|
|
|
bool Win32Frame::GetWindowedModeShowDiskiiStatus(void)
|
|
{
|
|
return m_showDiskiiStatus;
|
|
}
|
|
|
|
void Win32Frame::SetWindowedModeShowDiskiiStatus(bool bShow)
|
|
{
|
|
m_showDiskiiStatus = bShow;
|
|
m_redrawDiskiiStatus = true;
|
|
SetSlotUIOffsets();
|
|
}
|
|
|
|
void Win32Frame::SetSlotUIOffsets(void)
|
|
{
|
|
if (m_showDiskiiStatus)
|
|
{
|
|
yOffsetSlot5Label = D2FullUI::yOffsetSlot5Label;
|
|
yOffsetSlot5LEDNumbers = D2FullUI::yOffsetSlot5LEDNumbers;
|
|
yOffsetSlot5LEDs = D2FullUI::yOffsetSlot5LEDs;
|
|
}
|
|
else
|
|
{
|
|
yOffsetSlot5Label = D2CompactUI::yOffsetSlot5Label;
|
|
yOffsetSlot5LEDNumbers = D2CompactUI::yOffsetSlot5LEDNumbers;
|
|
yOffsetSlot5LEDs = D2CompactUI::yOffsetSlot5LEDs;
|
|
}
|
|
}
|
|
|
|
void Win32Frame::FrameDrawDiskLEDS()
|
|
{
|
|
FrameDrawDiskLEDS((HDC)0);
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::FrameDrawDiskLEDS( HDC passdc )
|
|
{
|
|
g_eStatusDrive1 = DISK_STATUS_OFF;
|
|
g_eStatusDrive2 = DISK_STATUS_OFF;
|
|
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6)).GetLightStatus(&g_eStatusDrive1, &g_eStatusDrive2);
|
|
|
|
// Draw Track/Sector
|
|
FrameReleaseDC();
|
|
HDC dc = (passdc ? passdc : GetDC(g_hFrameWindow));
|
|
const int x = buttonx;
|
|
const int y = buttony + BUTTONS * BUTTONCY + 1;
|
|
|
|
if (g_bIsFullScreen)
|
|
{
|
|
if (!g_bFullScreen_ShowSubunitStatus)
|
|
return;
|
|
|
|
SelectObject(dc,smallfont);
|
|
SetBkMode(dc,OPAQUE);
|
|
SetBkColor(dc,RGB(0,0,0));
|
|
SetTextAlign(dc,TA_LEFT | TA_TOP);
|
|
|
|
SetTextColor(dc, g_aDiskFullScreenColorsLED[g_eStatusDrive1]);
|
|
TextOut(dc,x+ 3,y+2,"1",1);
|
|
|
|
SetTextColor(dc, g_aDiskFullScreenColorsLED[g_eStatusDrive2]);
|
|
TextOut(dc,x+13,y+2,"2",1);
|
|
}
|
|
else
|
|
{
|
|
RECT rDiskLed = { 0,0,8,8 };
|
|
DrawBitmapRect(dc, x + 12, y + yOffsetSlot6LEDs, &rDiskLed, g_hDiskWindowedLED[g_eStatusDrive1]);
|
|
DrawBitmapRect(dc, x + 31, y + yOffsetSlot6LEDs, &rDiskLed, g_hDiskWindowedLED[g_eStatusDrive2]);
|
|
|
|
if (g_nViewportScale > 1 && GetCardMgr().QuerySlot(SLOT5) == CT_Disk2)
|
|
{
|
|
Disk_Status_e eDrive1StatusSlot5 = DISK_STATUS_OFF;
|
|
Disk_Status_e eDrive2StatusSlot5 = DISK_STATUS_OFF;
|
|
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT5)).GetLightStatus(&eDrive1StatusSlot5, &eDrive2StatusSlot5);
|
|
|
|
DrawBitmapRect(dc, x + 12, y + yOffsetSlot5LEDs, &rDiskLed, g_hDiskWindowedLED[eDrive1StatusSlot5]);
|
|
DrawBitmapRect(dc, x + 31, y + yOffsetSlot5LEDs, &rDiskLed, g_hDiskWindowedLED[eDrive2StatusSlot5]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Win32Frame::FrameDrawDiskStatus()
|
|
{
|
|
FrameDrawDiskStatus((HDC)0);
|
|
}
|
|
|
|
void Win32Frame::GetTrackSector(UINT slot, int& drive1Track, int& drive2Track, int& activeFloppy)
|
|
{
|
|
// DOS3.3 ProDOS
|
|
// Slot $B7E9 $BE3C(DEFSLT=Default Slot) ; ref: Beneath Apple ProDOS 8-3. Not ProDOS v1.9 (16-JUL-90)
|
|
// Drive $B7EA $BE3D(DEFDRV=Default Drive) ; ref: Beneath Apple ProDOS 8-3
|
|
// Track $B7EC LC1 $D356
|
|
// Sector $B7ED LC1 $D357
|
|
// Unit - LC1 $D359 ; ref: Beneath Apple ProDOS 1.20 and 1.30 Supplement, p83
|
|
// RWTS LC1 $D300
|
|
|
|
drive1Track = drive2Track = activeFloppy = 0;
|
|
if (GetCardMgr().QuerySlot(slot) != CT_Disk2)
|
|
return;
|
|
|
|
Disk2InterfaceCard& disk2Card = dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(slot));
|
|
activeFloppy = disk2Card.GetCurrentDrive();
|
|
drive1Track = disk2Card.GetTrack(DRIVE_1);
|
|
drive2Track = disk2Card.GetTrack(DRIVE_2);
|
|
|
|
// Probe known OS's for default Slot/Track/Sector
|
|
const bool isProDOS = ReadByteFromMemory(0xBF00) == 0x4C;
|
|
bool isSectorValid = false;
|
|
int drive1Sector = -1, drive2Sector = -1;
|
|
|
|
// Try DOS3.3 Sector
|
|
if (!isProDOS)
|
|
{
|
|
const int nDOS33slot = ReadByteFromMemory(0xB7E9) / 16;
|
|
const int nDOS33track = ReadByteFromMemory(0xB7EC);
|
|
const int nDOS33sector = ReadByteFromMemory(0xB7ED);
|
|
|
|
if ((nDOS33slot == slot)
|
|
&& (nDOS33track >= 0 && nDOS33track < 40)
|
|
&& (nDOS33sector >= 0 && nDOS33sector < 16))
|
|
{
|
|
#if _DEBUG && 0
|
|
if (nDOS33track != nDisk1Track)
|
|
{
|
|
LogOutput("\n\n\nWARNING: DOS33Track: %d (%02X) != nDisk1Track: %d (%02X)\n\n\n", nDOS33track, nDOS33track, nDisk1Track, nDisk1Track);
|
|
}
|
|
#endif // _DEBUG
|
|
|
|
/**/ if (activeFloppy == 0) drive1Sector = nDOS33sector;
|
|
else if (activeFloppy == 1) drive2Sector = nDOS33sector;
|
|
|
|
isSectorValid = true;
|
|
}
|
|
}
|
|
else // isProDOS
|
|
{
|
|
// we can't just read from mem[ 0xD357 ] since it might be bank-switched from ROM
|
|
// and we need the Language Card RAM
|
|
const int nProDOStrack = *MemGetMainPtrWithLC(0xC356); // LC1 $D356
|
|
const int nProDOSsector = *MemGetMainPtrWithLC(0xC357); // LC1 $D357
|
|
const int nProDOSslot = *MemGetMainPtrWithLC(0xC359) / 16; // LC1 $D359
|
|
|
|
if ((nProDOSslot == slot)
|
|
&& (nProDOStrack >= 0 && nProDOStrack < 40)
|
|
&& (nProDOSsector >= 0 && nProDOSsector < 16))
|
|
{
|
|
/**/ if (activeFloppy == 0) drive1Sector = nProDOSsector;
|
|
else if (activeFloppy == 1) drive2Sector = nProDOSsector;
|
|
|
|
isSectorValid = true;
|
|
}
|
|
}
|
|
|
|
// Preserve sector so don't get "??" when switching drives, eg. if using COPYA to copy from drive-1 to drive-2
|
|
if (isSectorValid)
|
|
{
|
|
if (activeFloppy == 0) g_nSector[slot][0] = drive1Sector;
|
|
else g_nSector[slot][1] = drive2Sector;
|
|
}
|
|
else
|
|
{
|
|
if (activeFloppy == 0) g_nSector[slot][0] = -1;
|
|
else g_nSector[slot][1] = -1;
|
|
}
|
|
}
|
|
|
|
void Win32Frame::CreateTrackSectorStrings(int track, int sector, std::string& strTrack, std::string& strSector)
|
|
{
|
|
strTrack = StrFormat("%2d", track);
|
|
strSector = (sector < 0) ? "??" : StrFormat("%2d", sector);
|
|
}
|
|
|
|
void Win32Frame::DrawTrackSector(HDC dc, UINT slot, int drive1Track, int drive1Sector, int drive2Track, int drive2Sector)
|
|
{
|
|
const int x = buttonx;
|
|
const int y = buttony + BUTTONS * BUTTONCY + 1;
|
|
|
|
const UINT yOffsetSlotNTrackInfo = (slot == SLOT6) ? yOffsetSlot6TrackInfo : yOffsetSlot5TrackInfo;
|
|
const UINT yOffsetSlotNSectorInfo = yOffsetSlotNTrackInfo + smallfontHeight;
|
|
|
|
// Erase background (TrackInfo + SectorInfo)
|
|
SelectObject(dc, GetStockObject(NULL_PEN));
|
|
SelectObject(dc, btnfacebrush);
|
|
Rectangle(dc, x + 4, y + yOffsetSlotNTrackInfo, x + BUTTONCX + 1, y + yOffsetSlotNSectorInfo + smallfontHeight);
|
|
|
|
SetTextColor(dc, RGB(0, 0, 0));
|
|
SetBkMode(dc, TRANSPARENT);
|
|
|
|
std::string strTrackDrive1, strSectorDrive1, strTrackDrive2, strSectorDrive2;
|
|
CreateTrackSectorStrings(drive1Track, drive1Sector, strTrackDrive1, strSectorDrive1);
|
|
CreateTrackSectorStrings(drive2Track, drive2Sector, strTrackDrive2, strSectorDrive2);
|
|
|
|
std::string text;
|
|
text = "T" + strTrackDrive1;
|
|
TextOut(dc, x + 6, y + yOffsetSlotNTrackInfo, text.c_str(), (int)text.length());
|
|
text = "S" + strSectorDrive1;
|
|
TextOut(dc, x + 6, y + yOffsetSlotNSectorInfo, text.c_str(), (int)text.length());
|
|
|
|
text = "T" + strTrackDrive2;
|
|
TextOut(dc, x + 26, y + yOffsetSlotNTrackInfo, text.c_str(), (int)text.length());
|
|
text = "S" + strSectorDrive2;
|
|
TextOut(dc, x + 26, y + yOffsetSlotNSectorInfo, text.c_str(), (int)text.length());
|
|
}
|
|
|
|
// Feature Request #201 Show track status
|
|
// https://github.com/AppleWin/AppleWin/issues/201
|
|
//===========================================================================
|
|
void Win32Frame::FrameDrawDiskStatus( HDC passdc )
|
|
{
|
|
if (mem == NULL)
|
|
return;
|
|
|
|
if (g_nAppMode == MODE_LOGO)
|
|
return;
|
|
|
|
if (g_windowMinimized) // Prevent DC leaks when app window is minimised (GH#820)
|
|
return;
|
|
|
|
int nDrive1Track, nDrive2Track, nActiveFloppy;
|
|
GetTrackSector(SLOT6, nDrive1Track, nDrive2Track, nActiveFloppy);
|
|
|
|
// Draw Track/Sector
|
|
FrameReleaseDC();
|
|
HDC dc = (passdc ? passdc : GetDC(g_hFrameWindow));
|
|
const int x = buttonx;
|
|
const int y = buttony + BUTTONS * BUTTONCY + 1;
|
|
|
|
SelectObject(dc,smallfont);
|
|
SetBkMode(dc,OPAQUE);
|
|
SetBkColor(dc,RGB(0,0,0));
|
|
SetTextAlign(dc,TA_LEFT | TA_TOP);
|
|
|
|
if (g_bIsFullScreen)
|
|
{
|
|
// GH#57 - drive lights in full screen mode (Slot 6 only)
|
|
|
|
if (!g_bFullScreen_ShowSubunitStatus || GetCardMgr().QuerySlot(SLOT6) != CT_Disk2)
|
|
return;
|
|
|
|
std::string strTrackDrive1, strSectorDrive1, strTrackDrive2, strSectorDrive2;
|
|
CreateTrackSectorStrings(nDrive1Track, g_nSector[SLOT6][0], strTrackDrive1, strSectorDrive1);
|
|
CreateTrackSectorStrings(nDrive2Track, g_nSector[SLOT6][1], strTrackDrive2, strSectorDrive2);
|
|
|
|
std::string text = ( nActiveFloppy == 0 )
|
|
? StrFormat( "%s/%s ", strTrackDrive1.c_str(), strSectorDrive1.c_str() )
|
|
: StrFormat( "%s/%s ", strTrackDrive2.c_str(), strSectorDrive2.c_str() );
|
|
|
|
SetTextColor(dc, g_aDiskFullScreenColorsLED[ DISK_STATUS_READ ] );
|
|
TextOut(dc, x, y, text.c_str(), (int)text.length());
|
|
|
|
SetTextColor(dc, g_aDiskFullScreenColorsLED[g_eStatusDrive1]);
|
|
TextOut(dc, x + 3, y + smallfontHeight, "1", 1);
|
|
|
|
SetTextColor(dc, g_aDiskFullScreenColorsLED[g_eStatusDrive2]);
|
|
TextOut(dc, x + 13, y + smallfontHeight, "2", 1);
|
|
}
|
|
else
|
|
{
|
|
// NB. Only draw Track/Sector if 2x windowed
|
|
if (g_nViewportScale == 1 || !GetWindowedModeShowDiskiiStatus())
|
|
return;
|
|
|
|
// Slot 6's Disk II
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
{
|
|
DrawTrackSector(dc, SLOT6, nDrive1Track, g_nSector[SLOT6][0], nDrive2Track, g_nSector[SLOT6][1]);
|
|
}
|
|
|
|
// Slot 5's Disk II
|
|
if (GetCardMgr().QuerySlot(SLOT5) == CT_Disk2)
|
|
{
|
|
GetTrackSector(SLOT5, nDrive1Track, nDrive2Track, nActiveFloppy);
|
|
DrawTrackSector(dc, SLOT5, nDrive1Track, g_nSector[SLOT5][0], nDrive2Track, g_nSector[SLOT5][1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::DrawStatusArea(HDC passdc, int drawflags)
|
|
{
|
|
if (g_hFrameWindow == NULL)
|
|
{
|
|
// TC: Fix drawing of drive buttons before frame created:
|
|
// . Main init loop: LoadConfiguration() called before FrameCreateWindow(), eg:
|
|
// LoadConfiguration() -> Disk_LoadLastDiskImage() -> DiskInsert() -> GetFrame().FrameRefreshStatus()
|
|
return;
|
|
}
|
|
|
|
FrameReleaseDC();
|
|
HDC dc = (passdc ? passdc : GetDC(g_hFrameWindow));
|
|
const int x = buttonx;
|
|
const int y = buttony + BUTTONS * BUTTONCY + 1;
|
|
const bool bCaps = KeybGetCapsStatus();
|
|
|
|
Disk_Status_e eHardDriveStatus = DISK_STATUS_OFF;
|
|
if (GetCardMgr().QuerySlot(SLOT7) == CT_GenericHDD)
|
|
dynamic_cast<HarddiskInterfaceCard&>(GetCardMgr().GetRef(SLOT7)).GetLightStatus(&eHardDriveStatus);
|
|
|
|
if (g_bIsFullScreen)
|
|
{
|
|
if (!g_bFullScreen_ShowSubunitStatus)
|
|
{
|
|
// Erase Config button icon too, as trk/sec is written here - see FrameDrawDiskStatus()
|
|
RECT rect = {x-2,y-BUTTONCY,x+BUTTONCX+2,y+BUTTONCY}; // Extend rect's width by +/-2 as the TITLE_PAUSED text is wider than an icon button
|
|
FillRect(dc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
}
|
|
else
|
|
{
|
|
SelectObject(dc,smallfont);
|
|
|
|
if (drawflags & DRAW_DISK_STATUS)
|
|
{
|
|
FrameDrawDiskStatus(dc);
|
|
}
|
|
|
|
if (GetCardMgr().QuerySlot(SLOT7) == CT_GenericHDD)
|
|
{
|
|
SetTextAlign(dc, TA_RIGHT | TA_TOP);
|
|
SetTextColor(dc, g_aDiskFullScreenColorsLED[eHardDriveStatus]);
|
|
TextOut(dc, x + 23, y + 2, "H", 1);
|
|
}
|
|
|
|
if (!IS_APPLE2)
|
|
{
|
|
SetTextAlign(dc,TA_RIGHT | TA_TOP);
|
|
SetTextColor(dc,(bCaps
|
|
? RGB(128,128,128)
|
|
: RGB( 0, 0, 0) ));
|
|
|
|
TextOut(dc,x+BUTTONCX,y+2,"A",1); // NB. Caps Lock indicator is already flush right!
|
|
}
|
|
|
|
//
|
|
|
|
static const char* pCurrentAppModeText = NULL;
|
|
|
|
const char* const pNewAppModeText = (g_nAppMode == MODE_PAUSED)
|
|
? TITLE_PAUSED
|
|
: (g_nAppMode == MODE_STEPPING)
|
|
? TITLE_STEPPING
|
|
: NULL;
|
|
|
|
SetTextAlign(dc, TA_CENTER | TA_TOP);
|
|
|
|
if (pCurrentAppModeText && pNewAppModeText != pCurrentAppModeText)
|
|
{
|
|
SetTextColor(dc, RGB(0,0,0));
|
|
TextOut(dc, x+BUTTONCX/2, y+13, pCurrentAppModeText, (int)strlen(pCurrentAppModeText));
|
|
pCurrentAppModeText = NULL;
|
|
}
|
|
|
|
if (pNewAppModeText)
|
|
{
|
|
SetTextColor(dc, RGB(255,255,255));
|
|
TextOut(dc, x+BUTTONCX/2, y+13, pNewAppModeText, (int)strlen(pNewAppModeText));
|
|
pCurrentAppModeText = pNewAppModeText;
|
|
}
|
|
}
|
|
}
|
|
else // !g_bIsFullScreen
|
|
{
|
|
if (drawflags & DRAW_BACKGROUND)
|
|
{
|
|
// Erase background (Slot6 drive LEDs, HDD LED & Caps)
|
|
SelectObject(dc,GetStockObject(NULL_PEN));
|
|
SelectObject(dc,btnfacebrush);
|
|
Rectangle(dc,x,y,x+BUTTONCX+2,y+34);
|
|
Draw3dRect(dc,x+1,y+3,x+BUTTONCX,y+30,0);
|
|
|
|
// Add text for Slot6 drives: "1" & "2"
|
|
SelectObject(dc,smallfont);
|
|
SetTextAlign(dc,TA_CENTER | TA_TOP);
|
|
SetTextColor(dc,RGB(0,0,0));
|
|
SetBkMode(dc,TRANSPARENT);
|
|
TextOut(dc, x + 7, y + yOffsetSlot6LEDNumbers, "1", 1);
|
|
TextOut(dc, x + 27, y + yOffsetSlot6LEDNumbers, "2", 1);
|
|
|
|
// Add text for Slot7 harddrive: "H"
|
|
TextOut(dc, x + 7, y + yOffsetCapsLock, "H", 1);
|
|
|
|
if (g_nViewportScale > 1)
|
|
{
|
|
if (m_redrawDiskiiStatus)
|
|
{
|
|
m_redrawDiskiiStatus = false;
|
|
|
|
// Erase background (Slot6's TrackInfo + SectorInfo + "Slot 5" + LEDs + TrackInfo + SectorInfo)
|
|
SelectObject(dc, GetStockObject(NULL_PEN));
|
|
SelectObject(dc, btnfacebrush);
|
|
Rectangle(dc, x + 1, y + yOffsetSlot6TrackInfo, x + BUTTONCX + 1, y + yOffsetSlot5SectorInfo + smallfontHeight);
|
|
}
|
|
|
|
if (GetCardMgr().QuerySlot(SLOT5) == CT_Disk2)
|
|
{
|
|
std::string slot5 = "Slot 5:";
|
|
TextOut(dc, x + 15, y + yOffsetSlot5Label, slot5.c_str(), (int)slot5.length());
|
|
TextOut(dc, x + 7, y + yOffsetSlot5LEDNumbers, "1", 1);
|
|
TextOut(dc, x + 27, y + yOffsetSlot5LEDNumbers, "2", 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (drawflags & DRAW_LEDS)
|
|
{
|
|
FrameDrawDiskLEDS( dc );
|
|
|
|
if (drawflags & DRAW_DISK_STATUS)
|
|
FrameDrawDiskStatus( dc );
|
|
|
|
if (!IS_APPLE2)
|
|
{
|
|
RECT rCapsLed = {0,0,10,12}; // HACK: HARD-CODED bitmaps size
|
|
switch (g_Apple2Type)
|
|
{
|
|
case A2TYPE_APPLE2:
|
|
case A2TYPE_APPLE2PLUS:
|
|
case A2TYPE_APPLE2E:
|
|
case A2TYPE_APPLE2EENHANCED:
|
|
default: DrawBitmapRect(dc, x + 31, y + yOffsetCapsLock, &rCapsLed, g_hCapsLockBitmap[bCaps != 0]); break;
|
|
case A2TYPE_PRAVETS82:
|
|
case A2TYPE_PRAVETS8M: DrawBitmapRect(dc, x + 31, y + yOffsetCapsLock, &rCapsLed, g_hCapsBitmapP8[bCaps != 0]); break; // TODO: FIXME: Shouldn't one of these use g_hCapsBitmapLat ??
|
|
case A2TYPE_PRAVETS8A: DrawBitmapRect(dc, x + 31, y + yOffsetCapsLock, &rCapsLed, g_hCapsBitmapP8[bCaps != 0]); break;
|
|
}
|
|
|
|
RECT rDiskLed = {0,0,8,8};
|
|
DrawBitmapRect(dc, x + 12, y + yOffsetHardDiskLED, &rDiskLed, g_hDiskWindowedLED[eHardDriveStatus]);
|
|
}
|
|
}
|
|
|
|
if (drawflags & DRAW_TITLE)
|
|
{
|
|
GetAppleWindowTitle(); // SetWindowText() // WindowTitle
|
|
SendMessage(g_hFrameWindow,WM_SETTEXT,0,(LPARAM)g_pAppTitle.c_str());
|
|
}
|
|
|
|
if (drawflags & DRAW_BUTTON_DRIVES)
|
|
{
|
|
DrawButton(dc, BTN_DRIVE1);
|
|
DrawButton(dc, BTN_DRIVE2);
|
|
}
|
|
}
|
|
|
|
if (!passdc)
|
|
ReleaseDC(g_hFrameWindow,dc);
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::EraseButton (int number) {
|
|
RECT rect;
|
|
rect.left = buttonx;
|
|
rect.right = rect.left+BUTTONCX;
|
|
rect.top = buttony+number*BUTTONCY;
|
|
rect.bottom = rect.top+BUTTONCY;
|
|
|
|
InvalidateRect(g_hFrameWindow,&rect,1);
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
LRESULT CALLBACK Win32Frame::FrameWndProc(
|
|
HWND window,
|
|
UINT message,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
Win32Frame& win32Frame = Win32Frame::GetWin32Frame();
|
|
return win32Frame.WndProc(window, message, wparam, lparam);
|
|
}
|
|
|
|
LRESULT Win32Frame::WndProc(
|
|
HWND window,
|
|
UINT message,
|
|
WPARAM wparam,
|
|
LPARAM lparam)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_ACTIVATE: // Sent when window is activated/deactivated. wParam indicates WA_ACTIVE, WA_INACTIVE, etc
|
|
// Eg. Deactivate when Config dialog is active, AppleWin app loses focus, etc
|
|
JoyReset();
|
|
SetUsingCursor(FALSE);
|
|
RevealCursor();
|
|
FullScreenRevealCursor();
|
|
g_bFrameActive = (wparam != WA_INACTIVE);
|
|
break;
|
|
|
|
case WM_ACTIVATEAPP: // Sent when different app's window is activated/deactivated.
|
|
// Eg. Deactivate when AppleWin app loses focus
|
|
g_bAppActive = (wparam ? TRUE : FALSE);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
switch(wparam)
|
|
{
|
|
case SIZE_RESTORED:
|
|
case SIZE_MAXIMIZED:
|
|
g_windowMinimized = false;
|
|
break;
|
|
case SIZE_MINIMIZED:
|
|
g_windowMinimized = true;
|
|
break;
|
|
default: // SIZE_MAXSHOW, SIZE_MAXHIDE
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
LogFileOutput("WM_CLOSE\n");
|
|
if (g_bIsFullScreen && g_bRestart)
|
|
g_bRestartFullScreen = true;
|
|
if (g_bIsFullScreen)
|
|
SetNormalMode();
|
|
if (!IsIconic(window))
|
|
GetWindowRect(window,&framerect);
|
|
RegSaveValue(REG_PREFS, REGVALUE_PREF_WINDOW_X_POS, true, framerect.left);
|
|
RegSaveValue(REG_PREFS, REGVALUE_PREF_WINDOW_Y_POS, true, framerect.top);
|
|
FrameReleaseDC();
|
|
SetUsingCursor(FALSE);
|
|
if (helpquit) {
|
|
helpquit = 0;
|
|
HtmlHelp(NULL,NULL,HH_CLOSE_ALL,0);
|
|
}
|
|
if (g_TimerIDEvent_100msec)
|
|
{
|
|
BOOL bRes = KillTimer(g_hFrameWindow, g_TimerIDEvent_100msec);
|
|
LogFileOutput("KillTimer(g_TimerIDEvent_100msec), res=%d\n", bRes ? 1 : 0);
|
|
g_TimerIDEvent_100msec = 0;
|
|
}
|
|
LogFileOutput("WM_CLOSE (done)\n");
|
|
// Exit via DefWindowProc(), which does the default action for WM_CLOSE, which is to call DestroyWindow(), posting WM_DESTROY
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
LogFileOutput("WM_DESTROY\n");
|
|
DragAcceptFiles(window,0);
|
|
if (!g_bRestart) // GH#564: Only save-state on shutdown (not on a restart)
|
|
Snapshot_Shutdown();
|
|
DebugDestroy();
|
|
GetCardMgr().Destroy();
|
|
CpuDestroy();
|
|
MemDestroy();
|
|
SpkrDestroy();
|
|
Destroy();
|
|
DeleteGdiObjects();
|
|
DIMouse::DirectInputUninit(window); // NB. do before window is destroyed
|
|
PostQuitMessage(0); // Post WM_QUIT message to the thread's message queue
|
|
g_hFrameWindow = (HWND)0;
|
|
LogFileOutput("WM_DESTROY (done)\n");
|
|
break;
|
|
|
|
case WM_CREATE:
|
|
LogFileOutput("WM_CREATE\n");
|
|
g_hFrameWindow = window; // NB. g_hFrameWindow by CreateWindow()
|
|
|
|
CreateGdiObjects();
|
|
LogFileOutput("WM_CREATE: CreateGdiObjects()\n");
|
|
|
|
DSInit(); // NB. Need g_hFrameWindow for IDirectSound::SetCooperativeLevel()
|
|
LogFileOutput("WM_CREATE: DSInit()\n");
|
|
|
|
DIMouse::DirectInputInit(window);
|
|
LogFileOutput("WM_CREATE: DIMouse::DirectInputInit()\n");
|
|
|
|
SpkrInitialize();
|
|
LogFileOutput("WM_CREATE: SpkrInitialize()\n");
|
|
|
|
DragAcceptFiles(window,1);
|
|
LogFileOutput("WM_CREATE: DragAcceptFiles()\n");
|
|
|
|
LogFileOutput("WM_CREATE (done)\n");
|
|
break;
|
|
|
|
case WM_DDE_INITIATE: {
|
|
LogFileOutput("WM_DDE_INITIATE\n");
|
|
ATOM application = GlobalAddAtom("applewin");
|
|
ATOM topic = GlobalAddAtom("system");
|
|
if(LOWORD(lparam) == application && HIWORD(lparam) == topic)
|
|
SendMessage((HWND)wparam,WM_DDE_ACK,(WPARAM)window,MAKELPARAM(application,topic));
|
|
GlobalDeleteAtom(application);
|
|
GlobalDeleteAtom(topic);
|
|
LogFileOutput("WM_DDE_INITIATE (done)\n");
|
|
break;
|
|
}
|
|
|
|
case WM_DDE_EXECUTE:
|
|
{
|
|
LogFileOutput("WM_DDE_EXECUTE\n");
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
{
|
|
Disk2InterfaceCard& disk2Card = dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6));
|
|
LPTSTR filename = (LPTSTR)GlobalLock((HGLOBAL)lparam);
|
|
ImageError_e Error = disk2Card.InsertDisk(DRIVE_1, filename, IMAGE_USE_FILES_WRITE_PROTECT_STATUS, IMAGE_DONT_CREATE);
|
|
if (Error == eIMAGE_ERROR_NONE)
|
|
{
|
|
if (!g_bIsFullScreen)
|
|
DrawButton((HDC)0,BTN_DRIVE1);
|
|
|
|
PostMessage(window, WM_USER_BOOT, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
disk2Card.NotifyInvalidImage(DRIVE_1, filename, Error);
|
|
}
|
|
}
|
|
GlobalUnlock((HGLOBAL)lparam);
|
|
LogFileOutput("WM_DDE_EXECUTE (done)\n");
|
|
break;
|
|
}
|
|
|
|
case WM_DISPLAYCHANGE:
|
|
GetVideo().VideoReinitialize(false);
|
|
break;
|
|
|
|
case WM_DROPFILES:
|
|
{
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
{
|
|
Disk2InterfaceCard& disk2Card = dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6));
|
|
char filename[MAX_PATH];
|
|
DragQueryFile((HDROP)wparam,0,filename,sizeof(filename));
|
|
POINT point;
|
|
DragQueryPoint((HDROP)wparam,&point);
|
|
RECT rect;
|
|
rect.left = buttonx;
|
|
rect.right = rect.left+BUTTONCX+1;
|
|
rect.top = buttony+BTN_DRIVE2*BUTTONCY+1;
|
|
rect.bottom = rect.top+BUTTONCY;
|
|
const int iDrive = PtInRect(&rect,point) ? DRIVE_2 : DRIVE_1;
|
|
ImageError_e Error = disk2Card.InsertDisk(iDrive, filename, IMAGE_USE_FILES_WRITE_PROTECT_STATUS, IMAGE_DONT_CREATE);
|
|
if (Error == eIMAGE_ERROR_NONE)
|
|
{
|
|
if (!g_bIsFullScreen)
|
|
DrawButton((HDC)0,PtInRect(&rect,point) ? BTN_DRIVE2 : BTN_DRIVE1);
|
|
rect.top = buttony+BTN_DRIVE1*BUTTONCY+1;
|
|
if (!PtInRect(&rect,point))
|
|
{
|
|
SetForegroundWindow(window);
|
|
ProcessButtonClick(BTN_RUN);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
disk2Card.NotifyInvalidImage(iDrive, filename, Error);
|
|
}
|
|
}
|
|
DragFinish((HDROP)wparam);
|
|
break;
|
|
}
|
|
|
|
// @see: http://answers.google.com/answers/threadview?id=133059
|
|
// Win32 doesn't pass the PrintScreen key via WM_CHAR
|
|
// else if (wparam == VK_SNAPSHOT)
|
|
// Solution: 2 choices:
|
|
// 1) register hotkey, or
|
|
// 2) Use low level Keyboard hooks
|
|
// We use the 1st one since it is compatible with Win95
|
|
case WM_HOTKEY:
|
|
// wparam = user id
|
|
// lparam = modifiers: shift, ctrl, alt, win
|
|
if (wparam == VK_SNAPSHOT_560)
|
|
{
|
|
Video_TakeScreenShot( Video::SCREENSHOT_560x384 );
|
|
}
|
|
else
|
|
if (wparam == VK_SNAPSHOT_280) // ( lparam & MOD_SHIFT )
|
|
{
|
|
Video_TakeScreenShot( Video::SCREENSHOT_280x192 );
|
|
}
|
|
else
|
|
if (wparam == VK_SNAPSHOT_TEXT) // ( lparam & MOD_CONTROL )
|
|
{
|
|
char *pText;
|
|
size_t nSize = 0;
|
|
|
|
// if viewing the debugger, get the last virtual debugger screen
|
|
if ((g_nAppMode == MODE_DEBUG) && !DebugGetVideoMode(NULL))
|
|
nSize = Util_GetDebuggerText( pText );
|
|
else
|
|
nSize = Util_GetTextScreen( pText );
|
|
Util_CopyTextToClipboard( nSize, pText );
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
KeybUpdateCtrlShiftStatus();
|
|
|
|
// Processing is done in WM_KEYUP for: VK_F1 VK_F2 VK_F3 VK_F4 VK_F5 VK_F6 VK_F7 VK_F8
|
|
if ((wparam >= VK_F1) && (wparam <= VK_F8) && (buttondown == -1))
|
|
{
|
|
SetUsingCursor(FALSE);
|
|
buttondown = (int)(wparam-VK_F1);
|
|
if (g_bIsFullScreen && (buttonover != -1)) {
|
|
if (buttonover != buttondown)
|
|
EraseButton(buttonover);
|
|
buttonover = -1;
|
|
}
|
|
DrawButton((HDC)0,buttondown);
|
|
}
|
|
else if (wparam == VK_F9)
|
|
{
|
|
// F9 Next Video Mode
|
|
// SHIFT+F9 Prev Video Mode
|
|
// CTRL+SHIFT+F9 Toggle 50% Scan Lines
|
|
// ALT+F9 Can't use Alt-F9 as Alt is Open-Apple = Joystick Button #1
|
|
|
|
if ( !KeybGetCtrlStatus() && !KeybGetShiftStatus() ) // F9
|
|
{
|
|
GetVideo().IncVideoType();
|
|
}
|
|
else if ( !KeybGetCtrlStatus() && KeybGetShiftStatus() ) // SHIFT+F9
|
|
{
|
|
GetVideo().DecVideoType();
|
|
}
|
|
else if ( KeybGetCtrlStatus() && KeybGetShiftStatus() ) // CTRL+SHIFT+F9
|
|
{
|
|
GetVideo().SetVideoStyle( (VideoStyle_e) (GetVideo().GetVideoStyle() ^ VS_HALF_SCANLINES) );
|
|
}
|
|
|
|
// TODO: Clean up code:FrameRefreshStatus(DRAW_TITLE) DrawStatusArea((HDC)0,DRAW_TITLE)
|
|
DrawStatusArea( (HDC)0, DRAW_TITLE );
|
|
ApplyVideoModeChange();
|
|
}
|
|
else if (wparam == VK_F10)
|
|
{
|
|
if (g_Apple2Type == A2TYPE_APPLE2E || g_Apple2Type == A2TYPE_APPLE2EENHANCED || g_Apple2Type == A2TYPE_BASE64A)
|
|
{
|
|
GetVideo().SetVideoRomRockerSwitch( !GetVideo().GetVideoRomRockerSwitch() ); // F10: toggle rocker switch
|
|
NTSC_VideoInitAppleType();
|
|
}
|
|
else if (g_Apple2Type == A2TYPE_PRAVETS8A)
|
|
{
|
|
GetPravets().ToggleP8ACapsLock(); // F10: Toggles Pravets8A Capslock
|
|
}
|
|
}
|
|
else if (wparam == VK_F11 && !KeybGetCtrlStatus()) // Save state (F11)
|
|
{
|
|
SoundCore_SetFade(FADE_OUT);
|
|
if(GetPropertySheet().SaveStateSelectImage(window, true))
|
|
{
|
|
Snapshot_SaveState();
|
|
}
|
|
SoundCore_SetFade(FADE_IN);
|
|
}
|
|
else if (wparam == VK_F12) // Load state (F12 or Ctrl+F12)
|
|
{
|
|
SoundCore_SetFade(FADE_OUT);
|
|
if(GetPropertySheet().SaveStateSelectImage(window, false))
|
|
{
|
|
Snapshot_LoadState();
|
|
}
|
|
SoundCore_SetFade(FADE_IN);
|
|
}
|
|
else if (wparam == VK_CAPITAL)
|
|
{
|
|
KeybToggleCapsLock();
|
|
}
|
|
else if (wparam == VK_PAUSE)
|
|
{
|
|
SetUsingCursor(FALSE);
|
|
switch (g_nAppMode)
|
|
{
|
|
case MODE_RUNNING:
|
|
g_nAppMode = MODE_PAUSED;
|
|
SoundCore_SetFade(FADE_OUT);
|
|
RevealCursor();
|
|
break;
|
|
case MODE_PAUSED:
|
|
g_nAppMode = MODE_RUNNING;
|
|
SoundCore_SetFade(FADE_IN);
|
|
// Don't call FrameShowCursor(FALSE) else ClipCursor() won't be called
|
|
break;
|
|
case MODE_STEPPING:
|
|
SoundCore_SetFade(FADE_OUT);
|
|
DebugStopStepping();
|
|
break;
|
|
}
|
|
DrawStatusArea((HDC)0,DRAW_TITLE);
|
|
if ((g_nAppMode != MODE_LOGO) && (g_nAppMode != MODE_DEBUG))
|
|
VideoRedrawScreen();
|
|
}
|
|
else if ((wparam == VK_SCROLL) && GetPropertySheet().GetScrollLockToggle())
|
|
{
|
|
g_bScrollLock_FullSpeed = !g_bScrollLock_FullSpeed;
|
|
}
|
|
else if ((g_nAppMode == MODE_RUNNING) || (g_nAppMode == MODE_LOGO) || (g_nAppMode == MODE_STEPPING))
|
|
{
|
|
// NB. Alt Gr (Right-Alt): this normally send 2 WM_KEYDOWN messages for: VK_LCONTROL, then VK_RMENU
|
|
// . NB. The keyboard hook filter will suppress VK_LCONTROL (if -hook-altgr-control is passed on the cmd-line)
|
|
bool extended = (HIWORD(lparam) & KF_EXTENDED) != 0;
|
|
bool down = true;
|
|
bool autorep = (HIWORD(lparam) & KF_REPEAT) != 0;
|
|
BOOL IsJoyKey = JoyProcessKey((int)wparam, extended, down, autorep);
|
|
|
|
#if DEBUG_KEY_MESSAGES
|
|
LogOutput("WM_KEYDOWN: %08X (scanCode=%04X)\n", wparam, (lparam>>16)&0xfff);
|
|
#endif
|
|
if (!IsJoyKey &&
|
|
(g_nAppMode != MODE_LOGO)) // !MODE_LOGO - not emulating so don't pass to the VM's keyboard
|
|
{
|
|
// GH#678 Alternate key(s) to toggle max speed
|
|
// . Ctrl-0: Toggle speed: custom speed / Full-Speed
|
|
// . Ctrl-1: Speed = 1 MHz
|
|
// . Ctrl-3: Speed = Full-Speed
|
|
bool keyHandled = false;
|
|
if( KeybGetCtrlStatus() &&
|
|
!KeybGetAltStatus() && // GH#749 - AltGr also fakes CTRL being pressed!
|
|
wparam >= '0' && wparam <= '9' )
|
|
{
|
|
switch (wparam)
|
|
{
|
|
case '0': // Toggle speed: custom speed / Full-Speed
|
|
if (g_dwSpeed == SPEED_MAX)
|
|
REGLOAD_DEFAULT(REGVALUE_EMULATION_SPEED, &g_dwSpeed, SPEED_NORMAL);
|
|
else
|
|
g_dwSpeed = SPEED_MAX;
|
|
keyHandled = true; break;
|
|
case '1': // Speed = 1 MHz
|
|
g_dwSpeed = SPEED_NORMAL;
|
|
REGSAVE(REGVALUE_EMULATION_SPEED, g_dwSpeed);
|
|
keyHandled = true; break;
|
|
case '3': // Speed = Full-Speed
|
|
g_dwSpeed = SPEED_MAX;
|
|
keyHandled = true; break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (keyHandled)
|
|
SetCurrentCLK6502();
|
|
}
|
|
|
|
if (!keyHandled)
|
|
KeybQueueKeypress(wparam, NOT_ASCII);
|
|
|
|
if (!autorep)
|
|
KeybAnyKeyDown(WM_KEYDOWN, wparam, extended);
|
|
}
|
|
}
|
|
else if (g_nAppMode == MODE_DEBUG)
|
|
{
|
|
DebuggerProcessKey((int)wparam); // Debugger already active, re-direct key to debugger
|
|
}
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
if ((g_nAppMode == MODE_RUNNING) || (g_nAppMode == MODE_STEPPING) || (g_nAppMode == MODE_LOGO))
|
|
{
|
|
if (!g_bDebuggerEatKey)
|
|
{
|
|
#if DEBUG_KEY_MESSAGES
|
|
LogOutput("WM_CHAR: %08X\n", wparam);
|
|
#endif
|
|
if (g_nAppMode != MODE_LOGO) // !MODE_LOGO - not emulating so don't pass to the VM's keyboard
|
|
KeybQueueKeypress(wparam, ASCII);
|
|
}
|
|
|
|
g_bDebuggerEatKey = false;
|
|
}
|
|
else if (g_nAppMode == MODE_DEBUG)
|
|
{
|
|
DebuggerInputConsoleChar((char)wparam);
|
|
}
|
|
break;
|
|
|
|
case WM_KEYUP:
|
|
if ((wparam >= VK_F1) && (wparam <= VK_F8) && (buttondown == (int)wparam-VK_F1))
|
|
{
|
|
buttondown = -1;
|
|
if (g_bIsFullScreen)
|
|
EraseButton((int)(wparam-VK_F1));
|
|
else
|
|
DrawButton((HDC)0, (int)(wparam-VK_F1));
|
|
|
|
const int iButton = (int)(wparam-VK_F1);
|
|
if (KeybGetCtrlStatus() && (wparam == VK_F3 || wparam == VK_F4)) // Ctrl+F3/F4 for drive pop-up menu (GH#817)
|
|
{
|
|
KeybUpdateCtrlShiftStatus(); // #1364
|
|
POINT pt; // location of mouse click
|
|
pt.x = buttonx + BUTTONCX/2;
|
|
pt.y = buttony + BUTTONCY/2 + iButton * BUTTONCY;
|
|
const int iDrive = (int)(wparam - VK_F3);
|
|
ProcessDiskPopupMenu( window, pt, iDrive );
|
|
|
|
FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS);
|
|
DrawButton((HDC)0, iButton);
|
|
}
|
|
else
|
|
{
|
|
ProcessButtonClick(iButton, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool extended = (HIWORD(lparam) & KF_EXTENDED) != 0;
|
|
bool down = false;
|
|
bool autorep = false;
|
|
BOOL bIsJoyKey = JoyProcessKey((int)wparam, extended, down, autorep);
|
|
|
|
#if DEBUG_KEY_MESSAGES
|
|
LogOutput("WM_KEYUP: %08X\n", wparam);
|
|
#endif
|
|
if (!bIsJoyKey)
|
|
KeybAnyKeyDown(WM_KEYUP, wparam, extended);
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
KeybUpdateCtrlShiftStatus();
|
|
|
|
if (buttondown == -1)
|
|
{
|
|
int x = LOWORD(lparam);
|
|
int y = HIWORD(lparam);
|
|
if ((x >= buttonx) &&
|
|
(y >= buttony) &&
|
|
(y <= buttony+BUTTONS*BUTTONCY))
|
|
{
|
|
buttonactive = buttondown = (y-buttony-1)/BUTTONCY;
|
|
DrawButton((HDC)0,buttonactive);
|
|
SetCapture(window);
|
|
}
|
|
else if (g_bUsingCursor && !GetCardMgr().IsMouseCardInstalled())
|
|
{
|
|
if (wparam & (MK_CONTROL | MK_SHIFT))
|
|
{
|
|
SetUsingCursor(FALSE);
|
|
}
|
|
else
|
|
{
|
|
JoySetButton(BUTTON0, BUTTON_DOWN);
|
|
}
|
|
}
|
|
else if ( ((x < buttonx) && JoyUsingMouse() && ((g_nAppMode == MODE_RUNNING) || (g_nAppMode == MODE_STEPPING))) )
|
|
{
|
|
SetUsingCursor(TRUE);
|
|
}
|
|
else if (GetCardMgr().IsMouseCardInstalled())
|
|
{
|
|
if (wparam & (MK_CONTROL | MK_SHIFT))
|
|
{
|
|
RevealCursor();
|
|
}
|
|
else if (g_nAppMode == MODE_RUNNING || g_nAppMode == MODE_STEPPING)
|
|
{
|
|
CMouseInterface* pMouseCard = GetCardMgr().GetMouseCard();
|
|
|
|
if (pMouseCard)
|
|
{
|
|
if (!pMouseCard->IsEnabled())
|
|
{
|
|
pMouseCard->SetEnabled(true);
|
|
|
|
POINT Point;
|
|
GetCursorPos(&Point);
|
|
ScreenToClient(g_hFrameWindow, &Point);
|
|
const int iOutOfBoundsX=0, iOutOfBoundsY=0;
|
|
UpdateMouseInAppleViewport(iOutOfBoundsX, iOutOfBoundsY, Point.x, Point.y);
|
|
|
|
// Don't call SetButton() when 1st enabled (else get the confusing action of both enabling & an Apple mouse click)
|
|
}
|
|
else
|
|
{
|
|
pMouseCard->SetButton(BUTTON0, BUTTON_DOWN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DebuggerMouseClick( x, y );
|
|
}
|
|
RelayEvent(WM_LBUTTONDOWN,wparam,lparam);
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if (buttonactive != -1) {
|
|
ReleaseCapture();
|
|
if (buttondown == buttonactive) {
|
|
buttondown = -1;
|
|
if (g_bIsFullScreen)
|
|
EraseButton(buttonactive);
|
|
else
|
|
DrawButton((HDC)0,buttonactive);
|
|
ProcessButtonClick(buttonactive, true);
|
|
}
|
|
buttonactive = -1;
|
|
}
|
|
else if (g_bUsingCursor && !GetCardMgr().IsMouseCardInstalled())
|
|
{
|
|
JoySetButton(BUTTON0, BUTTON_UP);
|
|
}
|
|
else if (GetCardMgr().IsMouseCardInstalled())
|
|
{
|
|
GetCardMgr().GetMouseCard()->SetButton(BUTTON0, BUTTON_UP);
|
|
}
|
|
RelayEvent(WM_LBUTTONUP,wparam,lparam);
|
|
break;
|
|
|
|
case WM_MOUSEMOVE: {
|
|
// MSDN: "WM_MOUSEMOVE message" : Do not use the LOWORD or HIWORD macros to extract the x- and y- coordinates...
|
|
int x = GET_X_LPARAM(lparam);
|
|
int y = GET_Y_LPARAM(lparam);
|
|
int newover = (((x >= buttonx) &&
|
|
(x <= buttonx+BUTTONCX) &&
|
|
(y >= buttony) &&
|
|
(y <= buttony+BUTTONS*BUTTONCY))
|
|
? (y-buttony-1)/BUTTONCY : -1);
|
|
if (buttonactive != -1) {
|
|
int newdown = (newover == buttonactive) ? buttonactive : -1;
|
|
if (newdown != buttondown) {
|
|
buttondown = newdown;
|
|
DrawButton((HDC)0,buttonactive);
|
|
}
|
|
}
|
|
else if (g_bIsFullScreen && (newover != buttonover) && (buttondown == -1)) {
|
|
if (buttonover != -1)
|
|
EraseButton(buttonover);
|
|
buttonover = newover;
|
|
if (buttonover != -1)
|
|
DrawButton((HDC)0,buttonover);
|
|
}
|
|
else if (g_bUsingCursor && !GetCardMgr().IsMouseCardInstalled())
|
|
{
|
|
DrawCrosshairs(x,y);
|
|
JoySetPosition(x-viewportx-2, g_nViewportCX-4, y-viewporty-2, g_nViewportCY-4);
|
|
}
|
|
else if (GetCardMgr().IsMouseCardInstalled() && GetCardMgr().GetMouseCard()->IsActiveAndEnabled() && (g_nAppMode == MODE_RUNNING || g_nAppMode == MODE_STEPPING))
|
|
{
|
|
if (g_bLastCursorInAppleViewport)
|
|
break;
|
|
|
|
// Outside Apple viewport
|
|
|
|
const int iAppleScreenMaxX = g_nViewportCX-1;
|
|
const int iAppleScreenMaxY = g_nViewportCY-1;
|
|
const int iBoundMinX = viewportx;
|
|
const int iBoundMaxX = iAppleScreenMaxX;
|
|
const int iBoundMinY = viewporty;
|
|
const int iBoundMaxY = iAppleScreenMaxY;
|
|
|
|
int iOutOfBoundsX=0, iOutOfBoundsY=0;
|
|
if (x < iBoundMinX) iOutOfBoundsX=-1;
|
|
if (x > iBoundMaxX) iOutOfBoundsX=1;
|
|
if (y < iBoundMinY) iOutOfBoundsY=-1;
|
|
if (y > iBoundMaxY) iOutOfBoundsY=1;
|
|
|
|
UpdateMouseInAppleViewport(iOutOfBoundsX, iOutOfBoundsY, x, y);
|
|
}
|
|
|
|
FullScreenRevealCursor();
|
|
|
|
RelayEvent(WM_MOUSEMOVE,wparam,lparam);
|
|
break;
|
|
}
|
|
|
|
case WM_TIMER:
|
|
if (wparam == IDEVENT_TIMER_MOUSE)
|
|
{
|
|
// NB. Need to check /g_bAppActive/ since WM_TIMER events still occur after AppleWin app has lost focus
|
|
if (g_bAppActive && GetCardMgr().IsMouseCardInstalled() && GetCardMgr().GetMouseCard()->IsActiveAndEnabled() && (g_nAppMode == MODE_RUNNING || g_nAppMode == MODE_STEPPING))
|
|
{
|
|
if (!g_bLastCursorInAppleViewport)
|
|
break;
|
|
|
|
// Inside Apple viewport
|
|
|
|
int iOutOfBoundsX=0, iOutOfBoundsY=0;
|
|
|
|
long dX,dY;
|
|
if (DIMouse::ReadImmediateData(&dX, &dY) == S_OK)
|
|
GetCardMgr().GetMouseCard()->SetPositionRel(dX, dY, &iOutOfBoundsX, &iOutOfBoundsY);
|
|
|
|
UpdateMouseInAppleViewport(iOutOfBoundsX, iOutOfBoundsY);
|
|
}
|
|
}
|
|
else if (wparam == IDEVENT_TIMER_100MSEC) // GH#504
|
|
{
|
|
if (g_bIsFullScreen
|
|
&& !GetCardMgr().IsMouseCardInstalled() // Don't interfere if there's a mousecard present!
|
|
&& !g_bUsingCursor // Using mouse for joystick emulation (or mousecard restricted to window)
|
|
&& g_bShowingCursor
|
|
&& g_bFrameActive) // Frame inactive when eg. Config or 'Select Disk Image' dialogs are opened
|
|
{
|
|
g_uCount100msec++;
|
|
if (g_uCount100msec > 20) // Hide every 2sec of mouse inactivity
|
|
{
|
|
FrameShowCursor(FALSE);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// VSCROLL
|
|
// SB_LINEUP // Line Scrolling
|
|
// SB_PAGEUP // Page Scrolling
|
|
case WM_MOUSEWHEEL:
|
|
if (g_nAppMode == MODE_DEBUG)
|
|
{
|
|
KeybUpdateCtrlShiftStatus();
|
|
int zDelta = (short) HIWORD( wparam );
|
|
if (zDelta > 0)
|
|
{
|
|
DebuggerProcessKey( VK_UP );
|
|
}
|
|
else
|
|
{
|
|
DebuggerProcessKey( VK_DOWN );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_NOTIFY: // Tooltips for Drive buttons
|
|
if (((LPNMTTDISPINFO)lparam)->hdr.hwndFrom == tooltipwindow && ((LPNMTTDISPINFO)lparam)->hdr.code == TTN_GETDISPINFO)
|
|
{
|
|
LPNMTTDISPINFO pInfo = (LPNMTTDISPINFO)lparam;
|
|
if (pInfo->hdr.idFrom == TTID_DRIVE1_BUTTON || pInfo->hdr.idFrom == TTID_DRIVE2_BUTTON)
|
|
{
|
|
SendMessage(pInfo->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, 150);
|
|
|
|
Disk2InterfaceCard* pDisk2Slot5 = NULL, * pDisk2Slot6 = NULL;
|
|
|
|
if (GetCardMgr().QuerySlot(SLOT5) == CT_Disk2)
|
|
pDisk2Slot5 = dynamic_cast<Disk2InterfaceCard*>(GetCardMgr().GetObj(SLOT5));
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
pDisk2Slot6 = dynamic_cast<Disk2InterfaceCard*>(GetCardMgr().GetObj(SLOT6));
|
|
|
|
std::string slot5 = pDisk2Slot5 ? pDisk2Slot5->GetFullDiskFilename((int)(pInfo->hdr.idFrom)) : "";
|
|
std::string slot6 = pDisk2Slot6 ? pDisk2Slot6->GetFullDiskFilename((int)(pInfo->hdr.idFrom)) : "";
|
|
|
|
if (pDisk2Slot5)
|
|
{
|
|
if (slot6.empty()) slot6 = "<empty>";
|
|
if (slot5.empty()) slot5 = "<empty>";
|
|
slot6 = std::string("Slot6: ") + slot6;
|
|
slot5 = std::string("Slot5: ") + slot5;
|
|
}
|
|
|
|
std::string join = (!slot6.empty() && !slot5.empty()) ? "\n" : "";
|
|
driveTooltip = slot6 + join + slot5;
|
|
pInfo->lpszText = (LPTSTR)driveTooltip.c_str();
|
|
}
|
|
else if (pInfo->hdr.idFrom == TTID_SLOT6_TRK_SEC_INFO || pInfo->hdr.idFrom == TTID_SLOT5_TRK_SEC_INFO)
|
|
{
|
|
if (!GetWindowedModeShowDiskiiStatus())
|
|
break;
|
|
|
|
SendMessage(pInfo->hdr.hwndFrom, TTM_SETMAXTIPWIDTH, 0, 200);
|
|
|
|
const UINT slot = (pInfo->hdr.idFrom == TTID_SLOT6_TRK_SEC_INFO) ? SLOT6 : SLOT5;
|
|
Disk2InterfaceCard& disk2Card = dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(slot));
|
|
float drive1Track = disk2Card.GetPhase(DRIVE_1) / 2;
|
|
float drive2Track = disk2Card.GetPhase(DRIVE_2) / 2;
|
|
|
|
// Example tooltip:
|
|
//
|
|
// Drive 1: Drive 2:
|
|
// T$10.00 (T16.00) T$0C.00 (T12.00)
|
|
// S$06 (S06) S$0F (S15)
|
|
//
|
|
driveTooltip = "Drive 1:\t\tDrive 2:\n";
|
|
driveTooltip += "T$";
|
|
driveTooltip += disk2Card.FormatIntFracString(drive1Track, true);
|
|
driveTooltip += " (T";
|
|
driveTooltip += disk2Card.FormatIntFracString(drive1Track, false);
|
|
driveTooltip += ")";
|
|
driveTooltip += "\tT$";
|
|
driveTooltip += disk2Card.FormatIntFracString(drive2Track, true);
|
|
driveTooltip += " (T";
|
|
driveTooltip += disk2Card.FormatIntFracString(drive2Track, false);
|
|
driveTooltip += ")";
|
|
driveTooltip += "\n";
|
|
// hex sector
|
|
driveTooltip += "S$";
|
|
|
|
const char* unknown = "??";
|
|
|
|
if (g_nSector[slot][0] >= 0)
|
|
driveTooltip += StrFormat("%02X", g_nSector[slot][0]);
|
|
else
|
|
driveTooltip += unknown;
|
|
|
|
// dec sector
|
|
driveTooltip += " (S";
|
|
if (g_nSector[slot][0] >= 0)
|
|
driveTooltip += StrFormat("%02d", g_nSector[slot][0]);
|
|
else
|
|
driveTooltip += unknown;
|
|
driveTooltip += ")";
|
|
|
|
// hex sector
|
|
driveTooltip += "\tS$";
|
|
if (g_nSector[slot][1] >= 0)
|
|
driveTooltip += StrFormat("%02X", g_nSector[slot][1]);
|
|
else
|
|
driveTooltip += unknown;
|
|
|
|
// dec sector
|
|
driveTooltip += " (S";
|
|
if (g_nSector[slot][1] >= 0)
|
|
driveTooltip += StrFormat("%02d", g_nSector[slot][1]);
|
|
else
|
|
driveTooltip += unknown;
|
|
driveTooltip += ")";
|
|
|
|
pInfo->lpszText = (LPTSTR)driveTooltip.c_str();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
if (GetUpdateRect(window,NULL,0)){
|
|
DrawFrameWindow(true);
|
|
}
|
|
break;
|
|
|
|
case WM_PALETTECHANGED:
|
|
// To avoid creating an infinite loop, a window that receives this
|
|
// message must not realize its palette, unless it determines that
|
|
// wParam does not contain its own window handle.
|
|
if ((HWND)wparam == window)
|
|
break;
|
|
// else fall through
|
|
|
|
case WM_QUERYNEWPALETTE:
|
|
DrawFrameWindow();
|
|
break;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
case WM_RBUTTONUP:
|
|
KeybUpdateCtrlShiftStatus(); // #1363: Shift+Right-Click to show advanced disk image formatting options
|
|
if ((buttonover == -1) && (message == WM_RBUTTONUP)) // HACK: BUTTON_NONE
|
|
{
|
|
int x = LOWORD(lparam);
|
|
int y = HIWORD(lparam);
|
|
|
|
if ((x >= buttonx) &&
|
|
(y >= buttony) &&
|
|
(y <= buttony+BUTTONS*BUTTONCY))
|
|
{
|
|
const int iButton = (y-buttony-1)/BUTTONCY;
|
|
const int iDrive = iButton - BTN_DRIVE1;
|
|
if ((iButton == BTN_DRIVE1) || (iButton == BTN_DRIVE2))
|
|
{
|
|
{
|
|
RECT rect; // client area
|
|
POINT pt; // location of mouse click
|
|
|
|
// Get the bounding rectangle of the client area.
|
|
GetClientRect(window, (LPRECT) &rect);
|
|
|
|
// Get the client coordinates for the mouse click.
|
|
pt.x = GET_X_LPARAM(lparam);
|
|
pt.y = GET_Y_LPARAM(lparam);
|
|
|
|
// If the mouse click took place inside the client
|
|
// area, execute the application-defined function
|
|
// that displays the shortcut menu.
|
|
if (PtInRect((LPRECT) &rect, pt))
|
|
ProcessDiskPopupMenu( window, pt, iDrive );
|
|
}
|
|
|
|
FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES | DRAW_DISK_STATUS);
|
|
DrawButton((HDC)0, iButton);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_bUsingCursor && !GetCardMgr().IsMouseCardInstalled())
|
|
JoySetButton(BUTTON1, (message == WM_RBUTTONDOWN) ? BUTTON_DOWN : BUTTON_UP);
|
|
else if (GetCardMgr().IsMouseCardInstalled())
|
|
GetCardMgr().GetMouseCard()->SetButton(BUTTON1, (message == WM_RBUTTONDOWN) ? BUTTON_DOWN : BUTTON_UP);
|
|
|
|
RelayEvent(message,wparam,lparam);
|
|
break;
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
#if DEBUG_DD_PALETTE
|
|
if( g_bIsFullScreen )
|
|
OutputDebugString( "WM_SYSCOLORCHANGE: Full Screen\n" );
|
|
else
|
|
OutputDebugString( "WM_SYSCOLORCHANGE: Windowed\n" );
|
|
#endif
|
|
|
|
DeleteGdiObjects();
|
|
CreateGdiObjects();
|
|
break;
|
|
|
|
case WM_SYSCOMMAND:
|
|
switch (wparam & 0xFFF0) {
|
|
case SC_KEYMENU:
|
|
if (g_bIsFullScreen && g_bAppActive)
|
|
return 0;
|
|
break;
|
|
case SC_MINIMIZE:
|
|
GetWindowRect(window,&framerect);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_SYSKEYDOWN: // ALT + any key; or F10
|
|
KeybUpdateCtrlShiftStatus();
|
|
|
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/gg153546(v=vs.85).aspx
|
|
if (g_bAltEnter_ToggleFullScreen && KeybGetAltStatus() && (wparam == VK_RETURN)) // NB. VK_RETURN = 0x0D; Normally WM_CHAR will be 0x0A but ALT key triggers as WM_SYSKEYDOWN and VK_MENU
|
|
return 0; // NOP -- eat key
|
|
|
|
PostMessage(window,WM_KEYDOWN,wparam,lparam);
|
|
|
|
if ((wparam == VK_F10) || (wparam == VK_MENU)) // VK_MENU == ALT Key
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case WM_SYSKEYUP:
|
|
KeybUpdateCtrlShiftStatus();
|
|
|
|
// F10: no WM_KEYUP handler for VK_F10. Don't allow WM_KEYUP to pass to default handler which will show the app window's "menu" (and lose focus)
|
|
if (wparam == VK_F10)
|
|
return 0;
|
|
|
|
if (g_bAltEnter_ToggleFullScreen && KeybGetAltStatus() && (wparam == VK_RETURN)) // NB. VK_RETURN = 0x0D; Normally WM_CHAR will be 0x0A but ALT key triggers as WM_SYSKEYDOWN and VK_MENU
|
|
ScreenWindowResize(false);
|
|
else
|
|
PostMessage(window,WM_KEYUP,wparam,lparam);
|
|
|
|
break;
|
|
|
|
case WM_MENUCHAR: // GH#556 - Suppress the Windows Default Beep (ie. Ding) whenever ALT+<key> is pressed
|
|
return (MNC_CLOSE << 16) | (wparam & 0xffff);
|
|
|
|
case WM_USER_BENCHMARK: {
|
|
UpdateWindow(window);
|
|
ResetMachineState();
|
|
DrawStatusArea((HDC)0,DRAW_TITLE);
|
|
HCURSOR oldcursor = SetCursor(LoadCursor(0,IDC_WAIT));
|
|
g_nAppMode = MODE_BENCHMARK;
|
|
Win32Frame::GetWin32Frame().Benchmark();
|
|
g_nAppMode = MODE_LOGO;
|
|
ResetMachineState();
|
|
SetCursor(oldcursor);
|
|
break;
|
|
}
|
|
|
|
case WM_USER_SAVESTATE: // Save state
|
|
Snapshot_SaveState();
|
|
break;
|
|
|
|
case WM_USER_LOADSTATE: // Load state
|
|
Snapshot_LoadState();
|
|
break;
|
|
|
|
case WM_USER_TCP_SERIAL: // TCP serial events
|
|
{
|
|
WORD error = WSAGETSELECTERROR(lparam);
|
|
if (error != 0)
|
|
{
|
|
LogOutput("TCP Serial Winsock error 0x%X (%d)\r", error, error);
|
|
switch (error)
|
|
{
|
|
case WSAENETRESET:
|
|
case WSAECONNABORTED:
|
|
case WSAECONNRESET:
|
|
case WSAENOTCONN:
|
|
case WSAETIMEDOUT:
|
|
if (GetCardMgr().IsSSCInstalled())
|
|
GetCardMgr().GetSSC()->CommTcpSerialClose();
|
|
break;
|
|
|
|
default:
|
|
if (GetCardMgr().IsSSCInstalled())
|
|
GetCardMgr().GetSSC()->CommTcpSerialCleanup();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WORD wSelectEvent = WSAGETSELECTEVENT(lparam);
|
|
switch(wSelectEvent)
|
|
{
|
|
case FD_ACCEPT:
|
|
if (GetCardMgr().IsSSCInstalled())
|
|
GetCardMgr().GetSSC()->CommTcpSerialAccept();
|
|
break;
|
|
|
|
case FD_CLOSE:
|
|
if (GetCardMgr().IsSSCInstalled())
|
|
GetCardMgr().GetSSC()->CommTcpSerialClose();
|
|
break;
|
|
|
|
case FD_READ:
|
|
if (GetCardMgr().IsSSCInstalled())
|
|
GetCardMgr().GetSSC()->CommTcpSerialReceive();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Message posted by: WM_DDE_EXECUTE & Cmd-line boot
|
|
case WM_USER_BOOT:
|
|
{
|
|
SetForegroundWindow(window);
|
|
Sleep(500); // Wait for SetForegroundWindow() to take affect (400ms seems OK, so use 500ms to be sure)
|
|
SoundCore_TweakVolumes();
|
|
ProcessButtonClick(BTN_RUN);
|
|
break;
|
|
}
|
|
|
|
// Message posted by: Cmd-line boot
|
|
case WM_USER_FULLSCREEN:
|
|
{
|
|
ScreenWindowResize(false);
|
|
break;
|
|
}
|
|
|
|
} // switch(message)
|
|
|
|
return DefWindowProc(window,message,wparam,lparam);
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
// Process: VK_F6
|
|
void Win32Frame::ScreenWindowResize(const bool bCtrlKey)
|
|
{
|
|
static int nOldViewportScale = kDEFAULT_VIEWPORT_SCALE;
|
|
|
|
if (g_bIsFullScreen) // if full screen: then switch back to normal
|
|
{
|
|
SetNormalMode();
|
|
FrameResizeWindow(nOldViewportScale);
|
|
}
|
|
else if (bCtrlKey) // if normal screen && CTRL: then toggle scaling
|
|
{
|
|
FrameResizeWindow( (g_nViewportScale == 1) ? 2 : 1 ); // Toggle between 1x and 2x
|
|
REGSAVE(REGVALUE_WINDOW_SCALE, g_nViewportScale);
|
|
}
|
|
else
|
|
{
|
|
nOldViewportScale = g_nViewportScale;
|
|
FrameResizeWindow(1); // reset to 1x
|
|
SetFullScreenMode();
|
|
}
|
|
}
|
|
|
|
bool Win32Frame::ConfirmReboot(bool bFromButtonUI)
|
|
{
|
|
if (!bFromButtonUI || !g_bConfirmReboot)
|
|
return true;
|
|
|
|
int res = FrameMessageBox(
|
|
"Are you sure you want to reboot?\n"
|
|
"(All data will be lost!)\n"
|
|
"\n"
|
|
"You can skip this dialog from displaying\n"
|
|
"in the future by unchecking:\n"
|
|
"\n"
|
|
" [ ] Confirm reboot\n"
|
|
"\n"
|
|
"in the Configuration dialog.\n"
|
|
, "Reboot", MB_ICONWARNING|MB_YESNO);
|
|
return res == IDYES;
|
|
}
|
|
|
|
void Win32Frame::ProcessButtonClick(int button, bool bFromButtonUI /*=false*/)
|
|
{
|
|
SoundCore_SetFade(FADE_OUT);
|
|
bool bAllowFadeIn = true;
|
|
|
|
#if DEBUG_DD_PALETTE
|
|
LogOutput( "Button: F%d Full Screen: %d\n", button+1, g_bIsFullScreen );
|
|
#endif
|
|
|
|
switch (button) {
|
|
|
|
case BTN_HELP:
|
|
{
|
|
const std::string filename = g_sProgramDir + "APPLEWIN.CHM";
|
|
|
|
// (GH#437) For any internet downloaded AppleWin.chm files (stored on an NTFS drive) there may be an Alt Data Stream containing a Zone Identifier
|
|
// - try to delete it, otherwise the content won't be displayed unless it's unblock (via File Properties)
|
|
{
|
|
const std::string filename_with_zone_identifier = filename + ":Zone.Identifier";
|
|
DeleteFile(filename_with_zone_identifier.c_str());
|
|
}
|
|
|
|
HtmlHelp(GetDesktopWindow(), filename.c_str(), HH_DISPLAY_TOC, 0); // GH#1403
|
|
helpquit = 1;
|
|
}
|
|
break;
|
|
|
|
case BTN_RUN:
|
|
KeybUpdateCtrlShiftStatus();
|
|
if( KeybGetCtrlStatus() )
|
|
{
|
|
CtrlReset();
|
|
if (g_nAppMode == MODE_DEBUG)
|
|
DebugDisplay(TRUE);
|
|
return;
|
|
}
|
|
|
|
if (g_nAppMode == MODE_LOGO)
|
|
{
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6)).Boot();
|
|
|
|
LogFileTimeUntilFirstKeyReadReset();
|
|
|
|
if (!DebugQueryAnyBreakpointsSet())
|
|
{
|
|
g_nAppMode = MODE_RUNNING;
|
|
}
|
|
else
|
|
{
|
|
// EG. When using -debugger-auto-run <file.txt> (to set breakpoints), then we want to be MODE_STEPPING when leaving MODE_LOGO
|
|
DebugBegin();
|
|
DebugExitDebugger(); // Exit debugger, switch to MODE_STEPPING
|
|
g_bDebuggerEatKey = false; // Don't "eat" the next keypress when leaving the debugger
|
|
}
|
|
}
|
|
else if ((g_nAppMode == MODE_RUNNING) || (g_nAppMode == MODE_DEBUG) || (g_nAppMode == MODE_STEPPING) || (g_nAppMode == MODE_PAUSED))
|
|
{
|
|
if (ConfirmReboot(bFromButtonUI))
|
|
{
|
|
ResetMachineState();
|
|
|
|
// NB. Don't exit debugger or stepping
|
|
|
|
if (g_nAppMode == MODE_DEBUG)
|
|
DebugDisplay(TRUE);
|
|
}
|
|
}
|
|
|
|
DrawStatusArea((HDC)0,DRAW_TITLE);
|
|
VideoRedrawScreen();
|
|
break;
|
|
|
|
case BTN_DRIVE1:
|
|
case BTN_DRIVE2:
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
{
|
|
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6)).UserSelectNewDiskImage(button-BTN_DRIVE1);
|
|
if (!g_bIsFullScreen)
|
|
DrawButton((HDC)0,button);
|
|
}
|
|
break;
|
|
|
|
case BTN_DRIVESWAP:
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
{
|
|
dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6)).DriveSwap();
|
|
}
|
|
break;
|
|
|
|
case BTN_FULLSCR:
|
|
KeybUpdateCtrlShiftStatus();
|
|
ScreenWindowResize( KeybGetCtrlStatus() );
|
|
break;
|
|
|
|
case BTN_DEBUG:
|
|
if (g_nAppMode == MODE_LOGO && !GetLoadedSaveStateFlag())
|
|
{
|
|
// FIXME: Why is this needed? Surely when state is MODE_LOGO, then AppleII system will have been reset!
|
|
// - Transition to MODE_LOGO when: (a) AppleWin starts, (b) there's a Config change to the AppleII h/w
|
|
ResetMachineState();
|
|
}
|
|
|
|
if (g_nAppMode == MODE_STEPPING)
|
|
{
|
|
// Allow F7 to enter debugger even when not MODE_RUNNING
|
|
DebugStopStepping();
|
|
bAllowFadeIn = false;
|
|
}
|
|
else if (g_nAppMode == MODE_DEBUG)
|
|
{
|
|
DebugExitDebugger(); // Exit debugger, switch to MODE_RUNNING or MODE_STEPPING
|
|
g_bDebuggerEatKey = false; // Don't "eat" the next keypress when leaving the debugger via F7 (or clicking the Debugger button)
|
|
}
|
|
else // MODE_RUNNING, MODE_LOGO, MODE_PAUSED
|
|
{
|
|
GetVideo().ClearSHRResidue(); // Clear the framebuffer to remove any SHR residue in the borders
|
|
DebugBegin();
|
|
}
|
|
break;
|
|
|
|
case BTN_SETUP:
|
|
{
|
|
GetPropertySheet().Init();
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
if((g_nAppMode != MODE_DEBUG) && (g_nAppMode != MODE_PAUSED) && bAllowFadeIn)
|
|
{
|
|
SoundCore_SetFade(FADE_IN);
|
|
}
|
|
}
|
|
|
|
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/resources/menus/usingmenus.asp
|
|
// http://www.codeproject.com/menu/MenusForBeginners.asp?df=100&forumid=67645&exp=0&select=903061
|
|
|
|
void Win32Frame::ProcessDiskPopupMenu(HWND hwnd, POINT pt, const int iDrive)
|
|
{
|
|
if (GetCardMgr().QuerySlot(SLOT6) != CT_Disk2)
|
|
return;
|
|
|
|
SoundCore_SetFade(FADE_OUT);
|
|
|
|
Disk2InterfaceCard& disk2Card = dynamic_cast<Disk2InterfaceCard&>(GetCardMgr().GetRef(SLOT6));
|
|
|
|
// This is the default installation path of CiderPress.
|
|
// It shall not be left blank, otherwise an explorer window will be open.
|
|
char PathToCiderPress[MAX_PATH];
|
|
RegLoadString(
|
|
"Configuration",
|
|
REGVALUE_CIDERPRESSLOC,
|
|
true,
|
|
PathToCiderPress,
|
|
MAX_PATH,
|
|
"C:\\Program Files\\faddenSoft\\CiderPress\\CiderPress.exe");
|
|
//TODO: A directory is open if an empty path to CiderPress is set. This has to be fixed.
|
|
|
|
static uint32_t bNewDiskCopyBASIC = true;
|
|
static uint32_t bNewDiskCopyBitsyBoot = true;
|
|
static uint32_t bNewDiskCopyBitsyBye = true;
|
|
static uint32_t bNewDiskCopyProDOS = true;
|
|
|
|
const char REG_KEY_DISK_PREFRENCES[] = "Preferences"; // NOTE: Keep in sync with REG_KEY_DISK_PREFRENCES and UtilPopup_Toggle
|
|
|
|
RegLoadValue( REG_KEY_DISK_PREFRENCES, REGVALUE_PREF_NEW_DISK_COPY_BASIC , true, &bNewDiskCopyBASIC );
|
|
RegLoadValue( REG_KEY_DISK_PREFRENCES, REGVALUE_PREF_NEW_DISK_COPY_BITSY_BOOT, true, &bNewDiskCopyBitsyBoot );
|
|
RegLoadValue( REG_KEY_DISK_PREFRENCES, REGVALUE_PREF_NEW_DISK_COPY_BITSY_BYE , true, &bNewDiskCopyBitsyBye );
|
|
RegLoadValue( REG_KEY_DISK_PREFRENCES, REGVALUE_PREF_NEW_DISK_COPY_PRODOS_SYS, true, &bNewDiskCopyProDOS );
|
|
|
|
class UtilPopup_Toggle
|
|
{
|
|
public:
|
|
UtilPopup_Toggle( HMENU hMenu, int iCommand, const char *pKey, uint32_t *pVal )
|
|
{
|
|
*pVal = ! *pVal;
|
|
int bChecked = *pVal ? MF_CHECKED : MF_UNCHECKED;
|
|
CheckMenuItem(hMenu,iCommand, bChecked);
|
|
|
|
RegSaveValue(
|
|
"Preferences", // NOTE: Keep in sync with REG_KEY_DISK_PREFRENCES and UtilPopup_Toggle
|
|
pKey,
|
|
true,
|
|
*pVal
|
|
);
|
|
}
|
|
};
|
|
|
|
class UtilPopup_CheckMenu
|
|
{
|
|
public:
|
|
UtilPopup_CheckMenu( HMENU hMenu, int iMenuItem, uint32_t bIsChecked )
|
|
{
|
|
if (bIsChecked)
|
|
{
|
|
CheckMenuItem(hMenu, iMenuItem, MF_CHECKED);
|
|
}
|
|
}
|
|
};
|
|
|
|
std::string filename1= "\"";
|
|
filename1.append( disk2Card.GetFullName(iDrive) );
|
|
filename1.append("\"");
|
|
std::string sFileNameEmpty = "\"";
|
|
sFileNameEmpty.append("\"");
|
|
|
|
// Load the menu template containing the shortcut menu from the
|
|
// application's resources.
|
|
HMENU hmenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MENU_DISK_POPUP)); // menu template
|
|
if (hmenu == NULL)
|
|
{
|
|
SoundCore_SetFade(FADE_IN);
|
|
return;
|
|
}
|
|
|
|
// Get the first shortcut menu in the menu template.
|
|
// This is the menu that TrackPopupMenu displays.
|
|
HMENU hmenuTrackPopup = GetSubMenu(hmenu, 0); // shortcut menu
|
|
|
|
// TrackPopup uses screen coordinates, so convert the
|
|
// coordinates of the mouse click to screen coordinates.
|
|
ClientToScreen(hwnd, (LPPOINT) &pt);
|
|
|
|
bool bIsProtected = disk2Card.GetProtect(iDrive);
|
|
bool bIsDriveEmpty = disk2Card.IsDriveEmpty(iDrive);
|
|
HINSTANCE hInstance = GetFrame().g_hInstance;
|
|
|
|
// Check menu depending on current floppy protection
|
|
{
|
|
int iMenuItem = ID_DISKMENU_WRITEPROTECTION_OFF;
|
|
if (bIsProtected)
|
|
iMenuItem = ID_DISKMENU_WRITEPROTECTION_ON;
|
|
|
|
CheckMenuItem(hmenu, iMenuItem, MF_CHECKED);
|
|
}
|
|
|
|
if (bIsDriveEmpty)
|
|
{
|
|
EnableMenuItem(hmenu, ID_DISKMENU_EJECT, MF_GRAYED);
|
|
}
|
|
|
|
if (bIsProtected)
|
|
{
|
|
// If image-file is read-only (or a gzip) then disable these menu items
|
|
EnableMenuItem(hmenu, ID_DISKMENU_WRITEPROTECTION_ON, MF_GRAYED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_WRITEPROTECTION_OFF, MF_GRAYED);
|
|
|
|
}
|
|
|
|
// New Disk options for ProDOS file copy on format
|
|
{
|
|
UtilPopup_CheckMenu( hmenu, ID_DISKMENU_NEW_DISK_COPY_PRODOS , bNewDiskCopyProDOS );
|
|
UtilPopup_CheckMenu( hmenu, ID_DISKMENU_NEW_DISK_COPY_BITSY_BOOT, bNewDiskCopyBitsyBoot );
|
|
UtilPopup_CheckMenu( hmenu, ID_DISKMENU_NEW_DISK_COPY_BITSY_BYE , bNewDiskCopyBitsyBye );
|
|
UtilPopup_CheckMenu( hmenu, ID_DISKMENU_NEW_DISK_COPY_BASIC , bNewDiskCopyBASIC );
|
|
};
|
|
|
|
// Disk images QoL always enabled since they prompt which disk image to create/modify.
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_PRODOS_140K_DISK, MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_PRODOS_160K_DISK, MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_PRODOS_800K_DISK, MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_PRODOS_32MB_DISK, MF_ENABLED);
|
|
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_DOS33_140K_DISK, MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_DOS33_160K_DISK, MF_ENABLED);
|
|
|
|
ModifyMenu (hmenu, ID_DISKMENU_ADVANCED_SEPARATOR , MF_BYCOMMAND | MF_SEPARATOR , ID_DISKMENU_ADVANCED_SEPARATOR, 0 );
|
|
EnableMenuItem(hmenu, ID_DISKMENU_SELECT_BOOT_SECTOR , MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_BLANK_140K_DISK, MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_BLANK_160K_DISK, MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_BLANK_800K_DISK, MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_NEW_BLANK_32MB_DISK, MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_FORMAT_PRODOS_DISK , MF_ENABLED);
|
|
EnableMenuItem(hmenu, ID_DISKMENU_FORMAT_DOS33_DISK , MF_ENABLED);
|
|
|
|
#if _DEBUG
|
|
// Never hide menu options in debug build
|
|
#else
|
|
// If SHIFT is pressed enable the advanced disk pop-up menus
|
|
// otherwise remove them (since there is no Win32 comamnd to hide menu items.)
|
|
//
|
|
// KeybGetCtrlStatus(); // Can't use since Ctrl-F3, Ctrl-F4 is shortcut
|
|
// KeybGetAltStatus(); // Can't use
|
|
bool bAdvanced = KeybGetShiftStatus(); // #1364
|
|
if (!bAdvanced)
|
|
{
|
|
RemoveMenu(hmenu, ID_DISKMENU_ADVANCED_SEPARATOR , MF_BYCOMMAND);
|
|
RemoveMenu(hmenu, ID_DISKMENU_SELECT_BOOT_SECTOR , MF_BYCOMMAND);
|
|
RemoveMenu(hmenu, ID_DISKMENU_NEW_BLANK_140K_DISK, MF_BYCOMMAND);
|
|
RemoveMenu(hmenu, ID_DISKMENU_NEW_BLANK_160K_DISK, MF_BYCOMMAND);
|
|
RemoveMenu(hmenu, ID_DISKMENU_NEW_BLANK_800K_DISK, MF_BYCOMMAND);
|
|
RemoveMenu(hmenu, ID_DISKMENU_NEW_BLANK_32MB_DISK, MF_BYCOMMAND);
|
|
RemoveMenu(hmenu, ID_DISKMENU_FORMAT_PRODOS_DISK , MF_BYCOMMAND);
|
|
RemoveMenu(hmenu, ID_DISKMENU_FORMAT_DOS33_DISK , MF_BYCOMMAND);
|
|
}
|
|
#endif
|
|
|
|
// Draw and track the shortcut menu.
|
|
int iCommand = TrackPopupMenu(
|
|
hmenuTrackPopup
|
|
, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RETURNCMD
|
|
, pt.x, pt.y
|
|
, 0
|
|
, hwnd, NULL );
|
|
|
|
if (iCommand == ID_DISKMENU_EJECT)
|
|
disk2Card.EjectDisk( iDrive );
|
|
else
|
|
if (iCommand == ID_DISKMENU_WRITEPROTECTION_ON)
|
|
disk2Card.SetProtect( iDrive, true );
|
|
else
|
|
if (iCommand == ID_DISKMENU_WRITEPROTECTION_OFF)
|
|
disk2Card.SetProtect( iDrive, false );
|
|
else
|
|
if (iCommand == ID_DISKMENU_SENDTO_CIDERPRESS)
|
|
{
|
|
static char szCiderpressNotFoundCaption[] = "CiderPress not found";
|
|
static char szCiderpressNotFoundText[] = "CiderPress not found!\n"
|
|
"Please install CiderPress.\n"
|
|
"Otherwise set the path to CiderPress from Configuration->Disk.";
|
|
|
|
disk2Card.FlushCurrentTrack(iDrive);
|
|
|
|
//if(!filename1.compare("\"\"") == false) //Do not use this, for some reason it does not work!!!
|
|
if(!filename1.compare(sFileNameEmpty) )
|
|
{
|
|
int MB_Result = FrameMessageBox("No disk image loaded. Do you want to run CiderPress anyway?" ,"No disk image.", MB_ICONINFORMATION|MB_YESNO);
|
|
if (MB_Result == IDYES)
|
|
{
|
|
if (FileExists (PathToCiderPress ))
|
|
{
|
|
HINSTANCE nResult = ShellExecute(NULL, "open", PathToCiderPress, "" , NULL, SW_SHOWNORMAL);
|
|
}
|
|
else
|
|
{
|
|
FrameMessageBox(szCiderpressNotFoundText, szCiderpressNotFoundCaption, MB_ICONINFORMATION|MB_OK);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FileExists (PathToCiderPress ))
|
|
{
|
|
HINSTANCE nResult = ShellExecute(NULL, "open", PathToCiderPress, filename1.c_str() , NULL, SW_SHOWNORMAL);
|
|
}
|
|
else
|
|
{
|
|
FrameMessageBox(szCiderpressNotFoundText, szCiderpressNotFoundCaption, MB_ICONINFORMATION|MB_OK);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if((iCommand == ID_DISKMENU_NEW_PRODOS_140K_DISK)
|
|
|| (iCommand == ID_DISKMENU_NEW_PRODOS_160K_DISK)
|
|
|| (iCommand == ID_DISKMENU_NEW_PRODOS_800K_DISK)
|
|
|| (iCommand == ID_DISKMENU_NEW_PRODOS_32MB_DISK)
|
|
|| (iCommand == ID_DISKMENU_NEW_DOS33_140K_DISK)
|
|
|| (iCommand == ID_DISKMENU_NEW_DOS33_160K_DISK))
|
|
{
|
|
const bool bIsDOS33 = ((iCommand == ID_DISKMENU_NEW_DOS33_140K_DISK ) || (iCommand == ID_DISKMENU_NEW_DOS33_160K_DISK));
|
|
const bool bIsFloppy =!((iCommand == ID_DISKMENU_NEW_PRODOS_800K_DISK) || (iCommand == ID_DISKMENU_NEW_PRODOS_32MB_DISK));
|
|
const bool bIsFloppy525 = ((iCommand == ID_DISKMENU_NEW_PRODOS_140K_DISK) || (iCommand == ID_DISKMENU_NEW_PRODOS_160K_DISK) || bIsDOS33);
|
|
const bool bIs40Track = ((iCommand == ID_DISKMENU_NEW_PRODOS_160K_DISK) || (iCommand == ID_DISKMENU_NEW_DOS33_160K_DISK));
|
|
const bool bIsUnidisk35 = (iCommand == ID_DISKMENU_NEW_PRODOS_800K_DISK);
|
|
const bool bIsHardDisk = (iCommand == ID_DISKMENU_NEW_PRODOS_32MB_DISK);
|
|
const size_t nDiskSize = bIsHardDisk
|
|
? HARDDISK_32M_SIZE
|
|
: bIsUnidisk35
|
|
? UNIDISK35_800K_SIZE
|
|
: bIs40Track
|
|
? TRACK_DENIBBLIZED_SIZE * TRACKS_MAX
|
|
: TRACK_DENIBBLIZED_SIZE * TRACKS_STANDARD
|
|
;
|
|
|
|
const char* pszTitle = "Select new blank disk image";
|
|
|
|
time_t timestamp = time( NULL );
|
|
tm datetime = *localtime(×tamp);
|
|
|
|
const size_t MAX_MONTH_LEN = 32;
|
|
int year = datetime.tm_year + 1900;
|
|
char mon[MAX_MONTH_LEN] = {0};
|
|
int day = datetime.tm_mday;
|
|
int hour = datetime.tm_hour;
|
|
int min = datetime.tm_min;
|
|
int sec = datetime.tm_sec;
|
|
strftime( mon, MAX_MONTH_LEN-1, "%b", &datetime );
|
|
|
|
std::string sExtension = bIsHardDisk
|
|
? ".hdv"
|
|
: bIsDOS33
|
|
? ".do"
|
|
: ".po"
|
|
;
|
|
|
|
std::string sFileName( StrFormat(
|
|
"blank_%s_%04d_%3s_%02d_%02dh_%02dm_%02ds%s"
|
|
, bIsFloppy ? "floppy" : "hard"
|
|
, year, mon, day, hour, min, sec
|
|
, sExtension.c_str()
|
|
));
|
|
|
|
std::string pathname = g_sCurrentDir;
|
|
pathname.append( sFileName );
|
|
|
|
const char *pTitle = "New Disk Image";
|
|
const char *pSaveFilter = "All Files\0*.*\0";
|
|
char sPathName[ MAX_PATH ];
|
|
strncpy( sPathName, sFileName.c_str(), MAX_PATH-1 );
|
|
int nRes = Util_SelectDiskImage( hwnd, hInstance, pTitle, true, sPathName, pSaveFilter );
|
|
if (nRes)
|
|
{
|
|
pathname = sPathName;
|
|
if (FileExists(pathname))
|
|
{
|
|
nRes = FrameMessageBox(
|
|
"WARNING: Disk image already exists!\n"
|
|
"\n"
|
|
"(ALL DATA WILL BE LOST!)\n"
|
|
"\n"
|
|
"Overwrite ?"
|
|
, pTitle
|
|
, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2 );
|
|
if (nRes == IDNO) nRes = 0;
|
|
else nRes = IDOK;
|
|
}
|
|
|
|
if (nRes)
|
|
{
|
|
New_DOSProDOS_Disk(pTitle, pathname, nDiskSize, bIsDOS33, !!bNewDiskCopyBitsyBoot, !!bNewDiskCopyBitsyBye, !!bNewDiskCopyBASIC, !!bNewDiskCopyProDOS, this);
|
|
}
|
|
}
|
|
}
|
|
else // --- New Disk Options ---
|
|
if (iCommand == ID_DISKMENU_NEW_DISK_COPY_PRODOS)
|
|
{
|
|
UtilPopup_Toggle toggle( hmenu, iCommand, REGVALUE_PREF_NEW_DISK_COPY_PRODOS_SYS, &bNewDiskCopyProDOS );
|
|
}
|
|
else
|
|
if (iCommand == ID_DISKMENU_NEW_DISK_COPY_BITSY_BOOT)
|
|
{
|
|
UtilPopup_Toggle toggle( hmenu, iCommand, REGVALUE_PREF_NEW_DISK_COPY_BITSY_BOOT, &bNewDiskCopyBitsyBoot );
|
|
}
|
|
else
|
|
if (iCommand == ID_DISKMENU_NEW_DISK_COPY_BITSY_BYE)
|
|
{
|
|
UtilPopup_Toggle toggle( hmenu, iCommand, REGVALUE_PREF_NEW_DISK_COPY_BITSY_BYE, &bNewDiskCopyBitsyBye );
|
|
}
|
|
else
|
|
if (iCommand == ID_DISKMENU_NEW_DISK_COPY_BASIC)
|
|
{
|
|
UtilPopup_Toggle toggle( hmenu, iCommand, REGVALUE_PREF_NEW_DISK_COPY_BASIC, &bNewDiskCopyBASIC );
|
|
}
|
|
else // --- Advanced ---
|
|
if (iCommand == ID_DISKMENU_SELECT_BOOT_SECTOR)
|
|
{
|
|
// Default to directory of g_cmdLine.sBootSectorFileName;
|
|
char sDisplayFileName[ 256+1 ] = {0};
|
|
char sFilename[ MAX_PATH ] = {0};
|
|
const size_t nDisplayFileNameMax = sizeof(sDisplayFileName);
|
|
const size_t nFilenameMax = sizeof(sFilename);
|
|
const char *pTitle = "Select boot sector file";
|
|
const char *pExistFilter = // No *.nib;*.woz;*.gz;*.zip
|
|
"Floppy Disk Images (*.bin,*.dsk,*.do;*.po)\0"
|
|
"*.bin;*.dsk;*.do;*.po\0"
|
|
"Hard Disk Images (*.hdv;)\0"
|
|
"*.hdv\0"
|
|
"All Files\0*.*\0";
|
|
|
|
strncpy( sDisplayFileName, g_cmdLine.sBootSectorFileName.c_str(), nDisplayFileNameMax - 2);
|
|
strncpy( sFilename , g_cmdLine.sBootSectorFileName.c_str(), nFilenameMax - 2);
|
|
|
|
sDisplayFileName[nDisplayFileNameMax-1] = 0;
|
|
sFilename [nFilenameMax -1] = 0;
|
|
|
|
if (!g_cmdLine.nBootSectorFileSize)
|
|
{
|
|
strcpy( sDisplayFileName, "(Built-in AppleWin boot sector)" );
|
|
}
|
|
else
|
|
if (g_cmdLine.sBootSectorFileName.length() > (nDisplayFileNameMax-1))
|
|
{
|
|
// Convert long filename to "smart" ellipsis
|
|
// +126 = First 126 chars filename
|
|
// + 4 = "...."
|
|
// +126 = Last 126 chars of filename
|
|
//= 256 chars
|
|
const char *pHead = g_cmdLine.sBootSectorFileName.c_str();
|
|
const char *pTail = pHead + g_cmdLine.sBootSectorFileName.length() - 126;
|
|
sDisplayFileName[0] = 0;
|
|
strncpy( sDisplayFileName + 0, pHead , 126 );
|
|
strncpy( sDisplayFileName + 126, "....", 4 );
|
|
strncpy( sDisplayFileName + 130, pTail , 126 );
|
|
}
|
|
|
|
std::string sMessage( StrFormat(
|
|
"Current boot sector file is: \n"
|
|
"\n"
|
|
"%s\n"
|
|
"\n"
|
|
"Use a custom boot sector file?\n"
|
|
, sDisplayFileName
|
|
));
|
|
|
|
// Show dialog with current boot sector disk image
|
|
// Ask user if they wish to replace it
|
|
//
|
|
// [Yes] = Use custom boot sector
|
|
// [No] = Use AppleWin boot sector
|
|
// [Cancel] = Quit
|
|
int nRes = FrameMessageBox(sMessage.c_str(), pTitle, MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON2);
|
|
if (nRes == IDNO)
|
|
{
|
|
g_cmdLine.nBootSectorFileSize = 0;
|
|
g_cmdLine.sBootSectorFileName = "";
|
|
}
|
|
else
|
|
if (nRes == IDYES)
|
|
{
|
|
int nRes = Util_SelectDiskImage( hwnd, hInstance, pTitle, false, sFilename, pExistFilter );
|
|
if (nRes)
|
|
{
|
|
char sPath[ MAX_PATH];
|
|
DWORD res = GetFullPathName(sFilename, MAX_PATH, sPath, NULL);
|
|
|
|
FILE *pFile = fopen( sPath, "rb");
|
|
if (pFile)
|
|
{
|
|
fseek( pFile, 0, SEEK_END );
|
|
size_t nSize = ftell( pFile );
|
|
fseek( pFile, 0, SEEK_SET );
|
|
fclose( pFile );
|
|
|
|
if (nSize > 0)
|
|
{
|
|
g_cmdLine.sBootSectorFileName = sPath;
|
|
g_cmdLine.nBootSectorFileSize = nSize;
|
|
}
|
|
else
|
|
{
|
|
g_cmdLine.nBootSectorFileSize = 0;
|
|
g_cmdLine.sBootSectorFileName = "";
|
|
FrameMessageBox("ERROR: Couldn't read custom boot sector file.\n\nDefaulting to built-in AppleWin Boot Sector.", pTitle, MB_ICONERROR | MB_OK);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_cmdLine.nBootSectorFileSize = 0;
|
|
g_cmdLine.sBootSectorFileName = "";
|
|
FrameMessageBox( "ERROR: Couldn't open custom boot sector file.\n\nDefaulting to built-in AppleWin Boot Sector.", pTitle, MB_ICONERROR | MB_OK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // --- Advanced ---
|
|
if((iCommand == ID_DISKMENU_NEW_BLANK_140K_DISK)
|
|
|| (iCommand == ID_DISKMENU_NEW_BLANK_160K_DISK)
|
|
|| (iCommand == ID_DISKMENU_NEW_BLANK_800K_DISK)
|
|
|| (iCommand == ID_DISKMENU_NEW_BLANK_32MB_DISK))
|
|
{
|
|
const bool bIsFloppy = (iCommand != ID_DISKMENU_NEW_BLANK_32MB_DISK);
|
|
const bool bIsFloppy525 = ((iCommand == ID_DISKMENU_NEW_BLANK_140K_DISK) || (iCommand == ID_DISKMENU_NEW_BLANK_160K_DISK));
|
|
const bool bIs40Track = (iCommand == ID_DISKMENU_NEW_BLANK_160K_DISK);
|
|
const bool bIsUnidisk35 = (iCommand == ID_DISKMENU_NEW_BLANK_800K_DISK);
|
|
const bool bIsHardDisk = (iCommand == ID_DISKMENU_NEW_BLANK_32MB_DISK);
|
|
const size_t nDiskSize = bIsHardDisk
|
|
? HARDDISK_32M_SIZE
|
|
: bIsUnidisk35
|
|
? UNIDISK35_800K_SIZE
|
|
: bIs40Track
|
|
? TRACK_DENIBBLIZED_SIZE * TRACKS_MAX
|
|
: TRACK_DENIBBLIZED_SIZE * TRACKS_STANDARD
|
|
;
|
|
|
|
const char* pszTitle = "Select new blank disk image";
|
|
|
|
time_t timestamp = time( NULL );
|
|
tm datetime = *localtime(×tamp);
|
|
|
|
const size_t MAX_MONTH_LEN = 32;
|
|
int year = datetime.tm_year + 1900;
|
|
char mon[MAX_MONTH_LEN] = {0};
|
|
int day = datetime.tm_mday;
|
|
int hour = datetime.tm_hour;
|
|
int min = datetime.tm_min;
|
|
int sec = datetime.tm_sec;
|
|
strftime( mon, MAX_MONTH_LEN-1, "%b", &datetime );
|
|
std::string sFileName( StrFormat(
|
|
"blank_%s_%04d_%3s_%02d_%02dh_%02dm_%02ds.%s"
|
|
, bIsFloppy ? "floppy" : "hard"
|
|
, year, mon, day, hour, min, sec
|
|
, bIsFloppy ? "dsk" : "hdv"
|
|
) );
|
|
|
|
std::string pathname = g_sCurrentDir;
|
|
pathname.append( sFileName );
|
|
|
|
const char *pTitle = "New Disk Image";
|
|
const char *pSaveFilter = "All Files\0*.*\0";
|
|
char sPathName[ MAX_PATH ];
|
|
strncpy( sPathName, sFileName.c_str(), MAX_PATH-1 );
|
|
int nRes = Util_SelectDiskImage( hwnd, hInstance, pTitle, true, sPathName, pSaveFilter );
|
|
if (nRes)
|
|
{
|
|
pathname = sPathName;
|
|
if (FileExists(pathname))
|
|
{
|
|
nRes = FrameMessageBox(
|
|
"WARNING: Disk image already exists!\n"
|
|
"\n"
|
|
"(ALL DATA WILL BE LOST!)\n"
|
|
"\n"
|
|
"Overwrite ?"
|
|
, pTitle
|
|
, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2 );
|
|
if (nRes == IDNO) nRes = 0;
|
|
else nRes = IDOK;
|
|
}
|
|
|
|
if (nRes)
|
|
{
|
|
New_Blank_Disk(pTitle, pathname, nDiskSize, bIsHardDisk, this);
|
|
}
|
|
}
|
|
}
|
|
else // --- Advanced ---
|
|
if (iCommand == ID_DISKMENU_FORMAT_PRODOS_DISK)
|
|
{
|
|
char szFilename[ MAX_PATH ] = {0};
|
|
const char *pTitle = "Select ProDOS disk image to format";
|
|
const char *pLoadFilter = // No *.nib;*.woz;*.gz;*.zip
|
|
"Floppy Disk Images (*.bin,*.dsk,*.po)\0"
|
|
"*.bin;*.dsk;*.po\0"
|
|
"Hard Disk Images (*.hdv;)\0"
|
|
"*.hdv\0"
|
|
"All Files\0*.*\0";
|
|
|
|
int nRes = FrameMessageBox(
|
|
"Are you sure you want to FORMAT an existing disk as a ProDOS data disk?\n"
|
|
"\n"
|
|
"(ALL DATA WILL BE LOST!)\n"
|
|
, "Format"
|
|
, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2
|
|
);
|
|
if (nRes == IDYES)
|
|
{
|
|
nRes = Util_SelectDiskImage( hwnd, hInstance, pTitle, true, szFilename, pLoadFilter );
|
|
if (nRes)
|
|
{
|
|
std::string pathname = szFilename;
|
|
if (FileExists( pathname ))
|
|
{
|
|
Format_ProDOS_Disk(pathname, this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (iCommand == ID_DISKMENU_FORMAT_DOS33_DISK)
|
|
{
|
|
char szFilename[ MAX_PATH ] = {0};
|
|
const char *pTitle = "Select DOS 3.3 disk image to format";
|
|
const char *pLoadFilter =
|
|
"Floppy Disk Images (*.bin,*.dsk,*.do)\0"
|
|
"*.bin;*.dsk;*.do\0"
|
|
"All Files\0*.*\0";
|
|
|
|
int nRes = FrameMessageBox(
|
|
"Are you sure you want to FORMAT an existing disk as a DOS 3.3 data disk?\n"
|
|
"\n"
|
|
"(ALL DATA WILL BE LOST!)\n"
|
|
, "Format", MB_ICONWARNING|MB_YESNO);
|
|
if (nRes == IDYES)
|
|
{
|
|
nRes = Util_SelectDiskImage( hwnd, hInstance, pTitle, false, szFilename, pLoadFilter );
|
|
if (nRes)
|
|
{
|
|
std::string pathname = szFilename;
|
|
if (FileExists( pathname ))
|
|
{
|
|
Format_DOS33_Disk(pathname, this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Destroy the menu.
|
|
BOOL bRes = DestroyMenu(hmenu);
|
|
_ASSERT(bRes);
|
|
|
|
SoundCore_SetFade(FADE_IN);
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
void Win32Frame::RelayEvent (UINT message, WPARAM wparam, LPARAM lparam) {
|
|
if (g_bIsFullScreen)
|
|
return;
|
|
MSG msg;
|
|
msg.hwnd = g_hFrameWindow;
|
|
msg.message = message;
|
|
msg.wParam = wparam;
|
|
msg.lParam = lparam;
|
|
SendMessage(tooltipwindow,TTM_RELAYEVENT,0,(LPARAM)&msg);
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
|
|
int Win32Frame::GetFullScreenOffsetX(void)
|
|
{
|
|
return g_win_fullscreen_offsetx;
|
|
}
|
|
|
|
int Win32Frame::GetFullScreenOffsetY(void)
|
|
{
|
|
return g_win_fullscreen_offsety;
|
|
}
|
|
|
|
void Win32Frame::SetFullScreenMode(void)
|
|
{
|
|
#ifdef NO_DIRECT_X
|
|
|
|
return;
|
|
|
|
#else // NO_DIRECT_X
|
|
|
|
if (m_bestWidthForFullScreen && m_bestHeightForFullScreen)
|
|
{
|
|
DEVMODE devMode;
|
|
memset(&devMode, 0, sizeof(devMode));
|
|
devMode.dmSize = sizeof(devMode);
|
|
devMode.dmPelsWidth = m_bestWidthForFullScreen;
|
|
devMode.dmPelsHeight = m_bestHeightForFullScreen;
|
|
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
|
|
uint32_t dwFlags = 0;
|
|
LONG res = ChangeDisplaySettings(&devMode, dwFlags);
|
|
m_changedDisplaySettings = true;
|
|
}
|
|
|
|
//
|
|
|
|
MONITORINFO monitor_info;
|
|
FULLSCREEN_SCALE_TYPE width, height, scalex, scaley;
|
|
int top, left;
|
|
|
|
buttonover = -1;
|
|
|
|
g_main_window_saved_style = GetWindowLong(g_hFrameWindow, GWL_STYLE);
|
|
g_main_window_saved_exstyle = GetWindowLong(g_hFrameWindow, GWL_EXSTYLE);
|
|
GetWindowRect(g_hFrameWindow, &g_main_window_saved_rect);
|
|
SetWindowLong(g_hFrameWindow, GWL_STYLE , g_main_window_saved_style & ~(WS_CAPTION | WS_THICKFRAME));
|
|
SetWindowLong(g_hFrameWindow, GWL_EXSTYLE, g_main_window_saved_exstyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
|
|
|
|
monitor_info.cbSize = sizeof(monitor_info);
|
|
GetMonitorInfo(MonitorFromWindow(g_hFrameWindow, MONITOR_DEFAULTTONEAREST), &monitor_info);
|
|
|
|
left = monitor_info.rcMonitor.left;
|
|
top = monitor_info.rcMonitor.top;
|
|
|
|
width = (FULLSCREEN_SCALE_TYPE)(monitor_info.rcMonitor.right - monitor_info.rcMonitor.left);
|
|
height = (FULLSCREEN_SCALE_TYPE)(monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top );
|
|
|
|
scalex = width / GetVideo().GetFrameBufferBorderlessWidth();
|
|
scaley = height / GetVideo().GetFrameBufferBorderlessHeight();
|
|
|
|
// Retain aspect ratio if user hasn't changed full-screen resolution (GH#1121)
|
|
if (!GetFullScreenResolutionChangedByUser())
|
|
{
|
|
const int minimumScale = (scalex <= scaley) ? scalex : scaley;
|
|
scalex = scaley = minimumScale;
|
|
}
|
|
|
|
// NB. Separate x,y scaling is OK in full-screen mode
|
|
// . eg. SHR 640x400 (scalex=2, scaley=3) => 1280x1200, which roughly gives a 4:3 aspect ratio for a resolution of 1600x1200
|
|
g_win_fullscreen_offsetx = ((int)width - (int)(scalex * GetVideo().GetFrameBufferBorderlessWidth())) / 2;
|
|
g_win_fullscreen_offsety = ((int)height - (int)(scaley * GetVideo().GetFrameBufferBorderlessHeight())) / 2;
|
|
SetWindowPos(g_hFrameWindow, NULL, left, top, (int)width, (int)height, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
|
|
g_bIsFullScreen = true;
|
|
|
|
SetFullScreenViewportScale(scalex, scaley);
|
|
|
|
buttonx = GetFullScreenOffsetX() + g_nViewportCX + VIEWPORTX*2;
|
|
buttony = GetFullScreenOffsetY();
|
|
viewportx = VIEWPORTX; // TC-TODO: Should be zero too? (Since there's no 3D border in full-screen)
|
|
viewporty = 0; // GH#464
|
|
|
|
InvalidateRect(g_hFrameWindow,NULL,1);
|
|
|
|
#endif // NO_DIRECT_X
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::SetNormalMode(void)
|
|
{
|
|
if (m_changedDisplaySettings)
|
|
{
|
|
// Only call ChangeDisplaySettings() if resolution has changed, otherwise there'll be a display flicker (GH#965)
|
|
ChangeDisplaySettings(NULL, 0); // restore default resolution
|
|
m_changedDisplaySettings = false;
|
|
}
|
|
|
|
FullScreenRevealCursor(); // Do before clearing g_bIsFullScreen flag
|
|
|
|
buttonover = -1;
|
|
buttonx = BUTTONX;
|
|
buttony = BUTTONY;
|
|
viewportx = VIEWPORTX;
|
|
viewporty = VIEWPORTY;
|
|
|
|
g_win_fullscreen_offsetx = 0;
|
|
g_win_fullscreen_offsety = 0;
|
|
SetWindowLong(g_hFrameWindow, GWL_STYLE, g_main_window_saved_style);
|
|
SetWindowLong(g_hFrameWindow, GWL_EXSTYLE, g_main_window_saved_exstyle);
|
|
SetWindowPos(g_hFrameWindow, NULL,
|
|
g_main_window_saved_rect.left,
|
|
g_main_window_saved_rect.top,
|
|
g_main_window_saved_rect.right - g_main_window_saved_rect.left,
|
|
g_main_window_saved_rect.bottom - g_main_window_saved_rect.top,
|
|
SWP_SHOWWINDOW);
|
|
g_bIsFullScreen = false;
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::SetUsingCursor (BOOL bNewValue)
|
|
{
|
|
if (bNewValue == g_bUsingCursor)
|
|
return;
|
|
|
|
g_bUsingCursor = bNewValue;
|
|
|
|
if (g_bUsingCursor)
|
|
{
|
|
// Set TRUE when:
|
|
// . Using mouse for joystick emulation
|
|
// . Using mousecard and mouse is restricted to window
|
|
SetCapture(g_hFrameWindow);
|
|
RECT rect = { viewportx+2, // left
|
|
viewporty+2, // top
|
|
viewportx+g_nViewportCX-1, // right
|
|
viewporty+g_nViewportCY-1}; // bottom
|
|
ClientToScreen(g_hFrameWindow,(LPPOINT)&rect.left);
|
|
ClientToScreen(g_hFrameWindow,(LPPOINT)&rect.right);
|
|
ClipCursor(&rect);
|
|
FrameShowCursor(FALSE);
|
|
POINT pt;
|
|
GetCursorPos(&pt);
|
|
ScreenToClient(g_hFrameWindow,&pt);
|
|
DrawCrosshairs(pt.x,pt.y);
|
|
}
|
|
else
|
|
{
|
|
DrawCrosshairs(0,0);
|
|
FrameShowCursor(TRUE);
|
|
ClipCursor(NULL);
|
|
ReleaseCapture();
|
|
}
|
|
}
|
|
|
|
int Win32Frame::GetViewportScale(void)
|
|
{
|
|
return g_nViewportScale;
|
|
}
|
|
|
|
int Win32Frame::SetViewportScale(int nNewScale, bool bForce /*=false*/)
|
|
{
|
|
if (!bForce && nNewScale > g_nMaxViewportScale)
|
|
nNewScale = g_nMaxViewportScale;
|
|
|
|
g_nViewportScale = nNewScale;
|
|
g_nViewportCX = g_nViewportScale * GetVideo().GetFrameBufferBorderlessWidth();
|
|
g_nViewportCY = g_nViewportScale * GetVideo().GetFrameBufferBorderlessHeight();
|
|
|
|
buttonx = BUTTONX; // NB. macro uses g_nViewportCX
|
|
buttony = BUTTONY;
|
|
|
|
return nNewScale;
|
|
}
|
|
|
|
void Win32Frame::SetFullScreenViewportScale(int nNewXScale, int nNewYScale)
|
|
{
|
|
g_nViewportScale = MIN(nNewXScale, nNewYScale); // Not needed in FS mode
|
|
g_nViewportCX = nNewXScale * GetVideo().GetFrameBufferBorderlessWidth();
|
|
g_nViewportCY = nNewYScale * GetVideo().GetFrameBufferBorderlessHeight();
|
|
|
|
buttonx = BUTTONX; // NB. macro uses g_nViewportCX
|
|
buttony = BUTTONY;
|
|
}
|
|
|
|
void Win32Frame::SetupTooltipControls(void)
|
|
{
|
|
TOOLINFO toolinfo;
|
|
toolinfo.cbSize = sizeof(toolinfo);
|
|
toolinfo.uFlags = TTF_CENTERTIP;
|
|
toolinfo.hwnd = g_hFrameWindow;
|
|
toolinfo.hinst = g_hInstance;
|
|
toolinfo.lpszText = LPSTR_TEXTCALLBACK;
|
|
toolinfo.rect.left = BUTTONX;
|
|
toolinfo.rect.right = toolinfo.rect.left+BUTTONCX+1;
|
|
|
|
toolinfo.uId = TTID_DRIVE1_BUTTON;
|
|
toolinfo.rect.top = BUTTONY+BTN_DRIVE1*BUTTONCY+1;
|
|
toolinfo.rect.bottom = toolinfo.rect.top+BUTTONCY;
|
|
SendMessage(tooltipwindow, TTM_ADDTOOL, 0, (LPARAM)&toolinfo);
|
|
|
|
toolinfo.uId = TTID_DRIVE2_BUTTON;
|
|
toolinfo.rect.top = BUTTONY+BTN_DRIVE2*BUTTONCY+1;
|
|
toolinfo.rect.bottom = toolinfo.rect.top+BUTTONCY;
|
|
SendMessage(tooltipwindow, TTM_ADDTOOL, 0, (LPARAM)&toolinfo);
|
|
|
|
if (g_nViewportScale > 1)
|
|
{
|
|
if (GetCardMgr().QuerySlot(SLOT6) == CT_Disk2)
|
|
{
|
|
toolinfo.uId = TTID_SLOT6_TRK_SEC_INFO;
|
|
toolinfo.rect.top = BUTTONY + BUTTONS * BUTTONCY + 1 + yOffsetSlot6TrackInfo;
|
|
toolinfo.rect.bottom = toolinfo.rect.top + smallfontHeight * 2;
|
|
SendMessage(tooltipwindow, TTM_ADDTOOL, 0, (LPARAM)&toolinfo);
|
|
}
|
|
|
|
if (GetCardMgr().QuerySlot(SLOT5) == CT_Disk2)
|
|
{
|
|
toolinfo.uId = TTID_SLOT5_TRK_SEC_INFO;
|
|
toolinfo.rect.top = BUTTONY + BUTTONS * BUTTONCY + 1 + yOffsetSlot5TrackInfo;
|
|
toolinfo.rect.bottom = toolinfo.rect.top + smallfontHeight * 2;
|
|
SendMessage(tooltipwindow, TTM_ADDTOOL, 0, (LPARAM)&toolinfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
// SM_CXPADDEDBORDER is not supported on 2000 & XP, but GetSystemMetrics() returns 0 for unknown values, so this use of SM_CXPADDEDBORDER works on 2000 & XP too:
|
|
// http://msdn.microsoft.com/en-nz/library/windows/desktop/ms724385(v=vs.85).aspx
|
|
// NB. GetSystemMetrics(SM_CXPADDEDBORDER) returns 0 for Win7, when built with VS2008 (see GH#571)
|
|
void Win32Frame::GetWidthHeight(int& nWidth, int& nHeight)
|
|
{
|
|
const int kWidthFix = 1; // Fix for Win32 Subsystem Version 6.00 (GH#1438)
|
|
|
|
nWidth = g_nViewportCX + VIEWPORTX*2
|
|
+ BUTTONCX
|
|
+ (GetSystemMetrics(SM_CXFIXEDFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER) + kWidthFix) * 2;
|
|
nHeight = g_nViewportCY + VIEWPORTY*2
|
|
+ (GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 // NB. No SM_CYPADDEDBORDER
|
|
+ GetSystemMetrics(SM_CYCAPTION);
|
|
|
|
#if 0 // GH#571
|
|
LogOutput("g_nViewportCX = %d\n", g_nViewportCX);
|
|
LogOutput("VIEWPORTX = %d (const)\n", VIEWPORTX);
|
|
LogOutput("BUTTONCX = %d (const)\n", BUTTONCX);
|
|
LogOutput("GetSystemMetrics(SM_CXFRAME) = %d (unused)\n", GetSystemMetrics(SM_CXFRAME));
|
|
LogOutput("GetSystemMetrics(SM_CXFIXEDFRAME) = %d\n", GetSystemMetrics(SM_CXFIXEDFRAME));
|
|
LogOutput("GetSystemMetrics(SM_CXBORDER) = %d (unused)\n", GetSystemMetrics(SM_CXBORDER));
|
|
LogOutput("GetSystemMetrics(SM_CXPADDEDBORDER) = %d\n", GetSystemMetrics(SM_CXPADDEDBORDER));
|
|
LogOutput("nWidth = %d\n", nWidth);
|
|
LogOutput("g_nViewportCY = %d\n", g_nViewportCY);
|
|
LogOutput("VIEWPORTY = %d (const)\n", VIEWPORTY);
|
|
LogOutput("GetSystemMetrics(SM_CYFRAME) = %d (unused)\n", GetSystemMetrics(SM_CYFRAME));
|
|
LogOutput("GetSystemMetrics(SM_CYFIXEDFRAME) = %d\n", GetSystemMetrics(SM_CYFIXEDFRAME));
|
|
LogOutput("GetSystemMetrics(SM_CYBORDER) = %d (unused)\n", GetSystemMetrics(SM_CYBORDER));
|
|
LogOutput("GetSystemMetrics(SM_CYCAPTION) = %d\n", GetSystemMetrics(SM_CYCAPTION));
|
|
LogOutput("nHeight = %d\n\n", nHeight);
|
|
#endif
|
|
}
|
|
|
|
// Window frame's border size has changed (eg. VidHD added/removed)
|
|
void Win32Frame::ResizeWindow(void)
|
|
{
|
|
FrameResizeWindow(GetViewportScale());
|
|
}
|
|
|
|
void Win32Frame::FrameResizeWindow(int nNewScale)
|
|
{
|
|
int nOldWidth, nOldHeight;
|
|
GetWidthHeight(nOldWidth, nOldHeight);
|
|
|
|
nNewScale = SetViewportScale(nNewScale);
|
|
|
|
GetWindowRect(g_hFrameWindow, &framerect);
|
|
int nXPos = framerect.left;
|
|
int nYPos = framerect.top;
|
|
|
|
// Invalidate old rect region
|
|
{
|
|
RECT irect;
|
|
irect.left = irect.top = 0;
|
|
irect.right = nOldWidth;
|
|
irect.bottom = nOldHeight;
|
|
InvalidateRect(g_hFrameWindow, &irect, TRUE);
|
|
}
|
|
|
|
// Resize the window
|
|
int nNewWidth, nNewHeight;
|
|
GetWidthHeight(nNewWidth, nNewHeight);
|
|
|
|
MoveWindow(g_hFrameWindow, nXPos, nYPos, nNewWidth, nNewHeight, TRUE);
|
|
UpdateWindow(g_hFrameWindow);
|
|
|
|
// Remove the tooltips for the old window size
|
|
TOOLINFO toolinfo = {0};
|
|
toolinfo.cbSize = sizeof(toolinfo);
|
|
toolinfo.hwnd = g_hFrameWindow;
|
|
|
|
for (UINT id = 0; id < TTID_MAX; id++)
|
|
{
|
|
toolinfo.uId = id;
|
|
SendMessage(tooltipwindow, TTM_DELTOOL, 0, (LPARAM)&toolinfo);
|
|
}
|
|
|
|
// Setup the tooltips for the new window size
|
|
SetupTooltipControls();
|
|
}
|
|
|
|
//
|
|
// ----- ALL GLOBALLY ACCESSIBLE FUNCTIONS ARE BELOW THIS LINE -----
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
void Win32Frame::FrameCreateWindow(void)
|
|
{
|
|
int nWidth, nHeight;
|
|
|
|
// Set g_nMaxViewportScale
|
|
{
|
|
int nOldViewportCX = g_nViewportCX;
|
|
int nOldViewportCY = g_nViewportCY;
|
|
|
|
g_nViewportCX = GetVideo().GetFrameBufferBorderlessWidth() * 2;
|
|
g_nViewportCY = GetVideo().GetFrameBufferBorderlessHeight() * 2;
|
|
GetWidthHeight(nWidth, nHeight); // Probe with 2x dimensions
|
|
|
|
g_nViewportCX = nOldViewportCX;
|
|
g_nViewportCY = nOldViewportCY;
|
|
|
|
if (nWidth > GetSystemMetrics(SM_CXSCREEN) || nHeight > GetSystemMetrics(SM_CYSCREEN))
|
|
g_nMaxViewportScale = 1;
|
|
}
|
|
|
|
GetWidthHeight(nWidth, nHeight);
|
|
|
|
// If screen is too small for 2x, then revert to 1x
|
|
if (g_nViewportScale == 2 && (nWidth > GetSystemMetrics(SM_CXSCREEN) || nHeight > GetSystemMetrics(SM_CYSCREEN)))
|
|
{
|
|
g_nMaxViewportScale = 1;
|
|
SetViewportScale(1);
|
|
GetWidthHeight(nWidth, nHeight);
|
|
}
|
|
|
|
// Restore Window X Position
|
|
int nXPos = -1;
|
|
{
|
|
const int nXScreen = GetSystemMetrics(SM_CXSCREEN) - nWidth;
|
|
|
|
if (RegLoadValue(REG_PREFS, REGVALUE_PREF_WINDOW_X_POS, true, (uint32_t*)&nXPos))
|
|
{
|
|
if ((nXPos > nXScreen) && !g_bMultiMon)
|
|
nXPos = -1; // Not fully visible, so default to centre position
|
|
}
|
|
|
|
if ((nXPos == -1) && !g_bMultiMon)
|
|
nXPos = nXScreen / 2;
|
|
}
|
|
|
|
// Restore Window Y Position
|
|
int nYPos = -1;
|
|
{
|
|
const int nYScreen = GetSystemMetrics(SM_CYSCREEN) - nHeight;
|
|
|
|
if (RegLoadValue(REG_PREFS, REGVALUE_PREF_WINDOW_Y_POS, true, (uint32_t*)&nYPos))
|
|
{
|
|
if ((nYPos > nYScreen) && !g_bMultiMon)
|
|
nYPos = -1; // Not fully visible, so default to centre position
|
|
}
|
|
|
|
if ((nYPos == -1) && !g_bMultiMon)
|
|
nYPos = nYScreen / 2;
|
|
}
|
|
|
|
//
|
|
|
|
buttonx = (g_nViewportCX + VIEWPORTX*2);
|
|
buttony = 0;
|
|
|
|
GetAppleWindowTitle();
|
|
|
|
// NB. g_hFrameWindow also set by WM_CREATE - NB. CreateWindow() must synchronously send WM_CREATE
|
|
g_hFrameWindow = CreateWindow(
|
|
"APPLE2FRAME",
|
|
g_pAppTitle.c_str(),
|
|
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU |
|
|
WS_MINIMIZEBOX | WS_VISIBLE,
|
|
nXPos, nYPos, nWidth, nHeight,
|
|
HWND_DESKTOP,
|
|
(HMENU)0,
|
|
g_hInstance, NULL );
|
|
|
|
InitCommonControls();
|
|
tooltipwindow = CreateWindow(
|
|
TOOLTIPS_CLASS,NULL,TTS_ALWAYSTIP|TTS_NOPREFIX /*Allow tabs in tooltips*/ ,
|
|
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
|
|
g_hFrameWindow,
|
|
(HMENU)0,
|
|
g_hInstance,NULL );
|
|
|
|
SetupTooltipControls();
|
|
|
|
_ASSERT(g_TimerIDEvent_100msec == 0);
|
|
g_TimerIDEvent_100msec = SetTimer(g_hFrameWindow, IDEVENT_TIMER_100MSEC, 100, NULL);
|
|
LogFileOutput("FrameCreateWindow: SetTimer(), id=0x%08X\n", g_TimerIDEvent_100msec);
|
|
}
|
|
|
|
//===========================================================================
|
|
HDC Win32Frame::FrameGetDC () {
|
|
if (!g_hFrameDC) {
|
|
g_hFrameDC = GetDC(g_hFrameWindow);
|
|
SetViewportOrgEx(g_hFrameDC,viewportx,viewporty,NULL);
|
|
}
|
|
return g_hFrameDC;
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::FrameReleaseDC () {
|
|
if (g_hFrameDC) {
|
|
SetViewportOrgEx(g_hFrameDC,0,0,NULL);
|
|
ReleaseDC(g_hFrameWindow,g_hFrameDC);
|
|
g_hFrameDC = (HDC)0;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::FrameRefreshStatus (int drawflags) {
|
|
DrawStatusArea((HDC)0,drawflags);
|
|
}
|
|
|
|
//===========================================================================
|
|
void Win32Frame::FrameRegisterClass () {
|
|
WNDCLASSEX wndclass;
|
|
memset(&wndclass, 0, sizeof(WNDCLASSEX));
|
|
wndclass.cbSize = sizeof(WNDCLASSEX);
|
|
wndclass.style = CS_OWNDC | CS_BYTEALIGNCLIENT;
|
|
wndclass.lpfnWndProc = FrameWndProc;
|
|
wndclass.hInstance = g_hInstance;
|
|
wndclass.hIcon = LoadIcon(g_hInstance,"APPLEWIN_ICON");
|
|
wndclass.hCursor = LoadCursor(0,IDC_ARROW);
|
|
wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
|
#if ENABLE_MENU
|
|
wndclass.lpszMenuName = (LPCSTR)IDR_MENU1;
|
|
#endif
|
|
wndclass.lpszClassName = "APPLE2FRAME";
|
|
wndclass.hIconSm = (HICON)LoadImage(g_hInstance,"APPLEWIN_ICON",
|
|
IMAGE_ICON,16,16,LR_DEFAULTCOLOR);
|
|
RegisterClassEx(&wndclass);
|
|
}
|
|
|
|
//===========================================================================
|
|
// TODO: FIXME: Util_TestFileExists()
|
|
static bool FileExists(std::string strFilename)
|
|
{
|
|
struct _stat64 stFileInfo;
|
|
int intStat = _stat64(strFilename.c_str(), &stFileInfo);
|
|
return (intStat == 0);
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
// Called when:
|
|
// . Mouse f/w sets abs position
|
|
// . UpdateMouseInAppleViewport() is called and inside Apple screen
|
|
void Win32Frame::FrameSetCursorPosByMousePos()
|
|
{
|
|
// _ASSERT(GetCardMgr().IsMouseCardInstalled()); // CMouseInterface::ctor calls this function, ie. before GetCardMgr()::m_pMouseCard is setup
|
|
if (!GetCardMgr().IsMouseCardInstalled())
|
|
return;
|
|
|
|
if (!g_hFrameWindow || g_bShowingCursor)
|
|
return;
|
|
|
|
int iX, iMinX, iMaxX;
|
|
int iY, iMinY, iMaxY;
|
|
GetCardMgr().GetMouseCard()->GetXY(iX, iMinX, iMaxX, iY, iMinY, iMaxY);
|
|
|
|
float fScaleX = (float)(iX-iMinX) / ((float)(iMaxX-iMinX));
|
|
float fScaleY = (float)(iY-iMinY) / ((float)(iMaxY-iMinY));
|
|
|
|
int iWindowX = (int)(fScaleX * (float)g_nViewportCX);
|
|
int iWindowY = (int)(fScaleY * (float)g_nViewportCY);
|
|
|
|
POINT Point = {viewportx+2, viewporty+2}; // top-left
|
|
ClientToScreen(g_hFrameWindow, &Point);
|
|
SetCursorPos(Point.x+iWindowX-VIEWPORTX, Point.y+iWindowY-VIEWPORTY);
|
|
|
|
#if defined(_DEBUG) && 0 // OutputDebugString() when cursor position changes since last time
|
|
static int OldX = 0, OldY = 0;
|
|
int X = Point.x + iWindowX - VIEWPORTX;
|
|
int Y = Point.y + iWindowY - VIEWPORTY;
|
|
if (X != OldX || Y != OldY)
|
|
{
|
|
LogOutput("[FrameSetCursorPosByMousePos] x,y=%d,%d (MaxX,Y=%d,%d)\n", X, Y, iMaxX, iMaxY);
|
|
OldX = X; OldY = Y;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Called when:
|
|
// . UpdateMouseInAppleViewport() is called and mouse leaving/entering Apple screen area
|
|
// . NB. Not called when leaving & mouse clipped to Apple screen area
|
|
void Win32Frame::FrameSetCursorPosByMousePos(int x, int y, int dx, int dy, bool bLeavingAppleScreen)
|
|
{
|
|
_ASSERT(GetCardMgr().IsMouseCardInstalled());
|
|
if (!GetCardMgr().IsMouseCardInstalled())
|
|
return;
|
|
|
|
if (!g_hFrameWindow || (g_bShowingCursor && bLeavingAppleScreen) || (!g_bShowingCursor && !bLeavingAppleScreen))
|
|
return;
|
|
|
|
int iX, iMinX, iMaxX;
|
|
int iY, iMinY, iMaxY;
|
|
GetCardMgr().GetMouseCard()->GetXY(iX, iMinX, iMaxX, iY, iMinY, iMaxY);
|
|
|
|
if (bLeavingAppleScreen)
|
|
{
|
|
// Set mouse x/y pos to edge of mouse's window
|
|
if (dx < 0) iX = iMinX;
|
|
if (dx > 0) iX = iMaxX;
|
|
if (dy < 0) iY = iMinY;
|
|
if (dy > 0) iY = iMaxY;
|
|
|
|
float fScaleX = (float)(iX-iMinX) / ((float)(iMaxX-iMinX));
|
|
float fScaleY = (float)(iY-iMinY) / ((float)(iMaxY-iMinY));
|
|
|
|
int iWindowX = (int)(fScaleX * (float)g_nViewportCX) + dx;
|
|
int iWindowY = (int)(fScaleY * (float)g_nViewportCY) + dy;
|
|
|
|
POINT Point = {viewportx+2, viewporty+2}; // top-left
|
|
ClientToScreen(g_hFrameWindow, &Point);
|
|
SetCursorPos(Point.x+iWindowX-VIEWPORTX, Point.y+iWindowY-VIEWPORTY);
|
|
//LogOutput("[MOUSE_LEAVING ] x=%d, y=%d (Scale: x,y=%f,%f; iX,iY=%d,%d)\n", iWindowX, iWindowY, fScaleX, fScaleY, iX, iY);
|
|
}
|
|
else // Mouse entering Apple screen area
|
|
{
|
|
//LogOutput("[MOUSE_ENTERING] x=%d, y=%d\n", x, y);
|
|
if (!g_bIsFullScreen) // GH#464
|
|
{
|
|
x -= (viewportx+2-VIEWPORTX); if (x < 0) x = 0;
|
|
y -= (viewporty+2-VIEWPORTY); if (y < 0) y = 0;
|
|
}
|
|
|
|
_ASSERT(x <= g_nViewportCX);
|
|
_ASSERT(y <= g_nViewportCY);
|
|
float fScaleX = (float)x / (float)g_nViewportCX;
|
|
float fScaleY = (float)y / (float)g_nViewportCY;
|
|
|
|
int iAppleX = iMinX + (int)(fScaleX * (float)(iMaxX-iMinX));
|
|
int iAppleY = iMinY + (int)(fScaleY * (float)(iMaxY-iMinY));
|
|
|
|
GetCardMgr().GetMouseCard()->SetCursorPos(iAppleX, iAppleY); // Set new entry position
|
|
|
|
// Dump initial deltas (otherwise can get big deltas since last read when entering Apple screen area)
|
|
DIMouse::ReadImmediateData();
|
|
}
|
|
}
|
|
|
|
void Win32Frame::DrawCrosshairsMouse()
|
|
{
|
|
_ASSERT(GetCardMgr().IsMouseCardInstalled());
|
|
if (!GetCardMgr().IsMouseCardInstalled())
|
|
return;
|
|
|
|
if (!GetPropertySheet().GetMouseShowCrosshair())
|
|
return;
|
|
|
|
int iX, iMinX, iMaxX;
|
|
int iY, iMinY, iMaxY;
|
|
GetCardMgr().GetMouseCard()->GetXY(iX, iMinX, iMaxX, iY, iMinY, iMaxY);
|
|
_ASSERT(iMinX == 0 && iMinY == 0);
|
|
|
|
float fScaleX = (float)(iX-iMinX) / ((float)(iMaxX-iMinX));
|
|
float fScaleY = (float)(iY-iMinY) / ((float)(iMaxY-iMinY));
|
|
|
|
int iWindowX = (int)(fScaleX * (float)g_nViewportCX);
|
|
int iWindowY = (int)(fScaleY * (float)g_nViewportCY);
|
|
|
|
DrawCrosshairs(iWindowX,iWindowY);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
//#define _DEBUG_SHOW_CURSOR // NB. Get an ASSERT on LMB (after Ctrl+LMB)
|
|
#endif
|
|
|
|
void Win32Frame::UpdateMouseInAppleViewport(int iOutOfBoundsX, int iOutOfBoundsY, int x, int y)
|
|
{
|
|
const bool bOutsideAppleViewport = iOutOfBoundsX || iOutOfBoundsY;
|
|
|
|
if (bOutsideAppleViewport)
|
|
{
|
|
if (GetPropertySheet().GetMouseRestrictToWindow())
|
|
return;
|
|
|
|
g_bLastCursorInAppleViewport = false;
|
|
|
|
if (!g_bShowingCursor)
|
|
{
|
|
// Mouse leaving Apple screen area
|
|
FrameSetCursorPosByMousePos(0, 0, iOutOfBoundsX, iOutOfBoundsY, true);
|
|
#ifdef _DEBUG_SHOW_CURSOR
|
|
g_bShowingCursor = true;
|
|
#else
|
|
FrameShowCursor(TRUE);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_bLastCursorInAppleViewport = true;
|
|
|
|
if (g_bShowingCursor)
|
|
{
|
|
// Mouse entering Apple screen area
|
|
FrameSetCursorPosByMousePos(x, y, 0, 0, false);
|
|
#ifdef _DEBUG_SHOW_CURSOR
|
|
g_bShowingCursor = false;
|
|
#else
|
|
FrameShowCursor(FALSE);
|
|
#endif
|
|
|
|
//
|
|
|
|
if (GetPropertySheet().GetMouseRestrictToWindow())
|
|
SetUsingCursor(TRUE);
|
|
}
|
|
else
|
|
{
|
|
FrameSetCursorPosByMousePos(); // Set cursor to Apple position each time
|
|
}
|
|
|
|
DrawCrosshairsMouse();
|
|
}
|
|
}
|
|
|
|
void Win32Frame::GetViewportCXCY(int& nViewportCX, int& nViewportCY)
|
|
{
|
|
nViewportCX = g_nViewportCX;
|
|
nViewportCY = g_nViewportCY;
|
|
}
|
|
|
|
// Call all funcs with dependency on g_Apple2Type
|
|
void Win32Frame::FrameUpdateApple2Type(void)
|
|
{
|
|
DeleteGdiObjects();
|
|
CreateGdiObjects();
|
|
|
|
// DRAW_TITLE : calls GetAppleWindowTitle()
|
|
// DRAW_LEDS : update LEDs (eg. CapsLock varies on Apple2 type)
|
|
DrawStatusArea( (HDC)0, DRAW_TITLE|DRAW_LEDS );
|
|
|
|
// Draw buttons & call DrawStatusArea(DRAW_BACKGROUND | DRAW_LEDS | DRAW_DISK_STATUS)
|
|
DrawFrameWindow();
|
|
}
|
|
|
|
bool Win32Frame::GetBestDisplayResolutionForFullScreen(UINT& bestWidth, UINT& bestHeight, UINT userSpecifiedWidth/*=0*/, UINT userSpecifiedHeight/*=0*/)
|
|
{
|
|
m_bestWidthForFullScreen = 0;
|
|
m_bestHeightForFullScreen = 0;
|
|
|
|
typedef std::vector< std::pair<UINT,UINT> > VEC_PAIR;
|
|
VEC_PAIR vecDisplayResolutions;
|
|
|
|
for (UINT iModeNum = 0; ; iModeNum++)
|
|
{
|
|
DEVMODE devMode;
|
|
devMode.dmSize = sizeof(DEVMODE);
|
|
devMode.dmDriverExtra = 0;
|
|
BOOL bValid = EnumDisplaySettings(NULL, iModeNum, &devMode);
|
|
if (!bValid)
|
|
break;
|
|
if (iModeNum == 0) // 0 is the initial "cache info about display device" operation
|
|
continue;
|
|
|
|
if (devMode.dmBitsPerPel != 32)
|
|
continue;
|
|
|
|
if (userSpecifiedHeight == 0 || userSpecifiedHeight == devMode.dmPelsHeight)
|
|
{
|
|
if (vecDisplayResolutions.size() == 0 || vecDisplayResolutions.back() != std::pair<UINT,UINT>(devMode.dmPelsWidth, devMode.dmPelsHeight) ) // Skip duplicate resolutions
|
|
{
|
|
vecDisplayResolutions.push_back( std::pair<UINT,UINT>(devMode.dmPelsWidth, devMode.dmPelsHeight) );
|
|
LogFileOutput("EnumDisplaySettings(%d) - %d x %d (ratio=%f)\n", iModeNum, devMode.dmPelsWidth, devMode.dmPelsHeight, (float)devMode.dmPelsWidth/(float)devMode.dmPelsHeight);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (userSpecifiedHeight)
|
|
{
|
|
if (vecDisplayResolutions.size() == 0)
|
|
return false;
|
|
|
|
// Pick user-specific width if it exists
|
|
// Else pick least width (such that it's wide enough to scale)
|
|
UINT width = (UINT)-1;
|
|
for (VEC_PAIR::iterator it = vecDisplayResolutions.begin(); it!= vecDisplayResolutions.end(); ++it)
|
|
{
|
|
if (it->first == userSpecifiedWidth)
|
|
{
|
|
width = userSpecifiedWidth;
|
|
break;
|
|
}
|
|
|
|
if (width > it->first)
|
|
{
|
|
UINT scaleFactor = it->second / GetVideo().GetFrameBufferBorderlessHeight();
|
|
if (it->first >= (GetVideo().GetFrameBufferBorderlessWidth() * scaleFactor))
|
|
{
|
|
width = it->first;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (width == (UINT)-1)
|
|
return false;
|
|
|
|
bestWidth = width;
|
|
bestHeight = userSpecifiedHeight;
|
|
|
|
m_bestWidthForFullScreen = bestWidth;
|
|
m_bestHeightForFullScreen = bestHeight;
|
|
return true;
|
|
}
|
|
|
|
// Pick max height that's an exact multiple of GetFrameBufferBorderlessHeight()
|
|
UINT tmpBestWidth = 0;
|
|
UINT tmpBestHeight = 0;
|
|
for (VEC_PAIR::iterator it = vecDisplayResolutions.begin(); it!= vecDisplayResolutions.end(); ++it)
|
|
{
|
|
if ((it->second % GetVideo().GetFrameBufferBorderlessHeight()) == 0)
|
|
{
|
|
if (it->second > tmpBestHeight)
|
|
{
|
|
UINT scaleFactor = it->second / GetVideo().GetFrameBufferBorderlessHeight();
|
|
if (it->first >= (GetVideo().GetFrameBufferBorderlessWidth() * scaleFactor))
|
|
{
|
|
tmpBestWidth = it->first;
|
|
tmpBestHeight = it->second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tmpBestWidth == 0)
|
|
return false;
|
|
|
|
bestWidth = tmpBestWidth;
|
|
bestHeight = tmpBestHeight;
|
|
|
|
m_bestWidthForFullScreen = bestWidth;
|
|
m_bestHeightForFullScreen = bestHeight;
|
|
return true;
|
|
}
|