///////////////////////////////////////////////////////////////////////////////
//
// 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 .
*
* 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
* .
*/
/*
** server.c: routines for server.
**
** Current maintainer: Wei Yongming.
**
** Create date: 2000/12/20
**
** NOTE: The idea comes from sample code in APUE.
*/
#include
#include
#include
#include
#include
#include "common.h"
#include "minigui.h"
#include "gdi.h"
#include "window.h"
#include "cliprect.h"
#include "internals.h"
#include "gal.h"
#include "ial.h"
#include "cursor.h"
#include "event.h"
#include "menu.h"
#include "ourhdr.h"
#include "sockio.h"
#include "client.h"
#include "server.h"
#include "sharedres.h"
#include "drawsemop.h"
#include "timer.h"
#include "license.h"
extern DWORD __mg_tick_counter;
ON_NEW_DEL_CLIENT OnNewDelClient = NULL;
#if 0
/* Since 5.0.0, use RegisterEventHookFunc to implement SetServerEventHook */
static SRVEVTHOOK srv_evt_hook = NULL;
SRVEVTHOOK GUIAPI SetServerEventHook (SRVEVTHOOK SrvEvtHook)
{
SRVEVTHOOK old_hook = srv_evt_hook;
srv_evt_hook = SrvEvtHook;
return old_hook;
}
#endif /* deprecated code; moved to window.c */
static void ParseEvent (PMSGQUEUE msg_que, int event)
{
LWEVENT lwe;
PMOUSEEVENT me;
PKEYEVENT ke;
MSG Msg;
ke = &(lwe.data.ke);
me = &(lwe.data.me);
me->x = 0; me->y = 0;
Msg.hwnd = HWND_DESKTOP;
Msg.wParam = 0;
Msg.lParam = 0;
ke->status = 0L;
me->status = 0L;
lwe.status = 0L;
if (!kernel_GetLWEvent (event, &lwe))
return;
Msg.time = __mg_tick_counter;
if (lwe.type == LWETYPE_TIMEOUT) {
Msg.message = MSG_TIMEOUT;
Msg.wParam = (WPARAM)lwe.count;
Msg.lParam = 0;
/*
* No need to send TIME_OUT message to the client
* Send2Client (&Msg, CLIENT_ACTIVE);
*/
kernel_QueueMessage (msg_que, &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;
}
#if 0 /* Since 5.0.0 */
if (__mg_do_drag_drop_window (Msg.message, 0, 0))
return;
if (!(srv_evt_hook && srv_evt_hook (&Msg))) {
kernel_QueueMessage (msg_que, &Msg);
}
#else /* deprecated code */
if (__mg_check_hook_func (HOOK_EVENT_KEY, &Msg) == HOOK_GOON) {
kernel_QueueMessage (msg_que, &Msg);
}
#endif /* use __mg_check_hook_func instead */
}
else if (lwe.type == LWETYPE_MOUSE) {
#if 0 /* Since 5.0.0: move to desktop */
static int down_client = -1;
static int down_by;
int cur_client = __mg_get_znode_at_point (__mg_zorder_info,
me->x, me->y, NULL);
#endif /* moved to desktop */
switch (me->event) {
case ME_MOVED:
Msg.message = MSG_MOUSEMOVE;
break;
case ME_LEFTDOWN:
Msg.message = MSG_LBUTTONDOWN;
break;
case ME_RIGHTDOWN:
Msg.message = MSG_RBUTTONDOWN;
break;
case ME_LEFTUP:
Msg.message = MSG_LBUTTONUP;
break;
case ME_LEFTDBLCLICK:
Msg.message = MSG_LBUTTONDBLCLK;
break;
case ME_RIGHTUP:
Msg.message = MSG_RBUTTONUP;
break;
case ME_RIGHTDBLCLICK:
Msg.message = MSG_RBUTTONDBLCLK;
break;
}
Msg.wParam = me->status;
Msg.lParam = MAKELONG (me->x, me->y);
if (__mg_check_hook_func (HOOK_EVENT_MOUSE, &Msg) == HOOK_GOON) {
kernel_QueueMessage (msg_que, &Msg);
}
#if 0 /* Since 5.0.0: move to desktop */
if (__mg_do_drag_drop_window (Msg.message, me->x, me->y)) {
down_client = -1;
return;
}
switch (Msg.message) {
case MSG_LBUTTONDOWN:
case MSG_RBUTTONDOWN:
if (cur_client >= 0 && down_client == -1) {
down_client = cur_client;
down_by = Msg.message;
}
break;
}
if (cur_client == -1)
SetDefaultCursor (GetSystemCursor (IDC_ARROW));
if (__mg_handle_mouse_hook (Msg.message,
Msg.wParam, Msg.lParam) == HOOK_STOP)
return;
if (!(srv_evt_hook && srv_evt_hook (&Msg))) {
int target_client = cur_client;
if (down_client < 0) {
if (!__mg_captured_wnd && me->event == ME_MOVED) {
int cli = __mg_handle_normal_mouse_move (__mg_zorder_info,
me->x, me->y);
if (cli > 0)
target_client = cli;
}
}
else
target_client = down_client;
if (target_client > 0) {
Msg.hwnd = 0;
#ifdef _MG_CONFIG_FAST_MOUSEMOVE
if (Msg.message == MSG_MOUSEMOVE) {
LOCK_MOUSEMOVE_SEM();
if (SHAREDRES_MOUSEMOVECLIENT > 0 &&
SHAREDRES_MOUSEMOVECLIENT != target_client) {
_DBG_PRINTF ("drop a mouse move message, old_client=%d, target_client=%d\n",
SHAREDRES_MOUSEMOVECLIENT, target_client);
}
SHAREDRES_MOUSEMOVECLIENT = target_client;
++ SHAREDRES_MOUSEMOVESERIAL;
UNLOCK_MOUSEMOVE_SEM();
}
else
#endif
{
Send2Client (&Msg, target_client);
}
}
else
kernel_QueueMessage (msg_que, &Msg);
}
if (Msg.message == MSG_LBUTTONUP && down_by == MSG_LBUTTONDOWN) {
down_client = -1;
}
if (Msg.message == MSG_RBUTTONUP && down_by == MSG_RBUTTONDOWN) {
down_client = -1;
}
#endif /* moved to desktop */
}
}
static int listenfd;
static int maxi;
BOOL GUIAPI ServerStartup (int nr_globals,
int def_nr_topmosts, int def_nr_normals)
{
if (!mgIsServer)
return FALSE;
if (nr_globals <= 0) nr_globals = DEF_NR_GLOBALS;
if (def_nr_topmosts <= 0) def_nr_topmosts = DEF_NR_TOPMOSTS;
if (def_nr_normals <= 0) def_nr_normals = DEF_NR_NORMALS;
nr_globals = (nr_globals + 7) & ~0x07;
nr_globals -= NR_FIXED_ZNODES;
def_nr_topmosts = (def_nr_topmosts + 7) & ~0x07;
def_nr_normals = (def_nr_normals + 7) & ~0x07;
SHAREDRES_NR_GLOBALS = nr_globals;
SHAREDRES_DEF_NR_TOPMOSTS = def_nr_topmosts;
SHAREDRES_DEF_NR_NORMALS = def_nr_normals;
if (__mg_init_layers () == -1)
return FALSE;
SHAREDRES_NR_LAYSERS = 1;
mgTopmostLayer = mgLayers;
SHAREDRES_TOPMOST_LAYER = (GHANDLE)mgTopmostLayer;
/* reserve the first client slot for the server */
__mg_client_id = __mg_client_add (0, getpid (), 0);
mgClients[0].layer = mgTopmostLayer;
/* obtain fd to listen for client requests on */
if ( (listenfd = serv_listen (CS_PATH)) < 0)
goto fail;
FD_SET (listenfd, &__mg_dsk_msg_queue->rfdset);
__mg_dsk_msg_queue->maxfd = listenfd;
maxi = -1;
mg_InitTimer (TRUE);
__mg_start_server_desktop ();
__mg_screensaver_create();
#ifdef _MGSCHEMA_COMPOSITING
if (!mg_InitCompositor ()) {
_ERR_PRINTF ("Can not initialize compositor!");
goto fail;
}
atexit (mg_TerminateCompositor);
#endif
return TRUE;
fail:
__mg_cleanup_layers ();
return FALSE;
}
void server_ServerCleanup (void)
{
__mg_cleanup_layers ();
mg_TerminateTimer (TRUE);
unlink (CS_PATH);
}
BOOL server_IdleHandler4Server (PMSGQUEUE msg_queue, BOOL wait)
{
int i, evt, clifd, nread;
pid_t pid;
uid_t uid;
struct timeval sel_timeout = {0, 0};
fd_set rset, wset, eset;
fd_set* wsetptr = NULL;
fd_set* esetptr = NULL;
EXTRA_INPUT_EVENT extra; // Since 4.0.0; for extra input events
int nevts = 0; // Since 5.0.0; for timer and fd events
#if 0 /* deprecated code */
/* since 5.0.0, use __mg_update_tick_count instead */
if (__mg_tick_counter != SHAREDRES_TIMER_COUNTER) {
__mg_tick_counter = SHAREDRES_TIMER_COUNTER;
AlertDesktopTimerEvent ();
}
#endif /* deprecated code */
/* rset gets modified each time around */
rset = __mg_dsk_msg_queue->rfdset;
if (__mg_dsk_msg_queue->nr_wfds) {
wset = __mg_dsk_msg_queue->wfdset;
wsetptr = &wset;
}
if (__mg_dsk_msg_queue->nr_efds) {
eset = __mg_dsk_msg_queue->efdset;
esetptr = &eset;
}
#if defined (_MGHAVE_CURSOR) && defined (_MGSCHEMA_SHAREDFB)
/* if the cursor has been hide by GDI function of clients
* this call will re-show the cursor */
kernel_ReShowCursor ();
#endif
#ifdef _MGSCHEMA_COMPOSITING
__mg_composite_dirty_znodes ();
#endif
extra.params_mask = 0;
if ((evt = IAL_WaitEvent (__mg_dsk_msg_queue->maxfd, &rset, wsetptr, esetptr,
wait?NULL:(&sel_timeout), &extra)) < 0) {
/* It is time to check event again. */
if (errno == EINTR) {
if (wait)
ParseEvent (msg_queue, 0);
return FALSE;
}
#ifdef _DEBUG
__mg_err_msg ("select error on server");
#endif
}
if (FD_ISSET (listenfd, &rset)) {
/* accept new client request */
if ( (clifd = serv_accept (listenfd, &pid, &uid)) < 0) {
#ifdef _DEBUG
__mg_err_msg ("serv_accept error: %d", clifd);
#endif
return TRUE;
}
if ((i = __mg_client_add (clifd, pid, uid)) == -1) {
/* can not accept this client */
close (clifd);
return TRUE;
}
if (OnNewDelClient) OnNewDelClient (LCO_NEW_CLIENT, i);
FD_SET (clifd, &__mg_dsk_msg_queue->rfdset);
if (clifd > __mg_dsk_msg_queue->maxfd)
__mg_dsk_msg_queue->maxfd = clifd; /* max fd for select() */
if (i > maxi)
maxi = i; /* max index in client[] array */
#ifdef _DEBUG
__mg_err_msg ("new connection: uid %d, fd %d", uid, clifd);
#endif
return TRUE;
}
for (i = 0; i <= maxi; i++) { /* go through client[] array */
if ( (clifd = mgClients[i].fd) < 0)
continue;
if (FD_ISSET (clifd, &rset)) {
int req_id;
/* read request id from client */
if ((nread = sock_read (clifd, &req_id, sizeof (int)))
== SOCKERR_IO) {
#ifdef _DEBUG
__mg_err_msg ("server: read error on fd %d", clifd);
#endif
if (OnNewDelClient) OnNewDelClient (LCO_DEL_CLIENT, i);
__mg_remove_client (i, clifd);
}
else if (nread == SOCKERR_CLOSED) {
if (OnNewDelClient) OnNewDelClient (LCO_DEL_CLIENT, i);
__mg_remove_client (i, clifd);
}
else /* process client's rquest */
__mg_handle_request (clifd, req_id, i);
}
}
/* handle intput event (mouse/touch-screen or keyboard) */
if (evt & IAL_MOUSEEVENT) {
ParseEvent (msg_queue, IAL_MOUSEEVENT);
nevts++;
}
if (evt & IAL_KEYEVENT) {
ParseEvent (msg_queue, IAL_KEYEVENT);
nevts++;
}
if (evt & IAL_EVENT_EXTRA) {
MSG msg;
msg.hwnd = HWND_DESKTOP;
msg.message = extra.event;
msg.wParam = extra.wparam;
msg.lParam = extra.lparam;
msg.time = __mg_tick_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];
if (__mg_check_hook_func (HOOK_EVENT_EXTRA, &msg) ==
HOOK_GOON) {
kernel_QueueMessage (msg_queue, &msg);
nevts++;
}
n++;
}
}
if (n > 0) {
msg.message = MSG_EXIN_END_CHANGES;
msg.wParam = n;
msg.lParam = 0;
if (__mg_check_hook_func (HOOK_EVENT_EXTRA, &msg) ==
HOOK_GOON) {
kernel_QueueMessage (msg_queue, &msg);
nevts++;
}
}
}
else {
if (__mg_check_hook_func (HOOK_EVENT_EXTRA, &msg) == HOOK_GOON) {
kernel_QueueMessage (msg_queue, &msg);
nevts++;
}
}
}
else if (evt == 0) {
ParseEvent (msg_queue, 0);
}
/* Since 5.0.0: Always check timers */
nevts += __mg_check_expired_timers (msg_queue,
SHAREDRES_TIMER_COUNTER - msg_queue->old_tick_count);
msg_queue->old_tick_count = SHAREDRES_TIMER_COUNTER;
/* go through registered listening fds */
nevts += __mg_kernel_check_listen_fds (msg_queue, &rset, wsetptr, esetptr);
return (nevts > 0);
}