mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-10-01 12:23:22 +08:00
415 lines
11 KiB
C
415 lines
11 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/>.
|
|
*/
|
|
/*
|
|
** client.c: maintain the clients in server.
|
|
**
|
|
** Current maintainer: Wei Yongming.
|
|
**
|
|
** Create date: 2000/12/20
|
|
**
|
|
** NOTE: The idea comes from sample code in APUE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
|
|
#include "common.h"
|
|
#include "minigui.h"
|
|
#include "gdi.h"
|
|
#include "window.h"
|
|
#include "cliprect.h"
|
|
#include "internals.h"
|
|
|
|
#include "ourhdr.h"
|
|
#include "sockio.h"
|
|
#include "client.h"
|
|
#include "server.h"
|
|
#include "sharedres.h"
|
|
#include "drawsemop.h"
|
|
|
|
#define NALLOC 4 /* #Client structs to alloc/realloc for */
|
|
|
|
extern DWORD __mg_timer_counter;
|
|
|
|
MG_Client* mgClients = NULL;
|
|
int mgClientSize = 0;
|
|
|
|
#define off_pointer(p,off) \
|
|
do { \
|
|
if (p) { \
|
|
intptr_t tmp; \
|
|
tmp = (intptr_t)p; \
|
|
tmp += off; \
|
|
p = (void*)tmp; \
|
|
} \
|
|
} while (0);
|
|
|
|
static void offset_pointers (MG_Client* new_head, int size, int off)
|
|
{
|
|
int i;
|
|
MG_Layer* layer;
|
|
|
|
if (off == 0) return;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
off_pointer (new_head [i].next, off);
|
|
off_pointer (new_head [i].prev, off);
|
|
}
|
|
|
|
layer = mgLayers;
|
|
while (layer) {
|
|
off_pointer (layer->cli_head, off);
|
|
off_pointer (layer->cli_active, off);
|
|
|
|
layer = layer->next;
|
|
}
|
|
}
|
|
|
|
/* alloc more entries in the client[] array */
|
|
static BOOL client_alloc (void)
|
|
{
|
|
int i;
|
|
|
|
if (mgClients == NULL)
|
|
mgClients = malloc (NALLOC * sizeof(MG_Client));
|
|
else {
|
|
char* new_head = realloc (mgClients,
|
|
(mgClientSize + NALLOC) * sizeof(MG_Client));
|
|
|
|
if (new_head) {
|
|
offset_pointers ((MG_Client*) new_head, mgClientSize,
|
|
new_head - (char*)mgClients);
|
|
}
|
|
|
|
mgClients = (MG_Client*) new_head;
|
|
}
|
|
|
|
if (mgClients == NULL)
|
|
return FALSE;
|
|
|
|
/* have to initialize the new entries */
|
|
for (i = mgClientSize; i < mgClientSize + NALLOC; i++) {
|
|
memset (mgClients + i, 0, sizeof (MG_Client));
|
|
mgClients[i].fd = -1; /* fd of -1 means entry available */
|
|
}
|
|
|
|
mgClientSize += NALLOC;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Called by IdleHandler4Server() when connection request
|
|
* from a new client arrives
|
|
*/
|
|
int __mg_client_add (int fd, pid_t pid, uid_t uid)
|
|
{
|
|
int i;
|
|
|
|
if (mgClients == NULL) /* first time we're called */
|
|
if (!client_alloc ())
|
|
return -1;
|
|
again:
|
|
for (i = 0; i < mgClientSize; i++) {
|
|
if (mgClients[i].fd == -1) { /* find an available entry */
|
|
mgClients[i].fd = fd;
|
|
mgClients[i].pid = pid;
|
|
mgClients[i].uid = uid;
|
|
mgClients[i].has_dirty = FALSE;
|
|
mgClients[i].last_live_time = __mg_timer_counter;
|
|
mgClients[i].layer = NULL;
|
|
return (i); /* return index in client[] array */
|
|
}
|
|
}
|
|
|
|
/* client array full, time to realloc for more */
|
|
if (!client_alloc ())
|
|
return -1;
|
|
|
|
goto again; /* and search again (will work this time) */
|
|
}
|
|
|
|
/* Called by IdleHandler4Server() when we're done with a client */
|
|
void __mg_client_del (int cli)
|
|
{
|
|
MG_Client* deleting = mgClients + cli;
|
|
MG_Layer* layer = deleting->layer;
|
|
|
|
_DBG_PRINTF ("SERVER: Remove a client: %s\n", deleting->name);
|
|
|
|
if (layer == NULL)
|
|
goto ret;
|
|
|
|
if (deleting->global_res)
|
|
__mg_release_global_res (cli);
|
|
__mg_remove_all_znodes_of_client (cli);
|
|
|
|
if (deleting->next)
|
|
deleting->next->prev = deleting->prev;
|
|
if (deleting->prev)
|
|
deleting->prev->next = deleting->next;
|
|
|
|
if (deleting == layer->cli_head) {
|
|
layer->cli_head = deleting->next;
|
|
#if 0
|
|
if (layer->cli_head == NULL) {
|
|
if (__mg_free_layer (layer)) {
|
|
SendMessage (HWND_DESKTOP, MSG_TOPMOSTCHANGED, 0, 0);
|
|
PostMessage (HWND_DESKTOP, MSG_ERASEDESKTOP, 0, 0);
|
|
}
|
|
PostMessage (HWND_DESKTOP, MSG_ERASEDESKTOP, 0, 0);
|
|
goto ret;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (deleting == layer->cli_active) {
|
|
layer->cli_active = deleting->next;
|
|
if (layer->cli_active == NULL) {
|
|
layer->cli_active = layer->cli_head;
|
|
}
|
|
}
|
|
|
|
ret:
|
|
memset (deleting, 0, sizeof (MG_Client));
|
|
deleting->fd = -1;
|
|
}
|
|
|
|
void __mg_remove_client (int cli, int clifd)
|
|
{
|
|
#ifdef _DEBUG
|
|
err_msg ("client closed: uid %d, fd %d", mgClients [cli].uid, clifd);
|
|
#endif
|
|
__mg_client_del (cli); /* client has closed conn */
|
|
FD_CLR (clifd, &mg_rfdset);
|
|
close (clifd);
|
|
}
|
|
|
|
int __mg_send2client (const MSG* Msg, MG_Client* client)
|
|
{
|
|
int ret;
|
|
|
|
if (__mg_timer_counter < (client->last_live_time + THRES_LIVE)) {
|
|
|
|
#if 1
|
|
ret = sock_write_t (client->fd, Msg, sizeof (MSG), TO_SOCKIO);
|
|
#else /* use sendmsg */
|
|
struct iovec iov[1];
|
|
struct msghdr msg;
|
|
|
|
iov[0].iov_base = &Msg;
|
|
iov[0].iov_len = sizeof (MSG);
|
|
|
|
msg.msg_name = NULL;
|
|
msg.msg_namelen = 0;
|
|
msg.msg_iov = iov;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = NULL;
|
|
msg.msg_controllen = 0;
|
|
ret = sock_sendmsg_t (client->fd, &msg, 0, TO_SOCKIO);
|
|
#endif
|
|
|
|
switch (ret) {
|
|
case SOCKERR_TIMEOUT:
|
|
client->last_live_time = 0;
|
|
break;
|
|
#if 0
|
|
case SOCKERR_CLOSED:
|
|
{
|
|
int cli = client - mgClients;
|
|
if (OnNewDelClient) OnNewDelClient (LCO_DEL_CLIENT, cli);
|
|
__mg_remove_client (cli, client->fd);
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
else
|
|
return SOCKERR_OK;
|
|
}
|
|
|
|
void __mg_set_active_client (MG_Client* client)
|
|
{
|
|
MG_Layer* layer;
|
|
|
|
if (client)
|
|
layer = client->layer;
|
|
else
|
|
layer = mgTopmostLayer;
|
|
|
|
if (layer == NULL || layer->cli_active == client)
|
|
return;
|
|
|
|
if (layer->cli_active) {
|
|
MSG msg = {0, MSG_SETFOCUS, 0, 0, __mg_timer_counter};
|
|
|
|
__mg_send2client (&msg, layer->cli_active);
|
|
}
|
|
|
|
/* Notify that active client changed. */
|
|
if (OnChangeLayer)
|
|
OnChangeLayer (LCO_ACTIVE_CHANGED, layer, client);
|
|
layer->cli_active = client;
|
|
}
|
|
|
|
/* send message to client(s) */
|
|
int GUIAPI Send2Client (const MSG* msg, int cli)
|
|
{
|
|
|
|
int i, n;
|
|
MG_Client* client;
|
|
|
|
if (!mgIsServer)
|
|
return -1;
|
|
|
|
if (cli >= 0 && cli < mgClientSize && mgClients [cli].fd != -1) {
|
|
return __mg_send2client (msg, mgClients + cli);
|
|
}
|
|
/* send to active client */
|
|
else if (cli == CLIENT_ACTIVE) {
|
|
if (mgTopmostLayer && mgTopmostLayer->cli_active) {
|
|
return __mg_send2client (msg, mgTopmostLayer->cli_active);
|
|
}
|
|
}
|
|
/* send to topmost clients */
|
|
else if (cli == CLIENTS_TOPMOST) {
|
|
if (mgTopmostLayer) {
|
|
client = mgTopmostLayer->cli_head;
|
|
while (client) {
|
|
if ((n = __mg_send2client (msg, client)) < 0)
|
|
return n;
|
|
client = client->next;
|
|
}
|
|
}
|
|
}
|
|
/* send to all clients except topmost client */
|
|
else if (cli == CLIENTS_EXCEPT_TOPMOST) {
|
|
client = mgClients;
|
|
for (i = 0; i < mgClientSize; i++, client++) {
|
|
if (client->layer != mgTopmostLayer) {
|
|
if ((n = __mg_send2client (msg, client)) < 0)
|
|
return n;
|
|
}
|
|
}
|
|
}
|
|
/* send to all clients */
|
|
else if (cli == CLIENTS_ALL) {
|
|
client = mgClients;
|
|
for (i = 0; i < mgClientSize; i++, client++) {
|
|
if ((n = __mg_send2client (msg, client)) < 0)
|
|
return n;
|
|
}
|
|
}
|
|
else
|
|
return SOCKERR_INVARG;
|
|
|
|
return SOCKERR_OK;
|
|
}
|
|
|
|
BOOL GUIAPI Send2TopMostClients (UINT nMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
MSG msg = {0, nMsg, wParam, lParam, __mg_timer_counter};
|
|
|
|
if (!mgIsServer)
|
|
return FALSE;
|
|
|
|
if (Send2Client (&msg, CLIENTS_TOPMOST) < 0)
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GUIAPI Send2ActiveWindow (const MG_Layer* layer,
|
|
UINT nMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
int active_win;
|
|
|
|
if (!mgIsServer || NULL == layer)
|
|
return FALSE;
|
|
|
|
active_win = ((ZORDERINFO*)layer->zorder_info)->active_win;
|
|
if (active_win <= 0)
|
|
return FALSE;
|
|
|
|
if (__mg_post_msg_by_znode (layer->zorder_info,
|
|
active_win, nMsg, wParam, lParam) > 0)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL GUIAPI SetTopmostClient (int cli)
|
|
{
|
|
if (!mgIsServer)
|
|
return FALSE;
|
|
|
|
if (cli < 0 || cli >= mgClientSize || mgClients [cli].fd == -1)
|
|
return FALSE;
|
|
|
|
return ServerSetTopmostLayer (mgClients [cli].layer);
|
|
}
|
|
|
|
int GUIAPI GetClientByPID (int pid)
|
|
{
|
|
int i;
|
|
|
|
if (!mgIsServer)
|
|
return -1;
|
|
|
|
for (i = 0; i < mgClientSize; i++) {
|
|
if (mgClients[i].pid == pid) {
|
|
if (mgClients[i].fd == -1)
|
|
return -1;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|