Added GUI, some menus dont work yet.

Fixed uniform scaling.
Removed splash screen.
This commit is contained in:
Muzychenko Andrey 2021-09-05 10:47:05 +03:00
parent 9a10d72e1f
commit e0638c598d
29 changed files with 50267 additions and 1242 deletions

View File

@ -53,8 +53,6 @@ set(SOURCE_FILES
SpaceCadetPinball/Sound.cpp SpaceCadetPinball/Sound.cpp
SpaceCadetPinball/Sound.h SpaceCadetPinball/Sound.h
SpaceCadetPinball/SpaceCadetPinball.cpp SpaceCadetPinball/SpaceCadetPinball.cpp
SpaceCadetPinball/splash.cpp
SpaceCadetPinball/splash.h
SpaceCadetPinball/TBall.cpp SpaceCadetPinball/TBall.cpp
SpaceCadetPinball/TBall.h SpaceCadetPinball/TBall.h
SpaceCadetPinball/TBlocker.cpp SpaceCadetPinball/TBlocker.cpp
@ -138,7 +136,23 @@ set(SOURCE_FILES
SpaceCadetPinball/winmain.cpp SpaceCadetPinball/winmain.cpp
SpaceCadetPinball/winmain.h SpaceCadetPinball/winmain.h
SpaceCadetPinball/zdrv.cpp SpaceCadetPinball/zdrv.cpp
SpaceCadetPinball/zdrv.h) SpaceCadetPinball/zdrv.h
SpaceCadetPinball/imconfig.h
SpaceCadetPinball/imgui_internal.h
SpaceCadetPinball/imgui.cpp
SpaceCadetPinball/imgui.h
SpaceCadetPinball/imgui_sdl.cpp
SpaceCadetPinball/imgui_sdl.h
SpaceCadetPinball/imgui_draw.cpp
SpaceCadetPinball/imgui_widgets.cpp
SpaceCadetPinball/imgui_tables.cpp
SpaceCadetPinball/imgui_demo.cpp
SpaceCadetPinball/imgui_impl_sdl.cpp
SpaceCadetPinball/imgui_impl_sdl.h
SpaceCadetPinball/imstb_textedit.h
SpaceCadetPinball/imstb_rectpack.h
SpaceCadetPinball/imstb_truetype.h
)
add_executable(SpaceCadetPinball ${SOURCE_FILES}) add_executable(SpaceCadetPinball ${SOURCE_FILES})

View File

@ -20,8 +20,31 @@
"cmakeCommandArgs": "", "cmakeCommandArgs": "",
"buildCommandArgs": "", "buildCommandArgs": "",
"ctestCommandArgs": "", "ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x86" ]
},
{
"name": "x86-Release",
"generator": "Ninja",
"configurationType": "Release",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x86" ], "inheritEnvironments": [ "msvc_x86" ],
"variables": [] "variables": []
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "Release",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ],
"variables": []
} }
] ]
} }

View File

