Merge pull request #245 from mkilgore/dialog-fixes
Improve dialog window association and _InputBox$ support on Windows
|
@ -59,6 +59,7 @@
|
|||
//#include <math.h> //<-causes overloading abs conflicts in Windows
|
||||
# include <cmath>
|
||||
# endif
|
||||
# include <stdint.h>
|
||||
# include <errno.h>
|
||||
# include <fcntl.h>
|
||||
# include <fstream>
|
||||
|
@ -82,7 +83,6 @@
|
|||
# else
|
||||
|
||||
# include <pthread.h>
|
||||
# include <stdint.h>
|
||||
# include <stdlib.h>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
|
|
|
@ -7,16 +7,6 @@
|
|||
* Should this be adapted to check for each type before defining?
|
||||
*/
|
||||
#ifndef QB64_OS_H_NO_TYPES
|
||||
# ifdef QB64_WINDOWS
|
||||
# define uint64 unsigned __int64
|
||||
# define uint32 unsigned __int32
|
||||
# define uint16 unsigned __int16
|
||||
# define uint8 unsigned __int8
|
||||
# define int64 __int64
|
||||
# define int32 __int32
|
||||
# define int16 __int16
|
||||
# define int8 __int8
|
||||
# else
|
||||
# define int64 int64_t
|
||||
# define int32 int32_t
|
||||
# define int16 int16_t
|
||||
|
@ -25,7 +15,6 @@
|
|||
# define uint32 uint32_t
|
||||
# define uint16 uint16_t
|
||||
# define uint8 uint8_t
|
||||
# endif
|
||||
|
||||
# define ptrszint intptr_t
|
||||
# define uptrszint uintptr_t
|
||||
|
|
|
@ -918,6 +918,120 @@ static int fileExists(char const * aFilePathAndName)
|
|||
}
|
||||
}
|
||||
|
||||
// Checks that it's the top-level Window for the process
|
||||
static BOOL isMainWindow(HWND handle)
|
||||
{
|
||||
return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
|
||||
}
|
||||
|
||||
struct handle_data {
|
||||
unsigned long process_id;
|
||||
HWND window_handle;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK enumCallback(HWND handle, LPARAM lParam)
|
||||
{
|
||||
struct handle_data *data = (struct handle_data*)lParam;
|
||||
unsigned long process_id = 0;
|
||||
|
||||
GetWindowThreadProcessId(handle, &process_id);
|
||||
|
||||
if (data->process_id != process_id || !isMainWindow(handle))
|
||||
return TRUE; // Keep going to the next window
|
||||
|
||||
data->window_handle = handle;
|
||||
return FALSE; // End the enumeration
|
||||
}
|
||||
|
||||
static HWND TryGetMainWindow()
|
||||
{
|
||||
struct handle_data data = {
|
||||
.process_id = GetCurrentProcessId(),
|
||||
.window_handle = 0,
|
||||
};
|
||||
|
||||
EnumWindows(enumCallback, (LPARAM)&data);
|
||||
|
||||
return data.window_handle;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK hiddenWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HWND CreateHiddenWindow()
|
||||
{
|
||||
WNDCLASSEX wndclass = {
|
||||
sizeof(WNDCLASSEX),
|
||||
CS_DBLCLKS,
|
||||
hiddenWindowProc,
|
||||
0,
|
||||
0,
|
||||
GetModuleHandle(0),
|
||||
LoadIcon(0, IDI_APPLICATION),
|
||||
LoadCursor(0, IDC_ARROW),
|
||||
(HBRUSH)(COLOR_WINDOW + 1),
|
||||
0,
|
||||
"hiddenWindowClass",
|
||||
LoadIcon(0, IDI_APPLICATION)
|
||||
};
|
||||
|
||||
if (!RegisterClassEx(&wndclass))
|
||||
return 0;
|
||||
|
||||
return CreateWindowEx(
|
||||
0,
|
||||
"hiddenWindowClass",
|
||||
"hiddenWindow",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
0,
|
||||
0,
|
||||
GetModuleHandle(NULL),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
static HWND windowForDialogs = 0;
|
||||
|
||||
// Returns a window handle sutable for displaying dialogs and notifications
|
||||
//
|
||||
// We attempt to find a window associated with the program, if none exists then
|
||||
// we'll create a hidden one.
|
||||
static HWND GetDialogWindow()
|
||||
{
|
||||
if (windowForDialogs)
|
||||
return windowForDialogs;
|
||||
|
||||
// First see if our process has a window already
|
||||
windowForDialogs = TryGetMainWindow();
|
||||
if (windowForDialogs)
|
||||
return windowForDialogs;
|
||||
|
||||
// Check if we have an associated console
|
||||
windowForDialogs = GetConsoleWindow();
|
||||
if (windowForDialogs)
|
||||
return windowForDialogs;
|
||||
|
||||
// No sutable window could be found, create a hidden one for us to use
|
||||
windowForDialogs = CreateHiddenWindow();
|
||||
return windowForDialogs;
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
/* source and destination can be the same or ovelap*/
|
||||
|
@ -1036,7 +1150,7 @@ int tinyfd_messageBoxW(
|
|||
|
||||
aCode += MB_TOPMOST;
|
||||
|
||||
lBoxReturnValue = MessageBoxW(GetForegroundWindow(), aMessage, aTitle, aCode);
|
||||
lBoxReturnValue = MessageBoxW(GetDialogWindow(), aMessage, aTitle, aCode);
|
||||
|
||||
if ( (lBoxReturnValue == IDNO) && (aDialogType && !wcscmp(L"yesnocancel", aDialogType)) )
|
||||
{
|
||||
|
@ -1052,64 +1166,12 @@ int tinyfd_messageBoxW(
|
|||
}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK hiddenNotifyWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HWND hiddenWindow = NULL;
|
||||
static HICON notifyIcon = NULL;
|
||||
|
||||
// Creates a hidden window to tie the Notifications too
|
||||
static void setupHiddenWindowHandles()
|
||||
static void setupNotifyIconHandle()
|
||||
{
|
||||
if (hiddenWindow || notifyIcon)
|
||||
return;
|
||||
|
||||
WNDCLASSEX wndclass = {
|
||||
sizeof(WNDCLASSEX),
|
||||
CS_DBLCLKS,
|
||||
hiddenNotifyWindowProc,
|
||||
0,
|
||||
0,
|
||||
GetModuleHandle(0),
|
||||
LoadIcon(0,IDI_APPLICATION),
|
||||
LoadCursor(0,IDC_ARROW),
|
||||
(HBRUSH)(COLOR_WINDOW + 1),
|
||||
0,
|
||||
"notifyIconClass",
|
||||
LoadIcon(0,IDI_APPLICATION)
|
||||
};
|
||||
|
||||
if (!RegisterClassEx(&wndclass))
|
||||
return;
|
||||
|
||||
hiddenWindow = CreateWindowEx(
|
||||
0,
|
||||
"notifyIconClass",
|
||||
"title",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
0,
|
||||
0,
|
||||
GetModuleHandle(NULL),
|
||||
0
|
||||
);
|
||||
|
||||
if (!hiddenWindow)
|
||||
if (notifyIcon)
|
||||
return;
|
||||
|
||||
// Attempt to load 32x32 image
|
||||
|
@ -1131,7 +1193,7 @@ static void createWinNotificationEntry()
|
|||
memset(¬ificationData, 0, sizeof(notificationData));
|
||||
|
||||
notificationData.cbSize = sizeof(notificationData);
|
||||
notificationData.hWnd = hiddenWindow;
|
||||
notificationData.hWnd = GetDialogWindow();
|
||||
notificationData.uID = 0;
|
||||
notificationData.hIcon = notifyIcon;
|
||||
|
||||
|
@ -1146,7 +1208,7 @@ static void sendWinNotification(const wchar_t *title, const wchar_t *message, co
|
|||
NOTIFYICONDATAW notificationData;
|
||||
memset(¬ificationData, 0, sizeof(notificationData));
|
||||
notificationData.cbSize = sizeof(notificationData);
|
||||
notificationData.hWnd = hiddenWindow;
|
||||
notificationData.hWnd = GetDialogWindow();
|
||||
notificationData.uID = 0;
|
||||
notificationData.hIcon = notifyIcon;
|
||||
|
||||
|
@ -1175,10 +1237,10 @@ int tinyfd_notifyPopupW(
|
|||
{
|
||||
if (aTitle && !wcscmp(aTitle, L"tinyfd_query")) { strcpy(tinyfd_response, "windows_wchar"); return 1; }
|
||||
|
||||
setupHiddenWindowHandles();
|
||||
setupNotifyIconHandle();
|
||||
|
||||
// Can't send notification if the handles aren't setup
|
||||
if (!hiddenWindow || !notifyIcon)
|
||||
if (!notifyIcon)
|
||||
return 0;
|
||||
|
||||
createWinNotificationEntry();
|
||||
|
@ -1228,7 +1290,7 @@ static BOOL CALLBACK dialogBoxCallback(HWND hwndDlg, UINT message, WPARAM wParam
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *title, const wchar_t *lpszMessage, const wchar_t *defaultText)
|
||||
static LRESULT displayInputBox(HINSTANCE hinst, HWND hwndOwner, const wchar_t *title, const wchar_t *lpszMessage, const wchar_t *defaultText)
|
||||
{
|
||||
HGLOBAL hgbl;
|
||||
LPDLGTEMPLATE lpdt;
|
||||
|
@ -1252,7 +1314,7 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
|
|||
|
||||
// Define a dialog box.
|
||||
|
||||
lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT;
|
||||
lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT | DS_SYSMODAL;
|
||||
lpdt->cdit = 4; // Number of controls
|
||||
lpdt->x = 0; lpdt->y = 0;
|
||||
lpdt->cx = 200; lpdt->cy = 70;
|
||||
|
@ -1271,6 +1333,27 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
|
|||
for (lpwsz = (LPWSTR)lpw; ((*lpwsz++) = *font++);); // Font name
|
||||
lpw = (LPWORD)lpwsz;
|
||||
|
||||
//-----------------------
|
||||
// Define a edit text control.
|
||||
//-----------------------
|
||||
lpw = lpwAlignDWORD(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
|
||||
lpdit = (LPDLGITEMTEMPLATE)lpw;
|
||||
lpdit->x = 5; lpdit->y = 53;
|
||||
lpdit->cx = 190; lpdit->cy = 12;
|
||||
lpdit->id = ID_TEXT; // Text identifier
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL | ES_LEFT;
|
||||
|
||||
if (wcslen(defaultText) == 0)
|
||||
lpdit->style |= ES_PASSWORD;
|
||||
|
||||
lpw = (LPWORD)(lpdit + 1);
|
||||
*lpw++ = 0xFFFF;
|
||||
*lpw++ = 0x0081; // Edit class
|
||||
|
||||
for (lpwsz = (LPWSTR)lpw; ((*lpwsz++) = *defaultText++););
|
||||
lpw = (LPWORD)lpwsz;
|
||||
*lpw++ = 0; // No creation data
|
||||
|
||||
//-----------------------
|
||||
// Define an OK button.
|
||||
//-----------------------
|
||||
|
@ -1279,7 +1362,7 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
|
|||
lpdit->x = 160; lpdit->y = 6;
|
||||
lpdit->cx = 35; lpdit->cy = 12;
|
||||
lpdit->id = IDOK; // OK button identifier
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON;
|
||||
|
||||
lpw = (LPWORD)(lpdit + 1);
|
||||
*lpw++ = 0xFFFF;
|
||||
|
@ -1298,7 +1381,7 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
|
|||
lpdit->x = 160; lpdit->y = 21;
|
||||
lpdit->cx = 35; lpdit->cy = 12;
|
||||
lpdit->id = IDCANCEL; // Cancel button identifier
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON;
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON;
|
||||
|
||||
lpw = (LPWORD)(lpdit + 1);
|
||||
*lpw++ = 0xFFFF;
|
||||
|
@ -1309,27 +1392,6 @@ static LRESULT DisplayMyMessage(HINSTANCE hinst, HWND hwndOwner, const wchar_t *
|
|||
lpw += nchar;
|
||||
*lpw++ = 0; // No creation data
|
||||
|
||||
//-----------------------
|
||||
// Define a edit text control.
|
||||
//-----------------------
|
||||
lpw = lpwAlignDWORD(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
|
||||
lpdit = (LPDLGITEMTEMPLATE)lpw;
|
||||
lpdit->x = 5; lpdit->y = 53;
|
||||
lpdit->cx = 190; lpdit->cy = 12;
|
||||
lpdit->id = ID_TEXT; // Text identifier
|
||||
lpdit->style = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_LEFT;
|
||||
|
||||
if (wcslen(defaultText) == 0)
|
||||
lpdit->style |= ES_PASSWORD;
|
||||
|
||||
lpw = (LPWORD)(lpdit + 1);
|
||||
*lpw++ = 0xFFFF;
|
||||
*lpw++ = 0x0081; // Edit class
|
||||
|
||||
for (lpwsz = (LPWSTR)lpw; ((*lpwsz++) = *defaultText++););
|
||||
lpw = (LPWORD)lpwsz;
|
||||
*lpw++ = 0; // No creation data
|
||||
|
||||
//-----------------------
|
||||
// Define a static text control.
|
||||
//-----------------------
|
||||
|
@ -1373,8 +1435,7 @@ wchar_t * tinyfd_inputBoxW(
|
|||
aMessage = aMessage? aMessage: L"";
|
||||
aDefaultInput = aDefaultInput? aDefaultInput: L"";
|
||||
|
||||
setupHiddenWindowHandles();
|
||||
DisplayMyMessage(GetModuleHandle(NULL), hiddenWindow, aTitle, aMessage, aDefaultInput);
|
||||
displayInputBox(GetModuleHandle(NULL), GetDialogWindow(), aTitle, aMessage, aDefaultInput);
|
||||
|
||||
mbstowcs(lBuff, dialogContents, MAX_PATH_OR_CMD);
|
||||
return lBuff;
|
||||
|
@ -1436,7 +1497,7 @@ wchar_t * tinyfd_saveFileDialogW(
|
|||
}
|
||||
|
||||
ofn.lStructSize = sizeof(OPENFILENAMEW);
|
||||
ofn.hwndOwner = GetForegroundWindow();
|
||||
ofn.hwndOwner = GetDialogWindow();
|
||||
ofn.hInstance = 0;
|
||||
ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL;
|
||||
ofn.lpstrCustomFilter = NULL;
|
||||
|
@ -1552,7 +1613,7 @@ wchar_t * tinyfd_openFileDialogW(
|
|||
}
|
||||
|
||||
ofn.lStructSize = sizeof(OPENFILENAME);
|
||||
ofn.hwndOwner = GetForegroundWindow();
|
||||
ofn.hwndOwner = GetDialogWindow();
|
||||
ofn.hInstance = 0;
|
||||
ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL;
|
||||
ofn.lpstrCustomFilter = NULL;
|
||||
|
@ -1672,7 +1733,7 @@ wchar_t * tinyfd_selectFolderDialogW(
|
|||
|
||||
lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||
|
||||
bInfo.hwndOwner = GetForegroundWindow();
|
||||
bInfo.hwndOwner = GetDialogWindow();
|
||||
bInfo.pidlRoot = NULL;
|
||||
bInfo.pszDisplayName = lBuff;
|
||||
bInfo.lpszTitle = aTitle && wcslen(aTitle) ? aTitle : NULL;
|
||||
|
@ -1734,7 +1795,7 @@ wchar_t * tinyfd_colorChooserW(
|
|||
|
||||
/* we can't use aTitle */
|
||||
cc.lStructSize = sizeof(CHOOSECOLOR);
|
||||
cc.hwndOwner = GetForegroundWindow();
|
||||
cc.hwndOwner = GetDialogWindow();
|
||||
cc.hInstance = NULL;
|
||||
cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]);
|
||||
cc.lpCustColors = crCustColors;
|
||||
|
|
|
@ -13,7 +13,7 @@ _Dest test1&
|
|||
Circle (64, 25), 25, 7
|
||||
Paint (64, 25), TILE$, 7
|
||||
|
||||
AssertImage test1&, "tile_border_black_bg.bmp"
|
||||
AssertImage test1&, "tile_border_black_bg.png"
|
||||
System
|
||||
|
||||
'$include:'../utilities/imageassert.bm'
|
||||
|
|
Before Width: | Height: | Size: 19 KiB |
BIN
tests/compile_tests/paint/tile_border_black_bg.png
Normal file
After Width: | Height: | Size: 421 B |
|
@ -17,7 +17,7 @@ Line (0, 0)-(127, 49), 7, BF
|
|||
Circle (64, 25), 25, 0
|
||||
Paint (64, 25), TILE$, 7
|
||||
|
||||
AssertImage test1&, "tile_border_black_nocolor.bmp"
|
||||
AssertImage test1&, "tile_border_black_nocolor.png"
|
||||
System
|
||||
|
||||
'$include:'../utilities/imageassert.bm'
|
||||
|
|
Before Width: | Height: | Size: 19 KiB |
BIN
tests/compile_tests/paint/tile_border_black_nocolor.png
Normal file
After Width: | Height: | Size: 382 B |
|
@ -22,7 +22,7 @@ Pset (64, 25), 15 ' Set the starting location a third color
|
|||
|
||||
Paint (64, 25), TILE$, 0
|
||||
|
||||
AssertImage test1&, "tile_border_white_bg.bmp"
|
||||
AssertImage test1&, "tile_border_white_bg.png"
|
||||
System
|
||||
|
||||
'$include:'../utilities/imageassert.bm'
|
||||
|
|
Before Width: | Height: | Size: 19 KiB |
BIN
tests/compile_tests/paint/tile_border_white_bg.png
Normal file
After Width: | Height: | Size: 425 B |
|
@ -17,7 +17,7 @@ Line (0, 0)-(127, 49), 7, BF
|
|||
Circle (64, 25), 25, 0
|
||||
Paint (64, 25), TILE$, 7
|
||||
|
||||
AssertImage test1&, "tile_border_white_nocolor.bmp"
|
||||
AssertImage test1&, "tile_border_white_nocolor.png"
|
||||
System
|
||||
|
||||
'$include:'../utilities/imageassert.bm'
|
||||
|
|
Before Width: | Height: | Size: 19 KiB |
BIN
tests/compile_tests/paint/tile_border_white_nocolor.png
Normal file
After Width: | Height: | Size: 382 B |
|
@ -13,7 +13,7 @@ _Dest test1&
|
|||
Circle (64, 25), 25, 7
|
||||
Paint (64, 25), TILE$
|
||||
|
||||
AssertImage test1&, "tile_noborder_black_bg.bmp"
|
||||
AssertImage test1&, "tile_noborder_black_bg.png"
|
||||
System
|
||||
|
||||
'$include:'../utilities/imageassert.bm'
|
||||
|
|
Before Width: | Height: | Size: 19 KiB |
BIN
tests/compile_tests/paint/tile_noborder_black_bg.png
Normal file
After Width: | Height: | Size: 421 B |
|
@ -16,7 +16,7 @@ Line (0, 0)-(127, 49), 7, BF
|
|||
Circle (64, 25), 25, 0
|
||||
Paint (64, 25), TILE$
|
||||
|
||||
AssertImage test1&, "tile_noborder_white_bg.bmp"
|
||||
AssertImage test1&, "tile_noborder_white_bg.png"
|
||||
System
|
||||
|
||||
'$include:'../utilities/imageassert.bm'
|
||||
|
|
Before Width: | Height: | Size: 19 KiB |
BIN
tests/compile_tests/paint/tile_noborder_white_bg.png
Normal file
After Width: | Height: | Size: 425 B |