[PATCH v2 resend] user32: Do not call SendMessage() to show a window that is already visible.
Alexey Prokhin
alexey at prokhin.ru
Tue Aug 6 15:51:00 CDT 2019
Based on a patch made by Kimmo Myllyvirta.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=39731
Signed-off-by: Alexey Prokhin <alexey at prokhin.ru>
---
v2: handle all ShowWindow commands, add a better test case.
---
dlls/user32/tests/msg.c | 116 ++++++++++++++++++++++++++++++----------
dlls/user32/winpos.c | 64 +++++++++++++++++++---
2 files changed, 146 insertions(+), 34 deletions(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index a5bc1c5518..8aa00d5ba0 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -5041,16 +5041,6 @@ static void test_WM_DEVICECHANGE(HWND hwnd)
}
}
-static DWORD CALLBACK show_window_thread(LPVOID arg)
-{
- HWND hwnd = arg;
-
- /* function will not return if ShowWindow(SW_HIDE) calls SendMessage() */
- ok(ShowWindow(hwnd, SW_HIDE) == FALSE, "ShowWindow(SW_HIDE) expected FALSE\n");
-
- return 0;
-}
-
/* Helper function to easier test SetWindowPos messages */
#define test_msg_setpos( expected_list, flags, todo ) \
test_msg_setpos_( (expected_list), (flags), (todo), __FILE__, __LINE__)
@@ -5071,8 +5061,6 @@ static void test_msg_setpos_(const struct message *expected_list, UINT flags, BO
/* test if we receive the right sequence of messages */
static void test_messages(void)
{
- DWORD tid;
- HANDLE hthread;
HWND hwnd, hparent, hchild;
HWND hchild2, hbutton;
HMENU hmenu;
@@ -5111,14 +5099,6 @@ static void test_messages(void)
flush_events();
ok_sequence(WmEmptySeq, "ShowWindow(SW_HIDE):overlapped", FALSE);
- /* test ShowWindow(SW_HIDE) on a hidden window - multi-threaded */
- hthread = CreateThread(NULL, 0, show_window_thread, hwnd, 0, &tid);
- ok(hthread != NULL, "CreateThread failed, error %d\n", GetLastError());
- ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
- CloseHandle(hthread);
- flush_events();
- ok_sequence(WmEmptySeq, "ShowWindow(SW_HIDE):overlapped", FALSE);
-
ShowWindow(hwnd, SW_SHOW);
flush_events();
ok_sequence(WmShowOverlappedSeq, "ShowWindow(SW_SHOW):overlapped", TRUE);
@@ -13180,6 +13160,14 @@ static const struct message WmShowMaximized_3[] = {
{ 0 }
};
+static const char * const sw_cmd_name[13] =
+{
+ "SW_HIDE", "SW_SHOWNORMAL", "SW_SHOWMINIMIZED", "SW_SHOWMAXIMIZED",
+ "SW_SHOWNOACTIVATE", "SW_SHOW", "SW_MINIMIZE", "SW_SHOWMINNOACTIVE",
+ "SW_SHOWNA", "SW_RESTORE", "SW_SHOWDEFAULT", "SW_FORCEMINIMIZE",
+ "SW_NORMALNA" /* 0xCC */
+};
+
static void test_ShowWindow(void)
{
/* ShowWindow commands in random order */
@@ -13366,13 +13354,6 @@ static void test_ShowWindow(void)
for (i = 0; i < ARRAY_SIZE(sw); i++)
{
- static const char * const sw_cmd_name[13] =
- {
- "SW_HIDE", "SW_SHOWNORMAL", "SW_SHOWMINIMIZED", "SW_SHOWMAXIMIZED",
- "SW_SHOWNOACTIVATE", "SW_SHOW", "SW_MINIMIZE", "SW_SHOWMINNOACTIVE",
- "SW_SHOWNA", "SW_RESTORE", "SW_SHOWDEFAULT", "SW_FORCEMINIMIZE",
- "SW_NORMALNA" /* 0xCC */
- };
char comment[64];
INT idx; /* index into the above array of names */
@@ -13421,6 +13402,86 @@ if (0) /* FIXME: Wine behaves completely different here */
flush_events();
}
+struct show_window_thread_data
+{
+ const char *comment;
+ HWND hwnd;
+ int cmd;
+ BOOL ret;
+};
+
+static DWORD CALLBACK show_window_thread(LPVOID arg)
+{
+ struct show_window_thread_data *d = arg;
+ BOOL ret;
+
+ /* function will not return if ShowWindow() calls SendMessage() */
+ ret = ShowWindow(d->hwnd, d->cmd);
+ ok(!ret == !d->ret, "%s: expected ret %u, got %u\n", d->comment, d->ret, ret);
+
+ return 0;
+}
+
+static void test_ShowWindow_multithreaded(void)
+{
+ static const struct
+ {
+ INT initial_cmd; /* ShowWindow command to apply before the test */
+ INT cmd; /* ShowWindow command to apply in a thread */
+ BOOL ret; /* ShowWindow return value */
+ } sw[] = {
+ { SW_HIDE, SW_HIDE, FALSE },
+ { SW_RESTORE, SW_SHOW, TRUE },
+ { SW_RESTORE, SW_SHOWNOACTIVATE, TRUE },
+ { SW_RESTORE, SW_RESTORE, TRUE },
+ { SW_RESTORE, SW_SHOWNORMAL, TRUE },
+ { SW_RESTORE, SW_SHOWDEFAULT, TRUE },
+ { SW_MINIMIZE, SW_SHOW, TRUE },
+ { SW_MINIMIZE, SW_SHOWMINNOACTIVE, TRUE },
+ { SW_MINIMIZE, SW_MINIMIZE, TRUE },
+ { SW_MINIMIZE, SW_SHOWMINIMIZED, TRUE },
+ { SW_MINIMIZE, SW_FORCEMINIMIZE, TRUE },
+ { SW_SHOWMAXIMIZED, SW_SHOW, TRUE },
+ { SW_SHOWMAXIMIZED, SW_SHOWMAXIMIZED, TRUE },
+ };
+
+ DWORD tid;
+ HANDLE hthread;
+ HWND hwnd;
+ int i;
+ struct show_window_thread_data swtd;
+
+ hwnd = CreateWindowExA(0, "ShowWindowClass", NULL, WS_OVERLAPPEDWINDOW,
+ 120, 120, 90, 90,
+ 0, 0, 0, NULL);
+ assert(hwnd);
+
+ for (i = 0; i < ARRAY_SIZE(sw); ++i)
+ {
+ char comment[64];
+ sprintf(comment, "%d: ShowWindow(%s)", i+1, sw_cmd_name[sw[i].cmd]);
+
+ ShowWindow(hwnd, sw[i].initial_cmd);
+ flush_events();
+ flush_sequence();
+
+ swtd.comment = comment;
+ swtd.hwnd = hwnd;
+ swtd.cmd = sw[i].cmd;
+ swtd.ret = sw[i].ret;
+ hthread = CreateThread(NULL, 0, show_window_thread, &swtd, 0, &tid);
+ ok(hthread != NULL, "%d: CreateThread failed, error %d\n", i, GetLastError());
+ ok(WaitForSingleObject(hthread, 3000) == WAIT_OBJECT_0, "%d: WaitForSingleObject failed\n", i);
+ CloseHandle(hthread);
+
+ flush_events();
+ ok_sequence(WmOptionalPaint, comment, FALSE);
+ }
+
+ DestroyWindow(hwnd);
+ flush_events();
+}
+
static INT_PTR WINAPI test_dlg_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
struct recvd_message msg;
@@ -17748,6 +17809,7 @@ START_TEST(msg)
test_PostMessage();
test_broadcast();
test_ShowWindow();
+ test_ShowWindow_multithreaded();
test_PeekMessage();
test_PeekMessage2();
test_PeekMessage3();
diff --git a/dlls/user32/winpos.c b/dlls/user32/winpos.c
index 5837c179e4..96f86c4aeb 100644
--- a/dlls/user32/winpos.c
+++ b/dlls/user32/winpos.c
@@ -1032,6 +1032,51 @@ UINT WINPOS_MinMaximize( HWND hwnd, UINT cmd, LPRECT rect )
return swpFlags;
}
+/***********************************************************************
+ * is_window_state_changed
+ *
+ * A helper function for ShowWindow and ShowWindowAsync for checking if
+ * a window already has the requested style.
+ */
+static BOOL is_window_state_changed( LONG style, INT cmd )
+{
+ BOOL is_visible = (style & WS_VISIBLE) != 0;
+
+ switch (cmd) {
+ case SW_HIDE:
+ if (!is_visible)
+ return FALSE;
+ break;
+ case SW_SHOW:
+ if (is_visible)
+ return FALSE;
+ break;
+ case SW_SHOWMINNOACTIVE:
+ case SW_MINIMIZE:
+ case SW_FORCEMINIMIZE:
+ case SW_SHOWMINIMIZED:
+ if (is_visible && (style & WS_MINIMIZE))
+ return FALSE;
+ break;
+ case SW_SHOWMAXIMIZED: /* same as SW_MAXIMIZE */
+ if (is_visible && (style & WS_MAXIMIZE))
+ return FALSE;
+ break;
+ case SW_SHOWNA:
+ break;
+ case SW_SHOWNOACTIVATE:
+ case SW_RESTORE:
+ case SW_SHOWNORMAL: /* same as SW_NORMAL: */
+ case SW_SHOWDEFAULT:
+ if (is_visible && !(style & (WS_MINIMIZE | WS_MAXIMIZE)))
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
/***********************************************************************
* show_window
@@ -1056,7 +1101,6 @@ static BOOL show_window( HWND hwnd, INT cmd )
switch(cmd)
{
case SW_HIDE:
- if (!wasVisible) goto done;
showFlag = FALSE;
swp |= SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE;
if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
@@ -1070,14 +1114,12 @@ static BOOL show_window( HWND hwnd, INT cmd )
case SW_SHOWMINIMIZED:
swp |= SWP_SHOWWINDOW | SWP_FRAMECHANGED;
swp |= WINPOS_MinMaximize( hwnd, cmd, &newPos );
- if ((style & WS_MINIMIZE) && wasVisible) goto done;
break;
case SW_SHOWMAXIMIZED: /* same as SW_MAXIMIZE */
if (!wasVisible) swp |= SWP_SHOWWINDOW;
swp |= SWP_FRAMECHANGED;
swp |= WINPOS_MinMaximize( hwnd, SW_MAXIMIZE, &newPos );
- if ((style & WS_MAXIMIZE) && wasVisible) goto done;
break;
case SW_SHOWNA:
@@ -1085,7 +1127,6 @@ static BOOL show_window( HWND hwnd, INT cmd )
if (style & WS_CHILD) swp |= SWP_NOZORDER;
break;
case SW_SHOW:
- if (wasVisible) goto done;
swp |= SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE;
if (style & WS_CHILD) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
break;
@@ -1105,7 +1146,6 @@ static BOOL show_window( HWND hwnd, INT cmd )
}
else
{
- if (wasVisible) goto done;
swp |= SWP_NOSIZE | SWP_NOMOVE;
}
if (style & WS_CHILD && !(swp & SWP_STATECHANGED)) swp |= SWP_NOACTIVATE | SWP_NOZORDER;
@@ -1114,6 +1154,9 @@ static BOOL show_window( HWND hwnd, INT cmd )
goto done;
}
+ if (!is_window_state_changed( style, cmd ))
+ goto done;
+
if ((showFlag != wasVisible || cmd == SW_SHOWNA) && cmd != SW_SHOWMAXIMIZED && !(swp & SWP_STATECHANGED))
{
SendMessageW( hwnd, WM_SHOWWINDOW, showFlag, 0 );
@@ -1199,6 +1242,7 @@ done:
BOOL WINAPI ShowWindowAsync( HWND hwnd, INT cmd )
{
HWND full_handle;
+ LONG style;
if (is_broadcast(hwnd))
{
@@ -1209,6 +1253,10 @@ BOOL WINAPI ShowWindowAsync( HWND hwnd, INT cmd )
if ((full_handle = WIN_IsCurrentThread( hwnd )))
return show_window( full_handle, cmd );
+ style = GetWindowLongW( hwnd, GWL_STYLE );
+ if (!is_window_state_changed( style, cmd ))
+ return (style & WS_VISIBLE) != 0;
+
return SendNotifyMessageW( hwnd, WM_WINE_SHOWWINDOW, cmd, 0 );
}
@@ -1219,6 +1267,7 @@ BOOL WINAPI ShowWindowAsync( HWND hwnd, INT cmd )
BOOL WINAPI ShowWindow( HWND hwnd, INT cmd )
{
HWND full_handle;
+ LONG style;
if (is_broadcast(hwnd))
{
@@ -1228,8 +1277,9 @@ BOOL WINAPI ShowWindow( HWND hwnd, INT cmd )
if ((full_handle = WIN_IsCurrentThread( hwnd )))
return show_window( full_handle, cmd );
- if ((cmd == SW_HIDE) && !(GetWindowLongW( hwnd, GWL_STYLE ) & WS_VISIBLE))
- return FALSE;
+ style = GetWindowLongW( hwnd, GWL_STYLE );
+ if (!is_window_state_changed( style, cmd ))
+ return (style & WS_VISIBLE) != 0;
return SendMessageW( hwnd, WM_WINE_SHOWWINDOW, cmd, 0 );
}
--
2.20.1 (Apple Git-117)
More information about the wine-devel
mailing list