overlay: refactor overlay layering (#739)

## Link to GitHub Issue or related Pull Request, if one exists
n/a

## Description of change

Create three distinct layers for the overlay:

1. Bottommost persistent layer - non-interactable layer that is always
on. This was only for notifications, but now the FPS widget lives here
as well.
2. Overlay windows layer - most interactable windows go here.
3. Topmost main menu - this is reserved for the main menu (escape key)
and this is a modal dialog that occludes the layers below.

Why? 

- `toggle overlay` behavior with FPS widget *also* toggling on/off was a
bit confusing (now they're two separate keys)
- FPS widget is popular, but it caused the entire overlay to be active,
which affects how input is handled
- the main menu being a standalone window was a little awkward (now it's
a modal)

## Testing
This commit is contained in:
bicarus
2026-06-07 17:23:08 -07:00
committed by GitHub
parent bb87aa2944
commit 100caa0f5c
16 changed files with 348 additions and 102 deletions
+20 -6
View File
@@ -29,6 +29,14 @@ namespace overlay {
SOFTWARE,
};
// corner of the screen the FPS / clock / timer window is anchored to
enum class FpsLocation {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
};
// settings
extern bool ENABLED;
extern bool AUTO_SHOW_FPS;
@@ -37,7 +45,7 @@ namespace overlay {
extern bool AUTO_SHOW_KEYPAD_P1;
extern bool AUTO_SHOW_KEYPAD_P2;
extern bool USE_WM_CHAR_FOR_IMGUI_CHAR_INPUT;
extern bool FPS_SHOULD_FLIP;
extern FpsLocation FPS_LOCATION;
extern bool SHOW_DEBUG_LOG_WINDOW;
extern std::optional<uint32_t> UI_SCALE_PERCENT;
@@ -57,10 +65,8 @@ namespace overlay {
D3DDEVICE_CREATION_PARAMETERS creation_parameters {};
D3DADAPTER_IDENTIFIER9 adapter_identifier {};
bool hotkeys_enable = true;
// windows
Window *window_fps = nullptr;
Window *window_iopanel = nullptr;
Window *window_config = nullptr;
Window *window_keypad1 = nullptr;
@@ -72,6 +78,15 @@ namespace overlay {
Window *window_sub = nullptr;
Window *window_log = nullptr;
// not part of `windows`: drawn/updated on the persistent layer (like
// notifications), independent of the overlay's active state and input gates.
std::unique_ptr<Window> window_fps;
// not part of `windows`: the main menu / launcher. owned and drawn
// separately from the overlay window layer; it drives the overlay's
// active state while shown so its buttons still receive input.
std::unique_ptr<Window> window_main_menu;
explicit SpiceOverlay(HWND hWnd, IDirect3D9 *d3d, IDirect3DDevice9 *device);
#ifdef SPICE_D3D11
explicit SpiceOverlay(HWND hWnd, ID3D11Device *d3d11_device,
@@ -87,7 +102,7 @@ namespace overlay {
// after render() when the frame is being presented. In-game overlay draws in render().
void d3d9_render_draw(bool force_submit = false);
void update();
void toggle_active(bool overlay_key = false);
void toggle_active();
void show_main_menu();
void set_active(bool active);
bool get_active();
@@ -182,13 +197,12 @@ namespace overlay {
std::vector<std::unique_ptr<Window>> windows;
Window *window_main_menu = nullptr;
std::function<bool(LONG *, LONG *)> subscreen_mouse_handler = nullptr;
bool active = false;
bool toggle_down = false;
bool main_menu_down = false;
bool fps_down = false;
bool hotkey_toggle = false;
bool hotkey_toggle_last = false;