mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-09-28 16:17:09 +08:00
1794 lines
47 KiB
C
1794 lines
47 KiB
C
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IMPORTANT NOTICE
|
|
//
|
|
// The following open source license statement does not apply to any
|
|
// entity in the Exception List published by FMSoft.
|
|
//
|
|
// For more information, please visit:
|
|
//
|
|
// https://www.fmsoft.cn/exception-list
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
* This file is part of MiniGUI, a mature cross-platform windowing
|
|
* and Graphics User Interface (GUI) support system for embedded systems
|
|
* and smart IoT devices.
|
|
*
|
|
* Copyright (C) 2002~2020, Beijing FMSoft Technologies Co., Ltd.
|
|
* Copyright (C) 1998~2002, WEI Yongming
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Or,
|
|
*
|
|
* As this program is a library, any link to this program must follow
|
|
* GNU General Public License version 3 (GPLv3). If you cannot accept
|
|
* GPLv3, you need to be licensed from FMSoft.
|
|
*
|
|
* If you have got a commercial license of this program, please use it
|
|
* under the terms and conditions of the commercial license.
|
|
*
|
|
* For more information about the commercial license, please refer to
|
|
* <http://www.minigui.com/blog/minigui-licensing-policy/>.
|
|
*/
|
|
/*
|
|
** message.c: The messaging module.
|
|
**
|
|
** Current maintainer: Wei Yongming.
|
|
**
|
|
** Create date: 2000/11/05
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "common.h"
|
|
#include "minigui.h"
|
|
#include "gdi.h"
|
|
#include "window.h"
|
|
|
|
#include "blockheap.h"
|
|
#include "cliprect.h"
|
|
#include "gal.h"
|
|
#include "internals.h"
|
|
#include "ctrlclass.h"
|
|
#include "timer.h"
|
|
#include "misc.h"
|
|
#include "debug.h"
|
|
|
|
#ifdef HAVE_SELECT
|
|
#include "mgsock.h"
|
|
#endif
|
|
|
|
#ifdef _MGRM_PROCESSES
|
|
#include "sharedres.h"
|
|
#endif
|
|
|
|
#include "license.h"
|
|
#ifdef _MG_ENABLE_LICENSE
|
|
# include "../sysres/license/c_files/02_feiman.dat.c"
|
|
#endif
|
|
|
|
/******************************************************************************/
|
|
|
|
#if 0 /* deprecated code */
|
|
/* since 5.0.0, we use post_quit_to_all_message_threads instead */
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
|
|
#define SET_PADD(value) pMsg->pSyncMsg = value
|
|
#define SYNMSGNAME pMsg->pSyncMsg?"Sync":"Normal"
|
|
|
|
/* XXX: Need desktop thread keep alive to wakeup sem_wait()
|
|
by delivering messages */
|
|
#define TEST_NEED_TO_QUIT(queue) \
|
|
((__mg_quiting_stage <= _MG_QUITING_STAGE_FORCE) && \
|
|
((queue)!=__mg_dsk_msg_queue))
|
|
#else /* defined _MGHAVE_VIRTUAL_WINDOW */
|
|
|
|
#define SET_PADD(value)
|
|
#define SYNMSGNAME "Normal"
|
|
#define TEST_NEED_TO_QUIT(queue) \
|
|
(__mg_quiting_stage <= _MG_QUITING_STAGE_FORCE)
|
|
|
|
#endif /* not defined _MGHAVE_VIRTUAL_WINDOW */
|
|
|
|
#define TEST_IF_QUIT(queue, hWnd) \
|
|
do { \
|
|
if (TEST_NEED_TO_QUIT(queue)) { \
|
|
LOCK_MSGQ (queue); \
|
|
if (!(queue->dwState & QS_QUIT)){ \
|
|
queue->loop_depth++; \
|
|
queue->dwState |= QS_QUIT; \
|
|
UNLOCK_MSGQ (queue); \
|
|
if (IsDialog(hWnd)) { \
|
|
EndDialog (hWnd, IDCANCEL); \
|
|
} \
|
|
else { \
|
|
DestroyMainWindow (hWnd); \
|
|
} \
|
|
} else { \
|
|
UNLOCK_MSGQ (queue); \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
#endif /* deprecated code */
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
# define SET_PADD(value) pMsg->pSyncMsg = value
|
|
#else
|
|
# define SET_PADD(value)
|
|
#endif
|
|
|
|
#define TEST_IF_QUIT(queue, hWnd)
|
|
|
|
#define ALLOCQMSG() (PQMSG)BlockDataAlloc(&QMSGHeap)
|
|
#define FREEQMSG(pqmsg) BlockDataFree(&QMSGHeap, pqmsg)
|
|
#define GETWNDPROC(hWnd) (((PMAINWIN)hWnd)->MainWindowProc)
|
|
#define GETNOTIFPROC(hWnd) (((PMAINWIN)hWnd)->NotifProc)
|
|
|
|
/****************************** Message Allocation ****************************/
|
|
static BLOCKHEAP QMSGHeap;
|
|
|
|
/* QMSG allocation */
|
|
BOOL mg_InitFreeQMSGList (void)
|
|
{
|
|
InitBlockDataHeap (&QMSGHeap, sizeof (QMSG), SIZE_QMSG_HEAP);
|
|
return TRUE;
|
|
}
|
|
|
|
void mg_DestroyFreeQMSGList (void)
|
|
{
|
|
DestroyBlockDataHeap (&QMSGHeap);
|
|
}
|
|
|
|
/****************************** Message Queue Management ************************/
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
pthread_key_t __mg_threadinfo_key;
|
|
#endif
|
|
|
|
MSGQUEUE* mg_AllocMsgQueueForThisThread (void)
|
|
{
|
|
MSGQUEUE* pMsgQueue;
|
|
|
|
if (!(pMsgQueue = malloc (sizeof(MSGQUEUE)))) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!mg_InitMsgQueue (pMsgQueue, 0)) {
|
|
free (pMsgQueue);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
pMsgQueue->th = pthread_self();
|
|
pthread_setspecific (__mg_threadinfo_key, pMsgQueue);
|
|
#endif
|
|
|
|
// register this new message queue
|
|
SendMessage (HWND_DESKTOP, MSG_MANAGE_MSGTHREAD,
|
|
MSGTHREAD_SIGNIN, (LPARAM)pMsgQueue);
|
|
return pMsgQueue;
|
|
}
|
|
|
|
void mg_FreeMsgQueueForThisThread (void)
|
|
{
|
|
MSGQUEUE* pMsgQueue;
|
|
|
|
pMsgQueue = getMsgQueueForThisThread ();
|
|
|
|
if (pMsgQueue) {
|
|
if (pMsgQueue->nrWindows > 0) {
|
|
_WRN_PRINTF ("there are still some windows not destroyed\n");
|
|
}
|
|
|
|
mg_DestroyMsgQueue (pMsgQueue);
|
|
free (pMsgQueue);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
#ifdef __VXWORKS__
|
|
pthread_setspecific (__mg_threadinfo_key, (void*)-1);
|
|
#else
|
|
pthread_setspecific (__mg_threadinfo_key, NULL);
|
|
#endif
|
|
#endif /* defined _MGHAVE_VIRTUAL_WINDOW */
|
|
}
|
|
else {
|
|
_WRN_PRINTF ("message queue for this thread has gone\n");
|
|
}
|
|
}
|
|
|
|
static int handle_idle_message (MSGQUEUE* msg_queue)
|
|
{
|
|
int n = 0;
|
|
|
|
msg_queue->idle_counter++;
|
|
if (msg_queue->idle_counter >= MAX_IDLE_COUNTER) {
|
|
// it is time to post MSG_IDLE to all windows in this thread.
|
|
MSG msg = { HWND_NULL, MSG_IDLE, __mg_tick_counter, 0 };
|
|
n = __mg_broadcast_message (msg_queue, &msg);
|
|
msg_queue->idle_counter = 0;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
#ifdef HAVE_SELECT
|
|
/* the idle handler for a message queue with checking listening fds */
|
|
static BOOL std_idle_handler (MSGQUEUE* msg_queue, BOOL wait)
|
|
{
|
|
int retval, n = 0;
|
|
fd_set rset, wset, eset;
|
|
fd_set* rsetptr = NULL;
|
|
fd_set* wsetptr = NULL;
|
|
fd_set* esetptr = NULL;
|
|
struct timeval sel_timeout;
|
|
|
|
if (wait) {
|
|
sel_timeout.tv_sec = 0;
|
|
sel_timeout.tv_usec = USEC_10MS;
|
|
}
|
|
else {
|
|
sel_timeout.tv_sec = 0;
|
|
sel_timeout.tv_usec = 0;
|
|
}
|
|
|
|
/* a fdset got modified each time around */
|
|
if (msg_queue->nr_rfds) {
|
|
rset = msg_queue->rfdset;
|
|
rsetptr = &rset;
|
|
}
|
|
if (msg_queue->nr_wfds) {
|
|
wset = msg_queue->wfdset;
|
|
wsetptr = &wset;
|
|
}
|
|
if (msg_queue->nr_efds) {
|
|
eset = msg_queue->efdset;
|
|
esetptr = &eset;
|
|
}
|
|
|
|
if ((retval = select (msg_queue->maxfd + 1,
|
|
rsetptr, wsetptr, esetptr, &sel_timeout)) < 0) {
|
|
/* no event. */
|
|
if (errno != EINTR) {
|
|
_WRN_PRINTF ("unexpected error of select(): %m\n");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
n += __mg_check_expired_timers (msg_queue,
|
|
__mg_tick_counter - msg_queue->old_tick_count);
|
|
msg_queue->old_tick_count = __mg_tick_counter;
|
|
|
|
if (rsetptr || wsetptr || esetptr) {
|
|
n += __mg_kernel_check_listen_fds (msg_queue, rsetptr, wsetptr, esetptr);
|
|
}
|
|
|
|
return n > 0;
|
|
}
|
|
|
|
#else /* defined HAVE_SELECT */
|
|
|
|
static BOOL std_idle_handler (MSGQUEUE* msg_queue, BOOL wait)
|
|
{
|
|
int n = 0;
|
|
long timeout_ms;
|
|
|
|
if (wait) {
|
|
timeout_ms = 10;
|
|
}
|
|
else {
|
|
timeout_ms = 0;
|
|
}
|
|
|
|
if (timeout_ms > 0)
|
|
__mg_os_time_delay (timeout_ms);
|
|
|
|
n = __mg_check_expired_timers (msg_queue,
|
|
__mg_tick_counter - msg_queue->old_tick_count);
|
|
msg_queue->old_tick_count = __mg_tick_counter;
|
|
return n > 0;
|
|
}
|
|
|
|
#endif /* not defined HAVE_SELECT */
|
|
|
|
BOOL mg_InitMsgQueue (PMSGQUEUE pMsgQueue, int iBufferLen)
|
|
{
|
|
memset (pMsgQueue, 0, sizeof(MSGQUEUE));
|
|
|
|
pMsgQueue->dwState = QS_EMPTY;
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
/* since 5.0.0, we use recursive lock */
|
|
{
|
|
int ret;
|
|
pthread_mutexattr_t my_attr;
|
|
|
|
ret = pthread_mutexattr_init (&my_attr);
|
|
if (ret) return FALSE;
|
|
ret = pthread_mutexattr_settype (&my_attr, PTHREAD_MUTEX_RECURSIVE);
|
|
if (ret) return FALSE;
|
|
ret = pthread_mutex_init (&pMsgQueue->lock, &my_attr);
|
|
if (ret) return FALSE;
|
|
ret = pthread_mutexattr_destroy (&my_attr);
|
|
if (ret) return FALSE;
|
|
ret = sem_init (&pMsgQueue->wait, 0, 0);
|
|
if (ret) return FALSE;
|
|
//sem_init (&pMsgQueue->sync_msg, 0, 0);
|
|
}
|
|
#endif
|
|
|
|
if (iBufferLen <= 0)
|
|
iBufferLen = DEF_MSGQUEUE_LEN;
|
|
|
|
pMsgQueue->msg = malloc (sizeof (MSG) * iBufferLen);
|
|
|
|
if (!pMsgQueue->msg) {
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
pthread_mutex_destroy (&pMsgQueue->lock);
|
|
sem_destroy (&pMsgQueue->wait);
|
|
//sem_destroy (&pMsgQueue->sync_msg);
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
pMsgQueue->len = iBufferLen;
|
|
|
|
pMsgQueue->OnIdle = std_idle_handler;
|
|
|
|
/* Since 5.0.0, MiniGUI provides support for timers per message thread */
|
|
// pMsgQueue->first_timer_slot = 0;
|
|
// pMsgQueue->expired_timer_mask = 0;
|
|
// memset (pMsgQueue->timer_slots, 0, sizeof (pMsgQueue->timer_slots));
|
|
pMsgQueue->old_tick_count = __mg_tick_counter;
|
|
|
|
#ifdef HAVE_SELECT
|
|
/* Since 5.0.0, MiniGUI supports listening file descriptors
|
|
per message thread */
|
|
// pMsgQueue->nr_fd_slots = 0;
|
|
// pMsgQueue->maxfd = 0;
|
|
// pMsgQueue->nr_rfds = 0;
|
|
// pMsgQueue->nr_wfds = 0;
|
|
// pMsgQueue->nr_efds = 0;
|
|
// pMsgQueue->fd_slots = NULL;
|
|
mg_fd_zero (&pMsgQueue->rfdset);
|
|
mg_fd_zero (&pMsgQueue->wfdset);
|
|
mg_fd_zero (&pMsgQueue->efdset);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void mg_DestroyMsgQueue (PMSGQUEUE pMsgQueue)
|
|
{
|
|
PQMSG head;
|
|
PQMSG next;
|
|
|
|
if (pMsgQueue->nrWindows > 0) {
|
|
_WRN_PRINTF ("There are still some windows not destroyed.\n");
|
|
}
|
|
|
|
head = next = pMsgQueue->pFirstNotifyMsg;
|
|
while (head) {
|
|
next = head->next;
|
|
|
|
FREEQMSG (head);
|
|
head = next;
|
|
}
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
pthread_mutex_destroy (&pMsgQueue->lock);
|
|
sem_destroy (&pMsgQueue->wait);
|
|
//sem_destroy (&pMsgQueue->sync_msg);
|
|
#endif
|
|
|
|
__mg_remove_timers_by_msg_queue (pMsgQueue);
|
|
|
|
if (pMsgQueue->msg)
|
|
free (pMsgQueue->msg);
|
|
pMsgQueue->msg = NULL;
|
|
}
|
|
|
|
/* post a message to a message queue */
|
|
BOOL kernel_QueueMessage (PMSGQUEUE msg_que, PMSG msg)
|
|
{
|
|
if (msg_que == NULL || msg == NULL)
|
|
return FALSE;
|
|
|
|
LOCK_MSGQ(msg_que);
|
|
|
|
msg->time = __mg_tick_counter;
|
|
|
|
/* check for the duplicated messages */
|
|
if (msg->message == MSG_MOUSEMOVE
|
|
|| msg->message == MSG_NCMOUSEMOVE
|
|
|| msg->message == MSG_DT_MOUSEMOVE) {
|
|
int readpos = msg_que->readpos;
|
|
PMSG a_msg, last_msg = NULL;
|
|
|
|
while (readpos != msg_que->writepos) {
|
|
a_msg = msg_que->msg + readpos;
|
|
|
|
if (a_msg->message == msg->message
|
|
&& a_msg->wParam == msg->wParam
|
|
&& a_msg->hwnd == msg->hwnd) {
|
|
last_msg = a_msg;
|
|
}
|
|
|
|
readpos ++;
|
|
readpos %= msg_que->len;
|
|
}
|
|
|
|
if (last_msg) {
|
|
last_msg->lParam = msg->lParam;
|
|
last_msg->time = msg->time;
|
|
goto ret;
|
|
}
|
|
}
|
|
else if (msg->message == MSG_TIMEOUT
|
|
|| msg->message == MSG_IDLE
|
|
|| msg->message == MSG_CARETBLINK) {
|
|
int readpos = msg_que->readpos;
|
|
PMSG a_msg;
|
|
|
|
while (readpos != msg_que->writepos) {
|
|
a_msg = msg_que->msg + readpos;
|
|
|
|
if (a_msg->message == msg->message
|
|
&& a_msg->hwnd == msg->hwnd) {
|
|
a_msg->wParam = msg->wParam;
|
|
a_msg->lParam = msg->lParam;
|
|
a_msg->time = msg->time;
|
|
goto ret;
|
|
}
|
|
|
|
readpos ++;
|
|
readpos %= msg_que->len;
|
|
}
|
|
}
|
|
|
|
if ((msg_que->writepos + 1) % msg_que->len == msg_que->readpos) {
|
|
// message queue is full.
|
|
UNLOCK_MSGQ(msg_que);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Write the data and advance write pointer */
|
|
msg_que->msg [msg_que->writepos] = *msg;
|
|
msg_que->writepos++;
|
|
msg_que->writepos %= msg_que->len;
|
|
|
|
ret:
|
|
msg_que->dwState |= QS_POSTMSG;
|
|
|
|
UNLOCK_MSGQ (msg_que);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!getMainWinIfWindowInThisThread (msg->hwnd))
|
|
POST_MSGQ (msg_que);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
HWND kernel_CheckInvalidRegion (PMAINWIN pWin)
|
|
{
|
|
PCONTROL pCtrl = (PCONTROL)pWin;
|
|
HWND hwnd;
|
|
|
|
/* Since 5.0.0: skip virtual window */
|
|
if (pWin->WinType == TYPE_VIRTWIN)
|
|
return HWND_NULL;
|
|
|
|
if (pCtrl->InvRgn.rgn.head)
|
|
return (HWND)pCtrl;
|
|
|
|
pCtrl = pCtrl->children;
|
|
while (pCtrl) {
|
|
|
|
if ((hwnd = kernel_CheckInvalidRegion ((PMAINWIN) pCtrl)))
|
|
return hwnd;
|
|
|
|
pCtrl = pCtrl->next;
|
|
}
|
|
|
|
return HWND_NULL;
|
|
}
|
|
|
|
static HWND msgCheckHostedTree (PMAINWIN pHosting)
|
|
{
|
|
HWND hNeedPaint;
|
|
PMAINWIN pHosted;
|
|
|
|
/* Since 5.0.0: always skip desktop */
|
|
if (pHosting != __mg_dsk_win &&
|
|
(hNeedPaint = kernel_CheckInvalidRegion (pHosting)))
|
|
return hNeedPaint;
|
|
|
|
pHosted = pHosting->pFirstHosted;
|
|
while (pHosted) {
|
|
if ((hNeedPaint = msgCheckHostedTree (pHosted)))
|
|
return hNeedPaint;
|
|
|
|
pHosted = pHosted->pNextHosted;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL GUIAPI HavePendingMessageEx (HWND hWnd, BOOL bNoDeskTimer)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
|
|
if (hWnd == HWND_NULL) {
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!(pMsgQueue = getMsgQueueForThisThread ()))
|
|
return FALSE;
|
|
#else
|
|
pMsgQueue = __mg_dsk_msg_queue;
|
|
#endif
|
|
}
|
|
else {
|
|
if (!(pMsgQueue = getMsgQueueIfWindowInThisThread (hWnd)))
|
|
return FALSE;
|
|
}
|
|
|
|
TEST_IF_QUIT(pMsgQueue, hWnd);
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (pMsgQueue->dwState & QS_SYNCMSG) {
|
|
if (pMsgQueue->pFirstSyncMsg) goto retok;
|
|
}
|
|
#endif
|
|
|
|
if (pMsgQueue->dwState & QS_NOTIFYMSG) {
|
|
if (pMsgQueue->pFirstNotifyMsg) goto retok;
|
|
}
|
|
|
|
if (pMsgQueue->dwState & QS_POSTMSG) {
|
|
if (pMsgQueue->readpos != pMsgQueue->writepos) goto retok;
|
|
}
|
|
|
|
if (pMsgQueue->dwState & (QS_QUIT | QS_PAINT)) {
|
|
goto retok;
|
|
}
|
|
|
|
if (pMsgQueue->expired_timer_mask)
|
|
goto retok;
|
|
|
|
/*
|
|
* We do not need to check QS_DESKTIMER, because it is for the
|
|
* desktop window, and user don't care it.
|
|
*/
|
|
if (!bNoDeskTimer && pMsgQueue == __mg_dsk_msg_queue &&
|
|
(pMsgQueue->dwState & QS_DESKTIMER)) {
|
|
goto retok;
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
|
|
/* Since 5.0.0, always call OnIdle with no wait */
|
|
if (pMsgQueue->OnIdle)
|
|
return pMsgQueue->OnIdle (pMsgQueue, FALSE);
|
|
return FALSE;
|
|
|
|
retok:
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GUIAPI HavePendingMessage (HWND hWnd)
|
|
{
|
|
return HavePendingMessageEx (hWnd, FALSE);
|
|
}
|
|
|
|
int GUIAPI BroadcastMessage (UINT nMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
MSG msg;
|
|
|
|
msg.message = nMsg;
|
|
msg.wParam = wParam;
|
|
msg.lParam = lParam;
|
|
|
|
return SendMessage (HWND_DESKTOP, MSG_BROADCASTMSG, 0, (LPARAM)(&msg));
|
|
}
|
|
|
|
static void travel_all_hosted (PMAINWIN pWin,
|
|
void (*cb) (PMAINWIN, void*), void* data)
|
|
{
|
|
PMAINWIN pNext = pWin->pFirstHosted;
|
|
|
|
while (pNext) {
|
|
|
|
travel_all_hosted (pNext, cb, data);
|
|
cb (pNext, data);
|
|
|
|
pNext = pNext->pNextHosted;
|
|
}
|
|
}
|
|
|
|
static void _my_travel_cb (PMAINWIN win, void* data)
|
|
{
|
|
MSG* msg = (MSG*)data;
|
|
|
|
msg->hwnd = (HWND)win;
|
|
if (kernel_QueueMessage (win->pMsgQueue, msg)) {
|
|
msg->time++;
|
|
}
|
|
}
|
|
|
|
int __mg_broadcast_message (PMSGQUEUE msg_queue, MSG* msg)
|
|
{
|
|
PMAINWIN win;
|
|
|
|
msg->time = 0;
|
|
|
|
if ((win = msg_queue->pRootMainWin)) {
|
|
travel_all_hosted (win, _my_travel_cb, msg);
|
|
_my_travel_cb (win, msg);
|
|
}
|
|
|
|
return (int)msg->time;
|
|
}
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
int GUIAPI BroadcastMessageInThisThread (UINT nMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
MSGQUEUE* pMsgQueue;
|
|
MSG msg = { HWND_NULL, nMsg, wParam, lParam };
|
|
|
|
pMsgQueue = (MSGQUEUE*)pthread_getspecific (__mg_threadinfo_key);
|
|
#ifdef __VXWORKS__
|
|
if (pMsgQueue == (void *)-1) {
|
|
pMsgQueue = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (pMsgQueue == NULL)
|
|
return -1;
|
|
|
|
return __mg_broadcast_message (pMsgQueue, &msg);
|
|
}
|
|
|
|
#endif /* defined _MGHAVE_VIRTUAL_WINDOW */
|
|
|
|
#ifdef _MGHAVE_MSG_STRING
|
|
#include "msgstr.h"
|
|
|
|
const char* GUIAPI Message2Str (UINT message)
|
|
{
|
|
if (message >= 0x0000 && message <= 0x006F)
|
|
return __mg_msgstr1 [message];
|
|
else if (message >= 0x0070 && message <= 0x009F)
|
|
return __mg_msgstr11 [message - 0x0070];
|
|
else if (message >= 0x00A0 && message <= 0x010F)
|
|
return __mg_msgstr2 [message - 0x00A0];
|
|
else if (message >= 0x0120 && message <= 0x017F)
|
|
return __mg_msgstr3 [message - 0x0120];
|
|
else if (message >= 0xF000)
|
|
return "Control Messages";
|
|
else
|
|
return "MSG_USER";
|
|
}
|
|
|
|
void GUIAPI PrintMessage (FILE* fp, HWND hWnd,
|
|
UINT nMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
fprintf (fp, "Message %s: hWnd: %p, wP: %p, lP: %p.\n",
|
|
Message2Str (nMsg), hWnd, (PVOID)wParam, (PVOID)lParam);
|
|
}
|
|
#endif /* defined _MGHAVE_MSG_STRING */
|
|
|
|
static inline void CheckCapturedMouseMessage (PMSG pMsg)
|
|
{
|
|
if (__mg_captured_wnd == pMsg->hwnd
|
|
&& pMsg->message >= MSG_FIRSTMOUSEMSG
|
|
&& pMsg->message <= MSG_LASTMOUSEMSG
|
|
&& !(pMsg->wParam & KS_CAPTURED)) {
|
|
int x, y;
|
|
x = LOSWORD (pMsg->lParam);
|
|
y = HISWORD (pMsg->lParam);
|
|
|
|
ClientToScreen (pMsg->hwnd, &x, &y);
|
|
|
|
pMsg->lParam = MAKELONG (x, y);
|
|
pMsg->wParam |= KS_CAPTURED;
|
|
}
|
|
}
|
|
|
|
#define IS_MSG_WANTED(message) \
|
|
((nMsgFilterMin <= 0 && nMsgFilterMax <= 0) || \
|
|
(nMsgFilterMin > 0 && nMsgFilterMax >= nMsgFilterMin && \
|
|
message >= nMsgFilterMin && message <= nMsgFilterMax))
|
|
|
|
BOOL PeekMessageEx (PMSG pMsg, HWND hWnd,
|
|
UINT nMsgFilterMin, UINT nMsgFilterMax, BOOL bWait, UINT uRemoveMsg)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
PQMSG phead;
|
|
|
|
if (MG_UNLIKELY (pMsg == NULL ||
|
|
(hWnd != HWND_DESKTOP && !MG_IS_MAIN_VIRT_WINDOW (hWnd))))
|
|
return FALSE;
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!(pMsgQueue = getMsgQueueForThisThread ())) {
|
|
_WRN_PRINTF ("not a message thread.\n");
|
|
return FALSE;
|
|
}
|
|
#else
|
|
pMsgQueue = __mg_dsk_msg_queue;
|
|
#endif
|
|
|
|
memset (pMsg, 0, sizeof(MSG));
|
|
pMsg->time = __mg_tick_counter;
|
|
|
|
checkagain:
|
|
|
|
TEST_CANCEL;
|
|
|
|
TEST_IF_QUIT(pMsgQueue, hWnd);
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
if ((pMsgQueue->dwState & QS_QUIT)) {
|
|
pMsg->hwnd = hWnd;
|
|
pMsg->message = MSG_QUIT;
|
|
pMsg->wParam = 0;
|
|
pMsg->lParam = 0;
|
|
SET_PADD (NULL);
|
|
|
|
if (uRemoveMsg == PM_REMOVE) {
|
|
pMsgQueue->loop_depth --;
|
|
if (pMsgQueue->loop_depth == 0) {
|
|
pMsgQueue->dwState &= ~QS_QUIT;
|
|
}
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Dealing with sync messages before notify messages is better ? */
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (pMsgQueue->dwState & QS_SYNCMSG) {
|
|
if (pMsgQueue->pFirstSyncMsg) {
|
|
*pMsg = pMsgQueue->pFirstSyncMsg->Msg;
|
|
SET_PADD (pMsgQueue->pFirstSyncMsg);
|
|
if (IS_MSG_WANTED(pMsg->message)) {
|
|
if (uRemoveMsg == PM_REMOVE) {
|
|
pMsgQueue->pFirstSyncMsg = pMsgQueue->pFirstSyncMsg->pNext;
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
goto got_ret;
|
|
}
|
|
}
|
|
else
|
|
pMsgQueue->dwState &= ~QS_SYNCMSG;
|
|
}
|
|
#endif
|
|
|
|
if (pMsgQueue->dwState & QS_NOTIFYMSG) {
|
|
if (pMsgQueue->pFirstNotifyMsg) {
|
|
phead = pMsgQueue->pFirstNotifyMsg;
|
|
*pMsg = phead->Msg;
|
|
SET_PADD (NULL);
|
|
|
|
if (IS_MSG_WANTED(pMsg->message)) {
|
|
if (uRemoveMsg == PM_REMOVE) {
|
|
pMsgQueue->pFirstNotifyMsg = phead->next;
|
|
if (pMsgQueue->pFirstNotifyMsg == NULL)
|
|
pMsgQueue->pLastNotifyMsg = NULL;
|
|
FREEQMSG (phead);
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
goto got_ret;
|
|
}
|
|
}
|
|
else
|
|
pMsgQueue->dwState &= ~QS_NOTIFYMSG;
|
|
}
|
|
|
|
if (pMsgQueue->dwState & QS_POSTMSG) {
|
|
if (pMsgQueue->readpos != pMsgQueue->writepos) {
|
|
*pMsg = pMsgQueue->msg[pMsgQueue->readpos];
|
|
SET_PADD (NULL);
|
|
if (IS_MSG_WANTED(pMsg->message)) {
|
|
CheckCapturedMouseMessage (pMsg);
|
|
if (uRemoveMsg == PM_REMOVE) {
|
|
pMsgQueue->readpos++;
|
|
pMsgQueue->readpos %= pMsgQueue->len;
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
goto got_ret;
|
|
}
|
|
}
|
|
else
|
|
pMsgQueue->dwState &= ~QS_POSTMSG;
|
|
}
|
|
|
|
/*
|
|
* check invalidate region of the windows
|
|
*/
|
|
if (pMsgQueue->dwState & QS_PAINT && IS_MSG_WANTED(MSG_PAINT)) {
|
|
PMAINWIN pHostingRoot;
|
|
HWND hNeedPaint;
|
|
PMAINWIN pWin;
|
|
|
|
pMsg->message = MSG_PAINT;
|
|
pMsg->wParam = 0;
|
|
pMsg->lParam = 0;
|
|
SET_PADD (NULL);
|
|
|
|
pHostingRoot = pMsgQueue->pRootMainWin;
|
|
|
|
if ((hNeedPaint = msgCheckHostedTree (pHostingRoot))) {
|
|
pMsg->hwnd = hNeedPaint;
|
|
|
|
//dump_window (hNeedPaint, "repainting");
|
|
|
|
pWin = (PMAINWIN) hNeedPaint;
|
|
pMsg->lParam = (LPARAM)(&pWin->InvRgn.rgn);
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
goto got_ret;
|
|
}
|
|
|
|
/* no paint message */
|
|
pMsgQueue->dwState &= ~QS_PAINT;
|
|
}
|
|
|
|
/* handle desktop timer here */
|
|
if (pMsgQueue == __mg_dsk_msg_queue && pMsgQueue->dwState & QS_DESKTIMER) {
|
|
pMsg->hwnd = HWND_DESKTOP;
|
|
pMsg->message = MSG_TIMEOUT;
|
|
pMsg->wParam = 0;
|
|
pMsg->lParam = 0;
|
|
|
|
if (uRemoveMsg == PM_REMOVE) {
|
|
pMsgQueue->dwState &= ~QS_DESKTIMER;
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
goto got_ret;
|
|
}
|
|
|
|
/* handle general timer here */
|
|
if (pMsgQueue->expired_timer_mask && IS_MSG_WANTED(MSG_TIMER)) {
|
|
int slot;
|
|
TIMER* timer;
|
|
|
|
/* get the first expired timer slot */
|
|
slot = pMsgQueue->first_timer_slot;
|
|
do {
|
|
if (pMsgQueue->expired_timer_mask & (0x01UL << slot))
|
|
break;
|
|
|
|
slot ++;
|
|
slot %= DEF_NR_TIMERS;
|
|
if (slot == pMsgQueue->first_timer_slot) {
|
|
slot = -1;
|
|
break;
|
|
}
|
|
} while (TRUE);
|
|
|
|
pMsgQueue->first_timer_slot ++;
|
|
pMsgQueue->first_timer_slot %= DEF_NR_TIMERS;
|
|
|
|
if ((timer = pMsgQueue->timer_slots[slot])) {
|
|
pMsgQueue->expired_timer_mask &= ~(0x01UL << slot);
|
|
|
|
if (timer->proc) {
|
|
BOOL ret_timer_proc;
|
|
|
|
/* unlock the message queue when calling timer proc */
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
|
|
/* calling the timer callback procedure */
|
|
ret_timer_proc = timer->proc (timer->hWnd,
|
|
timer->id, timer->ticks_fired);
|
|
|
|
/* lock the message queue again */
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
if (!ret_timer_proc) {
|
|
/* remove the timer */
|
|
__mg_remove_timer (pMsgQueue, slot);
|
|
}
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
goto checkagain;
|
|
}
|
|
else {
|
|
pMsg->message = MSG_TIMER;
|
|
pMsg->hwnd = timer->hWnd;
|
|
pMsg->wParam = timer->id;
|
|
pMsg->lParam = timer->ticks_fired;
|
|
SET_PADD (NULL);
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
goto got_ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (bWait) {
|
|
/* no message, wait again. */
|
|
if (pMsgQueue->OnIdle) {
|
|
if (sem_trywait (&pMsgQueue->wait) == 0)
|
|
goto checkagain;
|
|
|
|
if (!pMsgQueue->OnIdle (pMsgQueue, TRUE)) {
|
|
handle_idle_message (pMsgQueue);
|
|
}
|
|
}
|
|
else {
|
|
sem_wait (&pMsgQueue->wait);
|
|
}
|
|
|
|
goto checkagain;
|
|
}
|
|
#else /* defined _MGHAVE_VIRTUAL_WINDOW */
|
|
/* no message, idle */
|
|
if (bWait) {
|
|
assert (pMsgQueue->OnIdle);
|
|
if (!pMsgQueue->OnIdle (pMsgQueue, TRUE)) {
|
|
handle_idle_message (pMsgQueue);
|
|
}
|
|
goto checkagain;
|
|
}
|
|
#endif /* not defined _MGHAVE_VIRTUAL_WINDOW */
|
|
|
|
/* no message */
|
|
return FALSE;
|
|
|
|
got_ret:
|
|
if (pMsg->hwnd != HWND_DESKTOP) {
|
|
/* since 5.0.0, reset idle counter */
|
|
pMsgQueue->idle_counter = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#if 0 /* moved code */
|
|
/* The following two functions are moved to window.h as inline functions. */
|
|
int GUIAPI GetMessage (PMSG pMsg, HWND hWnd)
|
|
{
|
|
return PeekMessageEx (pMsg, hWnd, 0, 0, TRUE, PM_REMOVE);
|
|
}
|
|
|
|
BOOL GUIAPI PeekMessage (PMSG pMsg, HWND hWnd, UINT nMsgFilterMin,
|
|
UINT nMsgFilterMax, UINT uRemoveMsg)
|
|
{
|
|
return PeekMessageEx (pMsg, hWnd, nMsgFilterMin, nMsgFilterMax,
|
|
FALSE, uRemoveMsg);
|
|
}
|
|
#endif /* moved code */
|
|
|
|
/* wait for message */
|
|
BOOL GUIAPI WaitMessage (PMSG pMsg, HWND hWnd)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
|
|
if (!pMsg)
|
|
return FALSE;
|
|
|
|
if (!(pMsgQueue = getMsgQueueIfWindowInThisThread(hWnd)))
|
|
return FALSE;
|
|
|
|
pMsgQueue->idle_counter = 0;
|
|
|
|
memset (pMsg, 0, sizeof(MSG));
|
|
|
|
checkagain:
|
|
|
|
TEST_CANCEL;
|
|
|
|
TEST_IF_QUIT(pMsgQueue, hWnd);
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
if ((pMsgQueue->dwState & QS_QUIT)) {
|
|
goto getit;
|
|
}
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (pMsgQueue->dwState & QS_SYNCMSG) {
|
|
if (pMsgQueue->pFirstSyncMsg) {
|
|
goto getit;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (pMsgQueue->dwState & QS_NOTIFYMSG) {
|
|
if (pMsgQueue->pFirstNotifyMsg) {
|
|
goto getit;
|
|
}
|
|
}
|
|
|
|
if (pMsgQueue->dwState & QS_POSTMSG) {
|
|
if (pMsgQueue->readpos != pMsgQueue->writepos) {
|
|
goto getit;
|
|
}
|
|
}
|
|
|
|
if (pMsgQueue->dwState & QS_PAINT) {
|
|
goto getit;
|
|
}
|
|
|
|
if (pMsgQueue == __mg_dsk_msg_queue && pMsgQueue->dwState & QS_DESKTIMER) {
|
|
goto getit;
|
|
}
|
|
|
|
if (pMsgQueue->expired_timer_mask) {
|
|
goto getit;
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
/* no message, try wait, if no message call idle. */
|
|
if (pMsgQueue->OnIdle) {
|
|
if (sem_trywait (&pMsgQueue->wait) == 0)
|
|
goto checkagain;
|
|
|
|
if (!pMsgQueue->OnIdle (pMsgQueue, TRUE)) {
|
|
handle_idle_message (pMsgQueue);
|
|
}
|
|
}
|
|
else {
|
|
sem_wait (&pMsgQueue->wait);
|
|
}
|
|
#else /* defined _MGHAVE_VIRTUAL_WINDOW */
|
|
/* no message, idle */
|
|
assert (pMsgQueue->OnIdle);
|
|
if (!pMsgQueue->OnIdle (pMsgQueue, TRUE)) {
|
|
handle_idle_message (pMsgQueue);
|
|
}
|
|
#endif /* not defined _MGHAVE_VIRTUAL_WINDOW */
|
|
|
|
goto checkagain;
|
|
|
|
getit:
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GUIAPI PeekPostMessage (PMSG pMsg, HWND hWnd, UINT nMsgFilterMin,
|
|
UINT nMsgFilterMax, UINT uRemoveMsg)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
PMSG pPostMsg;
|
|
|
|
if (!pMsg)
|
|
return FALSE;
|
|
|
|
if (!(pMsgQueue = getMsgQueueIfWindowInThisThread(hWnd)))
|
|
return FALSE;
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
memset (pMsg, 0, sizeof(MSG));
|
|
|
|
if (pMsgQueue->dwState & QS_POSTMSG) {
|
|
|
|
if (pMsgQueue->readpos != pMsgQueue->writepos) {
|
|
|
|
pPostMsg = pMsgQueue->msg + pMsgQueue->readpos;
|
|
if (nMsgFilterMin == 0 && nMsgFilterMax == 0)
|
|
*pMsg = *pPostMsg;
|
|
else if (pPostMsg->message <= nMsgFilterMax &&
|
|
pPostMsg->message >= nMsgFilterMin)
|
|
*pMsg = *pPostMsg;
|
|
else {
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
return FALSE;
|
|
}
|
|
|
|
SET_PADD (NULL);
|
|
|
|
if (uRemoveMsg == PM_REMOVE) {
|
|
pMsgQueue->readpos++;
|
|
pMsgQueue->readpos %= pMsgQueue->len;
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
return FALSE;
|
|
}
|
|
|
|
LRESULT GUIAPI SendMessage (HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
WNDPROC WndProc;
|
|
|
|
MG_CHECK_RET (MG_IS_WINDOW(hWnd), ERR_INV_HWND);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!getMainWinIfWindowInThisThread (hWnd))
|
|
return SendSyncMessage (hWnd, nMsg, wParam, lParam);
|
|
#endif
|
|
|
|
if (!(WndProc = GETWNDPROC(hWnd)))
|
|
return ERR_INV_HWND;
|
|
|
|
return (*WndProc)(hWnd, nMsg, wParam, lParam);
|
|
|
|
}
|
|
|
|
/* Since 5.0.0, delcare as a GUIAPI */
|
|
/* houhh 20090619, send a prior notify message. */
|
|
int GUIAPI SendPriorNotifyMessage (HWND hWnd, UINT nMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
PQMSG pqmsg;
|
|
|
|
MG_CHECK_RET (MG_IS_WINDOW(hWnd), ERR_INV_HWND);
|
|
|
|
if (!(pMsgQueue = getMsgQueue(hWnd)))
|
|
return ERR_INV_HWND;
|
|
|
|
pqmsg = ALLOCQMSG();
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
/* queue the notification message. */
|
|
pqmsg->Msg.hwnd = hWnd;
|
|
pqmsg->Msg.message = nMsg;
|
|
pqmsg->Msg.wParam = wParam;
|
|
pqmsg->Msg.lParam = lParam;
|
|
pqmsg->Msg.time = __mg_tick_counter;
|
|
pqmsg->next = NULL;
|
|
|
|
if (pMsgQueue->pFirstNotifyMsg == NULL) {
|
|
pMsgQueue->pFirstNotifyMsg = pMsgQueue->pLastNotifyMsg = pqmsg;
|
|
}
|
|
else {
|
|
pqmsg->next = pMsgQueue->pFirstNotifyMsg;
|
|
pMsgQueue->pFirstNotifyMsg = pqmsg;
|
|
}
|
|
|
|
pMsgQueue->dwState |= QS_NOTIFYMSG;
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!getMainWinIfWindowInThisThread(hWnd))
|
|
POST_MSGQ(pMsgQueue);
|
|
#endif
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
int GUIAPI SendNotifyMessage (HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
PQMSG pqmsg;
|
|
|
|
MG_CHECK_RET (MG_IS_WINDOW(hWnd), ERR_INV_HWND);
|
|
|
|
if (!(pMsgQueue = getMsgQueue(hWnd)))
|
|
return ERR_INV_HWND;
|
|
|
|
pqmsg = ALLOCQMSG();
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
/* queue the notification message. */
|
|
pqmsg->Msg.hwnd = hWnd;
|
|
pqmsg->Msg.message = nMsg;
|
|
pqmsg->Msg.wParam = wParam;
|
|
pqmsg->Msg.lParam = lParam;
|
|
pqmsg->Msg.time = __mg_tick_counter;
|
|
pqmsg->next = NULL;
|
|
|
|
if (pMsgQueue->pFirstNotifyMsg == NULL) {
|
|
pMsgQueue->pFirstNotifyMsg = pMsgQueue->pLastNotifyMsg = pqmsg;
|
|
}
|
|
else {
|
|
assert (pMsgQueue->pLastNotifyMsg);
|
|
pMsgQueue->pLastNotifyMsg->next = pqmsg;
|
|
pMsgQueue->pLastNotifyMsg = pqmsg;
|
|
}
|
|
|
|
pMsgQueue->dwState |= QS_NOTIFYMSG;
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!getMainWinIfWindowInThisThread(hWnd))
|
|
POST_MSGQ(pMsgQueue);
|
|
#endif
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
int GUIAPI NotifyWindow (HWND hWnd, LINT id, int code, DWORD dwAddData)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
PQMSG pqmsg;
|
|
|
|
MG_CHECK_RET (MG_IS_WINDOW(hWnd), ERR_INV_HWND);
|
|
|
|
if (!(pMsgQueue = getMsgQueue(hWnd)))
|
|
return ERR_INV_HWND;
|
|
|
|
pqmsg = ALLOCQMSG();
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
/* queue the notification message. */
|
|
pqmsg->Msg.hwnd = hWnd;
|
|
pqmsg->Msg.message = MSG_NOTIFICATION;
|
|
pqmsg->Msg.wParam = (WPARAM)id;
|
|
pqmsg->Msg.lParam = (LPARAM)code;
|
|
pqmsg->Msg.time = dwAddData;
|
|
pqmsg->next = NULL;
|
|
|
|
if (pMsgQueue->pFirstNotifyMsg == NULL) {
|
|
pMsgQueue->pFirstNotifyMsg = pMsgQueue->pLastNotifyMsg = pqmsg;
|
|
}
|
|
else {
|
|
assert (pMsgQueue->pLastNotifyMsg);
|
|
pMsgQueue->pLastNotifyMsg->next = pqmsg;
|
|
pMsgQueue->pLastNotifyMsg = pqmsg;
|
|
}
|
|
|
|
pMsgQueue->dwState |= QS_NOTIFYMSG;
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!getMainWinIfWindowInThisThread(hWnd))
|
|
POST_MSGQ(pMsgQueue);
|
|
#endif
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
int GUIAPI PostMessage (HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
MSG msg;
|
|
|
|
if (!(pMsgQueue = getMsgQueue(hWnd)))
|
|
return ERR_INV_HWND;
|
|
|
|
if (nMsg == MSG_PAINT) {
|
|
LOCK_MSGQ (pMsgQueue);
|
|
pMsgQueue->dwState |= QS_PAINT;
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!getMainWinIfWindowInThisThread(hWnd))
|
|
POST_MSGQ(pMsgQueue);
|
|
#endif
|
|
return ERR_OK;
|
|
}
|
|
|
|
msg.hwnd = hWnd;
|
|
msg.message = nMsg;
|
|
msg.wParam = wParam;
|
|
msg.lParam = lParam;
|
|
|
|
if (!kernel_QueueMessage(pMsgQueue, &msg))
|
|
return ERR_QUEUE_FULL;
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
int GUIAPI PostQuitMessage (HWND hWnd)
|
|
{
|
|
PMSGQUEUE pMsgQueue = NULL;
|
|
|
|
if (hWnd && hWnd != HWND_INVALID) {
|
|
PMAINWIN pWin = (PMAINWIN)hWnd;
|
|
if (pWin->DataType == TYPE_HWND || pWin->DataType == TYPE_WINTODEL) {
|
|
pMsgQueue = pWin->pMainWin->pMsgQueue;
|
|
}
|
|
}
|
|
|
|
if (pMsgQueue == NULL)
|
|
return ERR_INV_HWND;
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
if (!(pMsgQueue->dwState & QS_QUIT)){
|
|
pMsgQueue->loop_depth ++;
|
|
pMsgQueue->dwState |= QS_QUIT;
|
|
}
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (!getMainWinIfWindowInThisThread (hWnd))
|
|
POST_MSGQ (pMsgQueue);
|
|
#endif
|
|
|
|
return ERR_OK;
|
|
}
|
|
|
|
#include "license.h"
|
|
|
|
LRESULT GUIAPI DispatchMessage (PMSG pMsg)
|
|
{
|
|
WNDPROC WndProc;
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
PSYNCMSG pSyncMsg;
|
|
#endif
|
|
LRESULT lRet;
|
|
|
|
LICENSE_MODIFY_MESSAGE(pMsg);
|
|
|
|
#ifdef _MGHAVE_TRACE_MSG
|
|
if (pMsg->message != MSG_TIMEOUT && pMsg->message != MSG_CARETBLINK) {
|
|
dump_message (pMsg, __func__);
|
|
}
|
|
#endif
|
|
|
|
if (pMsg->hwnd == HWND_INVALID) {
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (pMsg->pSyncMsg) {
|
|
pSyncMsg = (PSYNCMSG)pMsg->pSyncMsg;
|
|
pSyncMsg->retval = ERR_MSG_CANCELED;
|
|
sem_post (pSyncMsg->sem_handle);
|
|
}
|
|
#endif
|
|
|
|
#ifdef _MGHAVE_TRACE_MSG
|
|
if (pMsg->message != MSG_TIMEOUT && pMsg->message != MSG_CARETBLINK) {
|
|
dump_message (pMsg, "thrown away");
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
if (pMsg->hwnd == 0)
|
|
return -1;
|
|
|
|
if (!(WndProc = GETWNDPROC (pMsg->hwnd)))
|
|
return -1;
|
|
|
|
/* Since 5.0.0 */
|
|
if (pMsg->message == MSG_NOTIFICATION) {
|
|
NOTIFPROC NotifProc = GETNOTIFPROC (pMsg->hwnd);
|
|
if (NotifProc) {
|
|
NotifProc (pMsg->hwnd, (LINT)pMsg->wParam, (int)pMsg->lParam,
|
|
pMsg->time);
|
|
lRet = 0;
|
|
}
|
|
else {
|
|
lRet = WndProc (pMsg->hwnd, MSG_COMMAND,
|
|
MAKELONG (pMsg->wParam, pMsg->lParam), pMsg->time);
|
|
}
|
|
}
|
|
else {
|
|
lRet = WndProc (pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam);
|
|
}
|
|
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
/* this is a sync message. */
|
|
if (pMsg->pSyncMsg) {
|
|
pSyncMsg = (PSYNCMSG)pMsg->pSyncMsg;
|
|
pSyncMsg->retval = lRet;
|
|
sem_post (pSyncMsg->sem_handle);
|
|
}
|
|
#endif
|
|
|
|
#ifdef _MGHAVE_TRACE_MSG
|
|
if (pMsg->message != MSG_TIMEOUT && pMsg->message != MSG_CARETBLINK) {
|
|
dump_message_with_retval (pMsg, lRet, __func__);
|
|
}
|
|
#endif
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/* Throw away messages in the message queue for the specified window.
|
|
If the specified window is a main window, all messages sent
|
|
to the controls of the main window will be thrown away as well. */
|
|
int __mg_throw_away_messages (PMSGQUEUE pMsgQueue, HWND hWnd)
|
|
{
|
|
PMAINWIN pMainWin = (PMAINWIN)hWnd;
|
|
PMSG pMsg;
|
|
PQMSG pQMsg;
|
|
int nCountN = 0;
|
|
int nCountS = 0;
|
|
int nCountP = 0;
|
|
int readpos;
|
|
int slot;
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
/* for virtual window and main window, use pMainWin to check whether we
|
|
should throw away a message for controls of a main window.
|
|
checkAndGetMainWindowIfControl returns NULL for non control. */
|
|
if (pMainWin != HWND_NULL && pMainWin != HWND_INVALID &&
|
|
(pMainWin->WinType == TYPE_MAINWIN ||
|
|
pMainWin->WinType == TYPE_VIRTWIN))
|
|
pMainWin = (PMAINWIN)hWnd;
|
|
else
|
|
pMainWin = NULL;
|
|
|
|
if (pMsgQueue->pFirstNotifyMsg) {
|
|
PQMSG pPrev = NULL, pNext;
|
|
pQMsg = pMsgQueue->pFirstNotifyMsg;
|
|
|
|
while (pQMsg) {
|
|
pMsg = &pQMsg->Msg;
|
|
|
|
if (pMsg->hwnd == hWnd ||
|
|
checkAndGetMainWindowIfControl (pMsg->hwnd) == pMainWin) {
|
|
#if 0 /* deprecated code */
|
|
pMsg->hwnd = HWND_INVALID;
|
|
#else /* deprecated code */
|
|
// since 5.0.0, we free the msg structure for notification.
|
|
if (pPrev) {
|
|
pPrev->next = pQMsg->next;
|
|
}
|
|
else {
|
|
pMsgQueue->pFirstNotifyMsg = pQMsg->next;
|
|
}
|
|
|
|
if (pMsgQueue->pLastNotifyMsg == pQMsg) {
|
|
if (pPrev)
|
|
pMsgQueue->pLastNotifyMsg = pPrev;
|
|
else
|
|
pMsgQueue->pLastNotifyMsg = pMsgQueue->pFirstNotifyMsg;
|
|
}
|
|
|
|
/* keep pPrev unchanged */
|
|
pNext = pQMsg->next;
|
|
FREEQMSG (pQMsg);
|
|
pQMsg = pNext;
|
|
#endif
|
|
nCountN++;
|
|
}
|
|
else {
|
|
pPrev = pQMsg;
|
|
pQMsg = pQMsg->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
_DBG_PRINTF ("%d notification messages thrown for window: %p\n",
|
|
nCountN, hWnd);
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
if (pMsgQueue->pFirstSyncMsg) {
|
|
PSYNCMSG pSyncMsg, pSyncPrev = NULL;
|
|
pSyncMsg = pMsgQueue->pFirstSyncMsg;
|
|
|
|
while (pSyncMsg) {
|
|
pMsg = &pSyncMsg->Msg;
|
|
|
|
if (pMsg->hwnd == hWnd ||
|
|
checkAndGetMainWindowIfControl (pMsg->hwnd) == pMainWin) {
|
|
pMsg->hwnd = HWND_INVALID;
|
|
nCountS ++;
|
|
|
|
// notify the waiting thread and remove the node from msg queue
|
|
pSyncMsg->retval = ERR_MSG_CANCELED;
|
|
if (pSyncPrev) {
|
|
pSyncPrev->pNext = pSyncMsg->pNext;
|
|
}
|
|
else {
|
|
pSyncPrev = pSyncMsg;
|
|
pSyncMsg = pSyncMsg->pNext;
|
|
pMsgQueue->pFirstSyncMsg = pSyncMsg;
|
|
sem_post (pSyncPrev->sem_handle);
|
|
pSyncPrev = NULL;
|
|
continue;
|
|
}
|
|
sem_post (pSyncMsg->sem_handle);
|
|
}
|
|
|
|
pSyncPrev = pSyncMsg;
|
|
pSyncMsg = pSyncMsg->pNext;
|
|
}
|
|
}
|
|
|
|
_DBG_PRINTF ("%d sync messages thrown for window %p, pMainWin (%p)\n",
|
|
nCountS, hWnd, pMainWin);
|
|
#endif /* defined _MGHAVE_VIRTUAL_WINDOW */
|
|
|
|
readpos = pMsgQueue->readpos;
|
|
while (readpos != pMsgQueue->writepos) {
|
|
pMsg = pMsgQueue->msg + readpos;
|
|
|
|
if (pMsg->hwnd == hWnd ||
|
|
checkAndGetMainWindowIfControl (pMsg->hwnd) == pMainWin) {
|
|
|
|
nCountP++;
|
|
pMsg->hwnd = HWND_INVALID;
|
|
|
|
int pos = readpos;
|
|
int old_pos = readpos;
|
|
|
|
do {
|
|
pos++;
|
|
pos %= pMsgQueue->len;
|
|
|
|
if (pos != pMsgQueue->writepos) {
|
|
pMsgQueue->msg[old_pos] = pMsgQueue->msg[pos];
|
|
old_pos = pos;
|
|
}
|
|
else
|
|
break;
|
|
|
|
} while (1);
|
|
|
|
if (pMsgQueue->writepos > 0)
|
|
pMsgQueue->writepos--;
|
|
else
|
|
pMsgQueue->writepos = pMsgQueue->len - 1;
|
|
}
|
|
else {
|
|
readpos++;
|
|
readpos %= pMsgQueue->len;
|
|
}
|
|
}
|
|
|
|
_DBG_PRINTF ("%d post messages thrown for window %p\n", nCountP, hWnd);
|
|
|
|
/* clear timer message flags of this window */
|
|
for (slot = 0; slot < DEF_NR_TIMERS; slot++) {
|
|
if (pMsgQueue->expired_timer_mask & (0x01UL << slot)) {
|
|
HWND timer_wnd = pMsgQueue->timer_slots [slot]->hWnd;
|
|
if (timer_wnd == hWnd ||
|
|
checkAndGetMainWindowIfControl (timer_wnd) == pMainWin) {
|
|
pMsgQueue->expired_timer_mask &= ~(0x01UL << slot);
|
|
}
|
|
}
|
|
}
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
|
|
return nCountN + nCountS + nCountP;
|
|
}
|
|
|
|
int GUIAPI ThrowAwayMessages (HWND hWnd)
|
|
{
|
|
PMSGQUEUE pMsgQueue = NULL;
|
|
|
|
if (hWnd && hWnd != HWND_INVALID) {
|
|
PMAINWIN pWin = (PMAINWIN)hWnd;
|
|
if (pWin->DataType == TYPE_HWND || pWin->DataType == TYPE_WINTODEL) {
|
|
pMsgQueue = pWin->pMainWin->pMsgQueue;
|
|
}
|
|
}
|
|
|
|
if (pMsgQueue == NULL)
|
|
return ERR_INV_HWND;
|
|
|
|
return __mg_throw_away_messages (pMsgQueue, hWnd);
|
|
}
|
|
|
|
BOOL GUIAPI EmptyMessageQueue (HWND hWnd)
|
|
{
|
|
PMSGQUEUE pMsgQueue;
|
|
PQMSG pQMsg, temp;
|
|
|
|
if (!(pMsgQueue = getMsgQueueIfWindowInThisThread(hWnd)))
|
|
return FALSE;
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
/* Since 5.0.0, we cancel all sync messages */
|
|
if (pMsgQueue->pFirstSyncMsg) {
|
|
PSYNCMSG pSyncMsg, pSyncPrev = NULL;
|
|
pSyncMsg = pMsgQueue->pFirstSyncMsg;
|
|
|
|
while (pSyncMsg) {
|
|
pSyncMsg->Msg.hwnd = HWND_INVALID;
|
|
|
|
// notify the waiting thread and remove the node from msg queue
|
|
pSyncMsg->retval = ERR_MSG_CANCELED;
|
|
if (pSyncPrev) {
|
|
pSyncPrev->pNext = pSyncMsg->pNext;
|
|
}
|
|
else {
|
|
pSyncPrev = pSyncMsg;
|
|
pSyncMsg = pSyncMsg->pNext;
|
|
pMsgQueue->pFirstSyncMsg = pSyncMsg;
|
|
sem_post (pSyncPrev->sem_handle);
|
|
pSyncPrev = NULL;
|
|
continue;
|
|
}
|
|
sem_post (pSyncMsg->sem_handle);
|
|
pSyncPrev = pSyncMsg;
|
|
pSyncMsg = pSyncMsg->pNext;
|
|
}
|
|
}
|
|
#endif /* defined _MGHAVE_VIRTUAL_WINDOW */
|
|
|
|
if (pMsgQueue->pFirstNotifyMsg) {
|
|
pQMsg = pMsgQueue->pFirstNotifyMsg;
|
|
while (pQMsg) {
|
|
temp = pQMsg->next;
|
|
FREEQMSG (pQMsg);
|
|
pQMsg = temp;
|
|
}
|
|
}
|
|
|
|
pMsgQueue->pFirstNotifyMsg = NULL;
|
|
pMsgQueue->pLastNotifyMsg = NULL;
|
|
|
|
pMsgQueue->readpos = 0;
|
|
pMsgQueue->writepos = 0;
|
|
|
|
pMsgQueue->dwState = QS_EMPTY;
|
|
pMsgQueue->expired_timer_mask = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef _MGHAVE_VIRTUAL_WINDOW
|
|
|
|
/* send a synchronous message to a window in a different thread */
|
|
LRESULT SendSyncMessage (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PMSGQUEUE pMsgQueue, thinfo = NULL;
|
|
SYNCMSG SyncMsg;
|
|
sem_t sync_msg;
|
|
|
|
if (!(pMsgQueue = getMsgQueue(hWnd)))
|
|
return ERR_INV_HWND;
|
|
|
|
#define _SYNC_NEED_REENTERABLE
|
|
#ifndef _SYNC_NEED_REENTERABLE
|
|
if ((thinfo = getMsgQueueForThisThread ())) {
|
|
/* avoid to create a new semaphore object, Note: it's not reenterable.*/
|
|
SyncMsg.sem_handle = &thinfo->sync_msg;
|
|
}
|
|
else {
|
|
/* this is not a message thread */
|
|
sem_init (&sync_msg, 0, 0);
|
|
SyncMsg.sem_handle = &sync_msg;
|
|
}
|
|
#else /* not defined _SYNC_NEED_REENTERABLE */
|
|
/* For reentrantable implementation, we use a unique semaphore which is
|
|
created in the stack. */
|
|
thinfo = getMsgQueueForThisThread ();
|
|
sem_init (&sync_msg, 0, 0);
|
|
SyncMsg.sem_handle = &sync_msg;
|
|
#endif /* defined _SYNC_NEED_REENTERABLE */
|
|
|
|
/* queue the sync message. */
|
|
SyncMsg.Msg.hwnd = hWnd;
|
|
SyncMsg.Msg.message = msg;
|
|
SyncMsg.Msg.wParam = wParam;
|
|
SyncMsg.Msg.lParam = lParam;
|
|
SyncMsg.retval = ERR_MSG_CANCELED;
|
|
SyncMsg.pNext = NULL;
|
|
|
|
/* add by houhh 20081030, deal with itself's SyncMsg first before
|
|
* SendSyncMessage to other thread, because other thread maybe wait
|
|
* for SyncMsg for you.*/
|
|
if (thinfo) {
|
|
#if 0 /* deprecated code */
|
|
MSG msg;
|
|
PMSGQUEUE pMsgQueue = thinfo;
|
|
|
|
while (TRUE) {
|
|
LOCK_MSGQ (pMsgQueue);
|
|
if (pMsgQueue->dwState & QS_SYNCMSG) {
|
|
if (pMsgQueue->pFirstSyncMsg) {
|
|
msg = pMsgQueue->pFirstSyncMsg->Msg;
|
|
msg.pSyncMsg = (pMsgQueue->pFirstSyncMsg);
|
|
pMsgQueue->pFirstSyncMsg = pMsgQueue->pFirstSyncMsg->pNext;
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
TranslateMessage (&msg);
|
|
DispatchMessage (&msg);
|
|
}
|
|
else {
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
pMsgQueue->dwState &= ~QS_SYNCMSG;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
break;
|
|
}
|
|
}
|
|
#else /* deprecated code */
|
|
/* tuned since 5.0.0 */
|
|
MSG msg;
|
|
PMSGQUEUE msg_queue = thinfo;
|
|
|
|
LOCK_MSGQ (msg_queue);
|
|
while (msg_queue->dwState & QS_SYNCMSG) {
|
|
if (msg_queue->pFirstSyncMsg) {
|
|
msg = msg_queue->pFirstSyncMsg->Msg;
|
|
msg.pSyncMsg = (msg_queue->pFirstSyncMsg);
|
|
msg_queue->pFirstSyncMsg = msg_queue->pFirstSyncMsg->pNext;
|
|
TranslateMessage (&msg);
|
|
DispatchMessage (&msg);
|
|
}
|
|
else {
|
|
msg_queue->dwState &= ~QS_SYNCMSG;
|
|
}
|
|
}
|
|
|
|
UNLOCK_MSGQ (msg_queue);
|
|
#endif /* tuned code */
|
|
}
|
|
|
|
LOCK_MSGQ (pMsgQueue);
|
|
|
|
if (pMsgQueue->pFirstSyncMsg == NULL) {
|
|
pMsgQueue->pFirstSyncMsg = pMsgQueue->pLastSyncMsg = &SyncMsg;
|
|
}
|
|
else {
|
|
pMsgQueue->pLastSyncMsg->pNext = &SyncMsg;
|
|
pMsgQueue->pLastSyncMsg = &SyncMsg;
|
|
}
|
|
|
|
pMsgQueue->dwState |= QS_SYNCMSG;
|
|
|
|
UNLOCK_MSGQ (pMsgQueue);
|
|
POST_MSGQ (pMsgQueue);
|
|
|
|
/* suspend until the message has been handled. */
|
|
if (sem_wait (SyncMsg.sem_handle) < 0) {
|
|
_ERR_PRINTF ("SendSyncMessage: thread is interrupted abnormally!\n");
|
|
}
|
|
|
|
#ifndef _SYNC_NEED_REENTERABLE
|
|
if (thinfo == NULL)
|
|
sem_destroy (&sync_msg);
|
|
#else
|
|
sem_destroy (&sync_msg);
|
|
#endif
|
|
|
|
#undef _SYNC_NEED_REENTERABLE
|
|
return SyncMsg.retval;
|
|
}
|
|
|
|
LRESULT GUIAPI PostSyncMessage (HWND hWnd, UINT msg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
MG_CHECK_RET (MG_IS_WINDOW(hWnd), -1);
|
|
if (getMainWinIfWindowInThisThread(hWnd))
|
|
return -1;
|
|
|
|
return SendSyncMessage (hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
LRESULT GUIAPI SendAsyncMessage (HWND hWnd, UINT nMsg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
WNDPROC WndProc;
|
|
|
|
MG_CHECK_RET (MG_IS_WINDOW(hWnd), -1);
|
|
|
|
if (!(WndProc = GETWNDPROC(hWnd)))
|
|
return -1;
|
|
|
|
return WndProc (hWnd, nMsg, wParam, lParam);
|
|
}
|
|
|
|
#endif /* defined _MGHAVE_VIRTUAL_WINDOW */
|
|
|