@ -9,15 +9,8 @@
int fullscrn::screen_mode; int fullscrn::screen_mode;
HWND fullscrn::hWnd;
tagRECT fullscrn::WindowRect1, fullscrn::WindowRect2;
rectangle_type fullscrn::WHRect;
int fullscrn::fullscrn_flag1;
int fullscrn::display_changed; int fullscrn::display_changed;
int fullscrn::ChangeDisplay, fullscrn::ignoreNextDisplayChangeFg;
int fullscrn::trick = 1;
int fullscrn::MenuEnabled;
HMENU fullscrn::MenuHandle;
int fullscrn::resolution = 0; int fullscrn::resolution = 0;
int fullscrn::maxResolution = 0; int fullscrn::maxResolution = 0;
const resolution_info fullscrn::resolution_array[3] = const resolution_info fullscrn::resolution_array[3] =
@ -28,45 +21,12 @@ const resolution_info fullscrn::resolution_array[3] =
}; };
float fullscrn::ScaleX = 1; float fullscrn::ScaleX = 1;
float fullscrn::ScaleY = 1; float fullscrn::ScaleY = 1;
float fullscrn::OffsetX = 0; int fullscrn::OffsetX = 0;
float fullscrn::OffsetY = 0; int fullscrn::OffsetY = 0;
void fullscrn::init(int width, int height, int isFullscreen, HWND winHandle, HMENU menuHandle, int changeDisplay) void fullscrn::init()
{ {
WHRect.XPosition = 0;
WHRect.YPosition = 0;
ChangeDisplay = changeDisplay;
hWnd = winHandle;
MenuHandle = menuHandle;
WHRect.Width = width;
WHRect.Height = height;
GetWindowRect(GetDesktopWindow(), &fullscrn::WindowRect1);
int widht2 = width + 2 * GetSystemMetrics(SM_CXBORDER);
int height2 = height + 2 * GetSystemMetrics(SM_CYBORDER);
int menuHeight = GetSystemMetrics(SM_CYMENU);
int captionHeight = GetSystemMetrics(SM_CYCAPTION);
int borderHeight = WindowRect1.bottom - WindowRect1.top - height2;
WindowRect2.bottom = borderHeight / 2 - 2 + height2 + 4;
WindowRect2.right = (WindowRect1.right - WindowRect1.left - widht2) / 2 - 2 + widht2 + 4;
WindowRect2.left = (WindowRect1.right - WindowRect1.left - widht2) / 2 - 2;
WindowRect2.top = borderHeight / 2 - (captionHeight + menuHeight) - 2;
/*RECT client{0,0,width,height};
AdjustWindowRect(&client, winmain::WndStyle, true);*/
MoveWindow(
hWnd,
(WindowRect1.right - WindowRect1.left - widht2) / 2 - 2,
WindowRect2.top,
WindowRect2.right - WindowRect2.left + 10,
WindowRect2.bottom - WindowRect2.top + 10,
0);
// Todo: WH + 10 hack: original request 640x480 window but somehow receives 650x490, even thought spyxx says it is 640x480
fullscrn_flag1 = 0;
window_size_changed(); window_size_changed();
//assertm(ScaleX == 1 && ScaleY == 1, "Wrong default client size");
} }
void fullscrn::shutdown() void fullscrn::shutdown()
@ -83,72 +43,26 @@ int fullscrn::set_screen_mode(int isFullscreen)
screen_mode = isFullscreen; screen_mode = isFullscreen;
if (isFullscreen) if (isFullscreen)
{ {
if (IsWindowVisible(hWnd))
GetWindowRect(hWnd, &WindowRect2);
enableFullscreen(); enableFullscreen();
BYTE1(fullscrn_flag1) |= 0x80u; result = 1;
InvalidateRect(hWnd, nullptr, 1);
set_menu_mode(0);
result = disableWindowFlagsDisDlg();
} }
else else
{ {
disableFullscreen(); disableFullscreen();
BYTE1(fullscrn_flag1) |= 0x80u; result = 1;
InvalidateRect(hWnd, nullptr, 1);
set_menu_mode(1);
result = RedrawWindow(nullptr, nullptr, nullptr, 0x185u);
} }
return result; return result;
} }
int fullscrn::disableWindowFlagsDisDlg()
{
long style = GetWindowLongA(hWnd, -16);
return SetWindowLongA(hWnd, -16, style & ~(WS_CAPTION | WS_THICKFRAME));
}
int fullscrn::setWindowFlagsDisDlg()
{
int style = GetWindowLongA(hWnd, -16);
return SetWindowLongA(hWnd, -16, style | WS_CAPTION | WS_THICKFRAME);
}
int fullscrn::enableFullscreen() int fullscrn::enableFullscreen()
{ {
tagRECT Rect{}; if (!display_changed)
DEVMODEA DevMode{};
if (ChangeDisplay && !display_changed)
{ {
DevMode.dmSize = sizeof DevMode; SDL_SetWindowFullscreen(winmain::MainWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
DevMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; display_changed = 1;
DevMode.dmPelsWidth = resolution_array[resolution].ScreenWidth;
DevMode.dmPelsHeight = resolution_array[resolution].ScreenHeight;
DevMode.dmBitsPerPel = 32;
disableWindowFlagsDisDlg();
if (trick)
{
GetWindowRect(GetDesktopWindow(), &Rect);
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, Rect.right - Rect.left + 1,
Rect.bottom - Rect.top + 1, SWP_NOREDRAW);
}
ignoreNextDisplayChangeFg = 1;
LONG changeDispResult = ChangeDisplaySettingsA(&DevMode, CDS_FULLSCREEN);
if (changeDispResult == DISP_CHANGE_RESTART)
{
DevMode.dmFields &= ~DM_BITSPERPEL;
ignoreNextDisplayChangeFg = 1;
changeDispResult = ChangeDisplaySettingsA(&DevMode, CDS_FULLSCREEN);
}
display_changed = changeDispResult == DISP_CHANGE_SUCCESSFUL;
if (display_changed) if (display_changed)
return 1; return 1;
} }
GetWindowRect(GetDesktopWindow(), &Rect);
disableWindowFlagsDisDlg();
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, Rect.right - Rect.left + 1, Rect.bottom - Rect.top + 1, SWP_NOREDRAW);
return 0; return 0;
} }
@ -156,154 +70,13 @@ int fullscrn::disableFullscreen()
{ {
if (display_changed) if (display_changed)
{ {
SDL_SetWindowFullscreen(winmain::MainWindow, 0);
display_changed = 0; display_changed = 0;
ignoreNextDisplayChangeFg = 1;
ChangeDisplaySettingsA(nullptr, CDS_FULLSCREEN);
if (trick)
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
} }
setWindowFlagsDisDlg();
SetWindowPos(
hWnd,
HWND_TOP,
WindowRect2.left,
WindowRect2.top,
WindowRect2.right - WindowRect2.left,
WindowRect2.bottom - WindowRect2.top,
SWP_NOZORDER | SWP_NOACTIVATE);
return 0; return 0;
} }
bool fullscrn::set_menu_mode(int menuEnabled)
{
BOOL result;
MenuEnabled = menuEnabled;
GetWindowCenter();
if (MenuEnabled)
{
fullscrn_flag1 |= 2u;
InvalidateRect(hWnd, nullptr, 1);
result = SetMenu(hWnd, MenuHandle);
}
else
{
fullscrn_flag1 |= 1u;
InvalidateRect(hWnd, nullptr, 1);
result = SetMenu(hWnd, nullptr);
}
return result;
}
void fullscrn::GetWindowCenter()
{
int yPos;
tagRECT Rect{};
if (screen_mode)
{
GetWindowRect(GetDesktopWindow(), &Rect);
render::vscreen.XPosition = (Rect.right - render::vscreen.Width - Rect.left) / 2;
yPos = (Rect.bottom - render::vscreen.Height - Rect.top) / 2;
}
else
{
render::vscreen.XPosition = 0;
yPos = GetSystemMetrics(15);
}
render::vscreen.YPosition = yPos;
if (MenuEnabled)
render::vscreen.YPosition -= GetSystemMetrics(15);
}
void fullscrn::force_redraw()
{
BYTE1(fullscrn_flag1) |= 0x80u;
}
void fullscrn::center_in(HWND parent, HWND child)
{
LONG right;
tagRECT childRect{}, parentRect{}, desktopRect{};
GetWindowRect(parent, &parentRect);
GetWindowRect(child, &childRect);
GetWindowRect(GetDesktopWindow(), &desktopRect);
if (display_changed)
{
desktopRect.bottom = 480;
desktopRect.left = 0;
desktopRect.top = 0;
right = 640;
desktopRect.right = 640;
parentRect.left = 0;
parentRect.top = 0;
parentRect.right = 640;
parentRect.bottom = 480;
}
else
{
right = desktopRect.right;
}
int childHeight = childRect.bottom - childRect.top;
int smthWidth = parentRect.left + (parentRect.right + childRect.left - childRect.right - parentRect.left) / 2;
int smthHeight = parentRect.top + (parentRect.bottom + childRect.top - childRect.bottom - parentRect.top) / 2;
if (childRect.right - childRect.left + smthWidth > right)
smthWidth = right - (childRect.right - childRect.left);
if (childHeight + smthHeight > desktopRect.bottom)
smthHeight = desktopRect.bottom - childHeight;
if (smthWidth < desktopRect.left)
smthWidth = desktopRect.left;
if (smthHeight < desktopRect.top)
smthHeight = desktopRect.top;
MoveWindow(child, smthWidth, smthHeight, childRect.right - childRect.left, childRect.bottom - childRect.top, 0);
}
int fullscrn::displaychange()
{
int result = 0;
if (ignoreNextDisplayChangeFg)
{
ignoreNextDisplayChangeFg = 0;
}
else
{
if (screen_mode && display_changed)
{
display_changed = 0;
screen_mode = 0;
setWindowFlagsDisDlg();
BYTE1(fullscrn_flag1) |= 0x80u;
InvalidateRect(hWnd, nullptr, 1);
set_menu_mode(1);
SetWindowPos(
hWnd,
HWND_TOP,
WindowRect2.left,
WindowRect2.top,
WindowRect2.right - WindowRect2.left,
WindowRect2.bottom - WindowRect2.top,
SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
result = 1;
}
else
{
SetWindowPos(
hWnd,
HWND_TOP,
WindowRect2.left,
WindowRect2.top,
WindowRect2.right - WindowRect2.left,
WindowRect2.bottom - WindowRect2.top,
SWP_NOZORDER | SWP_NOACTIVATE);
}
center_in(GetDesktopWindow(), hWnd);
}
return result;
}
void fullscrn::activate(int flag) void fullscrn::activate(int flag)
{ {
if (screen_mode) if (screen_mode)
@ -311,76 +84,10 @@ void fullscrn::activate(int flag)
if (!flag) if (!flag)
{ {
set_screen_mode(0); set_screen_mode(0);
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
} }
} }
} }
void fullscrn::fillRect(int right, int bottom, int left, int top)
{
RECT rc;
auto brush = CreateSolidBrush(0);
if (brush)
{
auto dc = winmain::_GetDC(hWnd);
if (dc)
{
auto prevBrush = SelectObject(dc, brush);
rc.right = left + right + 1;
rc.bottom = top + bottom + 1;
rc.left = left;
rc.top = top;
FillRect(dc, &rc, brush);
SelectObject(dc, prevBrush);
ReleaseDC(hWnd, dc);
}
DeleteObject(brush);
}
}
unsigned fullscrn::convert_mouse_pos(unsigned int mouseXY)
{
uint16_t x = mouseXY & 0xffFF - render::vscreen.XPosition;
uint16_t y = (mouseXY >> 16) - render::vscreen.YPosition;
return x | y << 16;
}
void fullscrn::getminmaxinfo(MINMAXINFO* maxMin)
{
/*Block down-scaling lower than min resolution*/
maxMin->ptMinTrackSize = POINT
{
resolution_array[0].ScreenWidth / 2,
resolution_array[0].ScreenHeight / 2
};
}
void fullscrn::paint()
{
int menuHeight;
if (screen_mode)
{
if ((fullscrn_flag1 & 0x8000) == 0 && fullscrn_flag1)
{
if (fullscrn_flag1 & 1)
{
menuHeight = GetSystemMetrics(SM_CYMENU);
fillRect(WindowRect1.right - 1, menuHeight, 0, 0);
}
}
else
{
if (MenuEnabled)
menuHeight = GetSystemMetrics(SM_CYMENU);
else
menuHeight = 0;
fillRect(WindowRect1.right, menuHeight + WindowRect1.bottom, 0, 0);
}
}
render::paint();
fullscrn_flag1 = 0;
}
int fullscrn::GetResolution() int fullscrn::GetResolution()
{ {
return resolution; return resolution;
@ -432,16 +139,8 @@ int fullscrn::get_screen_resolution()
void fullscrn::window_size_changed() void fullscrn::window_size_changed()
{ {
/*No scaling in fullscreen mode*/
if (display_changed)
{
ScaleY = ScaleX = 1;
OffsetX = OffsetY = 0;
return;
}
int width, height; int width, height;
SDL_GetWindowSize(winmain::MainWindow, &width, &height); SDL_GetRendererOutputSize(winmain::Renderer, &width, &height);
auto res = &resolution_array[resolution]; auto res = &resolution_array[resolution];
ScaleX = static_cast<float>(width) / res->TableWidth; ScaleX = static_cast<float>(width) / res->TableWidth;
ScaleY = static_cast<float>(height) / res->TableHeight; ScaleY = static_cast<float>(height) / res->TableHeight;
@ -450,13 +149,13 @@ void fullscrn::window_size_changed()
if (options::Options.UniformScaling) if (options::Options.UniformScaling)
{ {
ScaleY = ScaleX = min(ScaleX, ScaleY); ScaleY = ScaleX = min(ScaleX, ScaleY);
OffsetX = floor((width - res->TableWidth * ScaleX) / 2); OffsetX = static_cast<int>(floor((width - res->TableWidth * ScaleX) / 2));
OffsetY = floor((height - res->TableHeight * ScaleY) / 2); OffsetY = static_cast<int>(floor((height - res->TableHeight * ScaleY) / 2));
auto dc = GetDC(hWnd);
if (dc)
{
BitBlt(dc, 0, 0, width, height, dc, 0, 0, BLACKNESS);
ReleaseDC(hWnd, dc);
}
} }
gdrv::DestinationRect = SDL_Rect
{
OffsetX, OffsetY,
width - OffsetX * 2, height - OffsetY * 2
};
} }

View File

@ -1,10 +1,4 @@
#pragma once #pragma once
#include "maths.h"
#define BYTEn(x, n) (*((unsigned char*)&(x)+n))
#define BYTE1(x) BYTEn(x, 1) // byte 1 (counting from 0)
#define BYTE2(x) BYTEn(x, 2)
struct resolution_info struct resolution_info
{ {
@ -18,32 +12,18 @@ struct resolution_info
class fullscrn class fullscrn
{ {
public: public:
static int screen_mode; static int screen_mode;
static HWND hWnd;
static tagRECT WindowRect1, WindowRect2;
static rectangle_type WHRect;
static int fullscrn_flag1;
static int display_changed; static int display_changed;
static int ChangeDisplay, ignoreNextDisplayChangeFg;
static int trick;
static const resolution_info resolution_array[3]; static const resolution_info resolution_array[3];
static float ScaleX; static float ScaleX;
static float ScaleY; static float ScaleY;
static float OffsetX; static int OffsetX;
static float OffsetY; static int OffsetY;
static void init(int width, int height, int isFullscreen, HWND winHandle, HMENU menuHandle, int changeDisplay); static void init();
static void shutdown(); static void shutdown();
static int set_screen_mode(int isFullscreen); static int set_screen_mode(int isFullscreen);
static void force_redraw();
static void center_in(HWND parent, HWND child);
static int displaychange();
static void activate(int flag); static void activate(int flag);
static unsigned convert_mouse_pos(unsigned int mouseXY);
static void getminmaxinfo(MINMAXINFO* maxMin);
static void paint();
static bool set_menu_mode(int menuEnabled);
static int GetResolution(); static int GetResolution();
static void SetResolution(int resolution); static void SetResolution(int resolution);
static int GetMaxResolution(); static int GetMaxResolution();
@ -52,15 +32,9 @@ public:
static int get_screen_resolution(); static int get_screen_resolution();
static void window_size_changed(); static void window_size_changed();
private : private :
static int MenuEnabled;
static HMENU MenuHandle;
static int resolution; static int resolution;
static int maxResolution; static int maxResolution;
static void GetWindowCenter();
static int disableWindowFlagsDisDlg();
static int setWindowFlagsDisDlg();
static int enableFullscreen(); static int enableFullscreen();
static int disableFullscreen(); static int disableFullscreen();
static void fillRect(int right, int bottom, int left, int top);
}; };

View File

@ -1,6 +1,7 @@
#include "pch.h" #include "pch.h"
#include "gdrv.h" #include "gdrv.h"
#include "fullscrn.h"
#include "memory.h" #include "memory.h"
#include "render.h" #include "render.h"
#include "winmain.h" #include "winmain.h"
@ -9,6 +10,7 @@ SDL_Texture* gdrv::vScreenTex = nullptr;
char* gdrv::vScreenPixels = nullptr; char* gdrv::vScreenPixels = nullptr;
int gdrv::vScreenWidth, gdrv::vScreenHeight; int gdrv::vScreenWidth, gdrv::vScreenHeight;
ColorRgba gdrv::current_palette[256]{}; ColorRgba gdrv::current_palette[256]{};
SDL_Rect gdrv::DestinationRect{};
int gdrv::init(int width, int height) int gdrv::init(int width, int height)
{ {
@ -297,6 +299,6 @@ void gdrv::BlitScreen()
&pitch &pitch
); );
std::memcpy(lockedPixels, vScreenPixels, vScreenWidth * vScreenHeight * 4); std::memcpy(lockedPixels, vScreenPixels, vScreenWidth * vScreenHeight * 4);
SDL_UnlockTexture(vScreenTex); SDL_UnlockTexture(vScreenTex);
SDL_RenderCopyEx(winmain::Renderer, vScreenTex, nullptr, nullptr, 0, nullptr, SDL_FLIP_VERTICAL); SDL_RenderCopyEx(winmain::Renderer, vScreenTex, nullptr, &DestinationRect, 0, nullptr, SDL_FLIP_VERTICAL);
} }

View File

@ -15,7 +15,6 @@ struct gdrv_bitmap8
int Height; int Height;
int Stride; int Stride;
BitmapType BitmapType; BitmapType BitmapType;
int Color6;
int XPosition; int XPosition;
int YPosition; int YPosition;
}; };
@ -49,6 +48,8 @@ struct LOGPALETTEx256 : LOGPALETTE
class gdrv class gdrv
{ {
public: public:
static SDL_Rect DestinationRect;
static int init(int width, int height); static int init(int width, int height);
static int uninit(); static int uninit();
static void get_focus(); static void get_focus();

View File

@ -10,8 +10,9 @@
int high_score::dlg_enter_name; int high_score::dlg_enter_name;
int high_score::dlg_score; int high_score::dlg_score;
int high_score::dlg_position; int high_score::dlg_position;
LPCSTR high_score::default_name; char high_score::default_name[32]{};
high_score_struct* high_score::dlg_hst; high_score_struct* high_score::dlg_hst;
bool high_score::ShowDialog = false;
winhelp_entry high_score::help[21] winhelp_entry high_score::help[21]
{ {
@ -167,7 +168,7 @@ void high_score::show_high_score_dialog(high_score_struct* table)
dlg_enter_name = 0; dlg_enter_name = 0;
dlg_score = 0; dlg_score = 0;
dlg_hst = table; dlg_hst = table;
DialogBoxParamA(nullptr, "dlg_highscores", nullptr, HighScore, 0); ShowDialog = true;
} }
void high_score::show_and_set_high_score_dialog(high_score_struct* table, int score, int pos, LPCSTR defaultName) void high_score::show_and_set_high_score_dialog(high_score_struct* table, int score, int pos, LPCSTR defaultName)
@ -176,126 +177,99 @@ void high_score::show_and_set_high_score_dialog(high_score_struct* table, int sc
dlg_score = score; dlg_score = score;
dlg_hst = table; dlg_hst = table;
dlg_enter_name = 1; dlg_enter_name = 1;
default_name = defaultName; strncpy_s(default_name, defaultName, 32);
/*while (DialogBoxParamA(winmain::hinst, "dlg_highscores", winmain::hwnd_frame, HighScore, 0)) ShowDialog = true;
{
}*/
} }
INT_PTR high_score::HighScore(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) void high_score::RenderHighScoreDialog()
{ {
HWND parent; if (ShowDialog == true)
int nIDDlgItem;
CHAR String1[256];
CHAR name[32];
switch (msg)
{ {
case WM_CLOSE: ShowDialog = false;
SendMessageA(hWnd, WM_COMMAND, WM_DESTROY, 0); if (dlg_position == -1)
break;
case WM_HELP:
WinHelpA(static_cast<HWND>(reinterpret_cast<HELPINFO*>(lParam)->hItemHandle), "pinball.hlp", HELP_WM_HELP,
(ULONG_PTR)help);
break;
case WM_CONTEXTMENU:
WinHelpA((HWND)wParam, "pinball.hlp", HELP_CONTEXTMENU, (ULONG_PTR)help);
break;
case WM_INITDIALOG:
show_high_scores(hWnd, dlg_hst);
for (nIDDlgItem = DLG_HIGHSCORES_EditName1; nIDDlgItem < 611; ++nIDDlgItem)
{ {
ShowWindow(GetDlgItem(hWnd, nIDDlgItem), 0); dlg_enter_name = 0;
return;
} }
if (dlg_enter_name == 1) ImGui::OpenPopup("High Scores");
}
bool unused_open = true;
if (ImGui::BeginPopupModal("High Scores", &unused_open, ImGuiWindowFlags_AlwaysAutoResize))
{
if (ImGui::BeginTable("table1", 3, 0))
{ {
if (dlg_position == -1) char buf[36];
ImGui::TableSetupColumn("Rank");
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Score");
ImGui::TableHeadersRow();
high_score_struct* tablePtr = dlg_hst;
for (int row = 0; row < 5; row++)
{ {
dlg_enter_name = 0; ImGui::TableNextRow();
return 1; ImGui::TableNextColumn();
_itoa_s(row, buf, 10);
ImGui::TextUnformatted(buf);
auto score = tablePtr->Score;
ImGui::TableNextColumn();
if (dlg_enter_name == 1 && dlg_position == row)
{
score = dlg_score;
ImGui::PushItemWidth(200);
ImGui::InputText("", default_name, IM_ARRAYSIZE(default_name));
}
else
{
ImGui::TextUnformatted(tablePtr->Name);
}
ImGui::TableNextColumn();
score::string_format(score, buf);
ImGui::TextUnformatted(buf);
tablePtr++;
} }
HWND nameTextBox = GetDlgItem(hWnd, dlg_position + DLG_HIGHSCORES_EditName1); ImGui::EndTable();
ShowWindow(nameTextBox, 5);
EnableWindow(nameTextBox, 1);
SetFocus(nameTextBox);
if (default_name)
{
SetWindowTextA(nameTextBox, default_name);
SendMessageA(nameTextBox, EM_SETSEL, 0, -1);
}
SendMessageA(nameTextBox, EM_SETLIMITTEXT, 31u, 0);
} }
else ImGui::Separator();
if (ImGui::Button("Ok"))
{ {
SetFocus(hWnd); if (dlg_enter_name)
} {
parent = GetParent(hWnd); default_name[31] = 0;
if (parent) place_new_score_into(dlg_hst, dlg_score, default_name, dlg_position);
fullscrn::center_in(parent, hWnd);
return 0;
case WM_COMMAND:
switch (wParam)
{
case DLG_HIGHSCORES_Ok:
if (dlg_enter_name != 1)
{
break;
} }
GetDlgItemTextA(hWnd, dlg_position + DLG_HIGHSCORES_EditName1, name, 32); ImGui::CloseCurrentPopup();
name[31] = 0; }
place_new_score_into(dlg_hst, dlg_score, name, dlg_position);
break; ImGui::SameLine();
case DLG_HIGHSCORES_Cancel: if (ImGui::Button("Cancel"))
break; ImGui::CloseCurrentPopup();
case DLG_HIGHSCORES_Clear:
lstrcpyA(String1, pinball::get_rc_string(41, 0)); ImGui::SameLine();
if (MessageBoxA(hWnd, pinball::get_rc_string(40, 0), String1, MB_DEFBUTTON2 | MB_OKCANCEL) == 1) if (ImGui::Button("Clear"))
ImGui::OpenPopup("Confirm");
if (ImGui::BeginPopupModal("Confirm", nullptr, ImGuiWindowFlags_MenuBar))
{
ImGui::TextUnformatted(pinball::get_rc_string(40, 0));
if (ImGui::Button("OK", ImVec2(120, 0)))
{ {
clear_table(dlg_hst); clear_table(dlg_hst);
if (dlg_enter_name) ImGui::CloseCurrentPopup();
EndDialog(hWnd, 1);
else
EndDialog(hWnd, 0);
} }
return 0; ImGui::SetItemDefaultFocus();
default: ImGui::SameLine();
return 0; if (ImGui::Button("Cancel", ImVec2(120, 0)))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
} }
dlg_enter_name = 0; ImGui::EndPopup();
EndDialog(hWnd, 0);
return 1;
}
return 0;
}
void high_score::show_high_scores(HWND hDlg, high_score_struct* table)
{
high_score_struct* tablePtr = table;
int nextPosition = 0;
for (int i = 0; i < 5; ++i)
{
if (dlg_enter_name == 1 && dlg_position == i)
{
hsdlg_show_score(hDlg, " ", dlg_score, i);
nextPosition = 1;
}
hsdlg_show_score(hDlg, tablePtr->Name, tablePtr->Score, i + nextPosition);
++tablePtr;
}
}
void high_score::hsdlg_show_score(HWND hDlg, LPCSTR name, int score, int position)
{
CHAR scoreStr[36];
if (position < 5)
{
score::string_format(score, scoreStr);
if (scoreStr[0])
{
SetWindowTextA(GetDlgItem(hDlg, position + DLG_HIGHSCORES_StaticName1), name);
SetWindowTextA(GetDlgItem(hDlg, position + DLG_HIGHSCORES_Score1), scoreStr);
}
} }
} }

View File

@ -20,14 +20,13 @@ public:
static void show_high_score_dialog(high_score_struct* table); static void show_high_score_dialog(high_score_struct* table);
static void show_and_set_high_score_dialog(high_score_struct* table, int score, int pos, LPCSTR defaultName); static void show_and_set_high_score_dialog(high_score_struct* table, int score, int pos, LPCSTR defaultName);
static INT_PTR __stdcall HighScore(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); static void RenderHighScoreDialog();
static void show_high_scores(HWND hDlg, high_score_struct* table);
static void hsdlg_show_score(HWND hDlg, LPCSTR name, int score, int position);
private : private :
static int dlg_enter_name; static int dlg_enter_name;
static int dlg_score; static int dlg_score;
static int dlg_position; static int dlg_position;
static LPCSTR default_name; static char default_name[32];
static high_score_struct* dlg_hst; static high_score_struct* dlg_hst;
static winhelp_entry help[21]; static winhelp_entry help[21];
static bool ShowDialog;
}; };

View File

