mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-02-06 10:21:57 +08:00
690 lines
17 KiB
C
690 lines
17 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~2018, 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/>.
|
|
*/
|
|
/*
|
|
** init.c: The Initialization/Termination routines for MiniGUI-Threads.
|
|
**
|
|
** Current maintainer: Wei Yongming.
|
|
**
|
|
** Create date: 2000/11/05
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "common.h"
|
|
#include "minigui.h"
|
|
|
|
#ifndef __NOUNIX__
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <sys/termios.h>
|
|
#endif
|
|
|
|
#include "minigui.h"
|
|
#include "gdi.h"
|
|
#include "window.h"
|
|
#include "cliprect.h"
|
|
#include "gal.h"
|
|
#include "ial.h"
|
|
#include "internals.h"
|
|
#include "ctrlclass.h"
|
|
#include "cursor.h"
|
|
#include "event.h"
|
|
#include "misc.h"
|
|
#include "menu.h"
|
|
#include "timer.h"
|
|
#include "accelkey.h"
|
|
#include "clipboard.h"
|
|
#include "license.h"
|
|
|
|
int __mg_quiting_stage;
|
|
|
|
#ifdef _MGRM_THREADS
|
|
int __mg_enter_terminategui;
|
|
|
|
/******************************* extern data *********************************/
|
|
extern void* __kernel_desktop_main (void* data);
|
|
|
|
/************************* Entry of the thread of parsor *********************/
|
|
static void ParseEvent (PLWEVENT lwe)
|
|
{
|
|
PMOUSEEVENT me;
|
|
PKEYEVENT ke;
|
|
MSG Msg;
|
|
static int mouse_x = 0, mouse_y = 0;
|
|
|
|
ke = &(lwe->data.ke);
|
|
me = &(lwe->data.me);
|
|
Msg.hwnd = HWND_DESKTOP;
|
|
Msg.wParam = 0;
|
|
Msg.lParam = 0;
|
|
|
|
Msg.time = __mg_timer_counter;
|
|
|
|
if (lwe->type == LWETYPE_TIMEOUT) {
|
|
Msg.message = MSG_TIMEOUT;
|
|
Msg.wParam = (WPARAM)lwe->count;
|
|
Msg.lParam = 0;
|
|
QueueDeskMessage (&Msg);
|
|
}
|
|
else if (lwe->type == LWETYPE_KEY) {
|
|
Msg.wParam = ke->scancode;
|
|
Msg.lParam = ke->status;
|
|
if(ke->event == KE_KEYDOWN){
|
|
Msg.message = MSG_KEYDOWN;
|
|
}
|
|
else if (ke->event == KE_KEYUP) {
|
|
Msg.message = MSG_KEYUP;
|
|
}
|
|
else if (ke->event == KE_KEYLONGPRESS) {
|
|
Msg.message = MSG_KEYLONGPRESS;
|
|
}
|
|
else if (ke->event == KE_KEYALWAYSPRESS) {
|
|
Msg.message = MSG_KEYALWAYSPRESS;
|
|
}
|
|
QueueDeskMessage (&Msg);
|
|
}
|
|
else if(lwe->type == LWETYPE_MOUSE) {
|
|
Msg.wParam = me->status;
|
|
Msg.lParam = MAKELONG (me->x, me->y);
|
|
|
|
switch (me->event) {
|
|
case ME_MOVED:
|
|
Msg.message = MSG_MOUSEMOVE;
|
|
break;
|
|
case ME_LEFTDOWN:
|
|
Msg.message = MSG_LBUTTONDOWN;
|
|
break;
|
|
case ME_LEFTUP:
|
|
Msg.message = MSG_LBUTTONUP;
|
|
break;
|
|
case ME_LEFTDBLCLICK:
|
|
Msg.message = MSG_LBUTTONDBLCLK;
|
|
break;
|
|
case ME_RIGHTDOWN:
|
|
Msg.message = MSG_RBUTTONDOWN;
|
|
break;
|
|
case ME_RIGHTUP:
|
|
Msg.message = MSG_RBUTTONUP;
|
|
break;
|
|
case ME_RIGHTDBLCLICK:
|
|
Msg.message = MSG_RBUTTONDBLCLK;
|
|
break;
|
|
case ME_MIDDLEDOWN:
|
|
Msg.message = MSG_MBUTTONDOWN;
|
|
break;
|
|
case ME_MIDDLEUP:
|
|
Msg.message = MSG_MBUTTONUP;
|
|
break;
|
|
case ME_MIDDLEDBLCLICK:
|
|
Msg.message = MSG_MBUTTONDBLCLK;
|
|
break;
|
|
}
|
|
|
|
if (me->event != ME_MOVED && (mouse_x != me->x || mouse_y != me->y)) {
|
|
int old = Msg.message;
|
|
|
|
Msg.message = MSG_MOUSEMOVE;
|
|
QueueDeskMessage (&Msg);
|
|
Msg.message = old;
|
|
|
|
mouse_x = me->x; mouse_y = me->y;
|
|
}
|
|
|
|
QueueDeskMessage (&Msg);
|
|
}
|
|
}
|
|
|
|
|
|
extern struct timeval __mg_event_timeout;
|
|
|
|
static void* EventLoop (void* data)
|
|
{
|
|
LWEVENT lwe;
|
|
int event;
|
|
|
|
lwe.data.me.x = 0; lwe.data.me.y = 0;
|
|
|
|
sem_post ((sem_t*)data);
|
|
|
|
while (__mg_quiting_stage > _MG_QUITING_STAGE_EVENT) {
|
|
EXTRA_INPUT_EVENT extra;
|
|
|
|
#if 0
|
|
ndef __USE_TIMER_THREAD
|
|
/* VM: Since 4.0.0, update timer counter in event loop */
|
|
if (__mg_quiting_stage > _MG_QUITING_STAGE_TIMER) {
|
|
extern void __mg_timer_action (void *data);
|
|
__mg_timer_action (NULL);
|
|
}
|
|
#endif
|
|
|
|
extra.params_mask = 0;
|
|
event = IAL_WaitEvent (0, NULL, NULL, NULL, &__mg_event_timeout, &extra);
|
|
if (event < 0)
|
|
continue;
|
|
|
|
lwe.status = 0L;
|
|
lwe.data.me.status = 0;
|
|
if (event & IAL_MOUSEEVENT && kernel_GetLWEvent (IAL_MOUSEEVENT, &lwe))
|
|
ParseEvent (&lwe);
|
|
|
|
lwe.status = 0L;
|
|
lwe.data.ke.status = 0;
|
|
if (event & IAL_KEYEVENT && kernel_GetLWEvent (IAL_KEYEVENT, &lwe))
|
|
ParseEvent (&lwe);
|
|
|
|
// for extra input events; since 4.0.0
|
|
if (event & IAL_EVENT_EXTRA) {
|
|
MSG msg;
|
|
msg.hwnd = HWND_DESKTOP;
|
|
msg.message = extra.event;
|
|
msg.wParam = extra.wparam;
|
|
msg.lParam = extra.lparam;
|
|
msg.time = __mg_timer_counter;
|
|
if (extra.params_mask) {
|
|
// packed multiple sub events
|
|
int i, n = 0;
|
|
for (i = 0; i < NR_PACKED_SUB_EVENTS; i++) {
|
|
if (extra.params_mask & (1 << i)) {
|
|
msg.wParam = extra.wparams[i];
|
|
msg.lParam = extra.lparams[i];
|
|
QueueDeskMessage (&msg);
|
|
n++;
|
|
}
|
|
}
|
|
|
|
if (n > 0) {
|
|
msg.message = MSG_EXIN_END_CHANGES;
|
|
msg.wParam = n;
|
|
msg.lParam = 0;
|
|
QueueDeskMessage (&msg);
|
|
}
|
|
}
|
|
else {
|
|
QueueDeskMessage (&msg);
|
|
}
|
|
}
|
|
else if (event == 0 && kernel_GetLWEvent (0, &lwe))
|
|
ParseEvent (&lwe);
|
|
}
|
|
|
|
/* printf("Quit from EventLoop()\n"); */
|
|
return NULL;
|
|
}
|
|
|
|
/************************** Thread Information ******************************/
|
|
|
|
pthread_key_t __mg_threadinfo_key;
|
|
|
|
static inline BOOL createThreadInfoKey (void)
|
|
{
|
|
if (pthread_key_create (&__mg_threadinfo_key, NULL))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static inline void deleteThreadInfoKey (void)
|
|
{
|
|
pthread_key_delete (__mg_threadinfo_key);
|
|
}
|
|
|
|
MSGQUEUE* mg_InitMsgQueueThisThread (void)
|
|
{
|
|
MSGQUEUE* pMsgQueue;
|
|
|
|
if (!(pMsgQueue = malloc(sizeof(MSGQUEUE))) ) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!mg_InitMsgQueue(pMsgQueue, 0)) {
|
|
free (pMsgQueue);
|
|
return NULL;
|
|
}
|
|
|
|
pthread_setspecific (__mg_threadinfo_key, pMsgQueue);
|
|
return pMsgQueue;
|
|
}
|
|
|
|
void mg_FreeMsgQueueThisThread (void)
|
|
{
|
|
MSGQUEUE* pMsgQueue;
|
|
|
|
pMsgQueue = pthread_getspecific (__mg_threadinfo_key);
|
|
#ifdef __VXWORKS__
|
|
if (pMsgQueue != (void *)0 && pMsgQueue != (void *)-1) {
|
|
#else
|
|
if (pMsgQueue) {
|
|
#endif
|
|
mg_DestroyMsgQueue (pMsgQueue);
|
|
free (pMsgQueue);
|
|
#ifdef __VXWORKS__
|
|
pthread_setspecific (__mg_threadinfo_key, (void*)-1);
|
|
#else
|
|
pthread_setspecific (__mg_threadinfo_key, NULL);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
The following function is moved to src/include/internals.h as an inline
|
|
function.
|
|
MSGQUEUE* GetMsgQueueThisThread (void)
|
|
{
|
|
return (MSGQUEUE*) pthread_getspecific (__mg_threadinfo_key);
|
|
}
|
|
*/
|
|
|
|
/************************** System Initialization ****************************/
|
|
|
|
int __mg_timer_init (void);
|
|
|
|
static BOOL SystemThreads(void)
|
|
{
|
|
sem_t wait;
|
|
|
|
sem_init (&wait, 0, 0);
|
|
|
|
// this is the thread for desktop window.
|
|
// this thread should have a normal priority same as
|
|
// other main window threads.
|
|
// if there is no main window can receive the low level events,
|
|
// this desktop window is the only one can receive the events.
|
|
// to close a MiniGUI application, we should close the desktop
|
|
// window.
|
|
#ifdef __NOUNIX__
|
|
{
|
|
pthread_attr_t new_attr;
|
|
pthread_attr_init (&new_attr);
|
|
pthread_attr_setstacksize (&new_attr, 16 * 1024);
|
|
pthread_create (&__mg_desktop, &new_attr, __kernel_desktop_main, &wait);
|
|
pthread_attr_destroy (&new_attr);
|
|
}
|
|
#else
|
|
pthread_create (&__mg_desktop, NULL, __kernel_desktop_main, &wait);
|
|
#endif
|
|
|
|
sem_wait (&wait);
|
|
|
|
__mg_timer_init ();
|
|
|
|
// this thread collect low level event from outside,
|
|
// if there is no event, this thread will suspend to wait a event.
|
|
// the events maybe mouse event, keyboard event, or timeout event.
|
|
//
|
|
// this thread also parse low level event and translate it to message,
|
|
// then post the message to the approriate message queue.
|
|
// this thread should also have a higher priority.
|
|
pthread_create (&__mg_parsor, NULL, EventLoop, &wait);
|
|
pthread_detach (__mg_parsor);
|
|
sem_wait (&wait);
|
|
|
|
sem_destroy (&wait);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GUIAPI ReinitDesktopEx (BOOL init_sys_text)
|
|
{
|
|
return SendMessage (HWND_DESKTOP, MSG_REINITSESSION, init_sys_text, 0) == 0;
|
|
}
|
|
|
|
#ifndef __NOUNIX__
|
|
static struct termios savedtermio;
|
|
|
|
void* GUIAPI GetOriginalTermIO (void)
|
|
{
|
|
return &savedtermio;
|
|
}
|
|
|
|
static void sig_handler (int v)
|
|
{
|
|
if (v == SIGSEGV) {
|
|
kill (getpid(), SIGABRT); /* cause core dump */
|
|
}
|
|
else if (v == SIGINT) {
|
|
_exit(1); /* force to quit */
|
|
}
|
|
else if (__mg_quiting_stage > 0) {
|
|
ExitGUISafely(-1);
|
|
}
|
|
else {
|
|
exit(1); /* force to quit */
|
|
}
|
|
}
|
|
|
|
static BOOL InstallSEGVHandler (void)
|
|
{
|
|
struct sigaction siga;
|
|
|
|
siga.sa_handler = sig_handler;
|
|
siga.sa_flags = 0;
|
|
|
|
memset (&siga.sa_mask, 0, sizeof (sigset_t));
|
|
sigaction (SIGSEGV, &siga, NULL);
|
|
sigaction (SIGTERM, &siga, NULL);
|
|
sigaction (SIGINT, &siga, NULL);
|
|
sigaction (SIGPIPE, &siga, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_LOCALE_H
|
|
#include <locale.h>
|
|
#endif
|
|
|
|
int GUIAPI InitGUI (int args, const char *agr[])
|
|
{
|
|
char engine [LEN_ENGINE_NAME + 1];
|
|
char mode [LEN_VIDEO_MODE + 1];
|
|
int step = 0;
|
|
|
|
__mg_quiting_stage = _MG_QUITING_STAGE_RUNNING;
|
|
__mg_enter_terminategui = 0;
|
|
|
|
#ifdef HAVE_SETLOCALE
|
|
setlocale (LC_ALL, "C");
|
|
#endif
|
|
|
|
#if defined (_USE_MUTEX_ON_SYSVIPC) || defined (_USE_SEM_ON_SYSVIPC)
|
|
step++;
|
|
if (_sysvipc_mutex_sem_init ())
|
|
return step;
|
|
#endif
|
|
|
|
#ifndef __NOUNIX__
|
|
// Save original termio
|
|
tcgetattr (0, &savedtermio);
|
|
#endif
|
|
|
|
/*initialize default window process*/
|
|
__mg_def_proc[0] = PreDefMainWinProc;
|
|
__mg_def_proc[1] = PreDefDialogProc;
|
|
__mg_def_proc[2] = PreDefControlProc;
|
|
|
|
step++;
|
|
if (!mg_InitSliceAllocator ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: failed to initialize slice allocator!\n");
|
|
return step;
|
|
}
|
|
|
|
step++;
|
|
if (!mg_InitFixStr ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init Fixed String module failure!\n");
|
|
return step;
|
|
}
|
|
|
|
step++;
|
|
/* Init miscelleous*/
|
|
if (!mg_InitMisc ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Initialization of misc things failure!\n");
|
|
return step;
|
|
}
|
|
|
|
step++;
|
|
switch (mg_InitGAL (engine, mode)) {
|
|
case ERR_CONFIG_FILE:
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Reading configuration failure!\n");
|
|
return step;
|
|
|
|
case ERR_NO_ENGINE:
|
|
_ERR_PRINTF ("KERNEL>InitGUI: No graphics engine defined!\n");
|
|
return step;
|
|
|
|
case ERR_NO_MATCH:
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Can not get graphics engine information!\n");
|
|
return step;
|
|
|
|
case ERR_GFX_ENGINE:
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Can not initialize graphics engine!\n");
|
|
return step;
|
|
}
|
|
|
|
#ifndef __NOUNIX__
|
|
InstallSEGVHandler ();
|
|
#endif
|
|
|
|
/*
|
|
* Load system resource here.
|
|
*/
|
|
step++;
|
|
if (!mg_InitSystemRes ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Can not initialize system resource!\n");
|
|
goto failure1;
|
|
}
|
|
|
|
/* Init GDI. */
|
|
step++;
|
|
if(!mg_InitGDI()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Initialization of GDI failure!\n");
|
|
goto failure1;
|
|
}
|
|
|
|
/* Init Master Screen DC here */
|
|
step++;
|
|
if (!mg_InitScreenDC (__gal_screen)) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Can not initialize screen DC!\n");
|
|
goto failure1;
|
|
}
|
|
|
|
#if 0
|
|
g_rcScr.left = 0;
|
|
g_rcScr.top = 0;
|
|
g_rcScr.right = GetGDCapability (HDC_SCREEN_SYS, GDCAP_MAXX) + 1;
|
|
g_rcScr.bottom = GetGDCapability (HDC_SCREEN_SYS, GDCAP_MAXY) + 1;
|
|
#endif
|
|
|
|
__mg_license_create();
|
|
__mg_splash_draw_framework();
|
|
|
|
/* Init mouse cursor. */
|
|
step++;
|
|
if( !mg_InitCursor() ) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Count not init mouse cursor!\n");
|
|
goto failure1;
|
|
}
|
|
__mg_splash_progress();
|
|
|
|
/* Init low level event */
|
|
step++;
|
|
if(!mg_InitLWEvent()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Low level event initialization failure!\n");
|
|
goto failure1;
|
|
}
|
|
__mg_splash_progress();
|
|
|
|
/** Init LF Manager */
|
|
step++;
|
|
if (!mg_InitLFManager ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Initialization of LF Manager failure!\n");
|
|
goto failure;
|
|
}
|
|
__mg_splash_progress();
|
|
|
|
#ifdef _MGHAVE_MENU
|
|
/* Init menu */
|
|
step++;
|
|
if (!mg_InitMenu ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init Menu module failure!\n");
|
|
goto failure;
|
|
}
|
|
#endif
|
|
|
|
/* Init control class */
|
|
step++;
|
|
if(!mg_InitControlClass()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init Control Class failure!\n");
|
|
goto failure;
|
|
}
|
|
__mg_splash_progress();
|
|
|
|
/* Init accelerator */
|
|
step++;
|
|
if(!mg_InitAccel()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init Accelerator failure!\n");
|
|
goto failure;
|
|
}
|
|
__mg_splash_progress();
|
|
|
|
step++;
|
|
if (!mg_InitDesktop ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init Desktop failure!\n");
|
|
goto failure;
|
|
}
|
|
__mg_splash_progress();
|
|
|
|
step++;
|
|
if (!mg_InitFreeQMSGList ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init free QMSG list failure!\n");
|
|
goto failure;
|
|
}
|
|
__mg_splash_progress();
|
|
|
|
step++;
|
|
if (!createThreadInfoKey ()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init thread hash table failure!\n");
|
|
goto failure;
|
|
}
|
|
|
|
__mg_splash_delay();
|
|
|
|
step++;
|
|
if (!SystemThreads()) {
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init system threads failure!\n");
|
|
goto failure;
|
|
}
|
|
|
|
SetKeyboardLayout ("default");
|
|
|
|
SetCursor (GetSystemCursor (IDC_ARROW));
|
|
|
|
SetCursorPos (g_rcScr.right >> 1, g_rcScr.bottom >> 1);
|
|
|
|
mg_TerminateMgEtc ();
|
|
|
|
return 0;
|
|
|
|
failure:
|
|
mg_TerminateLWEvent ();
|
|
failure1:
|
|
mg_TerminateGAL ();
|
|
_ERR_PRINTF ("KERNEL>InitGUI: Init failure, please check "
|
|
"your MiniGUI configuration or resource.\n");
|
|
return step;
|
|
}
|
|
|
|
void GUIAPI TerminateGUI (int not_used)
|
|
{
|
|
__mg_enter_terminategui = 1;
|
|
|
|
pthread_join (__mg_desktop, NULL);
|
|
|
|
/* DesktopProc() will set __mg_quiting_stage to _MG_QUITING_STAGE_EVENT */
|
|
pthread_join (__mg_parsor, NULL);
|
|
|
|
deleteThreadInfoKey ();
|
|
__mg_license_destroy();
|
|
|
|
__mg_quiting_stage = _MG_QUITING_STAGE_TIMER;
|
|
mg_TerminateTimer ();
|
|
|
|
mg_TerminateDesktop ();
|
|
|
|
mg_DestroyFreeQMSGList ();
|
|
mg_TerminateAccel ();
|
|
mg_TerminateControlClass ();
|
|
#ifdef _MGHAVE_MENU
|
|
mg_TerminateMenu ();
|
|
#endif
|
|
mg_TerminateMisc ();
|
|
mg_TerminateFixStr ();
|
|
|
|
#ifdef _MGHAVE_CURSOR
|
|
mg_TerminateCursor ();
|
|
#endif
|
|
mg_TerminateLWEvent ();
|
|
|
|
mg_TerminateScreenDC ();
|
|
mg_TerminateGDI ();
|
|
mg_TerminateLFManager ();
|
|
mg_TerminateGAL ();
|
|
|
|
#ifdef _MGHAVE_ADV_2DAPI
|
|
extern void mg_miFreeArcCache (void);
|
|
mg_miFreeArcCache ();
|
|
#endif
|
|
|
|
mg_TerminateSliceAllocator();
|
|
|
|
/*
|
|
* Restore original termio
|
|
*tcsetattr (0, TCSAFLUSH, &savedtermio);
|
|
*/
|
|
|
|
#if defined (_USE_MUTEX_ON_SYSVIPC) || defined (_USE_SEM_ON_SYSVIPC)
|
|
_sysvipc_mutex_sem_term ();
|
|
#endif
|
|
}
|
|
|
|
#endif /* _MGRM_THREADS */
|
|
|
|
void GUIAPI ExitGUISafely (int exitcode)
|
|
{
|
|
#ifdef _MGRM_PROCESSES
|
|
if (mgIsServer)
|
|
#endif
|
|
{
|
|
# define IDM_CLOSEALLWIN (MINID_RESERVED + 1) /* see src/kernel/desktop-*.c */
|
|
SendNotifyMessage(HWND_DESKTOP, MSG_COMMAND, IDM_CLOSEALLWIN, 0);
|
|
SendNotifyMessage(HWND_DESKTOP, MSG_ENDSESSION, 0, 0);
|
|
|
|
if (__mg_quiting_stage > 0) {
|
|
__mg_quiting_stage = _MG_QUITING_STAGE_START;
|
|
}
|
|
}
|
|
}
|