1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-10 18:07:59 +00:00
putty-source/windows/utils/shinydialogbox.c

112 lines
3.6 KiB
C
Raw Normal View History

/*
* PuTTY's own reimplementation of DialogBox() and EndDialog() which
* provide extra capabilities.
*
* Originally introduced in 2003 to work around a problem with our
* message loops, in which keystrokes pressed in the 'Change Settings'
* dialog in mid-session would _also_ be delivered to the main
* terminal window.
*
* But once we had our own wrapper it was convenient to put further
* things into it. In particular, this system allows you to provide a
* context pointer at setup time that's easy to retrieve from the
* window procedure.
*/
#include "putty.h"
struct ShinyDialogBoxState {
bool ended;
int result;
ShinyDlgProc proc;
void *ctx;
};
/*
* For use in re-entrant calls to the dialog procedure from
* CreateDialog itself, temporarily store the state pointer that we
* won't store in the usual window-memory slot until later.
*
* I don't _intend_ that this system will need to be used in multiple
* threads at all, let alone concurrently, but just in case, declaring
* sdb_tempstate as thread-local will protect against that possibility.
*/
static __declspec(thread) struct ShinyDialogBoxState *sdb_tempstate;
static inline struct ShinyDialogBoxState *ShinyDialogGetState(HWND hwnd)
{
if (sdb_tempstate)
return sdb_tempstate;
return (struct ShinyDialogBoxState *)GetWindowLongPtr(
hwnd, DLGWINDOWEXTRA);
}
static INT_PTR CALLBACK ShinyRealDlgProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
struct ShinyDialogBoxState *state = ShinyDialogGetState(hwnd);
return state->proc(hwnd, msg, wParam, lParam, state->ctx);
}
int ShinyDialogBox(HINSTANCE hinst, LPCTSTR tmpl, const char *winclass,
HWND hwndparent, ShinyDlgProc proc, void *ctx)
{
/*
* Register the window class ourselves in such a way that we
* allocate an extra word of window memory to store the state
* pointer.
*
* It would be nice in principle to load the dialog template
* resource and dig the class name out of it. But DLGTEMPLATEEX is
* a nasty variable-layout structure not declared conveniently in
* a header file, so I think that's too much effort. Callers of
* this function will just have to provide the right window class
* name to match their template resource via the 'winclass'
* parameter.
*/
WNDCLASS wc;
wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW;
wc.lpfnWndProc = DefDlgProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = DLGWINDOWEXTRA + sizeof(LONG_PTR);
wc.hInstance = hinst;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1);
wc.lpszMenuName = NULL;
wc.lpszClassName = winclass;
RegisterClass(&wc);
struct ShinyDialogBoxState state[1];
state->ended = false;
state->proc = proc;
state->ctx = ctx;
sdb_tempstate = state;
HWND hwnd = CreateDialog(hinst, tmpl, hwndparent, ShinyRealDlgProc);
SetWindowLongPtr(hwnd, DLGWINDOWEXTRA, (LONG_PTR)state);
sdb_tempstate = NULL;
MSG msg;
int gm;
while ((gm = GetMessage(&msg, NULL, 0, 0)) > 0) {
if (!state->ended && !IsDialogMessage(hwnd, &msg))
DispatchMessage(&msg);
if (state->ended)
break;
}
if (gm == 0)
PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */
DestroyWindow(hwnd);
return state->result;
}
void ShinyEndDialog(HWND hwnd, int ret)
{
struct ShinyDialogBoxState *state = ShinyDialogGetState(hwnd);
state->result = ret;
state->ended = true;
}