@ -0,0 +1,123 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
//-----------------------------------------------------------------------------
// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Disable all of Dear ImGui or don't implement standard windows.
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
// Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf.
// #define IMGUI_USE_STB_SPRINTF
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
//#define IMGUI_ENABLE_FREETYPE
//---- Use stb_truetype to build and rasterize the font atlas (default)
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
//#define IMGUI_ENABLE_STB_TRUETYPE
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/

11866
SpaceCadetPinball/imgui.cpp Normal file

File diff suppressed because it is too large Load Diff

2904
SpaceCadetPinball/imgui.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,443 @@
// dear imgui: Platform Backend for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// (Prefer SDL 2.0.5+ for full feature support.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST.
// 2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+)
// 2021-06-29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950)
// 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.
// 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).
// 2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
// 2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
// 2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.
// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
#include "imgui.h"
#include "imgui_impl_sdl.h"
// SDL
#include <SDL.h>
#include <SDL_syswm.h>
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
#if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS)
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1
#else
#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
#endif
#define SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH SDL_VERSION_ATLEAST(2,0,5)
#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
// SDL Data
struct ImGui_ImplSDL2_Data
{
SDL_Window* Window;
Uint64 Time;
bool MousePressed[3];
SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
char* ClipboardTextData;
bool MouseCanUseGlobalState;
ImGui_ImplSDL2_Data() { memset(this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplSDL2_Data*)ImGui::GetIO().BackendPlatformUserData : NULL;
}
// Functions
static const char* ImGui_ImplSDL2_GetClipboardText(void*)
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
if (bd->ClipboardTextData)
SDL_free(bd->ClipboardTextData);
bd->ClipboardTextData = SDL_GetClipboardText();
return bd->ClipboardTextData;
}
static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
{
SDL_SetClipboardText(text);
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
switch (event->type)
{
case SDL_MOUSEWHEEL:
{
if (event->wheel.x > 0) io.MouseWheelH += 1;
if (event->wheel.x < 0) io.MouseWheelH -= 1;
if (event->wheel.y > 0) io.MouseWheel += 1;
if (event->wheel.y < 0) io.MouseWheel -= 1;
return true;
}
case SDL_MOUSEBUTTONDOWN:
{
if (event->button.button == SDL_BUTTON_LEFT) { bd->MousePressed[0] = true; }
if (event->button.button == SDL_BUTTON_RIGHT) { bd->MousePressed[1] = true; }
if (event->button.button == SDL_BUTTON_MIDDLE) { bd->MousePressed[2] = true; }
return true;
}
case SDL_TEXTINPUT:
{
io.AddInputCharactersUTF8(event->text.text);
return true;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
{
int key = event->key.keysym.scancode;
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
#ifdef _WIN32
io.KeySuper = false;
#else
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
#endif
return true;
}
case SDL_WINDOWEVENT:
{
if (event->window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
io.AddFocusEvent(true);
else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST)
io.AddFocusEvent(false);
return true;
}
}
return false;
}
static bool ImGui_ImplSDL2_Init(SDL_Window* window)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!");
// Check and store if we are on a SDL backend that supports global mouse position
// ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
bool mouse_can_use_global_state = false;
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
const char* sdl_backend = SDL_GetCurrentVideoDriver();
const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)
if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
mouse_can_use_global_state = true;
#endif
// Setup backend capabilities flags
ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = "imgui_impl_sdl";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
bd->Window = window;
bd->MouseCanUseGlobalState = mouse_can_use_global_state;
// Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array.
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = SDL_SCANCODE_KP_ENTER;
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
io.ClipboardUserData = NULL;
// Load mouse cursors
bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
#ifdef _WIN32
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
if (SDL_GetWindowWMInfo(window, &info))
io.ImeWindowHandle = info.info.win.window;
#else
(void)window;
#endif
// Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
// Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
// (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
// It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
// you can ignore SDL_MOUSEBUTTONDOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
#if SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
#endif
return true;
}
bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
{
IM_UNUSED(sdl_gl_context); // Viewport branch will need this.
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
{
#if !SDL_HAS_VULKAN
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
{
#if !defined(_WIN32)
IM_ASSERT(0 && "Unsupported");
#endif
return ImGui_ImplSDL2_Init(window);
}
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
{
return ImGui_ImplSDL2_Init(window);
}
void ImGui_ImplSDL2_Shutdown()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
if (bd->ClipboardTextData)
SDL_free(bd->ClipboardTextData);
for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
SDL_FreeCursor(bd->MouseCursors[cursor_n]);
io.BackendPlatformName = NULL;
io.BackendPlatformUserData = NULL;
IM_DELETE(bd);
}
static void ImGui_ImplSDL2_UpdateMousePosAndButtons()
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
ImVec2 mouse_pos_prev = io.MousePos;
io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
// Update mouse buttons
int mouse_x_local, mouse_y_local;
Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x_local, &mouse_y_local);
io.MouseDown[0] = bd->MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
io.MouseDown[1] = bd->MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
io.MouseDown[2] = bd->MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
bd->MousePressed[0] = bd->MousePressed[1] = bd->MousePressed[2] = false;
// Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing)
#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
SDL_Window* focused_window = SDL_GetKeyboardFocus();
SDL_Window* hovered_window = SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH ? SDL_GetMouseFocus() : NULL; // This is better but is only reliably useful with SDL 2.0.5+ and SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH.
SDL_Window* mouse_window = NULL;
if (hovered_window && bd->Window == hovered_window)
mouse_window = hovered_window;
else if (focused_window && bd->Window == focused_window)
mouse_window = focused_window;
// SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
SDL_CaptureMouse(ImGui::IsAnyMouseDown() ? SDL_TRUE : SDL_FALSE);
#else
// SDL 2.0.3 and non-windowed systems: single-viewport only
SDL_Window* mouse_window = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) ? bd->Window : NULL;
#endif
if (mouse_window == NULL)
return;
// Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if (io.WantSetMousePos)
SDL_WarpMouseInWindow(bd->Window, (int)mouse_pos_prev.x, (int)mouse_pos_prev.y);
// Set Dear ImGui mouse position from OS position + get buttons. (this is the common behavior)
if (bd->MouseCanUseGlobalState)
{
// Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
// Unlike local position obtained earlier this will be valid when straying out of bounds.
int mouse_x_global, mouse_y_global;
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
int window_x, window_y;
SDL_GetWindowPosition(mouse_window, &window_x, &window_y);
io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y));
}
else
{
io.MousePos = ImVec2((float)mouse_x_local, (float)mouse_y_local);
}
}
static void ImGui_ImplSDL2_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return;
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
SDL_ShowCursor(SDL_FALSE);
}
else
{
// Show OS mouse cursor
SDL_SetCursor(bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow]);
SDL_ShowCursor(SDL_TRUE);
}
}
static void ImGui_ImplSDL2_UpdateGamepads()
{
ImGuiIO& io = ImGui::GetIO();
memset(io.NavInputs, 0, sizeof(io.NavInputs));
if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0)
return;
// Get gamepad
SDL_GameController* game_controller = SDL_GameControllerOpen(0);
if (!game_controller)
{
io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
return;
}
// Update gamepad inputs
#define MAP_BUTTON(NAV_NO, BUTTON_NO) { io.NavInputs[NAV_NO] = (SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0) ? 1.0f : 0.0f; }
#define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); if (vn > 1.0f) vn = 1.0f; if (vn > 0.0f && io.NavInputs[NAV_NO] < vn) io.NavInputs[NAV_NO] = vn; }
const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
MAP_BUTTON(ImGuiNavInput_Activate, SDL_CONTROLLER_BUTTON_A); // Cross / A
MAP_BUTTON(ImGuiNavInput_Cancel, SDL_CONTROLLER_BUTTON_B); // Circle / B
MAP_BUTTON(ImGuiNavInput_Menu, SDL_CONTROLLER_BUTTON_X); // Square / X
MAP_BUTTON(ImGuiNavInput_Input, SDL_CONTROLLER_BUTTON_Y); // Triangle / Y
MAP_BUTTON(ImGuiNavInput_DpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT); // D-Pad Left
MAP_BUTTON(ImGuiNavInput_DpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); // D-Pad Right
MAP_BUTTON(ImGuiNavInput_DpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP); // D-Pad Up
MAP_BUTTON(ImGuiNavInput_DpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN); // D-Pad Down
MAP_BUTTON(ImGuiNavInput_FocusPrev, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_FocusNext, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
MAP_BUTTON(ImGuiNavInput_TweakSlow, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); // L1 / LB
MAP_BUTTON(ImGuiNavInput_TweakFast, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); // R1 / RB
MAP_ANALOG(ImGuiNavInput_LStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
MAP_ANALOG(ImGuiNavInput_LStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
MAP_ANALOG(ImGuiNavInput_LStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32767);
MAP_ANALOG(ImGuiNavInput_LStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
#undef MAP_BUTTON
#undef MAP_ANALOG
}
void ImGui_ImplSDL2_NewFrame()
{
ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDL2_Init()?");
ImGuiIO& io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing)
int w, h;
int display_w, display_h;
SDL_GetWindowSize(bd->Window, &w, &h);
if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED)
w = h = 0;
SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h);
io.DisplaySize = ImVec2((float)w, (float)h);
if (w > 0 && h > 0)
io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
static Uint64 frequency = SDL_GetPerformanceFrequency();
Uint64 current_time = SDL_GetPerformanceCounter();
io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
bd->Time = current_time;
ImGui_ImplSDL2_UpdateMousePosAndButtons();
ImGui_ImplSDL2_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplSDL2_UpdateGamepads();
}

View File

