Release 951226
[wine/multimedia.git] / windows / message.c
blob43bb4e4e9d38312d074fbecee2ef8cb1439f4b68
1 /*
2 * Message queues related functions
4 * Copyright 1993, 1994 Alexandre Julliard
5 */
7 #include <stdlib.h>
8 #include <sys/time.h>
9 #include <sys/types.h>
10 #include <errno.h>
12 #include "message.h"
13 #include "win.h"
14 #include "gdi.h"
15 #include "sysmetrics.h"
16 #include "hook.h"
17 #include "event.h"
18 #include "spy.h"
19 #include "winpos.h"
20 #include "atom.h"
21 #include "dde.h"
22 #include "stddebug.h"
23 /* #define DEBUG_MSG */
24 #include "debug.h"
26 #define HWND_BROADCAST ((HWND)0xffff)
27 #define MAX_QUEUE_SIZE 120 /* Max. size of a message queue */
30 extern BOOL TIMER_CheckTimer( LONG *next, MSG *msg,
31 HWND hwnd, BOOL remove ); /* timer.c */
33 /* ------- Internal Queues ------ */
35 static HANDLE hmemSysMsgQueue = 0;
36 static MESSAGEQUEUE *sysMsgQueue = NULL;
38 HANDLE hActiveQ_G = 0;
39 static HANDLE hFirstQueue = 0;
41 /* ------- Miscellaneous ------ */
42 static int doubleClickSpeed = 452;
45 /***********************************************************************
46 * MSG_CreateMsgQueue
48 * Create a message queue.
50 static HANDLE MSG_CreateMsgQueue( int size )
52 HANDLE hQueue;
53 MESSAGEQUEUE * msgQueue;
54 int queueSize;
56 queueSize = sizeof(MESSAGEQUEUE) + size * sizeof(QMSG);
57 if (!(hQueue = GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, queueSize )))
58 return 0;
59 msgQueue = (MESSAGEQUEUE *) GlobalLock( hQueue );
60 msgQueue->msgSize = sizeof(QMSG);
61 msgQueue->queueSize = size;
62 msgQueue->wWinVersion = 0; /* FIXME? */
63 GlobalUnlock( hQueue );
64 return hQueue;
67 /***********************************************************************
68 * MSG_DeleteMsgQueue
70 BOOL MSG_DeleteMsgQueue( HANDLE hQueue )
72 MESSAGEQUEUE * msgQueue = (MESSAGEQUEUE*)GlobalLock(hQueue);
74 if( !hQueue )
76 dprintf_msg(stddeb,"DeleteMsgQueue: invalid argument.\n");
77 return 0;
80 if( !msgQueue ) return 0;
82 if( hQueue == hFirstQueue )
83 hFirstQueue = msgQueue->next;
84 else if( hFirstQueue )
86 MESSAGEQUEUE *msgQ = (MESSAGEQUEUE*)GlobalLock(hFirstQueue);
88 /* walk up queue list and relink if needed */
89 while( msgQ->next )
91 if( msgQ->next == hQueue )
92 msgQ->next = msgQueue->next;
94 /* should check for intertask sendmessages here */
97 msgQ = (MESSAGEQUEUE*)GlobalLock(msgQ->next);
101 GlobalFree( hQueue );
102 return 1;
105 /***********************************************************************
106 * MSG_CreateSysMsgQueue
108 * Create the system message queue, and set the double-click speed.
109 * Must be called only once.
111 BOOL MSG_CreateSysMsgQueue( int size )
113 if (size > MAX_QUEUE_SIZE) size = MAX_QUEUE_SIZE;
114 else if (size <= 0) size = 1;
115 if (!(hmemSysMsgQueue = MSG_CreateMsgQueue( size ))) return FALSE;
116 sysMsgQueue = (MESSAGEQUEUE *) GlobalLock( hmemSysMsgQueue );
117 doubleClickSpeed = GetProfileInt( "windows", "DoubleClickSpeed", 452 );
118 return TRUE;
122 /***********************************************************************
123 * MSG_AddMsg
125 * Add a message to the queue. Return FALSE if queue is full.
127 static int MSG_AddMsg( HANDLE hQueue, MSG * msg, DWORD extraInfo )
129 int pos;
130 MESSAGEQUEUE *msgQueue;
132 if (!(msgQueue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return FALSE;
133 pos = msgQueue->nextFreeMessage;
135 /* Check if queue is full */
136 if ((pos == msgQueue->nextMessage) && (msgQueue->msgCount > 0)) {
137 fprintf(stderr,"MSG_AddMsg // queue is full !\n");
138 return FALSE;
141 /* Store message */
142 msgQueue->messages[pos].msg = *msg;
143 msgQueue->messages[pos].extraInfo = extraInfo;
144 if (pos < msgQueue->queueSize-1) pos++;
145 else pos = 0;
146 msgQueue->nextFreeMessage = pos;
147 msgQueue->msgCount++;
148 msgQueue->status |= QS_POSTMESSAGE;
149 msgQueue->tempStatus |= QS_POSTMESSAGE;
150 return TRUE;
154 /***********************************************************************
155 * MSG_FindMsg
157 * Find a message matching the given parameters. Return -1 if none available.
159 static int MSG_FindMsg(MESSAGEQUEUE * msgQueue, HWND hwnd, int first, int last)
161 int i, pos = msgQueue->nextMessage;
163 dprintf_msg(stddeb,"MSG_FindMsg: hwnd=0x"NPFMT"\n\n", hwnd );
165 if (!msgQueue->msgCount) return -1;
166 if (!hwnd && !first && !last) return pos;
168 for (i = 0; i < msgQueue->msgCount; i++)
170 MSG * msg = &msgQueue->messages[pos].msg;
172 if (!hwnd || (msg->hwnd == hwnd))
174 if (!first && !last) return pos;
175 if ((msg->message >= first) && (msg->message <= last)) return pos;
177 if (pos < msgQueue->queueSize-1) pos++;
178 else pos = 0;
180 return -1;
184 /***********************************************************************
185 * MSG_RemoveMsg
187 * Remove a message from the queue (pos must be a valid position).
189 static void MSG_RemoveMsg( MESSAGEQUEUE * msgQueue, int pos )
191 if (pos >= msgQueue->nextMessage)
193 for ( ; pos > msgQueue->nextMessage; pos--)
194 msgQueue->messages[pos] = msgQueue->messages[pos-1];
195 msgQueue->nextMessage++;
196 if (msgQueue->nextMessage >= msgQueue->queueSize)
197 msgQueue->nextMessage = 0;
199 else
201 for ( ; pos < msgQueue->nextFreeMessage; pos++)
202 msgQueue->messages[pos] = msgQueue->messages[pos+1];
203 if (msgQueue->nextFreeMessage) msgQueue->nextFreeMessage--;
204 else msgQueue->nextFreeMessage = msgQueue->queueSize-1;
206 msgQueue->msgCount--;
207 if (!msgQueue->msgCount) msgQueue->status &= ~QS_POSTMESSAGE;
208 msgQueue->tempStatus = 0;
211 /***********************************************************************
212 * MSG_GetQueueTask
214 HTASK MSG_GetQueueTask( HANDLE hQueue )
216 MESSAGEQUEUE *msgQ = GlobalLock( hQueue );
218 return (msgQ) ? msgQ->hTask : 0 ;
221 /***********************************************************************
222 * MSG_GetWindowForEvent
224 * Find the window and hittest for a mouse event.
226 static INT MSG_GetWindowForEvent( POINT pt, HWND *phwnd )
228 WND *wndPtr;
229 HWND hwnd;
230 INT hittest = HTERROR;
231 INT x, y;
233 *phwnd = hwnd = GetDesktopWindow();
234 x = pt.x;
235 y = pt.y;
236 while (hwnd)
238 /* If point is in window, and window is visible, and it */
239 /* is enabled (or it's a top-level window), then explore */
240 /* its children. Otherwise, go to the next window. */
242 wndPtr = WIN_FindWndPtr( hwnd );
243 if ((wndPtr->dwStyle & WS_VISIBLE) &&
244 (!(wndPtr->dwStyle & WS_DISABLED) ||
245 !(wndPtr->dwStyle & WS_CHILD)) &&
246 (x >= wndPtr->rectWindow.left) &&
247 (x < wndPtr->rectWindow.right) &&
248 (y >= wndPtr->rectWindow.top) &&
249 (y < wndPtr->rectWindow.bottom))
251 *phwnd = hwnd;
252 x -= wndPtr->rectClient.left;
253 y -= wndPtr->rectClient.top;
254 /* If window is minimized or disabled, ignore its children */
255 if ((wndPtr->dwStyle & WS_MINIMIZE) ||
256 (wndPtr->dwStyle & WS_DISABLED)) break;
257 hwnd = wndPtr->hwndChild;
259 else hwnd = wndPtr->hwndNext;
262 /* Make point relative to parent again */
264 wndPtr = WIN_FindWndPtr( *phwnd );
265 x += wndPtr->rectClient.left;
266 y += wndPtr->rectClient.top;
268 /* Send the WM_NCHITTEST message */
270 while (*phwnd)
272 wndPtr = WIN_FindWndPtr( *phwnd );
273 if (wndPtr->dwStyle & WS_DISABLED) hittest = HTERROR;
274 else hittest = (INT)SendMessage( *phwnd, WM_NCHITTEST, 0,
275 MAKELONG( pt.x, pt.y ) );
276 if (hittest != HTTRANSPARENT) break; /* Found the window */
277 hwnd = wndPtr->hwndNext;
278 while (hwnd)
280 WND *nextPtr = WIN_FindWndPtr( hwnd );
281 if ((nextPtr->dwStyle & WS_VISIBLE) &&
282 (x >= nextPtr->rectWindow.left) &&
283 (x < nextPtr->rectWindow.right) &&
284 (y >= nextPtr->rectWindow.top) &&
285 (y < nextPtr->rectWindow.bottom)) break;
286 hwnd = nextPtr->hwndNext;
288 if (hwnd) *phwnd = hwnd; /* Found a suitable sibling */
289 else /* Go back to the parent */
291 if (!(*phwnd = wndPtr->hwndParent)) break;
292 wndPtr = WIN_FindWndPtr( *phwnd );
293 x += wndPtr->rectClient.left;
294 y += wndPtr->rectClient.top;
298 if (!*phwnd) *phwnd = GetDesktopWindow();
299 return hittest;
303 /***********************************************************************
304 * MSG_TranslateMouseMsg
306 * Translate an mouse hardware event into a real mouse message.
307 * Return value indicates whether the translated message must be passed
308 * to the user.
309 * Actions performed:
310 * - Find the window for this message.
311 * - Translate button-down messages in double-clicks.
312 * - Send the WM_NCHITTEST message to find where the cursor is.
313 * - Activate the window if needed.
314 * - Translate the message into a non-client message, or translate
315 * the coordinates to client coordinates.
316 * - Send the WM_SETCURSOR message.
318 static BOOL MSG_TranslateMouseMsg( MSG *msg, BOOL remove )
320 BOOL eatMsg = FALSE;
321 INT hittest;
322 static DWORD lastClickTime = 0;
323 static WORD lastClickMsg = 0;
324 static POINT lastClickPos = { 0, 0 };
325 POINT pt = msg->pt;
326 MOUSEHOOKSTRUCT hook = { msg->pt, 0, HTCLIENT, 0 };
328 BOOL mouseClick = ((msg->message == WM_LBUTTONDOWN) ||
329 (msg->message == WM_RBUTTONDOWN) ||
330 (msg->message == WM_MBUTTONDOWN));
332 /* Find the window */
334 if (GetCapture())
336 msg->hwnd = GetCapture();
337 ScreenToClient( msg->hwnd, &pt );
338 msg->lParam = MAKELONG( pt.x, pt.y );
339 /* No need to further process the message */
340 hook.hwnd = msg->hwnd;
341 return !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
342 msg->message, (LPARAM)MAKE_SEGPTR(&hook));
345 if ((hittest = MSG_GetWindowForEvent( msg->pt, &msg->hwnd )) != HTERROR)
348 /* Send the WM_PARENTNOTIFY message */
350 if (mouseClick) WIN_SendParentNotify( msg->hwnd, msg->message, 0,
351 MAKELONG( msg->pt.x, msg->pt.y ) );
353 /* Activate the window if needed */
355 if (mouseClick)
357 HWND hwndTop = WIN_GetTopParent( msg->hwnd );
358 if (hwndTop != GetActiveWindow())
360 LONG ret = SendMessage( msg->hwnd, WM_MOUSEACTIVATE,
361 (WPARAM)hwndTop,
362 MAKELONG( hittest, msg->message ) );
363 if ((ret == MA_ACTIVATEANDEAT) || (ret == MA_NOACTIVATEANDEAT))
364 eatMsg = TRUE;
365 if ((ret == MA_ACTIVATE) || (ret == MA_ACTIVATEANDEAT))
367 SetWindowPos( hwndTop, HWND_TOP, 0, 0, 0, 0,
368 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
369 WINPOS_ChangeActiveWindow( hwndTop, TRUE );
375 /* Send the WM_SETCURSOR message */
377 SendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
378 MAKELONG( hittest, msg->message ));
379 if (eatMsg) return FALSE;
381 /* Check for double-click */
383 if (mouseClick)
385 BOOL dbl_click = FALSE;
387 if ((msg->message == lastClickMsg) &&
388 (msg->time - lastClickTime < doubleClickSpeed) &&
389 (abs(msg->pt.x - lastClickPos.x) < SYSMETRICS_CXDOUBLECLK/2) &&
390 (abs(msg->pt.y - lastClickPos.y) < SYSMETRICS_CYDOUBLECLK/2))
391 dbl_click = TRUE;
393 if (dbl_click && (hittest == HTCLIENT))
395 /* Check whether window wants the double click message. */
396 WND * wndPtr = WIN_FindWndPtr( msg->hwnd );
397 if (!wndPtr || !(WIN_CLASS_STYLE(wndPtr) & CS_DBLCLKS))
398 dbl_click = FALSE;
401 if (dbl_click) switch(msg->message)
403 case WM_LBUTTONDOWN: msg->message = WM_LBUTTONDBLCLK; break;
404 case WM_RBUTTONDOWN: msg->message = WM_RBUTTONDBLCLK; break;
405 case WM_MBUTTONDOWN: msg->message = WM_MBUTTONDBLCLK; break;
408 if (remove)
410 lastClickTime = msg->time;
411 lastClickMsg = msg->message;
412 lastClickPos = msg->pt;
416 /* Build the translated message */
418 if (hittest == HTCLIENT)
419 ScreenToClient( msg->hwnd, &pt );
420 else
422 msg->wParam = hittest;
423 msg->message += WM_NCLBUTTONDOWN - WM_LBUTTONDOWN;
425 msg->lParam = MAKELONG( pt.x, pt.y );
427 hook.hwnd = msg->hwnd;
428 hook.wHitTestCode = hittest;
429 return !HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
430 msg->message, (LPARAM)MAKE_SEGPTR(&hook));
434 /***********************************************************************
435 * MSG_TranslateKeyboardMsg
437 * Translate an keyboard hardware event into a real message.
438 * Return value indicates whether the translated message must be passed
439 * to the user.
441 static BOOL MSG_TranslateKeyboardMsg( MSG *msg, BOOL remove )
443 /* Should check Ctrl-Esc and PrintScreen here */
445 msg->hwnd = GetFocus();
446 if (!msg->hwnd)
448 /* Send the message to the active window instead, */
449 /* translating messages to their WM_SYS equivalent */
450 msg->hwnd = GetActiveWindow();
451 msg->message += WM_SYSKEYDOWN - WM_KEYDOWN;
453 return !HOOK_CallHooks( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
454 msg->wParam, msg->lParam );
458 /***********************************************************************
459 * MSG_PeekHardwareMsg
461 * Peek for a hardware message matching the hwnd and message filters.
463 static BOOL MSG_PeekHardwareMsg( MSG *msg, HWND hwnd, WORD first, WORD last,
464 BOOL remove )
466 int i, pos = sysMsgQueue->nextMessage;
468 for (i = 0; i < sysMsgQueue->msgCount; i++, pos++)
470 if (pos >= sysMsgQueue->queueSize) pos = 0;
471 *msg = sysMsgQueue->messages[pos].msg;
473 /* Translate message */
475 if ((msg->message >= WM_MOUSEFIRST) && (msg->message <= WM_MOUSELAST))
477 if (!MSG_TranslateMouseMsg( msg, remove )) continue;
479 else if ((msg->message >= WM_KEYFIRST) && (msg->message <= WM_KEYLAST))
481 if (!MSG_TranslateKeyboardMsg( msg, remove )) continue;
483 else /* Non-standard hardware event */
485 HARDWAREHOOKSTRUCT hook = { msg->hwnd, msg->message,
486 msg->wParam, msg->lParam };
487 if (HOOK_CallHooks( WH_HARDWARE, remove ? HC_ACTION : HC_NOREMOVE,
488 0, (LPARAM)MAKE_SEGPTR(&hook) )) continue;
491 /* Check message against filters */
493 if (hwnd && (msg->hwnd != hwnd)) continue;
494 if ((first || last) &&
495 ((msg->message < first) || (msg->message > last))) continue;
496 if ((msg->hwnd != GetDesktopWindow()) &&
497 (GetWindowTask(msg->hwnd) != GetCurrentTask()))
498 continue; /* Not for this task */
499 if (remove)
501 MSG tmpMsg = *msg; /* FIXME */
502 HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION,
503 0, (LPARAM)MAKE_SEGPTR(&tmpMsg) );
504 MSG_RemoveMsg( sysMsgQueue, pos );
506 return TRUE;
508 return FALSE;
512 /**********************************************************************
513 * SetDoubleClickTime (USER.20)
515 void SetDoubleClickTime( WORD interval )
517 if (interval == 0)
518 doubleClickSpeed = 500;
519 else
520 doubleClickSpeed = interval;
524 /**********************************************************************
525 * GetDoubleClickTime (USER.21)
527 WORD GetDoubleClickTime()
529 return (WORD)doubleClickSpeed;
533 /***********************************************************************
534 * MSG_IncPaintCount
536 void MSG_IncPaintCount( HANDLE hQueue )
538 MESSAGEQUEUE *queue;
540 if (!(queue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return;
541 queue->wPaintCount++;
542 queue->status |= QS_PAINT;
543 queue->tempStatus |= QS_PAINT;
547 /***********************************************************************
548 * MSG_DecPaintCount
550 void MSG_DecPaintCount( HANDLE hQueue )
552 MESSAGEQUEUE *queue;
554 if (!(queue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return;
555 queue->wPaintCount--;
556 if (!queue->wPaintCount) queue->status &= ~QS_PAINT;
560 /***********************************************************************
561 * MSG_IncTimerCount
563 void MSG_IncTimerCount( HANDLE hQueue )
565 MESSAGEQUEUE *queue;
567 if (!(queue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return;
568 queue->wTimerCount++;
569 queue->status |= QS_TIMER;
570 queue->tempStatus |= QS_TIMER;
574 /***********************************************************************
575 * MSG_DecTimerCount
577 void MSG_DecTimerCount( HANDLE hQueue )
579 MESSAGEQUEUE *queue;
581 if (!(queue = (MESSAGEQUEUE *)GlobalLock( hQueue ))) return;
582 queue->wTimerCount--;
583 if (!queue->wTimerCount) queue->status &= ~QS_TIMER;
587 /***********************************************************************
588 * hardware_event
590 * Add an event to the system message queue.
591 * Note: the position is relative to the desktop window.
593 void hardware_event( WORD message, WORD wParam, LONG lParam,
594 int xPos, int yPos, DWORD time, DWORD extraInfo )
596 MSG *msg;
597 int pos;
599 if (!sysMsgQueue) return;
600 pos = sysMsgQueue->nextFreeMessage;
602 /* Merge with previous event if possible */
604 if ((message == WM_MOUSEMOVE) && sysMsgQueue->msgCount)
606 if (pos > 0) pos--;
607 else pos = sysMsgQueue->queueSize - 1;
608 msg = &sysMsgQueue->messages[pos].msg;
609 if ((msg->message == message) && (msg->wParam == wParam))
610 sysMsgQueue->msgCount--; /* Merge events */
611 else
612 pos = sysMsgQueue->nextFreeMessage; /* Don't merge */
615 /* Check if queue is full */
617 if ((pos == sysMsgQueue->nextMessage) && sysMsgQueue->msgCount)
619 /* Queue is full, beep (but not on every mouse motion...) */
620 if (message != WM_MOUSEMOVE) MessageBeep(0);
621 return;
624 /* Store message */
626 msg = &sysMsgQueue->messages[pos].msg;
627 msg->hwnd = 0;
628 msg->message = message;
629 msg->wParam = wParam;
630 msg->lParam = lParam;
631 msg->time = time;
632 msg->pt.x = xPos & 0xffff;
633 msg->pt.y = yPos & 0xffff;
634 sysMsgQueue->messages[pos].extraInfo = extraInfo;
635 if (pos < sysMsgQueue->queueSize - 1) pos++;
636 else pos = 0;
637 sysMsgQueue->nextFreeMessage = pos;
638 sysMsgQueue->msgCount++;
642 /***********************************************************************
643 * MSG_GetHardwareMessage
645 * Like GetMessage(), but only return mouse and keyboard events.
646 * Used internally for window moving and resizing. Mouse messages
647 * are not translated.
648 * Warning: msg->hwnd is always 0.
650 BOOL MSG_GetHardwareMessage( LPMSG msg )
652 int pos;
653 XEvent event;
655 while(1)
657 if ((pos = MSG_FindMsg( sysMsgQueue, 0, 0, 0 )) != -1)
659 *msg = sysMsgQueue->messages[pos].msg;
660 MSG_RemoveMsg( sysMsgQueue, pos );
661 break;
663 XNextEvent( display, &event );
664 EVENT_ProcessEvent( &event );
666 return TRUE;
670 /***********************************************************************
671 * SetMessageQueue (USER.266)
673 BOOL SetMessageQueue( int size )
675 HGLOBAL hQueue = 0;
676 HGLOBAL hNextQueue= hFirstQueue;
677 MESSAGEQUEUE *queuePrev = NULL;
678 MESSAGEQUEUE *queuePtr;
680 if ((size > MAX_QUEUE_SIZE) || (size <= 0)) return TRUE;
682 /* Free the old message queue */
683 if ((hQueue = GetTaskQueue(0)) != 0)
685 MESSAGEQUEUE *queuePtr = (MESSAGEQUEUE *)GlobalLock( hQueue );
687 if( queuePtr )
689 MESSAGEQUEUE *msgQ = (MESSAGEQUEUE*)GlobalLock(hFirstQueue);
691 hNextQueue = queuePtr->next;
693 if( msgQ )
694 if( hQueue != hFirstQueue )
695 while( msgQ->next )
697 if( msgQ->next == hQueue )
699 queuePrev = msgQ;
700 break;
702 msgQ = (MESSAGEQUEUE*)GlobalLock(msgQ->next);
704 GlobalUnlock( hQueue );
705 MSG_DeleteMsgQueue( hQueue );
709 if( !(hQueue = MSG_CreateMsgQueue( size )))
711 if(queuePrev)
712 /* it did have a queue */
713 queuePrev->next = hNextQueue;
714 return FALSE;
717 queuePtr = (MESSAGEQUEUE *)GlobalLock( hQueue );
718 queuePtr->hTask = GetCurrentTask();
719 queuePtr->next = hNextQueue;
721 if( !queuePrev )
722 hFirstQueue = hQueue;
723 else
724 queuePrev->next = hQueue;
726 SetTaskQueue( 0, hQueue );
727 return TRUE;
731 /***********************************************************************
732 * GetWindowTask (USER.224)
734 HTASK GetWindowTask( HWND hwnd )
736 WND *wndPtr = WIN_FindWndPtr( hwnd );
737 MESSAGEQUEUE *queuePtr;
739 if (!wndPtr) return 0;
740 queuePtr = (MESSAGEQUEUE *)GlobalLock( wndPtr->hmemTaskQ );
741 if (!queuePtr) return 0;
742 return queuePtr->hTask;
746 /***********************************************************************
747 * PostQuitMessage (USER.6)
749 void PostQuitMessage( int exitCode )
751 MESSAGEQUEUE *queue;
753 if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return;
754 queue->wPostQMsg = TRUE;
755 queue->wExitCode = exitCode;
759 /***********************************************************************
760 * GetQueueStatus (USER.334)
762 DWORD GetQueueStatus( int flags )
764 MESSAGEQUEUE *queue;
765 DWORD ret;
767 if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return 0;
768 ret = MAKELONG( queue->tempStatus, queue->status );
769 queue->tempStatus = 0;
770 return ret & MAKELONG( flags, flags );
774 /***********************************************************************
775 * GetInputState (USER.335)
777 BOOL GetInputState()
779 MESSAGEQUEUE *queue;
781 if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return FALSE;
782 return queue->status & (QS_KEY | QS_MOUSEBUTTON);
786 /***********************************************************************
787 * MSG_Synchronize
789 * Synchronize with the X server. Should not be used too often.
791 void MSG_Synchronize()
793 XEvent event;
795 XSync( display, False );
796 while (XPending( display ))
798 XNextEvent( display, &event );
799 EVENT_ProcessEvent( &event );
804 /***********************************************************************
805 * MSG_WaitXEvent
807 * Wait for an X event, but at most maxWait milliseconds (-1 for no timeout).
808 * Return TRUE if an event is pending, FALSE on timeout or error
809 * (for instance lost connection with the server).
811 BOOL MSG_WaitXEvent( LONG maxWait )
813 fd_set read_set;
814 struct timeval timeout;
815 XEvent event;
816 int fd = ConnectionNumber(display);
818 if (!XPending(display) && (maxWait != -1))
820 FD_ZERO( &read_set );
821 FD_SET( fd, &read_set );
823 timeout.tv_usec = (maxWait % 1000) * 1000;
824 timeout.tv_sec = maxWait / 1000;
826 #ifdef CONFIG_IPC
827 sigsetjmp(env_wait_x, 1);
828 stop_wait_op= CONT;
830 if (DDE_GetRemoteMessage()) {
831 while(DDE_GetRemoteMessage())
833 return TRUE;
835 stop_wait_op= STOP_WAIT_X;
836 /* The code up to the next "stop_wait_op= CONT" must be reentrant */
837 if (select( fd+1, &read_set, NULL, NULL, &timeout ) != 1 &&
838 !XPending(display)) {
839 stop_wait_op= CONT;
840 return FALSE;
841 } else {
842 stop_wait_op= CONT;
844 #else /* CONFIG_IPC */
845 if (select( fd+1, &read_set, NULL, NULL, &timeout ) != 1)
846 return FALSE; /* Timeout or error */
847 #endif /* CONFIG_IPC */
851 /* Process the event (and possibly others that occurred in the meantime) */
855 #ifdef CONFIG_IPC
856 if (DDE_GetRemoteMessage())
858 while(DDE_GetRemoteMessage()) ;
859 return TRUE;
861 #endif /* CONFIG_IPC */
863 XNextEvent( display, &event );
864 EVENT_ProcessEvent( &event );
866 while (XPending( display ));
867 return TRUE;
871 /***********************************************************************
872 * MSG_PeekMessage
874 static BOOL MSG_PeekMessage( LPMSG msg, HWND hwnd, WORD first, WORD last,
875 WORD flags, BOOL peek )
877 int pos, mask;
878 MESSAGEQUEUE *msgQueue;
879 LONG nextExp; /* Next timer expiration time */
881 #ifdef CONFIG_IPC
882 DDE_TestDDE(hwnd); /* do we have dde handling in the window ?*/
883 DDE_GetRemoteMessage();
884 #endif /* CONFIG_IPC */
886 if (first || last)
888 mask = QS_POSTMESSAGE; /* Always selectioned */
889 if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
890 if ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) mask |= QS_MOUSE;
891 if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
892 if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
893 if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
895 else mask = QS_MOUSE | QS_KEY | QS_POSTMESSAGE | QS_TIMER | QS_PAINT;
897 while(1)
899 msgQueue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) );
900 if (!msgQueue) return FALSE;
902 /* First handle a message put by SendMessage() */
903 if (msgQueue->status & QS_SENDMESSAGE)
905 if (!hwnd || (msgQueue->hWnd == hwnd))
907 if ((!first && !last) ||
908 ((msgQueue->msg >= first) && (msgQueue->msg <= last)))
910 msg->hwnd = msgQueue->hWnd;
911 msg->message = msgQueue->msg;
912 msg->wParam = msgQueue->wParam;
913 msg->lParam = msgQueue->lParam;
914 if (flags & PM_REMOVE) msgQueue->status &= ~QS_SENDMESSAGE;
915 break;
920 /* Now find a normal message */
921 pos = MSG_FindMsg( msgQueue, hwnd, first, last );
922 if (pos != -1)
924 QMSG *qmsg = &msgQueue->messages[pos];
925 *msg = qmsg->msg;
926 msgQueue->GetMessageTimeVal = msg->time;
927 msgQueue->GetMessagePosVal = *(DWORD *)&msg->pt;
928 msgQueue->GetMessageExtraInfoVal = qmsg->extraInfo;
930 if (flags & PM_REMOVE) MSG_RemoveMsg( msgQueue, pos );
931 break;
934 /* Now find a hardware event */
935 if (MSG_PeekHardwareMsg( msg, hwnd, first, last, flags & PM_REMOVE ))
937 /* Got one */
938 msgQueue->GetMessageTimeVal = msg->time;
939 msgQueue->GetMessagePosVal = *(DWORD *)&msg->pt;
940 msgQueue->GetMessageExtraInfoVal = 0; /* Always 0 for now */
941 break;
944 /* Now handle a WM_QUIT message */
945 if (msgQueue->wPostQMsg)
947 msg->hwnd = hwnd;
948 msg->message = WM_QUIT;
949 msg->wParam = msgQueue->wExitCode;
950 msg->lParam = 0;
951 break;
954 /* Now find a WM_PAINT message */
955 if ((msgQueue->status & QS_PAINT) && (mask & QS_PAINT))
957 msg->hwnd = WIN_FindWinToRepaint( hwnd );
958 msg->message = WM_PAINT;
959 msg->wParam = 0;
960 msg->lParam = 0;
961 if (msg->hwnd != 0) break;
964 /* Finally handle WM_TIMER messages */
965 if ((msgQueue->status & QS_TIMER) && (mask & QS_TIMER))
967 if (TIMER_CheckTimer( &nextExp, msg, hwnd, flags & PM_REMOVE ))
968 break; /* Got a timer msg */
970 else nextExp = -1; /* No timeout needed */
972 Yield();
974 /* Wait until something happens */
975 if (peek)
977 if (!MSG_WaitXEvent( 0 )) return FALSE; /* No pending event */
979 else /* Wait for an event, then restart the loop */
980 MSG_WaitXEvent( nextExp );
983 /* We got a message */
984 if (peek) return TRUE;
985 else return (msg->message != WM_QUIT);
989 /***********************************************************************
990 * MSG_InternalGetMessage
992 * GetMessage() function for internal use. Behave like GetMessage(),
993 * but also call message filters and optionally send WM_ENTERIDLE messages.
994 * 'hwnd' must be the handle of the dialog or menu window.
995 * 'code' is the message filter value (MSGF_??? codes).
997 BOOL MSG_InternalGetMessage( SEGPTR msg, HWND hwnd, HWND hwndOwner, short code,
998 WORD flags, BOOL sendIdle )
1000 for (;;)
1002 if (sendIdle)
1004 if (!MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
1005 0, 0, 0, flags, TRUE ))
1007 /* No message present -> send ENTERIDLE and wait */
1008 SendMessage( hwndOwner, WM_ENTERIDLE, code, (LPARAM)hwnd );
1009 MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
1010 0, 0, 0, flags, FALSE );
1013 else /* Always wait for a message */
1014 MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
1015 0, 0, 0, flags, FALSE );
1017 if (!CallMsgFilter( msg, code ))
1018 return (((MSG *)PTR_SEG_TO_LIN(msg))->message != WM_QUIT);
1020 /* Message filtered -> remove it from the queue */
1021 /* if it's still there. */
1022 if (!(flags & PM_REMOVE))
1023 MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
1024 0, 0, 0, PM_REMOVE, TRUE );
1029 /***********************************************************************
1030 * PeekMessage (USER.109)
1032 BOOL PeekMessage( LPMSG msg, HWND hwnd, WORD first, WORD last, WORD flags )
1034 return MSG_PeekMessage( msg, hwnd, first, last, flags, TRUE );
1038 /***********************************************************************
1039 * GetMessage (USER.108)
1041 BOOL GetMessage( SEGPTR msg, HWND hwnd, UINT first, UINT last )
1043 MSG_PeekMessage( (MSG *)PTR_SEG_TO_LIN(msg),
1044 hwnd, first, last, PM_REMOVE, FALSE );
1045 HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, 0, (LPARAM)msg );
1046 return (((MSG *)PTR_SEG_TO_LIN(msg))->message != WM_QUIT);
1051 /***********************************************************************
1052 * PostMessage (USER.110)
1054 BOOL PostMessage( HWND hwnd, WORD message, WORD wParam, LONG lParam )
1056 MSG msg;
1057 WND *wndPtr;
1059 msg.hwnd = hwnd;
1060 msg.message = message;
1061 msg.wParam = wParam;
1062 msg.lParam = lParam;
1063 msg.time = GetTickCount();
1064 msg.pt.x = 0;
1065 msg.pt.y = 0;
1067 #ifdef CONFIG_IPC
1068 if (DDE_PostMessage(&msg))
1069 return TRUE;
1070 #endif /* CONFIG_IPC */
1072 if (hwnd == HWND_BROADCAST) {
1073 dprintf_msg(stddeb,"PostMessage // HWND_BROADCAST !\n");
1074 hwnd = GetTopWindow(GetDesktopWindow());
1075 while (hwnd) {
1076 if (!(wndPtr = WIN_FindWndPtr(hwnd))) break;
1077 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION) {
1078 dprintf_msg(stddeb,"BROADCAST Message to hWnd="NPFMT" m=%04X w=%04X l=%08lX !\n",
1079 hwnd, message, wParam, lParam);
1080 PostMessage(hwnd, message, wParam, lParam);
1082 hwnd = wndPtr->hwndNext;
1084 dprintf_msg(stddeb,"PostMessage // End of HWND_BROADCAST !\n");
1085 return TRUE;
1088 wndPtr = WIN_FindWndPtr( hwnd );
1089 if (!wndPtr || !wndPtr->hmemTaskQ) return FALSE;
1091 return MSG_AddMsg( wndPtr->hmemTaskQ, &msg, 0 );
1094 /***********************************************************************
1095 * PostAppMessage (USER.116)
1097 BOOL PostAppMessage( HTASK hTask, WORD message, WORD wParam, LONG lParam )
1099 MSG msg;
1101 if (GetTaskQueue(hTask) == 0) return FALSE;
1102 msg.hwnd = 0;
1103 msg.message = message;
1104 msg.wParam = wParam;
1105 msg.lParam = lParam;
1106 msg.time = GetTickCount();
1107 msg.pt.x = 0;
1108 msg.pt.y = 0;
1110 return MSG_AddMsg( GetTaskQueue(hTask), &msg, 0 );
1114 /***********************************************************************
1115 * SendMessage (USER.111)
1117 LRESULT SendMessage( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
1119 WND * wndPtr;
1120 LONG ret;
1121 struct
1123 LPARAM lParam;
1124 WPARAM wParam;
1125 UINT wMsg;
1126 HWND hWnd;
1127 } msgstruct = { lParam, wParam, msg, hwnd };
1129 #ifdef CONFIG_IPC
1130 MSG DDE_msg = { hwnd, msg, wParam, lParam };
1131 if (DDE_SendMessage(&DDE_msg)) return TRUE;
1132 #endif /* CONFIG_IPC */
1134 if (hwnd == HWND_BROADCAST)
1136 dprintf_msg(stddeb,"SendMessage // HWND_BROADCAST !\n");
1137 hwnd = GetTopWindow(GetDesktopWindow());
1138 while (hwnd)
1140 if (!(wndPtr = WIN_FindWndPtr(hwnd))) break;
1141 if (wndPtr->dwStyle & WS_POPUP || wndPtr->dwStyle & WS_CAPTION)
1143 dprintf_msg(stddeb,"BROADCAST Message to hWnd="NPFMT" m=%04X w=%04lX l=%08lX !\n",
1144 hwnd, msg, (DWORD)wParam, lParam);
1145 ret |= SendMessage( hwnd, msg, wParam, lParam );
1147 hwnd = wndPtr->hwndNext;
1149 dprintf_msg(stddeb,"SendMessage // End of HWND_BROADCAST !\n");
1150 return TRUE;
1153 EnterSpyMessage(SPY_SENDMESSAGE, hwnd, msg, wParam, lParam);
1155 HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 1, (LPARAM)MAKE_SEGPTR(&msgstruct) );
1156 if (!(wndPtr = WIN_FindWndPtr( hwnd )))
1158 ExitSpyMessage(SPY_RESULT_INVALIDHWND,hwnd,msg,0);
1159 return 0;
1161 ret = CallWindowProc( wndPtr->lpfnWndProc, msgstruct.hWnd, msgstruct.wMsg,
1162 msgstruct.wParam, msgstruct.lParam );
1163 ExitSpyMessage(SPY_RESULT_OK,hwnd,msg,ret);
1164 return ret;
1168 /***********************************************************************
1169 * WaitMessage (USER.112)
1171 void WaitMessage( void )
1173 MSG msg;
1174 MESSAGEQUEUE *queue;
1175 LONG nextExp = -1; /* Next timer expiration time */
1177 #ifdef CONFIG_IPC
1178 DDE_GetRemoteMessage();
1179 #endif /* CONFIG_IPC */
1181 if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return;
1182 if ((queue->wPostQMsg) ||
1183 (queue->status & (QS_SENDMESSAGE | QS_PAINT)) ||
1184 (queue->msgCount) || (sysMsgQueue->msgCount) )
1185 return;
1186 if ((queue->status & QS_TIMER) &&
1187 TIMER_CheckTimer( &nextExp, &msg, 0, FALSE))
1188 return;
1189 /* FIXME: (dde) must check DDE & X-events simultaneously */
1190 MSG_WaitXEvent( nextExp );
1194 /***********************************************************************
1195 * TranslateMessage (USER.113)
1197 BOOL TranslateMessage( LPMSG msg )
1199 int message = msg->message;
1201 if ((message == WM_KEYDOWN) || (message == WM_KEYUP) ||
1202 (message == WM_SYSKEYDOWN) || (message == WM_SYSKEYUP))
1204 dprintf_msg(stddeb, "Translating key message\n" );
1205 return TRUE;
1207 return FALSE;
1211 /***********************************************************************
1212 * DispatchMessage (USER.114)
1214 LONG DispatchMessage( LPMSG msg )
1216 WND * wndPtr;
1217 LONG retval;
1218 int painting;
1220 EnterSpyMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
1221 msg->wParam, msg->lParam );
1223 /* Process timer messages */
1224 if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
1226 if (msg->lParam)
1228 #ifndef WINELIB32
1229 HINSTANCE ds = msg->hwnd ? WIN_GetWindowInstance( msg->hwnd )
1230 : (HINSTANCE)CURRENT_DS;
1231 #endif
1232 /* HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1233 return CallWndProc( (WNDPROC)msg->lParam, ds, msg->hwnd,
1234 msg->message, msg->wParam, GetTickCount() );
1238 if (!msg->hwnd) return 0;
1239 if (!(wndPtr = WIN_FindWndPtr( msg->hwnd ))) return 0;
1240 if (!wndPtr->lpfnWndProc) return 0;
1241 painting = (msg->message == WM_PAINT);
1242 if (painting) wndPtr->flags |= WIN_NEEDS_BEGINPAINT;
1243 /* HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, 0, FIXME ); */
1244 retval = CallWindowProc( wndPtr->lpfnWndProc, msg->hwnd, msg->message,
1245 msg->wParam, msg->lParam );
1246 if (painting && IsWindow(msg->hwnd) &&
1247 (wndPtr->flags & WIN_NEEDS_BEGINPAINT))
1249 fprintf(stderr, "BeginPaint not called on WM_PAINT for hwnd "NPFMT"!\n",
1250 msg->hwnd);
1251 wndPtr->flags &= ~WIN_NEEDS_BEGINPAINT;
1253 return retval;
1257 /***********************************************************************
1258 * GetMessagePos (USER.119)
1260 DWORD GetMessagePos(void)
1262 MESSAGEQUEUE *queue;
1264 if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return 0;
1265 return queue->GetMessagePosVal;
1269 /***********************************************************************
1270 * GetMessageTime (USER.120)
1272 LONG GetMessageTime(void)
1274 MESSAGEQUEUE *queue;
1276 if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return 0;
1277 return queue->GetMessageTimeVal;
1281 /***********************************************************************
1282 * GetMessageExtraInfo (USER.288)
1284 LONG GetMessageExtraInfo(void)
1286 MESSAGEQUEUE *queue;
1288 if (!(queue = (MESSAGEQUEUE *)GlobalLock( GetTaskQueue(0) ))) return 0;
1289 return queue->GetMessageExtraInfoVal;
1293 /***********************************************************************
1294 * RegisterWindowMessage (USER.118)
1296 WORD RegisterWindowMessage( SEGPTR str )
1298 dprintf_msg(stddeb, "RegisterWindowMessage: '"SPFMT"'\n", str );
1299 return GlobalAddAtom( str );
1303 /***********************************************************************
1304 * GetTickCount (USER.13) (KERNEL32.299)
1306 DWORD GetTickCount(void)
1308 struct timeval t;
1309 gettimeofday( &t, NULL );
1310 return (t.tv_sec * 1000) + (t.tv_usec / 1000);
1313 /***********************************************************************
1314 * GetCurrentTime (effectively identical to GetTickCount)
1316 DWORD GetCurrentTime(void)
1318 return GetTickCount();
1321 /***********************************************************************
1322 * InSendMessage (USER.192
1324 * According to the book, this should return true iff the current message
1325 * was send from another application. In that case, the application should
1326 * invoke ReplyMessage before calling message relevant API.
1327 * Currently, Wine will always return FALSE, as there is no other app.
1329 BOOL InSendMessage()
1331 return FALSE;