mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-02-07 02:52:42 +08:00
494 lines
14 KiB
C
494 lines
14 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/>.
|
|
*/
|
|
/*
|
|
** server.c: routines for server.
|
|
**
|
|
** Current maintainer: Wei Yongming.
|
|
**
|
|
** Create date: 2000/12/20
|
|
**
|
|
** NOTE: The idea comes from sample code in APUE.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <sys/times.h>
|
|
#include <sys/poll.h>
|
|
|
|
#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 "license.h"
|
|
|
|
extern DWORD __mg_timer_counter;
|
|
|
|
ON_NEW_DEL_CLIENT OnNewDelClient = NULL;
|
|
|
|
static SRVEVTHOOK srv_evt_hook = NULL;
|
|
|
|
SRVEVTHOOK GUIAPI SetServerEventHook (SRVEVTHOOK SrvEvtHook)
|
|
{
|
|
SRVEVTHOOK old_hook = srv_evt_hook;
|
|
|
|
if (!mgIsServer)
|
|
return NULL;
|
|
|
|
srv_evt_hook = SrvEvtHook;
|
|
|
|
return old_hook;
|
|
}
|
|
|
|
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_timer_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 (__mg_do_drag_drop_window (Msg.message, 0, 0))
|
|
return;
|
|
|
|
if (!(srv_evt_hook && srv_evt_hook (&Msg))) {
|
|
kernel_QueueMessage (msg_que, &Msg);
|
|
}
|
|
}
|
|
else if (lwe.type == LWETYPE_MOUSE) {
|
|
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);
|
|
Msg.wParam = me->status;
|
|
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;
|
|
}
|
|
|
|
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));
|
|
|
|
Msg.lParam = MAKELONG (me->x, me->y);
|
|
|
|
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_capture_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) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
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_ZERO (&mg_rfdset);
|
|
FD_SET (listenfd, &mg_rfdset);
|
|
mg_maxfd = listenfd;
|
|
maxi = -1;
|
|
|
|
mg_InitTimer ();
|
|
|
|
__mg_start_server_desktop ();
|
|
|
|
screensaver_create();
|
|
|
|
return TRUE;
|
|
|
|
fail:
|
|
__mg_cleanup_layers ();
|
|
return FALSE;
|
|
}
|
|
|
|
void server_ServerCleanup (void)
|
|
{
|
|
__mg_cleanup_layers ();
|
|
|
|
mg_UninstallIntervalTimer ();
|
|
|
|
unlink (CS_PATH);
|
|
}
|
|
|
|
BOOL server_IdleHandler4Server (PMSGQUEUE msg_queue)
|
|
{
|
|
int i, n, 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
|
|
|
|
if (__mg_timer_counter != SHAREDRES_TIMER_COUNTER) {
|
|
__mg_timer_counter = SHAREDRES_TIMER_COUNTER;
|
|
SetDesktopTimerFlag ();
|
|
}
|
|
|
|
rset = mg_rfdset; /* rset gets modified each time around */
|
|
if (mg_wfdset) {
|
|
wset = *mg_wfdset;
|
|
wsetptr = &wset;
|
|
}
|
|
if (mg_efdset) {
|
|
eset = *mg_efdset;
|
|
esetptr = &eset;
|
|
}
|
|
|
|
#if defined (_MGHAVE_CURSOR) && defined(_MGSCHEMA_SHAREDFB)
|
|
/* if the cursor has been hide by GDI function of clients
|
|
* this call will show the cursor
|
|
*/
|
|
kernel_ReShowCursor ();
|
|
#endif
|
|
|
|
extra.params_mask = 0;
|
|
if ((n = IAL_WaitEvent (mg_maxfd, &rset, wsetptr, esetptr,
|
|
msg_queue?NULL:(&sel_timeout), &extra)) < 0) {
|
|
|
|
/* It is time to check event again. */
|
|
if (errno == EINTR) {
|
|
if (msg_queue)
|
|
ParseEvent (msg_queue, 0);
|
|
return FALSE;
|
|
}
|
|
#ifdef _DEBUG
|
|
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
|
|
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_rfdset);
|
|
if (clifd > mg_maxfd)
|
|
mg_maxfd = clifd; /* max fd for select() */
|
|
if (i > maxi)
|
|
maxi = i; /* max index in client[] array */
|
|
#ifdef _DEBUG
|
|
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
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (msg_queue == NULL)
|
|
return (n > 0);
|
|
|
|
/* handle intput event (mouse/touch-screen or keyboard) */
|
|
if (n & IAL_MOUSEEVENT) ParseEvent (msg_queue, IAL_MOUSEEVENT);
|
|
if (n & IAL_KEYEVENT) ParseEvent (msg_queue, IAL_KEYEVENT);
|
|
if (n & 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];
|
|
kernel_QueueMessage (msg_queue, &msg);
|
|
n++;
|
|
}
|
|
}
|
|
|
|
if (n > 0) {
|
|
msg.message = MSG_EXIN_END_CHANGES;
|
|
msg.wParam = n;
|
|
msg.lParam = 0;
|
|
kernel_QueueMessage (msg_queue, &msg);
|
|
}
|
|
}
|
|
else {
|
|
kernel_QueueMessage (msg_queue, &msg);
|
|
}
|
|
}
|
|
else if (n == 0)
|
|
ParseEvent (msg_queue, 0);
|
|
|
|
/* go through registered listen fds */
|
|
for (i = 0; i < MAX_NR_LISTEN_FD; i++) {
|
|
MSG Msg;
|
|
|
|
Msg.message = MSG_FDEVENT;
|
|
|
|
if (mg_listen_fds [i].fd) {
|
|
fd_set* temp = NULL;
|
|
int type = mg_listen_fds [i].type;
|
|
|
|
switch (type) {
|
|
case POLLIN:
|
|
temp = &rset;
|
|
break;
|
|
case POLLOUT:
|
|
temp = wsetptr;
|
|
break;
|
|
case POLLERR:
|
|
temp = esetptr;
|
|
break;
|
|
}
|
|
|
|
if (temp && FD_ISSET (mg_listen_fds [i].fd, temp)) {
|
|
Msg.hwnd = (HWND)mg_listen_fds [i].hwnd;
|
|
Msg.wParam = MAKELONG (mg_listen_fds [i].fd, type);
|
|
Msg.lParam = (LPARAM)mg_listen_fds [i].context;
|
|
kernel_QueueMessage (msg_queue, &Msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|