2 * Message queues related functions
4 * Copyright 1993 Alexandre Julliard
8 * This code assumes that there is only one Windows task (hence
12 static char Copyright
[] = "Copyright Alexandre Julliard, 1993";
16 #include <sys/types.h>
21 #include "sysmetrics.h"
23 #define MAX_QUEUE_SIZE 120 /* Max. size of a message queue */
25 extern BOOL
TIMER_CheckTimer( DWORD
*next
); /* timer.c */
26 extern void EVENT_ProcessEvent( XEvent
*event
); /* event.c */
27 extern void WINPOS_ChangeActiveWindow( HWND hwnd
, BOOL mouseMsg
); /*winpos.c*/
29 extern Display
* display
;
31 /* System message queue (for hardware events) */
32 static HANDLE hmemSysMsgQueue
= 0;
33 static MESSAGEQUEUE
* sysMsgQueue
= NULL
;
35 /* Application message queue (should be a list, one queue per task) */
36 static HANDLE hmemAppMsgQueue
= 0;
37 static MESSAGEQUEUE
* appMsgQueue
= NULL
;
39 /* Double-click time */
40 static int doubleClickSpeed
= 452;
43 /***********************************************************************
46 * Create a message queue.
48 static HANDLE
MSG_CreateMsgQueue( int size
)
51 MESSAGEQUEUE
* msgQueue
;
54 queueSize
= sizeof(MESSAGEQUEUE
) + size
* sizeof(QMSG
);
55 if (!(hQueue
= GlobalAlloc( GMEM_FIXED
, queueSize
))) return 0;
56 msgQueue
= (MESSAGEQUEUE
*) GlobalLock( hQueue
);
59 msgQueue
->msgSize
= sizeof(QMSG
);
60 msgQueue
->msgCount
= 0;
61 msgQueue
->nextMessage
= 0;
62 msgQueue
->nextFreeMessage
= 0;
63 msgQueue
->queueSize
= size
;
64 msgQueue
->GetMessageTimeVal
= 0;
65 msgQueue
->GetMessagePosVal
= 0;
66 msgQueue
->GetMessageExtraInfoVal
= 0;
71 msgQueue
->wPostQMsg
= 0;
72 msgQueue
->wExitCode
= 0;
73 msgQueue
->InSendMessageHandle
= 0;
74 msgQueue
->wPaintCount
= 0;
75 msgQueue
->wTimerCount
= 0;
76 msgQueue
->tempStatus
= 0;
78 GlobalUnlock( hQueue
);
83 /***********************************************************************
84 * MSG_CreateSysMsgQueue
86 * Create the system message queue, and set the double-click speed.
87 * Must be called only once.
89 BOOL
MSG_CreateSysMsgQueue( int size
)
91 if (size
> MAX_QUEUE_SIZE
) size
= MAX_QUEUE_SIZE
;
92 else if (size
<= 0) size
= 1;
93 if (!(hmemSysMsgQueue
= MSG_CreateMsgQueue( size
))) return FALSE
;
94 sysMsgQueue
= (MESSAGEQUEUE
*) GlobalLock( hmemSysMsgQueue
);
95 doubleClickSpeed
= GetProfileInt( "windows", "DoubleClickSpeed", 452 );
100 /***********************************************************************
103 * Add a message to the queue. Return FALSE if queue is full.
105 static int MSG_AddMsg( MESSAGEQUEUE
* msgQueue
, MSG
* msg
, DWORD extraInfo
)
109 SpyMessage(msg
->hwnd
, msg
->message
, msg
->wParam
, msg
->lParam
);
111 if (!msgQueue
) return FALSE
;
112 pos
= msgQueue
->nextFreeMessage
;
114 /* Check if queue is full */
115 if ((pos
== msgQueue
->nextMessage
) && (msgQueue
->msgCount
> 0))
119 msgQueue
->messages
[pos
].msg
= *msg
;
120 msgQueue
->messages
[pos
].extraInfo
= extraInfo
;
121 if (pos
< msgQueue
->queueSize
-1) pos
++;
123 msgQueue
->nextFreeMessage
= pos
;
124 msgQueue
->msgCount
++;
125 msgQueue
->status
|= QS_POSTMESSAGE
;
126 msgQueue
->tempStatus
|= QS_POSTMESSAGE
;
131 /***********************************************************************
134 * Find a message matching the given parameters. Return -1 if none available.
136 static int MSG_FindMsg(MESSAGEQUEUE
* msgQueue
, HWND hwnd
, int first
, int last
)
138 int i
, pos
= msgQueue
->nextMessage
;
140 if (!msgQueue
->msgCount
) return -1;
141 if (!hwnd
&& !first
&& !last
) return pos
;
143 for (i
= 0; i
< msgQueue
->msgCount
; i
++)
145 MSG
* msg
= &msgQueue
->messages
[pos
].msg
;
147 if (!hwnd
|| (msg
->hwnd
== hwnd
))
149 if (!first
&& !last
) return pos
;
150 if ((msg
->message
>= first
) && (msg
->message
<= last
)) return pos
;
152 if (pos
< msgQueue
->queueSize
-1) pos
++;
159 /***********************************************************************
162 * Remove a message from the queue (pos must be a valid position).
164 static void MSG_RemoveMsg( MESSAGEQUEUE
* msgQueue
, int pos
)
168 if (!msgQueue
) return;
169 qmsg
= &msgQueue
->messages
[pos
];
171 if (pos
>= msgQueue
->nextMessage
)
173 int count
= pos
- msgQueue
->nextMessage
;
174 if (count
) memmove( &msgQueue
->messages
[msgQueue
->nextMessage
+1],
175 &msgQueue
->messages
[msgQueue
->nextMessage
],
176 count
* sizeof(QMSG
) );
177 msgQueue
->nextMessage
++;
178 if (msgQueue
->nextMessage
>= msgQueue
->queueSize
)
179 msgQueue
->nextMessage
= 0;
183 int count
= msgQueue
->nextFreeMessage
- pos
;
184 if (count
) memmove( &msgQueue
->messages
[pos
],
185 &msgQueue
->messages
[pos
+1], count
* sizeof(QMSG
) );
186 if (msgQueue
->nextFreeMessage
) msgQueue
->nextFreeMessage
--;
187 else msgQueue
->nextFreeMessage
= msgQueue
->queueSize
-1;
189 msgQueue
->msgCount
--;
190 if (!msgQueue
->msgCount
) msgQueue
->status
&= ~QS_POSTMESSAGE
;
191 msgQueue
->tempStatus
= 0;
195 /***********************************************************************
196 * MSG_TranslateMouseMsg
198 * Translate an mouse hardware event into a real mouse message.
199 * Return value indicates whether the translated message must be passed
202 * - Translate button-down messages in double-clicks.
203 * - Send the WM_NCHITTEST message to find where the cursor is.
204 * - Activate the window if needed.
205 * - Translate the message into a non-client message, or translate
206 * the coordinates to client coordinates.
207 * - Send the WM_SETCURSOR message.
209 static BOOL
MSG_TranslateMouseMsg( MSG
*msg
)
212 static DWORD lastClickTime
= 0;
213 static WORD lastClickMsg
= 0;
214 static POINT lastClickPos
= { 0, 0 };
216 BOOL mouseClick
= ((msg
->message
== WM_LBUTTONDOWN
) ||
217 (msg
->message
== WM_RBUTTONDOWN
) ||
218 (msg
->message
== WM_MBUTTONDOWN
));
220 /* Send the WM_NCHITTEST message */
222 LONG hittest_result
= SendMessage( msg
->hwnd
, WM_NCHITTEST
, 0,
223 MAKELONG( msg
->pt
.x
, msg
->pt
.y
) );
225 /* Activate the window if needed */
229 HWND parent
, hwndTop
= msg
->hwnd
;
230 while ((parent
= GetParent(hwndTop
)) != 0) hwndTop
= parent
;
231 if (hwndTop
!= GetActiveWindow())
233 LONG ret
= SendMessage( msg
->hwnd
, WM_MOUSEACTIVATE
, hwndTop
,
234 MAKELONG( hittest_result
, msg
->message
) );
235 if ((ret
== MA_ACTIVATEANDEAT
) || (ret
== MA_NOACTIVATEANDEAT
))
237 if ((ret
== MA_ACTIVATE
) || (ret
== MA_ACTIVATEANDEAT
))
239 SetWindowPos( hwndTop
, HWND_TOP
, 0, 0, 0, 0,
240 SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
);
241 WINPOS_ChangeActiveWindow( hwndTop
, TRUE
);
246 /* Send the WM_SETCURSOR message */
248 SendMessage( msg
->hwnd
, WM_SETCURSOR
, msg
->hwnd
,
249 MAKELONG( hittest_result
, msg
->message
));
250 if (eatMsg
) return FALSE
;
252 /* Check for double-click */
256 BOOL dbl_click
= FALSE
;
258 if ((msg
->message
== lastClickMsg
) &&
259 (msg
->time
- lastClickTime
< doubleClickSpeed
) &&
260 (abs(msg
->pt
.x
- lastClickPos
.x
) < SYSMETRICS_CXDOUBLECLK
/2) &&
261 (abs(msg
->pt
.y
- lastClickPos
.y
) < SYSMETRICS_CYDOUBLECLK
/2))
264 if (dbl_click
&& (hittest_result
== HTCLIENT
))
266 /* Check whether window wants the double click message. */
267 WND
* wndPtr
= WIN_FindWndPtr( msg
->hwnd
);
268 if (!wndPtr
|| !(wndPtr
->flags
& WIN_DOUBLE_CLICKS
))
272 if (dbl_click
) switch(msg
->message
)
274 case WM_LBUTTONDOWN
: msg
->message
= WM_LBUTTONDBLCLK
; break;
275 case WM_RBUTTONDOWN
: msg
->message
= WM_RBUTTONDBLCLK
; break;
276 case WM_MBUTTONDOWN
: msg
->message
= WM_MBUTTONDBLCLK
; break;
279 lastClickTime
= msg
->time
;
280 lastClickMsg
= msg
->message
;
281 lastClickPos
= msg
->pt
;
284 /* Build the translated message */
286 msg
->lParam
= MAKELONG( msg
->pt
.x
, msg
->pt
.y
);
287 if (hittest_result
== HTCLIENT
)
289 ScreenToClient( msg
->hwnd
, (LPPOINT
)&msg
->lParam
);
293 msg
->wParam
= hittest_result
;
294 msg
->message
+= WM_NCLBUTTONDOWN
- WM_LBUTTONDOWN
;
300 /**********************************************************************
301 * SetDoubleClickTime (USER.20)
303 void SetDoubleClickTime( WORD interval
)
306 doubleClickSpeed
= 500;
308 doubleClickSpeed
= interval
;
312 /**********************************************************************
313 * GetDoubleClickTime (USER.21)
315 WORD
GetDoubleClickTime()
317 return (WORD
)doubleClickSpeed
;
321 /***********************************************************************
324 void MSG_IncPaintCount( HANDLE hQueue
)
326 if (hQueue
!= hmemAppMsgQueue
) return;
327 appMsgQueue
->wPaintCount
++;
328 appMsgQueue
->status
|= QS_PAINT
;
329 appMsgQueue
->tempStatus
|= QS_PAINT
;
333 /***********************************************************************
336 void MSG_DecPaintCount( HANDLE hQueue
)
338 if (hQueue
!= hmemAppMsgQueue
) return;
339 appMsgQueue
->wPaintCount
--;
340 if (!appMsgQueue
->wPaintCount
) appMsgQueue
->status
&= ~QS_PAINT
;
344 /***********************************************************************
347 void MSG_IncTimerCount( HANDLE hQueue
)
349 if (hQueue
!= hmemAppMsgQueue
) return;
350 appMsgQueue
->wTimerCount
++;
351 appMsgQueue
->status
|= QS_TIMER
;
352 appMsgQueue
->tempStatus
|= QS_TIMER
;
356 /***********************************************************************
359 void MSG_DecTimerCount( HANDLE hQueue
)
361 if (hQueue
!= hmemAppMsgQueue
) return;
362 appMsgQueue
->wTimerCount
--;
363 if (!appMsgQueue
->wTimerCount
) appMsgQueue
->status
&= ~QS_TIMER
;
367 /***********************************************************************
370 * Add an event to the system message queue.
371 * Note: the position is in screen coordinates.
373 void hardware_event( HWND hwnd
, WORD message
, WORD wParam
, LONG lParam
,
374 WORD xPos
, WORD yPos
, DWORD time
, DWORD extraInfo
)
379 msg
.message
= message
;
385 if (!MSG_AddMsg( sysMsgQueue
, &msg
, extraInfo
))
386 printf( "hardware_event: Queue is full\n" );
390 /***********************************************************************
391 * MSG_GetHardwareMessage
393 * Like GetMessage(), but only return mouse and keyboard events.
394 * Used internally for window moving and resizing. Mouse messages
395 * are not translated.
397 BOOL
MSG_GetHardwareMessage( LPMSG msg
)
404 if ((pos
= MSG_FindMsg( sysMsgQueue
, 0, 0, 0 )) != -1)
406 *msg
= sysMsgQueue
->messages
[pos
].msg
;
407 MSG_RemoveMsg( sysMsgQueue
, pos
);
410 XNextEvent( display
, &event
);
411 EVENT_ProcessEvent( &event
);
417 /***********************************************************************
418 * SetTaskQueue (KERNEL.34)
420 WORD
SetTaskQueue( HANDLE hTask
, HANDLE hQueue
)
422 HANDLE prev
= hmemAppMsgQueue
;
423 hmemAppMsgQueue
= hQueue
;
428 /***********************************************************************
429 * GetTaskQueue (KERNEL.35)
431 WORD
GetTaskQueue( HANDLE hTask
)
433 return hmemAppMsgQueue
;
437 /***********************************************************************
438 * SetMessageQueue (USER.266)
440 BOOL
SetMessageQueue( int size
)
444 if ((size
> MAX_QUEUE_SIZE
) || (size
<= 0)) return TRUE
;
446 /* Free the old message queue */
447 if ((hQueue
= GetTaskQueue(0)) != 0)
449 GlobalUnlock( hQueue
);
450 GlobalFree( hQueue
);
453 if (!(hQueue
= MSG_CreateMsgQueue( size
))) return FALSE
;
454 SetTaskQueue( 0, hQueue
);
455 appMsgQueue
= (MESSAGEQUEUE
*)GlobalLock( hQueue
);
460 /***********************************************************************
461 * PostQuitMessage (USER.6)
463 void PostQuitMessage( int exitCode
)
465 if (!appMsgQueue
) return;
466 appMsgQueue
->wPostQMsg
= TRUE
;
467 appMsgQueue
->wExitCode
= exitCode
;
471 /***********************************************************************
472 * GetQueueStatus (USER.334)
474 DWORD
GetQueueStatus( int flags
)
476 unsigned long ret
= (appMsgQueue
->status
<< 16) | appMsgQueue
->tempStatus
;
477 appMsgQueue
->tempStatus
= 0;
478 return ret
& ((flags
<< 16) | flags
);
482 /***********************************************************************
483 * GetInputState (USER.335)
487 return appMsgQueue
->status
& (QS_KEY
| QS_MOUSEBUTTON
);
491 /***********************************************************************
494 static BOOL
MSG_PeekMessage( MESSAGEQUEUE
* msgQueue
, LPMSG msg
, HWND hwnd
,
495 WORD first
, WORD last
, WORD flags
, BOOL peek
)
498 DWORD nextExp
; /* Next timer expiration time */
503 mask
= QS_POSTMESSAGE
; /* Always selectioned */
504 if ((first
<= WM_KEYLAST
) && (last
>= WM_KEYFIRST
)) mask
|= QS_KEY
;
505 if ((first
<= WM_MOUSELAST
) && (last
>= WM_MOUSEFIRST
)) mask
|= QS_MOUSE
;
506 if ((first
<= WM_TIMER
) && (last
>= WM_TIMER
)) mask
|= WM_TIMER
;
507 if ((first
<= WM_SYSTIMER
) && (last
>= WM_SYSTIMER
)) mask
|= WM_TIMER
;
508 if ((first
<= WM_PAINT
) && (last
>= WM_PAINT
)) mask
|= WM_PAINT
;
510 else mask
= QS_MOUSE
| QS_KEY
| QS_POSTMESSAGE
| QS_TIMER
| QS_PAINT
;
512 while (XPending( display
))
514 XNextEvent( display
, &event
);
515 EVENT_ProcessEvent( &event
);
520 /* First handle a WM_QUIT message */
521 if (msgQueue
->wPostQMsg
)
524 msg
->message
= WM_QUIT
;
525 msg
->wParam
= msgQueue
->wExitCode
;
530 /* Then handle a message put by SendMessage() */
531 if (msgQueue
->status
& QS_SENDMESSAGE
)
533 if (!hwnd
|| (msgQueue
->hWnd
== hwnd
))
535 if ((!first
&& !last
) ||
536 ((msgQueue
->msg
>= first
) && (msgQueue
->msg
<= last
)))
538 msg
->hwnd
= msgQueue
->hWnd
;
539 msg
->message
= msgQueue
->msg
;
540 msg
->wParam
= msgQueue
->wParam
;
541 msg
->lParam
= msgQueue
->lParam
;
542 if (flags
& PM_REMOVE
) msgQueue
->status
&= ~QS_SENDMESSAGE
;
548 /* Now find a normal message */
549 pos
= MSG_FindMsg( msgQueue
, hwnd
, first
, last
);
552 QMSG
*qmsg
= &msgQueue
->messages
[pos
];
554 msgQueue
->GetMessageTimeVal
= msg
->time
;
555 msgQueue
->GetMessagePosVal
= *(DWORD
*)&msg
->pt
;
556 msgQueue
->GetMessageExtraInfoVal
= qmsg
->extraInfo
;
558 if (flags
& PM_REMOVE
) MSG_RemoveMsg( msgQueue
, pos
);
562 /* Now find a hardware event */
563 pos
= MSG_FindMsg( sysMsgQueue
, hwnd
, first
, last
);
566 QMSG
*qmsg
= &sysMsgQueue
->messages
[pos
];
568 msgQueue
->GetMessageTimeVal
= msg
->time
;
569 msgQueue
->GetMessagePosVal
= *(DWORD
*)&msg
->pt
;
570 msgQueue
->GetMessageExtraInfoVal
= qmsg
->extraInfo
;
572 if ((msg
->message
>= WM_MOUSEFIRST
) &&
573 (msg
->message
<= WM_MOUSELAST
))
574 if (!MSG_TranslateMouseMsg( msg
))
576 MSG_RemoveMsg( sysMsgQueue
, pos
);
579 if (flags
& PM_REMOVE
) MSG_RemoveMsg( sysMsgQueue
, pos
);
583 /* Now find a WM_PAINT message */
584 if ((msgQueue
->status
& QS_PAINT
) && (mask
& QS_PAINT
))
586 msg
->hwnd
= WIN_FindWinToRepaint( hwnd
);
587 msg
->message
= WM_PAINT
;
590 if (msg
->hwnd
!= 0) break;
593 /* Finally handle WM_TIMER messages */
594 if ((msgQueue
->status
& QS_TIMER
) && (mask
& QS_TIMER
))
595 if (TIMER_CheckTimer( &nextExp
))
596 continue; /* Restart the whole search */
598 /* Wait until something happens */
599 if (peek
) return FALSE
;
600 if (!XPending( display
) && (nextExp
!= -1))
603 struct timeval timeout
;
604 int fd
= ConnectionNumber(display
);
605 FD_ZERO( &read_set
);
606 FD_SET( fd
, &read_set
);
607 timeout
.tv_sec
= nextExp
/ 1000;
608 timeout
.tv_usec
= (nextExp
% 1000) * 1000;
609 if (select( fd
+1, &read_set
, NULL
, NULL
, &timeout
) != 1)
610 continue; /* On timeout or error, restart from the start */
612 XNextEvent( display
, &event
);
613 EVENT_ProcessEvent( &event
);
616 /* We got a message */
617 if (peek
) return TRUE
;
618 else return (msg
->message
!= WM_QUIT
);
622 /***********************************************************************
623 * PeekMessage (USER.109)
625 BOOL
PeekMessage( LPMSG msg
, HWND hwnd
, WORD first
, WORD last
, WORD flags
)
627 return MSG_PeekMessage( appMsgQueue
, msg
, hwnd
, first
, last
, flags
, TRUE
);
631 /***********************************************************************
632 * GetMessage (USER.108)
634 BOOL
GetMessage( LPMSG msg
, HWND hwnd
, WORD first
, WORD last
)
636 return MSG_PeekMessage( appMsgQueue
, msg
, hwnd
, first
, last
, PM_REMOVE
, FALSE
);
640 /***********************************************************************
641 * PostMessage (USER.110)
643 BOOL
PostMessage( HWND hwnd
, WORD message
, WORD wParam
, LONG lParam
)
648 msg
.message
= message
;
651 msg
.time
= GetTickCount();
655 return MSG_AddMsg( appMsgQueue
, &msg
, 0 );
659 /***********************************************************************
660 * SendMessage (USER.111)
662 LONG
SendMessage( HWND hwnd
, WORD msg
, WORD wParam
, LONG lParam
)
666 SpyMessage(hwnd
, msg
, wParam
, lParam
);
668 wndPtr
= WIN_FindWndPtr( hwnd
);
669 if (!wndPtr
) return 0;
670 return CallWindowProc( wndPtr
->lpfnWndProc
, hwnd
, msg
, wParam
, lParam
);
674 /***********************************************************************
675 * TranslateMessage (USER.113)
677 BOOL
TranslateMessage( LPMSG msg
)
679 int message
= msg
->message
;
681 if ((message
== WM_KEYDOWN
) || (message
== WM_KEYUP
) ||
682 (message
== WM_SYSKEYDOWN
) || (message
== WM_SYSKEYUP
))
685 printf( "Translating key message\n" );
693 /***********************************************************************
694 * DispatchMessage (USER.114)
696 LONG
DispatchMessage( LPMSG msg
)
703 printf( "Dispatch message hwnd=%08x msg=0x%x w=%d l=%d time=%u pt=%d,%d\n",
704 msg
->hwnd
, msg
->message
, msg
->wParam
, msg
->lParam
,
705 msg
->time
, msg
->pt
.x
, msg
->pt
.y
);
708 /* Process timer messages */
709 if ((msg
->message
== WM_TIMER
) || (msg
->message
== WM_SYSTIMER
))
712 return CallWindowProc( (FARPROC
)msg
->lParam
, msg
->hwnd
,
713 msg
->message
, msg
->wParam
, GetTickCount() );
716 if (!msg
->hwnd
) return 0;
717 if (!(wndPtr
= WIN_FindWndPtr( msg
->hwnd
))) return 0;
718 if (!wndPtr
->lpfnWndProc
) return 0;
719 painting
= (msg
->message
== WM_PAINT
);
720 if (painting
) wndPtr
->flags
|= WIN_NEEDS_BEGINPAINT
;
721 retval
= CallWindowProc( wndPtr
->lpfnWndProc
, msg
->hwnd
, msg
->message
,
722 msg
->wParam
, msg
->lParam
);
723 if (painting
&& (wndPtr
->flags
& WIN_NEEDS_BEGINPAINT
))
726 printf( "BeginPaint not called on WM_PAINT for hwnd %d!\n", msg
->hwnd
);
728 wndPtr
->flags
&= ~WIN_NEEDS_BEGINPAINT
;
734 /***********************************************************************
735 * GetMessagePos (USER.119)
737 DWORD
GetMessagePos(void)
739 return appMsgQueue
->GetMessagePosVal
;
743 /***********************************************************************
744 * GetMessageTime (USER.120)
746 LONG
GetMessageTime(void)
748 return appMsgQueue
->GetMessageTimeVal
;
752 /***********************************************************************
753 * GetMessageExtraInfo (USER.288)
755 LONG
GetMessageExtraInfo(void)
757 return appMsgQueue
->GetMessageExtraInfoVal
;
761 /***********************************************************************
762 * RegisterWindowMessage (USER.118)
764 WORD
RegisterWindowMessage( LPCSTR str
)
767 printf( "RegisterWindowMessage: '%s'\n", str
);
769 return GlobalAddAtom( str
);
773 /***********************************************************************
774 * GetTickCount (USER.13)
779 gettimeofday( &t
, NULL
);
780 return (t
.tv_sec
* 1000) + (t
.tv_usec
/ 1000);