@ -0,0 +1,34 @@
// dear imgui: Platform Backend for SDL2
// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
// (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard arrays indexed using SDL_SCANCODE_* codes, e.g. ImGui::IsKeyPressed(SDL_SCANCODE_SPACE).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// Missing features:
// [ ] Platform: SDL2 handling of IME under Windows appears to be broken and it explicitly disable the regular Windows IME. You can restore Windows IME by compiling SDL with SDL_DISABLE_WINDOWS_IME.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct SDL_Window;
typedef union SDL_Event SDL_Event;
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window);
IMGUI_IMPL_API bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window);
IMGUI_IMPL_API void ImGui_ImplSDL2_Shutdown();
IMGUI_IMPL_API void ImGui_ImplSDL2_NewFrame();
IMGUI_IMPL_API bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
static inline void ImGui_ImplSDL2_NewFrame(SDL_Window*) { ImGui_ImplSDL2_NewFrame(); } // 1.84: removed unnecessary parameter
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,676 @@
#include "imgui_sdl.h"
#include "SDL.h"
#include "imgui.h"
#include <map>
#include <list>
#include <cmath>
#include <array>
#include <vector>
#include <memory>
#include <iostream>
#include <algorithm>
#include <functional>
#include <unordered_map>
namespace
{
struct Device* CurrentDevice = nullptr;
namespace TupleHash
{
template <typename T> struct Hash
{
std::size_t operator()(const T& value) const
{
return std::hash<T>()(value);
}
};
template <typename T> void CombineHash(std::size_t& seed, const T& value)
{
seed ^= TupleHash::Hash<T>()(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
template <typename Tuple, std::size_t Index = std::tuple_size<Tuple>::value - 1> struct Hasher
{
static void Hash(std::size_t& seed, const Tuple& tuple)
{
Hasher<Tuple, Index - 1>::Hash(seed, tuple);
CombineHash(seed, std::get<Index>(tuple));
}
};
template <typename Tuple> struct Hasher<Tuple, 0>
{
static void Hash(std::size_t& seed, const Tuple& tuple)
{
CombineHash(seed, std::get<0>(tuple));
}
};
template <typename... T> struct Hash<std::tuple<T...>>
{
std::size_t operator()(const std::tuple<T...>& value) const
{
std::size_t seed = 0;
Hasher<std::tuple<T...>>::Hash(seed, value);
return seed;
}
};
}
template <typename Key, typename Value, std::size_t Size> class LRUCache
{
public:
bool Contains(const Key& key) const
{
return Container.find(key) != Container.end();
}
const Value& At(const Key& key)
{
assert(Contains(key));
const auto location = Container.find(key);
Order.splice(Order.begin(), Order, location->second);
return location->second->second;
}
void Insert(const Key& key, Value value)
{
const auto existingLocation = Container.find(key);
if (existingLocation != Container.end())
{
Order.erase(existingLocation->second);
Container.erase(existingLocation);
}
Order.push_front(std::make_pair(key, std::move(value)));
Container.insert(std::make_pair(key, Order.begin()));
Clean();
}
private:
void Clean()
{
while (Container.size() > Size)
{
auto last = Order.end();
last--;
Container.erase(last->first);
Order.pop_back();
}
}
std::list<std::pair<Key, Value>> Order;
std::unordered_map<Key, decltype(Order.begin()), TupleHash::Hash<Key>> Container;
};
struct Color
{
const float R, G, B, A;
explicit Color(uint32_t color)
: R(((color >> 0) & 0xff) / 255.0f), G(((color >> 8) & 0xff) / 255.0f), B(((color >> 16) & 0xff) / 255.0f), A(((color >> 24) & 0xff) / 255.0f) { }
Color(float r, float g, float b, float a) : R(r), G(g), B(b), A(a) { }
Color operator*(const Color& c) const { return Color(R * c.R, G * c.G, B * c.B, A * c.A); }
Color operator*(float v) const { return Color(R * v, G * v, B * v, A * v); }
Color operator+(const Color& c) const { return Color(R + c.R, G + c.G, B + c.B, A + c.A); }
uint32_t ToInt() const
{
return ((static_cast<int>(R * 255) & 0xff) << 0)
| ((static_cast<int>(G * 255) & 0xff) << 8)
| ((static_cast<int>(B * 255) & 0xff) << 16)
| ((static_cast<int>(A * 255) & 0xff) << 24);
}
void UseAsDrawColor(SDL_Renderer* renderer) const
{
SDL_SetRenderDrawColor(renderer,
static_cast<uint8_t>(R * 255),
static_cast<uint8_t>(G * 255),
static_cast<uint8_t>(B * 255),
static_cast<uint8_t>(A * 255));
}
};
struct Device
{
SDL_Renderer* Renderer;
struct ClipRect
{
int X, Y, Width, Height;
} Clip;
struct TriangleCacheItem
{
SDL_Texture* Texture = nullptr;
int Width = 0, Height = 0;
~TriangleCacheItem() { if (Texture) SDL_DestroyTexture(Texture); }
};
// You can tweak these to values that you find that work the best.
static constexpr std::size_t UniformColorTriangleCacheSize = 512;
static constexpr std::size_t GenericTriangleCacheSize = 64;
// Uniform color is identified by its color and the coordinates of the edges.
using UniformColorTriangleKey = std::tuple<uint32_t, int, int, int, int, int, int>;
// The generic triangle cache unfortunately has to be basically a full representation of the triangle.
// This includes the (offset) vertex positions, texture coordinates and vertex colors.
using GenericTriangleVertexKey = std::tuple<int, int, double, double, uint32_t>;
using GenericTriangleKey = std::tuple<GenericTriangleVertexKey, GenericTriangleVertexKey, GenericTriangleVertexKey>;
LRUCache<UniformColorTriangleKey, std::unique_ptr<TriangleCacheItem>, UniformColorTriangleCacheSize> UniformColorTriangleCache;
LRUCache<GenericTriangleKey, std::unique_ptr<TriangleCacheItem>, GenericTriangleCacheSize> GenericTriangleCache;
Device(SDL_Renderer* renderer) : Renderer(renderer) { }
void SetClipRect(const ClipRect& rect)
{
Clip = rect;
const SDL_Rect clip = { rect.X, rect.Y, rect.Width, rect.Height };
SDL_RenderSetClipRect(Renderer, &clip);
}
void EnableClip() { SetClipRect(Clip); }
void DisableClip() { SDL_RenderSetClipRect(Renderer, nullptr); }
void SetAt(int x, int y, const Color& color)
{
color.UseAsDrawColor(Renderer);
SDL_RenderDrawPoint(Renderer, x, y);
}
SDL_Texture* MakeTexture(int width, int height)
{
SDL_Texture* texture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, width, height);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
return texture;
}
void UseAsRenderTarget(SDL_Texture* texture)
{
SDL_SetRenderTarget(Renderer, texture);
if (texture)
{
SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 0);
SDL_RenderClear(Renderer);
}
}
};
struct Texture
{
SDL_Surface* Surface;
SDL_Texture* Source;
~Texture()
{
SDL_FreeSurface(Surface);
SDL_DestroyTexture(Source);
}
Color Sample(float u, float v) const
{
const int x = static_cast<int>(std::round(u * (Surface->w - 1) + 0.5f));
const int y = static_cast<int>(std::round(v * (Surface->h - 1) + 0.5f));
const int location = y * Surface->w + x;
assert(location < Surface->w * Surface->h);
return Color(static_cast<uint32_t*>(Surface->pixels)[location]);
}
};
template <typename T> class InterpolatedFactorEquation
{
public:
InterpolatedFactorEquation(const T& value0, const T& value1, const T& value2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2)
: Value0(value0), Value1(value1), Value2(value2), V0(v0), V1(v1), V2(v2),
Divisor((V1.y - V2.y) * (V0.x - V2.x) + (V2.x - V1.x) * (V0.y - V2.y)) { }
T Evaluate(float x, float y) const
{
const float w1 = ((V1.y - V2.y) * (x - V2.x) + (V2.x - V1.x) * (y - V2.y)) / Divisor;
const float w2 = ((V2.y - V0.y) * (x - V2.x) + (V0.x - V2.x) * (y - V2.y)) / Divisor;
const float w3 = 1.0f - w1 - w2;
return static_cast<T>((Value0 * w1) + (Value1 * w2) + (Value2 * w3));
}
private:
const T Value0;
const T Value1;
const T Value2;
const ImVec2& V0;
const ImVec2& V1;
const ImVec2& V2;
const float Divisor;
};
struct Rect
{
float MinX, MinY, MaxX, MaxY;
float MinU, MinV, MaxU, MaxV;
bool IsOnExtreme(const ImVec2& point) const
{
return (point.x == MinX || point.x == MaxX) && (point.y == MinY || point.y == MaxY);
}
bool UsesOnlyColor() const
{
const ImVec2& whitePixel = ImGui::GetIO().Fonts->TexUvWhitePixel;
return MinU == MaxU && MinU == whitePixel.x && MinV == MaxV && MaxV == whitePixel.y;
}
static Rect CalculateBoundingBox(const ImDrawVert& v0, const ImDrawVert& v1, const ImDrawVert& v2)
{
return Rect{
std::min({ v0.pos.x, v1.pos.x, v2.pos.x }),
std::min({ v0.pos.y, v1.pos.y, v2.pos.y }),
std::max({ v0.pos.x, v1.pos.x, v2.pos.x }),
std::max({ v0.pos.y, v1.pos.y, v2.pos.y }),
std::min({ v0.uv.x, v1.uv.x, v2.uv.x }),
std::min({ v0.uv.y, v1.uv.y, v2.uv.y }),
std::max({ v0.uv.x, v1.uv.x, v2.uv.x }),
std::max({ v0.uv.y, v1.uv.y, v2.uv.y })
};
}
};
struct FixedPointTriangleRenderInfo
{
int X1, X2, X3, Y1, Y2, Y3;
int MinX, MaxX, MinY, MaxY;
static FixedPointTriangleRenderInfo CalculateFixedPointTriangleInfo(const ImVec2& v1, const ImVec2& v2, const ImVec2& v3)
{
static constexpr float scale = 16.0f;
const int x1 = static_cast<int>(std::round(v1.x * scale));
const int x2 = static_cast<int>(std::round(v2.x * scale));
const int x3 = static_cast<int>(std::round(v3.x * scale));
const int y1 = static_cast<int>(std::round(v1.y * scale));
const int y2 = static_cast<int>(std::round(v2.y * scale));
const int y3 = static_cast<int>(std::round(v3.y * scale));
int minX = (std::min({ x1, x2, x3 }) + 0xF) >> 4;
int maxX = (std::max({ x1, x2, x3 }) + 0xF) >> 4;
int minY = (std::min({ y1, y2, y3 }) + 0xF) >> 4;
int maxY = (std::max({ y1, y2, y3 }) + 0xF) >> 4;
return FixedPointTriangleRenderInfo{ x1, x2, x3, y1, y2, y3, minX, maxX, minY, maxY };
}
};
void DrawTriangleWithColorFunction(const FixedPointTriangleRenderInfo& renderInfo, const std::function<Color(float x, float y)>& colorFunction, Device::TriangleCacheItem* cacheItem)
{
// Implementation source: https://web.archive.org/web/20171128164608/http://forum.devmaster.net/t/advanced-rasterization/6145.
// This is a fixed point implementation that rounds to top-left.
const int deltaX12 = renderInfo.X1 - renderInfo.X2;
const int deltaX23 = renderInfo.X2 - renderInfo.X3;
const int deltaX31 = renderInfo.X3 - renderInfo.X1;
const int deltaY12 = renderInfo.Y1 - renderInfo.Y2;
const int deltaY23 = renderInfo.Y2 - renderInfo.Y3;
const int deltaY31 = renderInfo.Y3 - renderInfo.Y1;
const int fixedDeltaX12 = deltaX12 << 4;
const int fixedDeltaX23 = deltaX23 << 4;
const int fixedDeltaX31 = deltaX31 << 4;
const int fixedDeltaY12 = deltaY12 << 4;
const int fixedDeltaY23 = deltaY23 << 4;
const int fixedDeltaY31 = deltaY31 << 4;
const int width = renderInfo.MaxX - renderInfo.MinX;
const int height = renderInfo.MaxY - renderInfo.MinY;
if (width == 0 || height == 0) return;
int c1 = deltaY12 * renderInfo.X1 - deltaX12 * renderInfo.Y1;
int c2 = deltaY23 * renderInfo.X2 - deltaX23 * renderInfo.Y2;
int c3 = deltaY31 * renderInfo.X3 - deltaX31 * renderInfo.Y3;
if (deltaY12 < 0 || (deltaY12 == 0 && deltaX12 > 0)) c1++;
if (deltaY23 < 0 || (deltaY23 == 0 && deltaX23 > 0)) c2++;
if (deltaY31 < 0 || (deltaY31 == 0 && deltaX31 > 0)) c3++;
int edgeStart1 = c1 + deltaX12 * (renderInfo.MinY << 4) - deltaY12 * (renderInfo.MinX << 4);
int edgeStart2 = c2 + deltaX23 * (renderInfo.MinY << 4) - deltaY23 * (renderInfo.MinX << 4);
int edgeStart3 = c3 + deltaX31 * (renderInfo.MinY << 4) - deltaY31 * (renderInfo.MinX << 4);
SDL_Texture* cache = CurrentDevice->MakeTexture(width, height);
CurrentDevice->DisableClip();
CurrentDevice->UseAsRenderTarget(cache);
for (int y = renderInfo.MinY; y < renderInfo.MaxY; y++)
{
int edge1 = edgeStart1;
int edge2 = edgeStart2;
int edge3 = edgeStart3;
for (int x = renderInfo.MinX; x < renderInfo.MaxX; x++)
{
if (edge1 > 0 && edge2 > 0 && edge3 > 0)
{
CurrentDevice->SetAt(x - renderInfo.MinX, y - renderInfo.MinY, colorFunction(x + 0.5f, y + 0.5f));
}
edge1 -= fixedDeltaY12;
edge2 -= fixedDeltaY23;
edge3 -= fixedDeltaY31;
}
edgeStart1 += fixedDeltaX12;
edgeStart2 += fixedDeltaX23;
edgeStart3 += fixedDeltaX31;
}
CurrentDevice->UseAsRenderTarget(nullptr);
CurrentDevice->EnableClip();
cacheItem->Texture = cache;
cacheItem->Width = width;
cacheItem->Height = height;
}
void DrawCachedTriangle(const Device::TriangleCacheItem& triangle, const FixedPointTriangleRenderInfo& renderInfo)
{
const SDL_Rect destination = { renderInfo.MinX, renderInfo.MinY, triangle.Width, triangle.Height };
SDL_RenderCopy(CurrentDevice->Renderer, triangle.Texture, nullptr, &destination);
}
void DrawTriangle(const ImDrawVert& v1, const ImDrawVert& v2, const ImDrawVert& v3, const Texture* texture)
{
// The naming inconsistency in the parameters is intentional. The fixed point algorithm wants the vertices in a counter clockwise order.
const auto& renderInfo = FixedPointTriangleRenderInfo::CalculateFixedPointTriangleInfo(v3.pos, v2.pos, v1.pos);
// First we check if there is a cached version of this triangle already waiting for us. If so, we can just do a super fast texture copy.
const auto key = std::make_tuple(
std::make_tuple(static_cast<int>(std::round(v1.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v1.pos.y)) - renderInfo.MinY, v1.uv.x, v1.uv.y, v1.col),
std::make_tuple(static_cast<int>(std::round(v2.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v2.pos.y)) - renderInfo.MinY, v2.uv.x, v2.uv.y, v2.col),
std::make_tuple(static_cast<int>(std::round(v3.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v3.pos.y)) - renderInfo.MinY, v3.uv.x, v3.uv.y, v3.col));
if (CurrentDevice->GenericTriangleCache.Contains(key))
{
const auto& cached = CurrentDevice->GenericTriangleCache.At(key);
DrawCachedTriangle(*cached, renderInfo);
return;
}
const InterpolatedFactorEquation<float> textureU(v1.uv.x, v2.uv.x, v3.uv.x, v1.pos, v2.pos, v3.pos);
const InterpolatedFactorEquation<float> textureV(v1.uv.y, v2.uv.y, v3.uv.y, v1.pos, v2.pos, v3.pos);
const InterpolatedFactorEquation<Color> shadeColor(Color(v1.col), Color(v2.col), Color(v3.col), v1.pos, v2.pos, v3.pos);
auto cached = std::make_unique<Device::TriangleCacheItem>();
DrawTriangleWithColorFunction(renderInfo, [&](float x, float y) {
const float u = textureU.Evaluate(x, y);
const float v = textureV.Evaluate(x, y);
const Color sampled = texture->Sample(u, v);
const Color shade = shadeColor.Evaluate(x, y);
return sampled * shade;
}, cached.get());
if (!cached->Texture) return;
const SDL_Rect destination = { renderInfo.MinX, renderInfo.MinY, cached->Width, cached->Height };
SDL_RenderCopy(CurrentDevice->Renderer, cached->Texture, nullptr, &destination);
CurrentDevice->GenericTriangleCache.Insert(key, std::move(cached));
}
void DrawUniformColorTriangle(const ImDrawVert& v1, const ImDrawVert& v2, const ImDrawVert& v3)
{
const Color color(v1.col);
// The naming inconsistency in the parameters is intentional. The fixed point algorithm wants the vertices in a counter clockwise order.
const auto& renderInfo = FixedPointTriangleRenderInfo::CalculateFixedPointTriangleInfo(v3.pos, v2.pos, v1.pos);
const auto key =std::make_tuple(v1.col,
static_cast<int>(std::round(v1.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v1.pos.y)) - renderInfo.MinY,
static_cast<int>(std::round(v2.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v2.pos.y)) - renderInfo.MinY,
static_cast<int>(std::round(v3.pos.x)) - renderInfo.MinX, static_cast<int>(std::round(v3.pos.y)) - renderInfo.MinY);
if (CurrentDevice->UniformColorTriangleCache.Contains(key))
{
const auto& cached = CurrentDevice->UniformColorTriangleCache.At(key);
DrawCachedTriangle(*cached, renderInfo);
return;
}
auto cached = std::make_unique<Device::TriangleCacheItem>();
DrawTriangleWithColorFunction(renderInfo, [&color](float, float) { return color; }, cached.get());
if (!cached->Texture) return;
const SDL_Rect destination = { renderInfo.MinX, renderInfo.MinY, cached->Width, cached->Height };
SDL_RenderCopy(CurrentDevice->Renderer, cached->Texture, nullptr, &destination);
CurrentDevice->UniformColorTriangleCache.Insert(key, std::move(cached));
}
void DrawRectangle(const Rect& bounding, SDL_Texture* texture, int textureWidth, int textureHeight, const Color& color, bool doHorizontalFlip, bool doVerticalFlip)
{
// We are safe to assume uniform color here, because the caller checks it and and uses the triangle renderer to render those.
const SDL_Rect destination = {
static_cast<int>(bounding.MinX),
static_cast<int>(bounding.MinY),
static_cast<int>(bounding.MaxX - bounding.MinX),
static_cast<int>(bounding.MaxY - bounding.MinY)
};
// If the area isn't textured, we can just draw a rectangle with the correct color.
if (bounding.UsesOnlyColor())
{
color.UseAsDrawColor(CurrentDevice->Renderer);
SDL_RenderFillRect(CurrentDevice->Renderer, &destination);
}
else
{
// We can now just calculate the correct source rectangle and draw it.
const SDL_Rect source = {
static_cast<int>(bounding.MinU * textureWidth),
static_cast<int>(bounding.MinV * textureHeight),
static_cast<int>((bounding.MaxU - bounding.MinU) * textureWidth),
static_cast<int>((bounding.MaxV - bounding.MinV) * textureHeight)
};
const SDL_RendererFlip flip = static_cast<SDL_RendererFlip>((doHorizontalFlip ? SDL_FLIP_HORIZONTAL : 0) | (doVerticalFlip ? SDL_FLIP_VERTICAL : 0));
SDL_SetTextureColorMod(texture, static_cast<uint8_t>(color.R * 255), static_cast<uint8_t>(color.G * 255), static_cast<uint8_t>(color.B * 255));
SDL_RenderCopyEx(CurrentDevice->Renderer, texture, &source, &destination, 0.0, nullptr, flip);
}
}
void DrawRectangle(const Rect& bounding, const Texture* texture, const Color& color, bool doHorizontalFlip, bool doVerticalFlip)
{
DrawRectangle(bounding, texture->Source, texture->Surface->w, texture->Surface->h, color, doHorizontalFlip, doVerticalFlip);
}
void DrawRectangle(const Rect& bounding, SDL_Texture* texture, const Color& color, bool doHorizontalFlip, bool doVerticalFlip)
{
int width, height;
SDL_QueryTexture(texture, nullptr, nullptr, &width, &height);
DrawRectangle(bounding, texture, width, height, color, doHorizontalFlip, doVerticalFlip);
}
}
namespace ImGuiSDL
{
void Initialize(SDL_Renderer* renderer, int windowWidth, int windowHeight)
{
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize.x = static_cast<float>(windowWidth);
io.DisplaySize.y = static_cast<float>(windowHeight);
ImGui::GetStyle().WindowRounding = 0.0f;
ImGui::GetStyle().AntiAliasedFill = false;
ImGui::GetStyle().AntiAliasedLines = false;
// Loads the font texture.
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
static constexpr uint32_t rmask = 0x000000ff, gmask = 0x0000ff00, bmask = 0x00ff0000, amask = 0xff000000;
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom(pixels, width, height, 32, 4 * width, rmask, gmask, bmask, amask);
Texture* texture = new Texture();
texture->Surface = surface;
texture->Source = SDL_CreateTextureFromSurface(renderer, surface);
io.Fonts->TexID = (void*)texture;
CurrentDevice = new Device(renderer);
}
void Deinitialize()
{
// Frees up the memory of the font texture.
ImGuiIO& io = ImGui::GetIO();
Texture* texture = static_cast<Texture*>(io.Fonts->TexID);
delete texture;
delete CurrentDevice;
}
void Render(ImDrawData* drawData)
{
SDL_BlendMode blendMode;
SDL_GetRenderDrawBlendMode(CurrentDevice->Renderer, &blendMode);
SDL_SetRenderDrawBlendMode(CurrentDevice->Renderer, SDL_BLENDMODE_BLEND);
Uint8 initialR, initialG, initialB, initialA;
SDL_GetRenderDrawColor(CurrentDevice->Renderer, &initialR, &initialG, &initialB, &initialA);
SDL_bool initialClipEnabled = SDL_RenderIsClipEnabled(CurrentDevice->Renderer);
SDL_Rect initialClipRect;
SDL_RenderGetClipRect(CurrentDevice->Renderer, &initialClipRect);
SDL_Texture* initialRenderTarget = SDL_GetRenderTarget(CurrentDevice->Renderer);
ImGuiIO& io = ImGui::GetIO();
for (int n = 0; n < drawData->CmdListsCount; n++)
{
auto commandList = drawData->CmdLists[n];
auto vertexBuffer = commandList->VtxBuffer;
auto indexBuffer = commandList->IdxBuffer.Data;
for (int cmd_i = 0; cmd_i < commandList->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* drawCommand = &commandList->CmdBuffer[cmd_i];
const Device::ClipRect clipRect = {
static_cast<int>(drawCommand->ClipRect.x),
static_cast<int>(drawCommand->ClipRect.y),
static_cast<int>(drawCommand->ClipRect.z - drawCommand->ClipRect.x),
static_cast<int>(drawCommand->ClipRect.w - drawCommand->ClipRect.y)
};
CurrentDevice->SetClipRect(clipRect);
if (drawCommand->UserCallback)
{
drawCommand->UserCallback(commandList, drawCommand);
}
else
{
const bool isWrappedTexture = drawCommand->TextureId == io.Fonts->TexID;
// Loops over triangles.
for (unsigned int i = 0; i + 3 <= drawCommand->ElemCount; i += 3)
{
const ImDrawVert& v0 = vertexBuffer[indexBuffer[i + 0]];
const ImDrawVert& v1 = vertexBuffer[indexBuffer[i + 1]];
const ImDrawVert& v2 = vertexBuffer[indexBuffer[i + 2]];
const Rect& bounding = Rect::CalculateBoundingBox(v0, v1, v2);
const bool isTriangleUniformColor = v0.col == v1.col && v1.col == v2.col;
const bool doesTriangleUseOnlyColor = bounding.UsesOnlyColor();
// Actually, since we render a whole bunch of rectangles, we try to first detect those, and render them more efficiently.
// How are rectangles detected? It's actually pretty simple: If all 6 vertices lie on the extremes of the bounding box,
// it's a rectangle.
if (i + 6 <= drawCommand->ElemCount)
{
const ImDrawVert& v3 = vertexBuffer[indexBuffer[i + 3]];
const ImDrawVert& v4 = vertexBuffer[indexBuffer[i + 4]];
const ImDrawVert& v5 = vertexBuffer[indexBuffer[i + 5]];
const bool isUniformColor = isTriangleUniformColor && v2.col == v3.col && v3.col == v4.col && v4.col == v5.col;
if (isUniformColor
&& bounding.IsOnExtreme(v0.pos)
&& bounding.IsOnExtreme(v1.pos)
&& bounding.IsOnExtreme(v2.pos)
&& bounding.IsOnExtreme(v3.pos)
&& bounding.IsOnExtreme(v4.pos)
&& bounding.IsOnExtreme(v5.pos))
{
// ImGui gives the triangles in a nice order: the first vertex happens to be the topleft corner of our rectangle.
// We need to check for the orientation of the texture, as I believe in theory ImGui could feed us a flipped texture,
// so that the larger texture coordinates are at topleft instead of bottomright.
// We don't consider equal texture coordinates to require a flip, as then the rectangle is mostlikely simply a colored rectangle.
const bool doHorizontalFlip = v2.uv.x < v0.uv.x;
const bool doVerticalFlip = v2.uv.x < v0.uv.x;
if (isWrappedTexture)
{
DrawRectangle(bounding, static_cast<const Texture*>(drawCommand->TextureId), Color(v0.col), doHorizontalFlip, doVerticalFlip);
}
else
{
DrawRectangle(bounding, static_cast<SDL_Texture*>(drawCommand->TextureId), Color(v0.col), doHorizontalFlip, doVerticalFlip);
}
i += 3; // Additional increment to account for the extra 3 vertices we consumed.
continue;
}
}
if (isTriangleUniformColor && doesTriangleUseOnlyColor)
{
DrawUniformColorTriangle(v0, v1, v2);
}
else
{
// Currently we assume that any non rectangular texture samples the font texture. Dunno if that's what actually happens, but it seems to work.
assert(isWrappedTexture);
DrawTriangle(v0, v1, v2, static_cast<const Texture*>(drawCommand->TextureId));
}
}
}
indexBuffer += drawCommand->ElemCount;
}
}
CurrentDevice->DisableClip();
SDL_SetRenderTarget(CurrentDevice->Renderer, initialRenderTarget);
SDL_RenderSetClipRect(CurrentDevice->Renderer, initialClipEnabled ? &initialClipRect : nullptr);
SDL_SetRenderDrawColor(CurrentDevice->Renderer,
initialR, initialG, initialB, initialA);
SDL_SetRenderDrawBlendMode(CurrentDevice->Renderer, blendMode);
}
}

