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);