Erich Hoover : user32: SetTimer and SetSystemTimer should respect the timeout limits.
Alexandre Julliard
julliard at winehq.org
Mon May 6 14:08:45 CDT 2013
Module: wine
Branch: master
Commit: 057b0d8bcac633ff80238c6de3ece04655eccf2c
URL: http://source.winehq.org/git/wine.git/?a=commit;h=057b0d8bcac633ff80238c6de3ece04655eccf2c
Author: Erich Hoover <ehoover at mines.edu>
Date: Thu May 2 21:18:03 2013 -0600
user32: SetTimer and SetSystemTimer should respect the timeout limits.
---
dlls/user32/message.c | 14 +++++--
dlls/user32/tests/msg.c | 84 ++++++++++++++++++++++++++++++++++++++++-------
include/winuser.h | 4 ++
3 files changed, 86 insertions(+), 16 deletions(-)
diff --git a/dlls/user32/message.c b/dlls/user32/message.c
index 09a4cb3..e819983 100644
--- a/dlls/user32/message.c
+++ b/dlls/user32/message.c
@@ -56,8 +56,6 @@ WINE_DECLARE_DEBUG_CHANNEL(key);
#define MAX_PACK_COUNT 4
-#define SYS_TIMER_RATE 55 /* min. timer rate in ms (actually 54.925)*/
-
/* the various structures that can be sent in messages, in platform-independent layout */
struct packed_CREATESTRUCTW
{
@@ -4385,12 +4383,16 @@ UINT_PTR WINAPI SetTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC proc )
if (proc) winproc = WINPROC_AllocProc( (WNDPROC)proc, FALSE );
+ /* MSDN states that the minimum timeout should be USER_TIMER_MINIMUM (10.0 ms), but testing
+ * indicates that the true minimum is closer to 15.6 ms. */
+ timeout = min( max( 15, timeout ), USER_TIMER_MAXIMUM );
+
SERVER_START_REQ( set_win_timer )
{
req->win = wine_server_user_handle( hwnd );
req->msg = WM_TIMER;
req->id = id;
- req->rate = max( timeout, SYS_TIMER_RATE );
+ req->rate = timeout;
req->lparam = (ULONG_PTR)winproc;
if (!wine_server_call_err( req ))
{
@@ -4416,12 +4418,16 @@ UINT_PTR WINAPI SetSystemTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC
if (proc) winproc = WINPROC_AllocProc( (WNDPROC)proc, FALSE );
+ /* MSDN states that the minimum timeout should be USER_TIMER_MINIMUM (10.0 ms), but testing
+ * indicates that the true minimum is closer to 15.6 ms. */
+ timeout = min( max( 15, timeout ), USER_TIMER_MAXIMUM );
+
SERVER_START_REQ( set_win_timer )
{
req->win = wine_server_user_handle( hwnd );
req->msg = WM_SYSTIMER;
req->id = id;
- req->rate = max( timeout, SYS_TIMER_RATE );
+ req->rate = timeout;
req->lparam = (ULONG_PTR)winproc;
if (!wine_server_call_err( req ))
{
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index ac3ba16..d88dc7e 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -1722,6 +1722,8 @@ static BOOL (WINAPI *pUnhookWinEvent)(HWINEVENTHOOK);
static BOOL (WINAPI *pGetMonitorInfoA)(HMONITOR,LPMONITORINFO);
static HMONITOR (WINAPI *pMonitorFromPoint)(POINT,DWORD);
static BOOL (WINAPI *pUpdateLayeredWindow)(HWND,HDC,POINT*,SIZE*,HDC,POINT*,COLORREF,BLENDFUNCTION*,DWORD);
+static UINT_PTR (WINAPI *pSetSystemTimer)(HWND, UINT_PTR, UINT, TIMERPROC);
+static UINT_PTR (WINAPI *pKillSystemTimer)(HWND, UINT_PTR);
/* kernel32 functions */
static BOOL (WINAPI *pGetCPInfoExA)(UINT, DWORD, LPCPINFOEXA);
@@ -1746,6 +1748,8 @@ static void init_procs(void)
GET_PROC(user32, GetMonitorInfoA)
GET_PROC(user32, MonitorFromPoint)
GET_PROC(user32, UpdateLayeredWindow)
+ GET_PROC(user32, SetSystemTimer)
+ GET_PROC(user32, KillSystemTimer)
GET_PROC(kernel32, GetCPInfoExA)
@@ -8156,7 +8160,15 @@ static VOID CALLBACK tfunc(HWND hwnd, UINT uMsg, UINT_PTR id, DWORD dwTime)
{
}
-#define TIMER_ID 0x19
+#define TIMER_ID 0x19
+#define TIMER_COUNT_EXPECTED 64
+#define TIMER_COUNT_TOLERANCE 9
+
+static int count = 0;
+static void CALLBACK callback_count(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ count++;
+}
static DWORD WINAPI timer_thread_proc(LPVOID x)
{
@@ -8176,7 +8188,9 @@ static DWORD WINAPI timer_thread_proc(LPVOID x)
static void test_timers(void)
{
struct timer_info info;
+ DWORD start;
DWORD id;
+ MSG msg;
info.hWnd = CreateWindow ("TestWindowClass", NULL,
WS_OVERLAPPEDWINDOW ,
@@ -8198,23 +8212,53 @@ static void test_timers(void)
ok( KillTimer(info.hWnd, TIMER_ID), "KillTimer failed\n");
- ok(DestroyWindow(info.hWnd), "failed to destroy window\n");
-}
+ /* Check the minimum allowed timeout for a timer. MSDN indicates that it should be 10.0 ms,
+ * but testing indicates that the minimum timeout is actually about 15.6 ms. Since there is
+ * some measurement error between test runs we're allowing for ±8 counts (~2 ms).
+ */
+ count = 0;
+ id = SetTimer(info.hWnd, TIMER_ID, 0, callback_count);
+ ok(id != 0, "did not get id from SetTimer.\n");
+ ok(id==TIMER_ID, "SetTimer timer ID different\n");
+ start = GetTickCount();
+ while (GetTickCount()-start < 1001 && GetMessage(&msg, info.hWnd, 0, 0))
+ DispatchMessage(&msg);
+ ok(abs(count-TIMER_COUNT_EXPECTED) < TIMER_COUNT_TOLERANCE
+ || broken(abs(count-43) < TIMER_COUNT_TOLERANCE) /* w2k3 */,
+ "did not get expected count for minimum timeout (%d != ~%d).\n",
+ count, TIMER_COUNT_EXPECTED);
+ ok(KillTimer(info.hWnd, id), "KillTimer failed\n");
+ /* Perform the same check on SetSystemTimer (only available on w2k3 and older) */
+ if (pSetSystemTimer)
+ {
+ int syscount = 0;
+
+ count = 0;
+ id = pSetSystemTimer(info.hWnd, TIMER_ID, 0, callback_count);
+ ok(id != 0, "did not get id from SetSystemTimer.\n");
+ ok(id==TIMER_ID, "SetTimer timer ID different\n");
+ start = GetTickCount();
+ while (GetTickCount()-start < 1001 && GetMessage(&msg, info.hWnd, 0, 0))
+ {
+ if (msg.message == WM_SYSTIMER)
+ syscount++;
+ DispatchMessage(&msg);
+ }
+ ok(abs(syscount-TIMER_COUNT_EXPECTED) < TIMER_COUNT_TOLERANCE,
+ "did not get expected count for minimum timeout (%d != ~%d).\n",
+ syscount, TIMER_COUNT_EXPECTED);
+ todo_wine ok(count == 0, "did not get expected count for callback timeout (%d != 0).\n",
+ count);
+ ok(pKillSystemTimer(info.hWnd, id), "KillSystemTimer failed\n");
+ }
-static int count = 0;
-static VOID CALLBACK callback_count(
- HWND hwnd,
- UINT uMsg,
- UINT_PTR idEvent,
- DWORD dwTime
-)
-{
- count++;
+ ok(DestroyWindow(info.hWnd), "failed to destroy window\n");
}
static void test_timers_no_wnd(void)
{
UINT_PTR id, id2;
+ DWORD start;
MSG msg;
count = 0;
@@ -8232,6 +8276,22 @@ static void test_timers_no_wnd(void)
Sleep(250);
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
ok(count == 1, "killing replaced timer did not work (%i).\n", count);
+
+ /* Check the minimum allowed timeout for a timer. MSDN indicates that it should be 10.0 ms,
+ * but testing indicates that the minimum timeout is actually about 15.6 ms. Since there is
+ * some measurement error between test runs we're allowing for ±8 counts (~2 ms).
+ */
+ count = 0;
+ id = SetTimer(NULL, 0, 0, callback_count);
+ ok(id != 0, "did not get id from SetTimer.\n");
+ start = GetTickCount();
+ while (GetTickCount()-start < 1001 && GetMessage(&msg, NULL, 0, 0))
+ DispatchMessage(&msg);
+ ok(abs(count-TIMER_COUNT_EXPECTED) < TIMER_COUNT_TOLERANCE,
+ "did not get expected count for minimum timeout (%d != ~%d).\n",
+ count, TIMER_COUNT_EXPECTED);
+ KillTimer(NULL, id);
+ /* Note: SetSystemTimer doesn't support a NULL window, see test_timers */
}
/* Various win events with arbitrary parameters */
diff --git a/include/winuser.h b/include/winuser.h
index 59d5b8e..fb0fa95 100644
--- a/include/winuser.h
+++ b/include/winuser.h
@@ -2521,6 +2521,10 @@ typedef struct tagMINIMIZEDMETRICS {
#define PM_QS_PAINT (QS_PAINT << 16)
#define PM_QS_SENDMESSAGE (QS_SENDMESSAGE << 16)
+/* SetTimer() limits */
+#define USER_TIMER_MINIMUM 0x0000000A
+#define USER_TIMER_MAXIMUM 0x7FFFFFFF
+
/* AnimateWindow() flags */
#define AW_SLIDE 0x00040000
#define AW_ACTIVATE 0x00020000
More information about the wine-cvs
mailing list