View File

@ -0,0 +1,17 @@
#pragma once
struct ImDrawData;
struct SDL_Renderer;
namespace ImGuiSDL
{
// Call this to initialize the SDL renderer device that is internally used by the renderer.
void Initialize(SDL_Renderer* renderer, int windowWidth, int windowHeight);
// Call this before destroying your SDL renderer or ImGui to ensure that proper cleanup is done. This doesn't do anything critically important though,
// so if you're fine with small memory leaks at the end of your application, you can even omit this.
void Deinitialize();
// Call this every frame after ImGui::Render with ImGui::GetDrawData(). This will use the SDL_Renderer provided to the interfrace with Initialize
// to draw the contents of the draw data to the screen.
void Render(ImDrawData* drawData);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,639 @@
// [DEAR IMGUI]
// This is a slightly modified version of stb_rect_pack.h 1.00.
// Those changes would need to be pushed into nothings/stb:
// - Added STBRP__CDECL
// Grep for [DEAR IMGUI] to find the changes.
// stb_rect_pack.h - v1.00 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
//
// Has only had a few tests run, may have issues.
//
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stdlib.
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
// Please note: better rectangle packers are welcome! Please
// implement them to the same API, but with a different init
// function.
//
// Credits
//
// Library
// Sean Barrett
// Minor features
// Martins Mozeiko
// github:IntellectualKitty
//
// Bugfixes / warning fixes
// Jeremy Jaussaud
// Fabian Giesen
//
// Version history:
//
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
// 0.09 (2016-08-27) fix compiler warnings
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
//
// LICENSE
//
// See end of file for license information.
//////////////////////////////////////////////////////////////////////////////
//
// INCLUDE SECTION
//
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#define STB_INCLUDE_STB_RECT_PACK_H
#define STB_RECT_PACK_VERSION 1
#ifdef STBRP_STATIC
#define STBRP_DEF static
#else
#define STBRP_DEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stbrp_context stbrp_context;
typedef struct stbrp_node stbrp_node;
typedef struct stbrp_rect stbrp_rect;
#ifdef STBRP_LARGE_RECTS
typedef int stbrp_coord;
#else
typedef unsigned short stbrp_coord;
#endif
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packed locations to rectangles. The rectangles are of type
// 'stbrp_rect' defined below, stored in the array 'rects', and there
// are 'num_rects' many of them.
//
// Rectangles which are successfully packed have the 'was_packed' flag
// set to a non-zero value and 'x' and 'y' store the minimum location
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
// if you imagine y increasing downwards). Rectangles which do not fit
// have the 'was_packed' flag set to 0.
//
// You should not try to access the 'rects' array from another thread
// while this function is running, as the function temporarily reorders
// the array while it executes.
//
// To pack into another rectangle, you need to call stbrp_init_target
// again. To continue packing into the same rectangle, you can call
// this function again. Calling this multiple times with multiple rect
// arrays will probably produce worse packing results than calling it
// a single time with the full rectangle array, but the option is
// available.
//
// The function returns 1 if all of the rectangles were successfully
// packed and 0 otherwise.
struct stbrp_rect
{
// reserved for your use:
int id;
// input:
stbrp_coord w, h;
// output:
stbrp_coord x, y;
int was_packed; // non-zero if valid packing
}; // 16 bytes, nominally
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
// Initialize a rectangle packer to:
// pack a rectangle that is 'width' by 'height' in dimensions
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
//
// You must call this function every time you start packing into a new target.
//
// There is no "shutdown" function. The 'nodes' memory must stay valid for
// the following stbrp_pack_rects() call (or calls), but can be freed after
// the call (or calls) finish.
//
// Note: to guarantee best results, either:
// 1. make sure 'num_nodes' >= 'width'
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
//
// If you don't do either of the above things, widths will be quantized to multiples
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
//
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
// may run out of temporary storage and be unable to pack some rectangles.
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
// Optionally call this function after init but before doing any packing to
// change the handling of the out-of-temp-memory scenario, described above.
// If you call init again, this will be reset to the default (false).
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
// Optionally select which packing heuristic the library should use. Different
// heuristics will produce better/worse results for different data sets.
// If you call init again, this will be reset to the default.
enum
{
STBRP_HEURISTIC_Skyline_default=0,
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
STBRP_HEURISTIC_Skyline_BF_sortHeight
};
//////////////////////////////////////////////////////////////////////////////
//
// the details of the following structures don't matter to you, but they must
// be visible so you can handle the memory allocations for them
struct stbrp_node
{
stbrp_coord x,y;
stbrp_node *next;
};
struct stbrp_context
{
int width;
int height;
int align;
int init_mode;
int heuristic;
int num_nodes;
stbrp_node *active_head;
stbrp_node *free_head;
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
};
#ifdef __cplusplus
}
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION SECTION
//
#ifdef STB_RECT_PACK_IMPLEMENTATION
#ifndef STBRP_SORT
#include <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
#define STBRP_ASSERT assert
#endif
// [DEAR IMGUI] Added STBRP__CDECL
#ifdef _MSC_VER
#define STBRP__NOTUSED(v) (void)(v)
#define STBRP__CDECL __cdecl
#else
#define STBRP__NOTUSED(v) (void)sizeof(v)
#define STBRP__CDECL
#endif
enum
{
STBRP__INIT_skyline = 1
};
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
{
switch (context->init_mode) {
case STBRP__INIT_skyline:
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
context->heuristic = heuristic;
break;
default:
STBRP_ASSERT(0);
}
}
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
{
if (allow_out_of_mem)
// if it's ok to run out of memory, then don't bother aligning them;
// this gives better packing, but may fail due to OOM (even though
// the rectangles easily fit). @TODO a smarter approach would be to only
// quantize once we've hit OOM, then we could get rid of this parameter.
context->align = 1;
else {
// if it's not ok to run out of memory, then quantize the widths
// so that num_nodes is always enough nodes.
//
// I.e. num_nodes * align >= width
// align >= width / num_nodes
// align = ceil(width/num_nodes)
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
}
}
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
{
int i;
#ifndef STBRP_LARGE_RECTS
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
#endif
for (i=0; i < num_nodes-1; ++i)
nodes[i].next = &nodes[i+1];
nodes[i].next = NULL;
context->init_mode = STBRP__INIT_skyline;
context->heuristic = STBRP_HEURISTIC_Skyline_default;
context->free_head = &nodes[0];
context->active_head = &context->extra[0];
context->width = width;
context->height = height;
context->num_nodes = num_nodes;
stbrp_setup_allow_out_of_mem(context, 0);
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
context->extra[0].x = 0;
context->extra[0].y = 0;
context->extra[0].next = &context->extra[1];
context->extra[1].x = (stbrp_coord) width;
#ifdef STBRP_LARGE_RECTS
context->extra[1].y = (1<<30);
#else
context->extra[1].y = 65535;
#endif
context->extra[1].next = NULL;
}
// find minimum y position if it starts at x1
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
{
stbrp_node *node = first;
int x1 = x0 + width;
int min_y, visited_width, waste_area;
STBRP__NOTUSED(c);
STBRP_ASSERT(first->x <= x0);
#if 0
// skip in case we're past the node
while (node->next->x <= x0)
++node;
#else
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
#endif
STBRP_ASSERT(node->x <= x0);
min_y = 0;
waste_area = 0;
visited_width = 0;
while (node->x < x1) {
if (node->y > min_y) {
// raise min_y higher.
// we've accounted for all waste up to min_y,
// but we'll now add more waste for everything we've visted
waste_area += visited_width * (node->y - min_y);
min_y = node->y;
// the first time through, visited_width might be reduced
if (node->x < x0)
visited_width += node->next->x - x0;
else
visited_width += node->next->x - node->x;
} else {
// add waste area
int under_width = node->next->x - node->x;
if (under_width + visited_width > width)
under_width = width - visited_width;
waste_area += under_width * (min_y - node->y);
visited_width += under_width;
}
node = node->next;
}
*pwaste = waste_area;
return min_y;
}
typedef struct
{
int x,y;
stbrp_node **prev_link;
} stbrp__findresult;
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
{
int best_waste = (1<<30), best_x, best_y = (1 << 30);
stbrp__findresult fr;
stbrp_node **prev, *node, *tail, **best = NULL;
// align to multiple of c->align
width = (width + c->align - 1);
width -= width % c->align;
STBRP_ASSERT(width % c->align == 0);
// if it can't possibly fit, bail immediately
if (width > c->width || height > c->height) {
fr.prev_link = NULL;
fr.x = fr.y = 0;
return fr;
}
node = c->active_head;
prev = &c->active_head;
while (node->x + width <= c->width) {
int y,waste;
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
// bottom left
if (y < best_y) {
best_y = y;
best = prev;
}
} else {
// best-fit
if (y + height <= c->height) {
// can only use it if it first vertically
if (y < best_y || (y == best_y && waste < best_waste)) {
best_y = y;
best_waste = waste;
best = prev;
}
}
}
prev = &node->next;
node = node->next;
}
best_x = (best == NULL) ? 0 : (*best)->x;
// if doing best-fit (BF), we also have to try aligning right edge to each node position
//
// e.g, if fitting
//
// ____________________
// |____________________|
//
// into
//
// | |
// | ____________|
// |____________|
//
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
//
// This makes BF take about 2x the time
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
tail = c->active_head;
node = c->active_head;
prev = &c->active_head;
// find first node that's admissible
while (tail->x < width)
tail = tail->next;
while (tail) {
int xpos = tail->x - width;
int y,waste;
STBRP_ASSERT(xpos >= 0);
// find the left position that matches this
while (node->next->x <= xpos) {
prev = &node->next;
node = node->next;
}
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
if (y + height <= c->height) {
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
STBRP_ASSERT(y <= best_y);
best_y = y;
best_waste = waste;
best = prev;
}
}
}
tail = tail->next;
}
}
fr.prev_link = best;
fr.x = best_x;
fr.y = best_y;
return fr;
}
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
{
// find best position according to heuristic
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
stbrp_node *node, *cur;
// bail if:
// 1. it failed
// 2. the best node doesn't fit (we don't always check this)
// 3. we're out of memory
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
res.prev_link = NULL;
return res;
}
// on success, create new node
node = context->free_head;
node->x = (stbrp_coord) res.x;
node->y = (stbrp_coord) (res.y + height);
context->free_head = node->next;
// insert the new node into the right starting point, and
// let 'cur' point to the remaining nodes needing to be
// stiched back in
cur = *res.prev_link;
if (cur->x < res.x) {
// preserve the existing one, so start testing with the next one
stbrp_node *next = cur->next;
cur->next = node;
cur = next;
} else {
*res.prev_link = node;
}
// from here, traverse cur and free the nodes, until we get to one
// that shouldn't be freed
while (cur->next && cur->next->x <= res.x + width) {
stbrp_node *next = cur->next;
// move the current node to the free list
cur->next = context->free_head;
context->free_head = cur;
cur = next;
}
// stitch the list back in
node->next = cur;
if (cur->x < res.x + width)
cur->x = (stbrp_coord) (res.x + width);
#ifdef _DEBUG
cur = context->active_head;
while (cur->x < context->width) {
STBRP_ASSERT(cur->x < cur->next->x);
cur = cur->next;
}
STBRP_ASSERT(cur->next == NULL);
{
int count=0;
cur = context->active_head;
while (cur) {
cur = cur->next;
++count;
}
cur = context->free_head;
while (cur) {
cur = cur->next;
++count;
}
STBRP_ASSERT(count == context->num_nodes+2);
}
#endif
return res;
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
if (p->h > q->h)
return -1;
if (p->h < q->h)
return 1;
return (p->w > q->w) ? -1 : (p->w < q->w);
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
}
#ifdef STBRP_LARGE_RECTS
#define STBRP__MAXVAL 0xffffffff
#else
#define STBRP__MAXVAL 0xffff
#endif
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
{
int i, all_rects_packed = 1;
// we use the 'was_packed' field internally to allow sorting/unsorting
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = i;
}
// sort according to heuristic
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
if (rects[i].w == 0 || rects[i].h == 0) {
rects[i].x = rects[i].y = 0; // empty rect needs no space
} else {
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
if (fr.prev_link) {
rects[i].x = (stbrp_coord) fr.x;
rects[i].y = (stbrp_coord) fr.y;
} else {
rects[i].x = rects[i].y = STBRP__MAXVAL;
}
}
}
// unsort
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
// set was_packed flags and all_rects_packed status
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
if (!rects[i].was_packed)
all_rects_packed = 0;
}
// return the all_rects_packed status
return all_rects_packed;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -95,6 +95,7 @@ void options::init(HMENU menuHandle)
Options.RightTableBumpKey = Options.RightTableBumpKeyDft; Options.RightTableBumpKey = Options.RightTableBumpKeyDft;
Options.Players = 1; Options.Players = 1;
Options.BottomTableBumpKey = Options.BottomTableBumpKeyDft; Options.BottomTableBumpKey = Options.BottomTableBumpKeyDft;
Options.UniformScaling = true;
/*Options.Sounds = get_int(nullptr, "Sounds", Options.Sounds); /*Options.Sounds = get_int(nullptr, "Sounds", Options.Sounds);
Options.Music = get_int(nullptr, "Music", Options.Music); Options.Music = get_int(nullptr, "Music", Options.Music);
Options.Average = get_int(nullptr, "Average", Options.Average); Options.Average = get_int(nullptr, "Average", Options.Average);
@ -341,7 +342,7 @@ void options::toggle(UINT uIDCheckItem)
Options.UniformScaling ^= true; Options.UniformScaling ^= true;
menu_check(Menu1_WindowUniformScale, Options.UniformScaling); menu_check(Menu1_WindowUniformScale, Options.UniformScaling);
fullscrn::window_size_changed(); fullscrn::window_size_changed();
fullscrn::paint(); pb::paint();
break; break;
default: default:
break; break;
@ -387,7 +388,7 @@ void options::init_resolution()
void options::keyboard() void options::keyboard()
{ {
DialogBoxParamA(nullptr, "KEYMAPPER", nullptr, KeyMapDlgProc, 0); //DialogBoxParamA(nullptr, "KEYMAPPER", nullptr, KeyMapDlgProc, 0);
} }
INT_PTR _stdcall options::KeyMapDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) INT_PTR _stdcall options::KeyMapDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)

View File

@ -27,6 +27,12 @@
#include "SDL.h" #include "SDL.h"
#include <SDL_mixer.h> #include <SDL_mixer.h>
//https://github.com/ocornut/imgui 7b913db1ce9dd2fd98e5790aa59974dd4496be3b
#include "imgui.h"
#include "imgui_impl_sdl.h"
//https://github.com/Tyyppi77/imgui_sdl 01deb04b102b6a1c15c7fdec1977a2c96a885e6f
#include "imgui_sdl.h"
//typedef const char* LPCSTR; //typedef const char* LPCSTR;
//typedef int HINSTANCE; //typedef int HINSTANCE;
//typedef int HWND; //typedef int HWND;

View File

@ -1,304 +0,0 @@
#include "pch.h"
#include "splash.h"
#include "memory.h"
#include "pinball.h"
HINSTANCE splash::HInstance;
HGDIOBJ splash::OriginalDcBitmap = nullptr;
splash_struct* splash::splash_screen(HINSTANCE hInstance, LPCSTR bmpName1, LPCSTR bmpName2)
{
WNDCLASSA WndClass{};
tagRECT Rect{};
auto splashStruct = memory::allocate<splash_struct>();
if (!splashStruct)
return nullptr;
lstrcpyA(splashStruct->BmpName1, bmpName1);
lstrcpyA(splashStruct->BmpName2, bmpName2);
if (!HInstance)
{
HInstance = hInstance;
WndClass.style = 0;
WndClass.lpfnWndProc = splash_message_handler;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 4;
WndClass.hInstance = hInstance;
WndClass.hIcon = nullptr;
WndClass.hCursor = LoadCursorA(nullptr, IDC_ARROW);
WndClass.hbrBackground = nullptr;
WndClass.lpszMenuName = "";
WndClass.lpszClassName = "3DPB_SPLASH_CLASS";
RegisterClassA(&WndClass);
}
splashStruct->Bitmap = nullptr;
HWND windowHandle = CreateWindowExA(0, "3DPB_SPLASH_CLASS", "", 0x80000000, -10, -10, 1, 1,
nullptr, nullptr, HInstance, nullptr);
splashStruct->WindowHandle = windowHandle;
if (!windowHandle)
{
memory::free(splashStruct);
return nullptr;
}
SetWindowLongPtrA(windowHandle, -21, reinterpret_cast<LONG_PTR>(splashStruct));
GetWindowRect(GetDesktopWindow(), &Rect);
splash_bitmap_setup(splashStruct);
//MoveWindow(splashStruct->WindowHandle, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top, 0);
/*Mod - less intrusive splash*/
auto centerX = (Rect.right - Rect.left - splashStruct->Width) / 2;
auto CenterY = (Rect.bottom - Rect.top - splashStruct->Height) / 2;
MoveWindow(splashStruct->WindowHandle, centerX, CenterY, splashStruct->Width, splashStruct->Height, 0);
ShowWindow(splashStruct->WindowHandle, 8);
SetFocus(splashStruct->WindowHandle);
UpdateWindow(splashStruct->WindowHandle);
return splashStruct;
}
void splash::splash_bitmap_setup(splash_struct* splashStruct)
{
HBITMAP bmpHandle2;
BITMAP bmp{};
HBITMAP bmpHandle1 = nullptr;
HDC desktopDC = GetDC(GetDesktopWindow());
if (desktopDC)
{
splashStruct->DrawingContext = CreateCompatibleDC(desktopDC);
bmpHandle1 = CreateCompatibleBitmap(desktopDC, 10, 10);
ReleaseDC(splashStruct->WindowHandle, desktopDC);
if (bmpHandle1)
{
if (splashStruct->DrawingContext)
{
OriginalDcBitmap = SelectObject(splashStruct->DrawingContext, bmpHandle1);
if ((GetDeviceCaps(splashStruct->DrawingContext, RASTERCAPS) & RC_PALETTE) != 0
|| GetDeviceCaps(splashStruct->DrawingContext, NUMCOLORS) >= 256)
{
bmpHandle2 = load_title_bitmap(HInstance, splashStruct->DrawingContext, splashStruct->BmpName1, 10,
236, &splashStruct->Palette);
}
else
{
bmpHandle2 = LoadBitmapA(HInstance, splashStruct->BmpName2);
splashStruct->Palette = nullptr;
}
splashStruct->Bitmap = bmpHandle2;
if (bmpHandle2)
{
SelectObject(splashStruct->DrawingContext, bmpHandle2);
DeleteObject(bmpHandle1);
GetObjectA(splashStruct->Bitmap, sizeof(BITMAP), &bmp);
splashStruct->Width = bmp.bmWidth;
splashStruct->Height = bmp.bmHeight;
return;
}
}
}
else
{
GetLastError();
}
}
if (splashStruct->Palette)
DeleteObject(splashStruct->Palette);
if (splashStruct->WindowHandle)
DestroyWindow(splashStruct->WindowHandle);
if (bmpHandle1)
DeleteObject(bmpHandle1);
if (splashStruct->DrawingContext)
DeleteDC(splashStruct->DrawingContext);
splashStruct->Bitmap = nullptr;
}
HBITMAP splash::load_title_bitmap(HMODULE hModule, HDC hdc, LPCSTR lpName, UINT iStart, int iEnd, HPALETTE* palettePtr)
{
LOGPALETTEx256 plpal;
auto resH = FindResourceA(hModule, lpName, RT_BITMAP);
if (!resH)
return nullptr;
auto resHGlobal = LoadResource(hModule, resH);
if (!resHGlobal)
return nullptr;
auto bmp = static_cast<BITMAPINFO*>(LockResource(resHGlobal));
int numColors = bmp->bmiHeader.biClrUsed;
if (!numColors)
numColors = 1 << LOBYTE(bmp->bmiHeader.biBitCount);
if (bmp->bmiHeader.biBitCount > 4u)
{
*palettePtr = splash_init_palette(&plpal);
if (*palettePtr)
{
int cEntries = 0;
if (iEnd > 0)
{
auto dst = &plpal.palPalEntry[iStart];
auto src = &bmp->bmiColors[0];
for (; cEntries < iEnd && cEntries < numColors; ++cEntries)
{
dst->peRed = src->rgbRed;
dst->peGreen = src->rgbGreen;
dst->peBlue = src->rgbBlue;
dst->peFlags = 4;
src++;
dst++;
}
}
SetPaletteEntries(*palettePtr, iStart, cEntries, &plpal.palPalEntry[iStart]);
SelectPalette(hdc, *palettePtr, 0);
RealizePalette(hdc);
}
}
auto resBmp = CreateDIBitmap(hdc, &bmp->bmiHeader, 4u, &bmp->bmiColors[numColors], bmp, 0);
FreeResource(resHGlobal);
return resBmp;
}
HPALETTE splash::splash_init_palette(LOGPALETTE* plpal)
{
plpal->palVersion = 768;
plpal->palNumEntries = 256;
auto hPalette = CreatePalette(static_cast<const LOGPALETTE*>(plpal));
auto dc = GetDC(GetDesktopWindow());
GetDeviceCaps(dc, RASTERCAPS);
if (GetDeviceCaps(dc, SIZEPALETTE) != 256)
{
if (hPalette)
DeleteObject(hPalette);
ReleaseDC(GetDesktopWindow(), dc);
return nullptr;
}
SetSystemPaletteUse(dc, 2u);
SetSystemPaletteUse(dc, 1u);
auto hPal = SelectPalette(dc, hPalette, 0);
RealizePalette(dc);
SelectPalette(dc, hPal, 0);
RealizePalette(dc);
GetSystemPaletteEntries(dc, 0, 256u, plpal->palPalEntry);
ReleaseDC(GetDesktopWindow(), dc);
auto dst = &plpal->palPalEntry[0];
for (auto index = 256; index; --index)
{
dst->peFlags = 0;
dst++;
}
dst = &plpal->palPalEntry[10];
for (auto index = 10; index < 246; ++index)
{
dst->peRed = index;
dst->peGreen = index;
dst->peBlue = index;
dst->peFlags = 4;
dst++;
}
ResizePalette(hPalette, 256u);
SetPaletteEntries(hPalette, 0, 256u, plpal->palPalEntry);
return hPalette;
}
void splash::splash_paint(splash_struct* splashStruct, HDC dc)
{
tagRECT Rect{};
if (splashStruct->Bitmap)
{
GetWindowRect(GetDesktopWindow(), &Rect);
splashStruct->CenterX = (Rect.right - Rect.left - splashStruct->Width) / 2;
splashStruct->CenterY = (Rect.bottom - Rect.top - splashStruct->Height) / 2;
SelectPalette(dc, splashStruct->Palette, 0);
RealizePalette(dc);
SelectPalette(splashStruct->DrawingContext, splashStruct->Palette, 0);
RealizePalette(splashStruct->DrawingContext);
/*BitBlt(dc, splashStruct->CenterX, splashStruct->CenterY, splashStruct->Width,
splashStruct->Height, splashStruct->DrawingContext, 0, 0, SRCCOPY);*/
/*Mod - less intrusive splash*/
BitBlt(dc, 0, 0, splashStruct->Width, splashStruct->Height,
splashStruct->DrawingContext, 0, 0, SRCCOPY);
}
}
void splash::splash_destroy(splash_struct* splashStruct)
{
if (splashStruct)
{
if (splashStruct->WindowHandle)
{
DestroyWindow(splashStruct->WindowHandle);
splashStruct->WindowHandle = nullptr;
if (splashStruct->Palette)
DeleteObject(splashStruct->Palette);
splashStruct->Palette = nullptr;
if (splashStruct->DrawingContext)
{
if (OriginalDcBitmap)
SelectObject(splashStruct->DrawingContext, OriginalDcBitmap);
DeleteDC(splashStruct->DrawingContext);
}
if (splashStruct->Bitmap)
DeleteObject(splashStruct->Bitmap);
}
memory::free(splashStruct);
}
if (HInstance)
{
UnregisterClassA("3DPB_SPLASH_CLASS", HInstance);
HInstance = nullptr;
}
}
void splash::splash_hide(splash_struct* splashStruct)
{
if (splashStruct && splashStruct->WindowHandle)
{
HDC dc = GetDC(splashStruct->WindowHandle);
BitBlt(dc, 0, 0, splashStruct->CenterX, splashStruct->CenterY, dc, 0, 0, 0x42u);
ReleaseDC(splashStruct->WindowHandle, dc);
}
}
LRESULT splash::splash_message_handler(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
tagPAINTSTRUCT Paint{};
switch (Msg)
{
case WM_PAINT:
{
auto splashStruct = reinterpret_cast<splash_struct*>(GetWindowLongPtrA(hWnd, -21));
BeginPaint(hWnd, &Paint);
EndPaint(hWnd, &Paint);
auto dc = GetDC(hWnd);
if (dc)
{
if (splashStruct)
{
BitBlt(dc, 0, 0, 10000, 10000, dc, 0, 0, BLACKNESS);
splash_paint(splashStruct, dc);
}
ReleaseDC(hWnd, dc);
}
break;
}
case WM_ERASEBKGND:
break;
default:
return DefWindowProcA(hWnd, Msg, wParam, lParam);
}
return 0;
}

View File

@ -1,35 +0,0 @@
#pragma once
struct LOGPALETTEx256;
struct splash_struct
{
HWND WindowHandle;
HPALETTE Palette;
HBITMAP Bitmap;
HDC DrawingContext;
int Width;
int Height;
int CenterX;
int CenterY;
char BmpName1[200];
char BmpName2[200];
};
class splash
{
public:
static splash_struct* splash_screen(HINSTANCE hInstance, LPCSTR bmpName1, LPCSTR bmpName2);
static void splash_bitmap_setup(splash_struct* splashStruct);
static HBITMAP load_title_bitmap(HMODULE hModule, HDC hdc, LPCSTR lpName, UINT iStart, int iEnd,
HPALETTE* palettePtr);
static HPALETTE splash_init_palette(LOGPALETTE* plpal);
static void splash_paint(splash_struct* splashStruct, HDC dc);
static void splash_destroy(splash_struct* splashStruct);
static void splash_hide(splash_struct* splashStruct);
static LRESULT __stdcall splash_message_handler(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
private:
static HINSTANCE HInstance;
static HGDIOBJ OriginalDcBitmap;
};

View File

@ -9,13 +9,13 @@
#include "pb.h" #include "pb.h"
#include "Sound.h" #include "Sound.h"
#include "resource.h" #include "resource.h"
#include "splash.h"
const double TargetFps = 60, TargetFrameTime = 1000 / TargetFps; const double TargetFps = 60, TargetFrameTime = 1000 / TargetFps;
HCURSOR winmain::mouse_hsave; HCURSOR winmain::mouse_hsave;
SDL_Window* winmain::MainWindow = nullptr; SDL_Window* winmain::MainWindow = nullptr;
SDL_Renderer* winmain::Renderer = nullptr; SDL_Renderer* winmain::Renderer = nullptr;
ImGuiIO* winmain::ImIO = nullptr;
int winmain::return_value = 0; int winmain::return_value = 0;
int winmain::bQuit = 0; int winmain::bQuit = 0;
@ -36,13 +36,15 @@ bool winmain::restart = false;
gdrv_bitmap8 winmain::gfr_display{}; gdrv_bitmap8 winmain::gfr_display{};
char winmain::DatFileName[300]{}; char winmain::DatFileName[300]{};
bool winmain::ShowAboutDialog = false;
bool winmain::ShowImGuiDemo = false;
uint32_t timeGetTimeAlt() uint32_t timeGetTimeAlt()
{ {
auto now = std::chrono::high_resolution_clock::now(); auto now = std::chrono::high_resolution_clock::now();
auto duration = now.time_since_epoch(); auto duration = now.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
return static_cast<uint32_t>(millis); return static_cast<uint32_t>(millis);
} }
@ -51,7 +53,7 @@ int winmain::WinMain(LPCSTR lpCmdLine)
memory::init(memalloc_failure); memory::init(memalloc_failure);
++memory::critical_allocation; ++memory::critical_allocation;
auto optionsRegPath = pinball::get_rc_string(165, 0); auto optionsRegPath = pinball::get_rc_string(165, 0);
options::path_init(optionsRegPath); options::path_init(optionsRegPath);
--memory::critical_allocation; --memory::critical_allocation;
// SDL init // SDL init
@ -60,7 +62,7 @@ int winmain::WinMain(LPCSTR lpCmdLine)
{ {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not initialize SDL2", SDL_GetError(), nullptr); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not initialize SDL2", SDL_GetError(), nullptr);
return 1; return 1;
} }
pinball::quickFlag = strstr(lpCmdLine, "-quick") != nullptr; pinball::quickFlag = strstr(lpCmdLine, "-quick") != nullptr;
auto regSpaceCadet = pinball::get_rc_string(166, 0); auto regSpaceCadet = pinball::get_rc_string(166, 0);
@ -78,7 +80,6 @@ int winmain::WinMain(LPCSTR lpCmdLine)
pb::FullTiltMode = true; pb::FullTiltMode = true;
} }
auto splash = splash::splash_screen(nullptr, "splash_bitmap", "splash_bitmap");
pinball::FindShiftKeys(); pinball::FindShiftKeys();
options::init_resolution(); options::init_resolution();
@ -87,7 +88,7 @@ int winmain::WinMain(LPCSTR lpCmdLine)
( (
pinball::get_rc_string(38, 0), pinball::get_rc_string(38, 0),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
800, 600, 800, 556,
SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE
); );
MainWindow = window; MainWindow = window;
@ -110,13 +111,21 @@ int winmain::WinMain(LPCSTR lpCmdLine)
return 1; return 1;
} }
// ImGui init
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiSDL::Initialize(renderer, 0, 0);
ImGui::StyleColorsDark();
ImGuiIO& io = ImGui::GetIO();
ImIO = &io;
// ImGui_ImplSDL2_Init is private, we are not actually using ImGui OpenGl backend
ImGui_ImplSDL2_InitForOpenGL(window, nullptr);
// PB init from message handler // PB init from message handler
{ {
RECT rect{};
++memory::critical_allocation; ++memory::critical_allocation;
auto prevCursor = SetCursor(LoadCursorA(nullptr, IDC_WAIT)); options::init(nullptr);
auto voiceCount = options::get_int(nullptr, "Voices", 8); auto voiceCount = options::get_int(nullptr, "Voices", 8);
if (!Sound::Init(voiceCount)) if (!Sound::Init(voiceCount))
options::menu_set(Menu1_Sounds, 0); options::menu_set(Menu1_Sounds, 0);
@ -125,24 +134,17 @@ int winmain::WinMain(LPCSTR lpCmdLine)
if (!pinball::quickFlag && !midi::music_init()) if (!pinball::quickFlag && !midi::music_init())
options::menu_set(Menu1_Music, 0); options::menu_set(Menu1_Music, 0);
if (pb::init()) if (pb::init())
_exit(0); {
SetCursor(prevCursor); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not load game data", "The .dat file is missing", window);
auto changeDisplayFg = options::get_int(nullptr, "Change Display", 1); return 1;
auto menuHandle = GetMenu((HWND)window); }
GetWindowRect(GetDesktopWindow(), &rect); fullscrn::init();
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
pb::window_size(&width, &height);
fullscrn::init(width, height, options::Options.FullScreen, (HWND)window, menuHandle,
changeDisplayFg);
--memory::critical_allocation; --memory::critical_allocation;
} }
auto menuHandle = GetMenu(nullptr);
options::init(menuHandle);
pb::reset_table(); pb::reset_table();
pb::firsttime_setup(); pb::firsttime_setup();
@ -153,15 +155,9 @@ int winmain::WinMain(LPCSTR lpCmdLine)
} }
SDL_ShowWindow(window); SDL_ShowWindow(window);
fullscrn::set_screen_mode(options::Options.FullScreen); fullscrn::set_screen_mode(options::Options.FullScreen);
if (splash) pinball::adjust_priority(options::Options.PriorityAdj);
{
splash::splash_hide(splash);
splash::splash_destroy(splash);
}
pinball::adjust_priority(options::Options.PriorityAdj);
if (strstr(lpCmdLine, "-demo")) if (strstr(lpCmdLine, "-demo"))
pb::toggle_demo(); pb::toggle_demo();
@ -171,10 +167,10 @@ int winmain::WinMain(LPCSTR lpCmdLine)
DWORD updateCounter = 300u, frameCounter = 0, prevTime = 0u; DWORD updateCounter = 300u, frameCounter = 0, prevTime = 0u;
then = timeGetTimeAlt(); then = timeGetTimeAlt();
double sdlTimerResMs = 1000.0 / static_cast<double>(SDL_GetPerformanceFrequency()); double sdlTimerResMs = 1000.0 / static_cast<double>(SDL_GetPerformanceFrequency());
auto frameStart = static_cast<double>(SDL_GetPerformanceCounter()); auto frameStart = static_cast<double>(SDL_GetPerformanceCounter());
while (true) while (true)
{ {
if (!updateCounter) if (!updateCounter)
{ {
updateCounter = 300; updateCounter = 300;
@ -186,7 +182,7 @@ int winmain::WinMain(LPCSTR lpCmdLine)
char buf[60]; char buf[60];
auto elapsedSec = static_cast<float>(curTime - prevTime) * 0.001f; auto elapsedSec = static_cast<float>(curTime - prevTime) * 0.001f;
sprintf_s(buf, "Updates/sec = %02.02f Frames/sec = %02.02f ", sprintf_s(buf, "Updates/sec = %02.02f Frames/sec = %02.02f ",
300.0f / elapsedSec, frameCounter / elapsedSec); 300.0f / elapsedSec, frameCounter / elapsedSec);
SDL_SetWindowTitle(window, buf); SDL_SetWindowTitle(window, buf);
frameCounter = 0; frameCounter = 0;
@ -205,8 +201,8 @@ int winmain::WinMain(LPCSTR lpCmdLine)
redGreen = i1; redGreen = i1;
} }
auto clr = Rgba{ redGreen, redGreen, blue, 0 }; auto clr = Rgba{redGreen, redGreen, blue, 0};
*pltPtr++ = { *reinterpret_cast<uint32_t*>(&clr) }; *pltPtr++ = {*reinterpret_cast<uint32_t*>(&clr)};
} }
gdrv::display_palette(plt); gdrv::display_palette(plt);
free(plt); free(plt);
@ -224,7 +220,7 @@ int winmain::WinMain(LPCSTR lpCmdLine)
prevTime = 0; prevTime = 0;
} }
} }
if (!ProcessWindowMessages() || bQuit) if (!ProcessWindowMessages() || bQuit)
break; break;
@ -278,8 +274,18 @@ int winmain::WinMain(LPCSTR lpCmdLine)
{ {
// Keep track of remainder, limited to one frame time. // Keep track of remainder, limited to one frame time.
frameStart = frameEnd - min(elapsedMs - TargetFrameTime, TargetFrameTime) / sdlTimerResMs; frameStart = frameEnd - min(elapsedMs - TargetFrameTime, TargetFrameTime) / sdlTimerResMs;
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
RenderUi();
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
gdrv::BlitScreen(); gdrv::BlitScreen();
ImGui::Render();
ImGuiSDL::Render(ImGui::GetDrawData());
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
frameCounter++; frameCounter++;
} }
@ -292,8 +298,11 @@ int winmain::WinMain(LPCSTR lpCmdLine)
pb::uninit(); pb::uninit();
Sound::Close(); Sound::Close();
gdrv::uninit(); gdrv::uninit();
ImGuiSDL::Deinitialize();
ImGui_ImplSDL2_Shutdown();
SDL_DestroyRenderer(renderer); SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
ImGui::DestroyContext();
SDL_Quit(); SDL_Quit();
options::path_uninit(); options::path_uninit();
@ -317,392 +326,182 @@ int winmain::WinMain(LPCSTR lpCmdLine)
return return_value; return return_value;
} }
LRESULT CALLBACK winmain::message_handler(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) void winmain::RenderUi()
{ {
int wParamI = static_cast<int>(wParam); // No demo window in release to save space
#ifndef NDEBUG
if (ShowImGuiDemo)
ImGui::ShowDemoWindow();
#endif
if (Msg == iFrostUniqueMsg) if (ImGui::BeginMainMenuBar())
{ {
if (IsIconic(hWnd)) if (ImGui::BeginMenu("Game"))
ShowWindow(hWnd, 9);
SetForegroundWindow(hWnd);
return 0;
}
if (Msg <= WM_ACTIVATEAPP)
{
switch (Msg)
{ {
case WM_ACTIVATEAPP: if (ImGui::MenuItem("New Game", "F2"))
if (wParam)
{ {
activated = 1; new_game();
Sound::Activate();
if (options::Options.Music && !single_step)
midi::play_pb_theme(0);
no_time_loss = 1;
pinball::adjust_priority(options::Options.PriorityAdj);
} }
else if (ImGui::MenuItem("Launch Ball"))
{ {
activated = 0; end_pause();
fullscrn::activate(0); pb::launch_ball();
options::menu_check(Menu1_Full_Screen, 0);
options::Options.FullScreen = 0;
SetThreadPriority(GetCurrentThread(), 0);
Sound::Deactivate();
midi::music_stop();
} }
if (ImGui::MenuItem("Pause/ Resume Game", "F3"))
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_KILLFOCUS:
has_focus = 0;
gdrv::get_focus();
pb::loose_focus();
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_CREATE:
{ {
RECT rect{};
++memory::critical_allocation;
auto prevCursor = SetCursor(LoadCursorA(nullptr, IDC_WAIT));
gdrv::init(0,0);
auto voiceCount = options::get_int(nullptr, "Voices", 8);
if (!Sound::Init(voiceCount))
options::menu_set(Menu1_Sounds, 0);
Sound::Activate();
if (!pinball::quickFlag && !midi::music_init())
options::menu_set(Menu1_Music, 0);
if (pb::init())
_exit(0);
SetCursor(prevCursor);
auto changeDisplayFg = options::get_int(nullptr, "Change Display", 1);
auto menuHandle = GetMenu(hWnd);
GetWindowRect(GetDesktopWindow(), &rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
pb::window_size(&width, &height);
fullscrn::init(width, height, options::Options.FullScreen, hWnd, menuHandle,
changeDisplayFg);
--memory::critical_allocation;
return DefWindowProcA(hWnd, Msg, wParam, lParam);
}
case WM_MOVE:
no_time_loss = 1;
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_SETFOCUS:
has_focus = 1;
no_time_loss = 1;
gdrv::get_focus();
fullscrn::force_redraw();
pb::paint();
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_PAINT:
{
PAINTSTRUCT paint{};
_BeginPaint(hWnd, &paint);
fullscrn::paint();
EndPaint(hWnd, &paint);
break;
}
case WM_CLOSE:
case WM_QUIT:
case WM_DESTROY:
end_pause();
bQuit = 1;
PostQuitMessage(0);
fullscrn::shutdown();
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_ERASEBKGND:
break;
case WM_SIZE:
fullscrn::window_size_changed();
fullscrn::force_redraw();
pb::paint();
return DefWindowProcA(hWnd, Msg, wParam, lParam);
default:
return DefWindowProcA(hWnd, Msg, wParam, lParam);
}
return 0;
}
switch (Msg)
{
case WM_MENUSELECT:
if (lParam)
return DefWindowProcA(hWnd, Msg, wParam, lParam);
if (fullscrn::screen_mode)
fullscrn::set_menu_mode(0);
return 0;
case WM_SYSKEYDOWN:
no_time_loss = 1;
if (fullscrn::screen_mode)
fullscrn::set_menu_mode(1);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_GETMINMAXINFO:
fullscrn::getminmaxinfo((MINMAXINFO*)lParam);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_DISPLAYCHANGE:
options::update_resolution_menu();
if (fullscrn::displaychange())
{
options::Options.FullScreen = 0;
options::menu_check(Menu1_Full_Screen, 0);
}
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_KEYUP:
pb::keyup(wParamI);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_KEYDOWN:
if (!(lParam & 0x40000000))
pb::keydown(wParamI);
switch (wParam)
{
case VK_ESCAPE:
if (options::Options.FullScreen)
options::toggle(0x193u);
SendMessageA(nullptr, 0x112u, 0xF020u, 0);
break;
case VK_F1:
help_introduction(nullptr, hWnd);
break;
case VK_F2:
new_game();
break;
case VK_F3:
pause();
break;
case VK_F4:
options::toggle(0x193u);
break;
case VK_F8:
if (!single_step)
pause(); pause();
options::keyboard(); }
break; ImGui::Separator();
default:
break; if (ImGui::MenuItem("High Scores..."))
}
if (!pb::cheat_mode)
return DefWindowProcA(hWnd, Msg, wParam, lParam);
switch (wParam)
{
case 'H':
DispGRhistory = 1;
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case 'Y':
SetWindowTextA(hWnd, "Pinball");
DispFrameRate = DispFrameRate == 0;
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case VK_F1:
pb::frame(10);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case VK_F15:
single_step = single_step == 0;
if (single_step == 0)
no_time_loss = 1;
return DefWindowProcA(hWnd, Msg, wParam, lParam);
default:
return DefWindowProcA(hWnd, Msg, wParam, lParam);
}
case WM_SYSCOMMAND:
switch (wParam & 0xFFF0)
{
case SC_MOVE:
if (fullscrn::screen_mode)
return 0;
break;
case SC_MINIMIZE:
if (!single_step)
pause();
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case SC_SCREENSAVE:
fullscrn::activate(0);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
default: break;
}
end_pause();
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_INITMENU:
no_time_loss = 1;
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_COMMAND:
no_time_loss = 1;
switch (wParam)
{
case Menu1_Launch_Ball:
end_pause();
pb::launch_ball();
break;
case Menu1_Pause_Resume_Game:
pause();
break;
case Menu1_Demo:
end_pause();
pb::toggle_demo();
break;
case Menu1_Select_Table:
{ {
if (!single_step) if (!single_step)
pause(); pause();
auto tmpBuf = memory::allocate(0x1F4u); pb::high_scores();
if (tmpBuf)
{
char cmdLine[0x1F4u];
options::get_string(nullptr, "Shell Exe", tmpBuf, "", 500);
auto iHwnd = reinterpret_cast<size_t>(nullptr);
sprintf_s(
cmdLine,
"%s %s%zX %s%zX",
tmpBuf,
"select=",
iHwnd,
"confirm=",
iHwnd * iHwnd * iHwnd * iHwnd * iHwnd * iHwnd * iHwnd);
if (static_cast<int>(WinExec(cmdLine, 5u)) < 32)
{
auto caption = pinball::get_rc_string(170, 0);
auto text = pinball::get_rc_string(171, 0);
MessageBoxA(nullptr, text, caption, 0x2010u);
}
memory::free(tmpBuf);
}
break;
} }
case Menu1_1Player: if (ImGui::MenuItem("Demo"))
case Menu1_2Players:
case Menu1_3Players:
case Menu1_4Players:
options::toggle(wParamI);
new_game();
break;
case Menu1_MaximumResolution:
case Menu1_640x480:
case Menu1_800x600:
case Menu1_1024x768:
case Menu1_WindowUniformScale:
options::toggle(wParamI);
break;
case Menu1_Help_Topics:
if (!single_step)
pause();
help_introduction(nullptr, hWnd);
break;
case 106: // End game button?
pb::end_game();
break;
case Menu1_Full_Screen:
case Menu1_Sounds:
case Menu1_Music:
if (!single_step)
pause();
options::toggle(wParamI);
break;
case Menu1_Player_Controls:
case 204: // Second controls button?
if (!single_step)
pause();
options::keyboard();
break;
case Menu1_Exit:
PostMessageA(hWnd, WM_QUIT, 0, 0);
break;
case Menu1_New_Game:
new_game();
break;
case Menu1_About_Pinball:
if (!single_step)
pause();
a_dialog(nullptr, hWnd);
break;
case Menu1_High_Scores:
if (!single_step)
pause();
pb::high_scores();
break;
case 1: // Unknown button
midi::restart_midi_seq(lParam);
break;
default:
break;
}
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_LBUTTONDOWN:
if (pb::game_mode)
{
if (pb::cheat_mode)
{ {
mouse_down = 1; end_pause();
mouse_hsave = SetCursor(nullptr); pb::toggle_demo();
auto mouseXY = fullscrn::convert_mouse_pos(static_cast<unsigned>(lParam));
last_mouse_x = mouseXY & 0xffFFu;
last_mouse_y = mouseXY >> 16;
SetCapture(hWnd);
} }
else if (ImGui::MenuItem("Exit"))
pb::keydown(options::Options.LeftFlipperKey); {
SDL_Event event{SDL_QUIT};
SDL_PushEvent(&event);
}
ImGui::EndMenu();
}
return DefWindowProcA(hWnd, Msg, wParam, lParam); if (ImGui::BeginMenu("Options"))
}
break;
case WM_LBUTTONUP:
if (mouse_down)
{ {
mouse_down = 0; if (ImGui::MenuItem("Full Screen", "F4", options::Options.FullScreen))
SetCursor(mouse_hsave); {
ReleaseCapture(); options::toggle(Menu1_Full_Screen);
}
if (ImGui::BeginMenu("Select Players"))
{
if (ImGui::MenuItem("1 Player", nullptr, options::Options.Players == 1))
{
options::toggle(Menu1_1Player);
new_game();
}
if (ImGui::MenuItem("2 Players", nullptr, options::Options.Players == 2))
{
options::toggle(Menu1_2Players);
new_game();
}
if (ImGui::MenuItem("3 Players", nullptr, options::Options.Players == 3))
{
options::toggle(Menu1_3Players);
new_game();
}
if (ImGui::MenuItem("4 Players", nullptr, options::Options.Players == 4))
{
options::toggle(Menu1_4Players);
new_game();
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::MenuItem("Sound", nullptr, options::Options.Sounds))
{
options::toggle(Menu1_Sounds);
}
if (ImGui::MenuItem("Music", nullptr, options::Options.Music))
{
options::toggle(Menu1_Music);
}
ImGui::Separator();
if (ImGui::MenuItem("Player Controls...", "F8"))
{
if (!single_step)
pause();
options::keyboard();
}
if (ImGui::BeginMenu("Table Resolution"))
{
if (ImGui::MenuItem("Not implemented"))
{
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Window"))
{
if (ImGui::MenuItem("Uniform Scaling", nullptr, options::Options.UniformScaling))
{
options::toggle(Menu1_WindowUniformScale);
}
ImGui::EndMenu();
}
ImGui::EndMenu();
} }
if (!pb::cheat_mode)
pb::keyup(options::Options.LeftFlipperKey); if (ImGui::BeginMenu("Help"))
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_RBUTTONDOWN:
if (!pb::cheat_mode)
pb::keydown(options::Options.RightFlipperKey);
if (pb::game_mode)
return DefWindowProcA(hWnd, Msg, wParam, lParam);
break;
case WM_RBUTTONUP:
if (!pb::cheat_mode)
pb::keyup(options::Options.RightFlipperKey);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_MBUTTONDOWN:
pb::keydown(options::Options.PlungerKey);
if (pb::game_mode)
return DefWindowProcA(hWnd, Msg, wParam, lParam);
break;
case WM_MBUTTONUP:
pb::keyup(options::Options.PlungerKey);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case WM_POWERBROADCAST:
if (wParam == 4 && options::Options.FullScreen)
{ {
options::Options.FullScreen = 0; #ifndef NDEBUG
options::menu_check(Menu1_Full_Screen, 0); if (ImGui::MenuItem("ImGui Demo"))
fullscrn::set_screen_mode(options::Options.FullScreen); {
ShowImGuiDemo ^= true;
}
#endif
if (ImGui::MenuItem("Help Topics", "F1"))
{
if (!single_step)
pause();
help_introduction(nullptr, (HWND)MainWindow);
}
ImGui::Separator();
if (ImGui::MenuItem("About Pinball"))
{
if (!single_step)
pause();
ShowAboutDialog = true;
}
ImGui::EndMenu();
} }
return DefWindowProcA(hWnd, Msg, wParam, lParam); ImGui::EndMainMenuBar();
case WM_PALETTECHANGED:
InvalidateRect(hWnd, nullptr, 0);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
case MM_MCINOTIFY:
if (wParam == 1)
midi::restart_midi_seq(lParam);
return DefWindowProcA(hWnd, Msg, wParam, lParam);
default:
return DefWindowProcA(hWnd, Msg, wParam, lParam);
} }
pb::mode_countdown(-1); a_dialog();
return DefWindowProcA(hWnd, Msg, wParam, lParam); high_score::RenderHighScoreDialog();
} }
int winmain::event_handler(const SDL_Event* event) int winmain::event_handler(const SDL_Event* event)
{ {
ImGui_ImplSDL2_ProcessEvent(event);
if (ImIO->WantCaptureMouse)
{
if (mouse_down)
{
mouse_down = 0;
SDL_ShowCursor(SDL_ENABLE);
SDL_SetWindowGrab(MainWindow, SDL_FALSE);
}
switch (event->type)
{
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEWHEEL:
return 1;
default: ;
}
}
if (ImIO->WantCaptureKeyboard)
{
switch (event->type)
{
case SDL_KEYDOWN:
case SDL_KEYUP:
return 1;
default: ;
}
}
switch (event->type) switch (event->type)
{ {
case SDL_QUIT: case SDL_QUIT:
@ -808,7 +607,7 @@ int winmain::event_handler(const SDL_Event* event)
case SDL_BUTTON_LEFT: case SDL_BUTTON_LEFT:
if (mouse_down) if (mouse_down)
{ {
mouse_down = 0; mouse_down = 0;
SDL_ShowCursor(SDL_ENABLE); SDL_ShowCursor(SDL_ENABLE);
SDL_SetWindowGrab(MainWindow, SDL_FALSE); SDL_SetWindowGrab(MainWindow, SDL_FALSE);
} }
@ -841,7 +640,6 @@ int winmain::event_handler(const SDL_Event* event)
pinball::adjust_priority(options::Options.PriorityAdj); pinball::adjust_priority(options::Options.PriorityAdj);
has_focus = 1; has_focus = 1;
gdrv::get_focus(); gdrv::get_focus();
fullscrn::force_redraw();
pb::paint(); pb::paint();
break; break;
case SDL_WINDOWEVENT_FOCUS_LOST: case SDL_WINDOWEVENT_FOCUS_LOST:
@ -857,9 +655,9 @@ int winmain::event_handler(const SDL_Event* event)
gdrv::get_focus(); gdrv::get_focus();
pb::loose_focus(); pb::loose_focus();
break; break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_RESIZED: case SDL_WINDOWEVENT_RESIZED:
fullscrn::window_size_changed(); fullscrn::window_size_changed();
fullscrn::force_redraw();
break; break;
default: ; default: ;
} }
@ -899,39 +697,33 @@ void winmain::memalloc_failure()
_exit(1); _exit(1);
} }
HDC winmain::_BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint) void winmain::a_dialog()
{ {
HDC context = BeginPaint(hWnd, lpPaint); if (ShowAboutDialog == true)
if (hWnd && GetLayout(context) & 1) {
SetLayout(context, 0); ShowAboutDialog = false;
return context; ImGui::OpenPopup("About");
} }
HDC winmain::_GetDC(HWND hWnd) bool unused_open = true;
{ if (ImGui::BeginPopupModal("About", &unused_open, ImGuiWindowFlags_AlwaysAutoResize))
HDC context = GetDC(hWnd); {
if (hWnd && GetLayout(context) & 1) ImGui::TextUnformatted("3D Pinball for Windows - Space Cadet");
SetLayout(context, 0); ImGui::TextUnformatted("Decompiled -> Ported to SDL");
return context; ImGui::Separator();
}
int winmain::a_dialog(HINSTANCE hInstance, HWND hWnd) if (ImGui::Button("Ok"))
{ {
char appName[100]; ImGui::CloseCurrentPopup();
char szOtherStuff[100]; }
ImGui::EndPopup();
lstrcpyA(appName, pinball::get_rc_string(38, 0)); }
lstrcpyA(szOtherStuff, pinball::get_rc_string(102, 0));
auto icon = LoadIconA(hInstance, "ICON_1");
return ShellAboutA(hWnd, appName, szOtherStuff, icon);
} }
void winmain::end_pause() void winmain::end_pause()
{ {
if (single_step) if (single_step)
{ {
if (fullscrn::screen_mode)
fullscrn::set_menu_mode(0);
pb::pause_continue(); pb::pause_continue();
no_time_loss = 1; no_time_loss = 1;
} }
@ -947,13 +739,6 @@ void winmain::new_game()
void winmain::pause() void winmain::pause()
{ {
if (fullscrn::screen_mode)
{
if (single_step)
fullscrn::set_menu_mode(0);
else
fullscrn::set_menu_mode(1);
}
pb::pause_continue(); pb::pause_continue();
no_time_loss = 1; no_time_loss = 1;
} }

View File

@ -8,14 +8,13 @@ public:
static int single_step; static int single_step;
static SDL_Window* MainWindow; static SDL_Window* MainWindow;
static SDL_Renderer* Renderer; static SDL_Renderer* Renderer;
static ImGuiIO* ImIO;
static int WinMain(LPCSTR lpCmdLine); static int WinMain(LPCSTR lpCmdLine);
static LRESULT CALLBACK message_handler(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
static int event_handler(const SDL_Event* event); static int event_handler(const SDL_Event* event);
static void memalloc_failure(); static void memalloc_failure();
static int ProcessWindowMessages(); static int ProcessWindowMessages();
static HDC _GetDC(HWND hWnd); static void a_dialog();
static int a_dialog(HINSTANCE hInstance, HWND hWnd);
static void end_pause(); static void end_pause();
static void new_game(); static void new_game();
static void pause(); static void pause();
@ -29,6 +28,8 @@ private:
static gdrv_bitmap8 gfr_display; static gdrv_bitmap8 gfr_display;
static HCURSOR mouse_hsave; static HCURSOR mouse_hsave;
static bool restart; static bool restart;
static bool ShowAboutDialog;
static HDC _BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint); static bool ShowImGuiDemo;
static void RenderUi();
}; };