mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-09-28 16:17:09 +08:00
1159 lines
30 KiB
C
1159 lines
30 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/>.
|
|
*/
|
|
/*
|
|
** layer.c: maintain the layers in server.
|
|
**
|
|
** Current maintainer: Wei Yongming.
|
|
**
|
|
** Create date: 2005/08/14
|
|
*/
|
|
|
|
#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"
|
|
#include "misc.h"
|
|
#include "dc.h"
|
|
|
|
#define SEM_PARAM 0666
|
|
MG_Layer* mgLayers = NULL;
|
|
MG_Layer* mgTopmostLayer = NULL;
|
|
MG_Layer* mgDefaultLayer = NULL;
|
|
|
|
ON_CHANGE_LAYER OnChangeLayer = NULL;
|
|
|
|
#define CHANGE_TOPMOST_LAYER(layer) \
|
|
LOCK_DRAW_SEM (); \
|
|
SHAREDRES_TOPMOST_LAYER = (GHANDLE)layer; \
|
|
UNLOCK_DRAW_SEM ();
|
|
|
|
static int sg_nr_layers = 0;
|
|
static unsigned char sem_usage [(MAX_NR_LAYERS + 7)/8];
|
|
|
|
static BOOL do_alloc_layer (MG_Layer* layer, const char* name,
|
|
int nr_topmosts, int nr_normals, BOOL with_maskrc_heap)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* znodes;
|
|
void* maskrect_usage_bmp;
|
|
int size_usage_bmp;
|
|
|
|
nr_topmosts = (nr_topmosts + 7) & ~0x07;
|
|
nr_normals = (nr_normals + 7) & ~0x07;
|
|
|
|
size_usage_bmp = SIZE_USAGE_BMP (SHAREDRES_NR_GLOBALS,
|
|
nr_topmosts, nr_normals);
|
|
|
|
layer->zorder_shmid = __kernel_alloc_z_order_info (nr_topmosts,
|
|
nr_normals, with_maskrc_heap);
|
|
|
|
if (layer->zorder_shmid == -1)
|
|
return FALSE;
|
|
|
|
/* Attach to the share memory. */
|
|
zi = shmat (layer->zorder_shmid, 0, 0);
|
|
if (zi == (void*)-1)
|
|
return FALSE;
|
|
|
|
if (shmctl (layer->zorder_shmid, IPC_RMID, NULL) < 0)
|
|
return FALSE;
|
|
|
|
strcpy (layer->name, name);
|
|
|
|
layer->zorder_info = zi;
|
|
|
|
zi->size_usage_bmp = size_usage_bmp;
|
|
if (with_maskrc_heap) {
|
|
zi->size_maskrect_usage_bmp = SIZE_MASKRECT_USAGE_BMP;
|
|
}
|
|
else {
|
|
zi->size_maskrect_usage_bmp = 0;
|
|
}
|
|
|
|
_DBG_PRINTF("size_zi:%lu, size_znode:%lu,"
|
|
" size_usage_bmp:%d, size_maskrect_usage_bmp:%d\n",
|
|
sizeof (*zi), sizeof (ZORDERNODE),
|
|
zi->size_usage_bmp, zi->size_maskrect_usage_bmp);
|
|
|
|
zi->max_nr_popupmenus = DEF_NR_POPUPMENUS;
|
|
zi->max_nr_tooltips = DEF_NR_TOOLTIPS;
|
|
zi->max_nr_globals = SHAREDRES_NR_GLOBALS;
|
|
zi->max_nr_screenlocks = DEF_NR_SCREENLOCKS;
|
|
zi->max_nr_dockers = DEF_NR_DOCKERS;
|
|
zi->max_nr_topmosts = nr_topmosts;
|
|
zi->max_nr_normals = nr_normals;
|
|
zi->max_nr_launchers = DEF_NR_LAUNCHERS;
|
|
|
|
zi->nr_popupmenus = 0;
|
|
zi->nr_tooltips = 0;
|
|
zi->nr_globals = 0;
|
|
zi->nr_screenlocks = 0;
|
|
zi->nr_dockers = 0;
|
|
zi->nr_topmosts = 0;
|
|
zi->nr_normals = 0;
|
|
zi->nr_launchers = 0;
|
|
|
|
zi->first_tooltip = 0;
|
|
zi->first_global = 0;
|
|
zi->first_screenlock = 0;
|
|
zi->first_docker = 0;
|
|
zi->first_topmost = 0;
|
|
zi->first_normal = 0;
|
|
zi->first_launcher = 0;
|
|
|
|
zi->active_win = 0;
|
|
|
|
zi->cli_trackmenu = -1;
|
|
zi->ptmi_in_cli = HWND_INVALID;
|
|
|
|
zi->zi_semid = SHAREDRES_SEMID_LAYER;
|
|
|
|
/* find one unused semaphore slot here */
|
|
zi->zi_semnum = __mg_lookfor_unused_slot (sem_usage, sizeof (sem_usage), 1);
|
|
|
|
/* init the semaphore with semctl */
|
|
{
|
|
union semun arg;
|
|
arg.val = zi->max_nr_popupmenus + MAX_NR_ZNODES(zi);
|
|
semctl (zi->zi_semid, zi->zi_semnum, SETVAL, arg);
|
|
}
|
|
#if 0 /* the pthread rwlock shared among processes must be located in
|
|
the global writable shared memory. */
|
|
{
|
|
int ret;
|
|
pthread_rwlockattr_t attr;
|
|
pthread_rwlockattr_init (&attr);
|
|
pthread_rwlockattr_setpshared (&attr, PTHREAD_PROCESS_SHARED);
|
|
ret = pthread_rwlock_init (&zi->rwlock, &attr);
|
|
pthread_rwlockattr_destroy(&attr);
|
|
|
|
if (ret) {
|
|
_ERR_PRINTF("Failed to initialize the pthread rwlock for layer");
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
#endif /* deprectated code */
|
|
|
|
/* init usage bitmap. */
|
|
memset (zi + 1, 0xFF, size_usage_bmp);
|
|
__mg_slot_set_use ((unsigned char*)(zi + 1), 0);
|
|
|
|
if (with_maskrc_heap) {
|
|
maskrect_usage_bmp = GET_MASKRECT_USAGEBMP(zi);
|
|
memset (maskrect_usage_bmp, 0xFF, zi->size_maskrect_usage_bmp);
|
|
__mg_slot_set_use ((unsigned char*)(maskrect_usage_bmp), 0);
|
|
}
|
|
|
|
/* init z-order node for desktop */
|
|
znodes = GET_ZORDERNODE(zi);
|
|
znodes [0].flags = ZOF_TYPE_DESKTOP | ZOF_VISIBLE;
|
|
znodes [0].rc = g_rcScr;
|
|
#ifndef _MGSCHEMA_COMPOSITING
|
|
znodes [0].age = 0;
|
|
znodes [0].dirty_rc.left = 0;
|
|
znodes [0].dirty_rc.top = 0;
|
|
znodes [0].dirty_rc.right = 0;
|
|
znodes [0].dirty_rc.bottom = 0;
|
|
#endif
|
|
znodes [0].cli = 0;
|
|
znodes [0].hwnd = HWND_DESKTOP;
|
|
znodes [0].next = 0;
|
|
znodes [0].prev = 0;
|
|
/*for mask rect.*/
|
|
znodes [0].idx_mask_rect = 0;
|
|
znodes [0].priv_data = NULL;
|
|
|
|
#if 0 /* deprecated code */
|
|
/* Since 5.0.0; allocate znodes for other fixed main windows */
|
|
{
|
|
int i;
|
|
static int fixed_ztypes [] = { ZNIT_TOOLTIP };
|
|
|
|
for (i = 0; i < TABLESIZE (fixed_ztypes); i++) {
|
|
znodes [i + ZNIDX_FIRST].flags = fixed_ztypes [i];
|
|
#ifndef _MGSCHEMA_COMPOSITING
|
|
znodes [i + ZNIDX_FIRST].age = 0;
|
|
znodes [i + ZNIDX_FIRST].dirty_rc.left = 0;
|
|
znodes [i + ZNIDX_FIRST].dirty_rc.top = 0;
|
|
znodes [i + ZNIDX_FIRST].dirty_rc.right = 0;
|
|
znodes [i + ZNIDX_FIRST].dirty_rc.bottom = 0;
|
|
#endif
|
|
znodes [i + ZNIDX_FIRST].cli = -1;
|
|
znodes [i + ZNIDX_FIRST].hwnd = HWND_NULL;
|
|
znodes [i + ZNIDX_FIRST].next = 0;
|
|
znodes [i + ZNIDX_FIRST].prev = 0;
|
|
znodes [i + ZNIDX_FIRST].idx_mask_rect = 0;
|
|
znodes [i + ZNIDX_FIRST].priv_data = NULL;
|
|
|
|
SetRectEmpty (&znodes [i + ZNIDX_FIRST].rc);
|
|
__mg_slot_set_use ((unsigned char*)(zi + 1), i + ZNIDX_FIRST);
|
|
}
|
|
}
|
|
#endif /* deprecated code */
|
|
|
|
sg_nr_layers ++;
|
|
return TRUE;
|
|
}
|
|
|
|
static void do_free_layer (MG_Layer* layer)
|
|
{
|
|
ZORDERINFO* zi = layer->zorder_info;
|
|
|
|
/* free zorder_semid for reuse here... */
|
|
__mg_slot_clear_use (sem_usage, zi->zi_semnum);
|
|
|
|
__kernel_free_z_order_info (zi);
|
|
|
|
if (layer->prev)
|
|
layer->prev->next = layer->next;
|
|
if (layer->next)
|
|
layer->next->prev = layer->prev;
|
|
|
|
if (layer == mgLayers) {
|
|
mgLayers = layer->next;
|
|
}
|
|
|
|
sg_nr_layers --;
|
|
}
|
|
|
|
BOOL GUIAPI ServerDeleteLayer (MG_Layer* layer)
|
|
{
|
|
ZORDERINFO* zi = layer->zorder_info;
|
|
MG_Client* client;
|
|
|
|
if (layer == NULL || strcmp (layer->name, NAME_DEF_LAYER) == 0) {
|
|
_WRN_PRINTF ("SERVER: Can not delete the default layer.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (zi->nr_topmosts + zi->nr_normals + zi->nr_popupmenus != 0) {
|
|
_WRN_PRINTF ("SERVER: Some objects are still in this layer.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (layer != mgLayers)
|
|
ServerSetTopmostLayer (mgLayers);
|
|
else
|
|
ServerSetTopmostLayer (layer->next);
|
|
|
|
if (OnChangeLayer) OnChangeLayer (LCO_DEL_LAYER, layer, NULL);
|
|
DO_COMPSOR_OP_ARGS (on_layer_op, LCO_DEL_LAYER, layer, NULL);
|
|
|
|
client = layer->cli_head;
|
|
while (client) {
|
|
|
|
MG_Client* next = client->next;
|
|
|
|
__mg_remove_client (client - mgClients, client->fd);
|
|
|
|
client = next;
|
|
}
|
|
|
|
do_free_layer (layer);
|
|
return TRUE;
|
|
}
|
|
|
|
#if 0 // Deprecated since 5.2.0 def _MGSCHEMA_COMPOSITING
|
|
|
|
static SemSetManager* _ssm_shared_surf;
|
|
|
|
int __mg_alloc_sem_for_shared_surf (void)
|
|
{
|
|
if (IsServer()) {
|
|
return __mg_alloc_mutual_sem (_ssm_shared_surf, NULL);
|
|
}
|
|
else {
|
|
int sem_num, ignore;
|
|
REQUEST req;
|
|
|
|
req.id = REQID_ALLOC_SURF_SEM;
|
|
req.data = &ignore;
|
|
req.len_data = sizeof (int);
|
|
|
|
if (ClientRequestEx (&req, NULL, 0,
|
|
&sem_num, sizeof (int)) < 0)
|
|
return -1;
|
|
|
|
return sem_num;
|
|
}
|
|
}
|
|
|
|
int __mg_free_sem_for_shared_surf (int sem_num)
|
|
{
|
|
if (IsServer()) {
|
|
/* the first semaphore is reserved for wallpaper pattern */
|
|
if (sem_num == 0)
|
|
return 0;
|
|
return __mg_free_mutual_sem (_ssm_shared_surf, sem_num);
|
|
}
|
|
else {
|
|
int ret_value;
|
|
REQUEST req;
|
|
|
|
req.id = REQID_FREE_SURF_SEM;
|
|
req.data = &sem_num;
|
|
req.len_data = sizeof (int);
|
|
|
|
if (ClientRequestEx (&req, NULL, 0,
|
|
&ret_value, sizeof (int)) < 0)
|
|
return FALSE;
|
|
|
|
return ret_value;
|
|
}
|
|
}
|
|
|
|
static void delete_sem_set_for_shared_surf (void)
|
|
{
|
|
union semun ignored;
|
|
if (semctl (SHAREDRES_SEMID_SHARED_SURF, 0, IPC_RMID, ignored) < 0)
|
|
goto error;
|
|
|
|
return;
|
|
|
|
error:
|
|
_ERR_PRINTF ("KERNEL: failed to remove semaphore set for shared surfaces: %m\n");
|
|
}
|
|
|
|
#endif /* defined _MGSCHEMA_COMPOSITING */
|
|
|
|
static void delete_sem_set_for_layers (void)
|
|
{
|
|
union semun ignored;
|
|
if (semctl (SHAREDRES_SEMID_LAYER, 0, IPC_RMID, ignored) < 0)
|
|
goto error;
|
|
|
|
return;
|
|
|
|
error:
|
|
_ERR_PRINTF ("KERNEL: failed to remove semaphore set for layers: %m\n");
|
|
}
|
|
|
|
int __mg_init_layers ()
|
|
{
|
|
int semid = 0;
|
|
key_t sem_key;
|
|
|
|
mgLayers = calloc (1, sizeof (MG_Layer));
|
|
if (mgLayers == NULL)
|
|
return -1;
|
|
|
|
memset (sem_usage, 0xFF, sizeof (sem_usage));
|
|
|
|
#if 0 // Deprecated since 5.2.0
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
if ((sem_key = get_sem_key_for_shared_surf ()) == -1) {
|
|
return -1;
|
|
}
|
|
|
|
semid = semget (sem_key, MAX_NR_SHARED_SURF, SEM_PARAM | IPC_CREAT | IPC_EXCL);
|
|
if (semid == -1)
|
|
return -1;
|
|
|
|
SHAREDRES_SEMID_SHARED_SURF = semid;
|
|
atexit (delete_sem_set_for_shared_surf);
|
|
|
|
_ssm_shared_surf = __mg_create_sem_set_manager (semid, MAX_NR_SHARED_SURF);
|
|
if (_ssm_shared_surf == NULL)
|
|
return -1;
|
|
|
|
/* mark the first semphore is used; it is reserved for wallpaper pattern */
|
|
__mg_alloc_mutual_sem (_ssm_shared_surf, NULL);
|
|
|
|
on_exit (__mg_delete_sem_set_manager, _ssm_shared_surf);
|
|
#endif
|
|
#endif
|
|
|
|
if ((sem_key = get_sem_key_for_layers ()) == -1) {
|
|
return -1;
|
|
}
|
|
|
|
semid = semget (sem_key, MAX_NR_LAYERS, SEM_PARAM | IPC_CREAT | IPC_EXCL);
|
|
if (semid == -1)
|
|
return -1;
|
|
|
|
SHAREDRES_SEMID_LAYER = semid;
|
|
atexit (delete_sem_set_for_layers);
|
|
|
|
/* allocate the first layer for the default layer. */
|
|
if (!do_alloc_layer (mgLayers, NAME_DEF_LAYER,
|
|
SHAREDRES_DEF_NR_TOPMOSTS, SHAREDRES_DEF_NR_NORMALS,
|
|
TRUE)) {
|
|
|
|
free (mgLayers);
|
|
mgLayers = NULL;
|
|
return -1;
|
|
}
|
|
|
|
mgDefaultLayer = mgLayers;
|
|
__mg_def_zorder_info = mgLayers->zorder_info;
|
|
__mg_zorder_info = mgLayers->zorder_info;
|
|
|
|
return semid;
|
|
}
|
|
|
|
void __mg_cleanup_layers (void)
|
|
{
|
|
MG_Layer *layer = mgLayers, *next;
|
|
|
|
while (layer) {
|
|
next = layer->next;
|
|
|
|
do_free_layer (layer);
|
|
free (layer);
|
|
|
|
layer = next;
|
|
|
|
};
|
|
|
|
mgLayers = NULL;
|
|
mgTopmostLayer = NULL;
|
|
__mg_zorder_info = NULL;
|
|
__mg_def_zorder_info = NULL;
|
|
}
|
|
|
|
/* allocate a layer slot from the layer pool for the new layer */
|
|
static MG_Layer* alloc_layer (const char* layer_name,
|
|
int nr_topmosts, int nr_normals)
|
|
{
|
|
MG_Layer* new_layer;
|
|
|
|
if (sg_nr_layers >= MAX_NR_LAYERS) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!(new_layer = calloc (1, sizeof (MG_Layer))))
|
|
return NULL;
|
|
|
|
if (!do_alloc_layer (new_layer, layer_name, nr_topmosts, nr_normals, FALSE)) {
|
|
free (new_layer);
|
|
return NULL;
|
|
}
|
|
|
|
_DBG_PRINTF ("SERVER: Create a new layer: %s\n", layer_name);
|
|
|
|
strcpy (new_layer->name, layer_name);
|
|
new_layer->cli_head = NULL;
|
|
new_layer->cli_active = NULL;
|
|
|
|
/* chain it */
|
|
if (mgLayers)
|
|
mgLayers->prev = new_layer;
|
|
new_layer->prev = NULL;
|
|
new_layer->next = mgLayers;
|
|
mgLayers = new_layer;
|
|
|
|
/* Notify that a new layer created. */
|
|
if (OnChangeLayer)
|
|
OnChangeLayer (LCO_NEW_LAYER, new_layer, NULL);
|
|
DO_COMPSOR_OP_ARGS (on_layer_op, LCO_NEW_LAYER, new_layer, NULL);
|
|
|
|
ServerSetTopmostLayer (new_layer);
|
|
return new_layer;
|
|
}
|
|
|
|
/* Retrives the layer by the name. */
|
|
MG_Layer* __mg_find_layer_by_name (const char* name)
|
|
{
|
|
MG_Layer* layer = mgLayers;
|
|
|
|
if (name == NULL || name [0] == '\0')
|
|
return NULL;
|
|
|
|
while (layer) {
|
|
if (strncmp (layer->name, name, LEN_LAYER_NAME) == 0)
|
|
return layer;
|
|
|
|
layer = layer->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* get information of a layer, the server side of GetLayerInfo */
|
|
void __mg_get_layer_info (int cli, const char* layer_name, LAYERINFO* info)
|
|
{
|
|
MG_Layer* layer;
|
|
MG_Client* client;
|
|
|
|
memset (info, 0, sizeof (LAYERINFO));
|
|
info->cli_active = -1;
|
|
|
|
if (layer_name [0]) {
|
|
layer = __mg_find_layer_by_name (layer_name);
|
|
if (layer == NULL) {
|
|
info->handle = INV_LAYER_HANDLE;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
layer = mgClients[cli].layer;
|
|
|
|
info->handle = (GHANDLE)layer;
|
|
if (info->handle) {
|
|
|
|
client = layer->cli_head;
|
|
while (client) {
|
|
info->nr_clients ++;
|
|
|
|
client = client->next;
|
|
}
|
|
|
|
if (mgTopmostLayer == layer)
|
|
info->is_topmost = TRUE;
|
|
info->cli_active = layer->cli_active - mgClients;
|
|
}
|
|
}
|
|
|
|
static void do_client_join_layer (int cli,
|
|
const JOINLAYERINFO* info, JOINEDCLIENTINFO* joined_info)
|
|
{
|
|
MG_Layer* layer = (MG_Layer*)(joined_info->layer);
|
|
MG_Client* new_client = mgClients + cli;
|
|
|
|
joined_info->def_zi_shmid = mgDefaultLayer->zorder_shmid;
|
|
|
|
if (new_client->layer == layer) { /* duplicated calling of JoinLayer */
|
|
|
|
joined_info->cli_id = cli;
|
|
joined_info->zi_shmid = layer->zorder_shmid;
|
|
return;
|
|
}
|
|
|
|
_DBG_PRINTF ("SERVER: Join a client (%s) to layer %s\n",
|
|
info->client_name, layer->name);
|
|
|
|
strcpy (new_client->name, info->client_name);
|
|
|
|
new_client->layer = layer;
|
|
|
|
/* Notify that a new client joined to this layer. */
|
|
if (OnChangeLayer)
|
|
OnChangeLayer (LCO_JOIN_CLIENT, layer, new_client);
|
|
DO_COMPSOR_OP_ARGS (on_layer_op, LCO_JOIN_CLIENT, layer, new_client);
|
|
|
|
new_client->prev = NULL;
|
|
new_client->next = layer->cli_head;
|
|
if (layer->cli_head)
|
|
layer->cli_head->prev = new_client;
|
|
layer->cli_head = new_client;
|
|
|
|
joined_info->cli_id = cli;
|
|
joined_info->zi_shmid = layer->zorder_shmid;
|
|
}
|
|
|
|
/* Join a client to a layer, the server side of JoinLayer */
|
|
void __mg_client_join_layer (int cli,
|
|
const JOINLAYERINFO* info, JOINEDCLIENTINFO* joined_info)
|
|
{
|
|
MG_Layer* layer;
|
|
|
|
if ((layer = __mg_find_layer_by_name (info->layer_name)) == NULL) {
|
|
layer = alloc_layer (info->layer_name,
|
|
(info->max_nr_topmosts <= 0)?SHAREDRES_DEF_NR_TOPMOSTS:
|
|
info->max_nr_topmosts,
|
|
(info->max_nr_normals<= 0)?SHAREDRES_DEF_NR_NORMALS:
|
|
info->max_nr_normals);
|
|
}
|
|
|
|
joined_info->layer = (GHANDLE)layer;
|
|
do_client_join_layer (cli, info, joined_info);
|
|
}
|
|
|
|
BOOL GUIAPI ServerSetTopmostLayer (MG_Layer* layer)
|
|
{
|
|
MG_Layer* old_topmost = mgTopmostLayer;
|
|
|
|
if (!mgIsServer)
|
|
return FALSE;
|
|
|
|
if (layer == mgTopmostLayer)
|
|
return TRUE; // return TRUE for already topmost layer
|
|
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
__mg_prepare_layer_for_compositing (layer);
|
|
DO_COMPSOR_OP_ARGS (transit_to_layer, layer);
|
|
#endif
|
|
|
|
CHANGE_TOPMOST_LAYER (layer);
|
|
|
|
__mg_do_change_topmost_layer (layer);
|
|
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
if (old_topmost) {
|
|
__mg_prepare_layer_for_compositing (old_topmost);
|
|
}
|
|
#else
|
|
{
|
|
MG_Client* client;
|
|
client = layer->cli_head;
|
|
while (client) {
|
|
MSG msg = {0, MSG_PAINT, 0, 0, __mg_tick_counter};
|
|
|
|
__mg_send2client (&msg, client);
|
|
|
|
client = client->next;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Notify that a new topmost layer have been set. */
|
|
if (OnChangeLayer)
|
|
OnChangeLayer (LCO_TOPMOST_CHANGED, mgTopmostLayer, NULL);
|
|
DO_COMPSOR_OP_ARGS (on_layer_op, LCO_TOPMOST_CHANGED, mgTopmostLayer, NULL);
|
|
|
|
#ifndef _MGSCHEMA_COMPOSITING
|
|
if (old_topmost && old_topmost->cli_head) {
|
|
SendMessage (HWND_DESKTOP, MSG_PAINT, 0, 0);
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
MG_Layer* GUIAPI ServerCreateLayer (const char* layer_name,
|
|
int max_nr_topmosts, int max_nr_normals)
|
|
{
|
|
MG_Layer* new_layer;
|
|
|
|
if (layer_name == NULL || layer_name [0] == '\0')
|
|
return NULL;
|
|
|
|
new_layer = __mg_find_layer_by_name (layer_name);
|
|
|
|
if (new_layer)
|
|
return new_layer;
|
|
|
|
if (sg_nr_layers >= MAX_NR_LAYERS) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!(new_layer = calloc (1, sizeof (MG_Layer))))
|
|
return NULL;
|
|
|
|
if (max_nr_topmosts <= 0) max_nr_topmosts = DEF_NR_TOPMOSTS;
|
|
if (max_nr_normals <= 0) max_nr_normals = DEF_NR_NORMALS;
|
|
max_nr_topmosts = (max_nr_topmosts + 7) & ~0x07;
|
|
max_nr_normals = (max_nr_normals + 7) & ~0x07;
|
|
|
|
if (!do_alloc_layer (new_layer, layer_name, max_nr_topmosts, max_nr_normals, 0)) {
|
|
free (new_layer);
|
|
return NULL;
|
|
}
|
|
|
|
strcpy (new_layer->name, layer_name);
|
|
new_layer->cli_head = NULL;
|
|
new_layer->cli_active = NULL;
|
|
|
|
/* chain it */
|
|
if (mgLayers)
|
|
mgLayers->prev = new_layer;
|
|
new_layer->prev = NULL;
|
|
new_layer->next = mgLayers;
|
|
mgLayers = new_layer;
|
|
|
|
__mg_sync_special_nodes (__mg_def_zorder_info, new_layer->zorder_info);
|
|
|
|
/* Notify that a new layer created. */
|
|
if (OnChangeLayer)
|
|
OnChangeLayer (LCO_NEW_LAYER, new_layer, NULL);
|
|
DO_COMPSOR_OP_ARGS (on_layer_op, LCO_NEW_LAYER, new_layer, NULL);
|
|
|
|
return new_layer;
|
|
}
|
|
|
|
void GUIAPI DisableClientsOutput (void)
|
|
{
|
|
if (!mgIsServer)
|
|
return;
|
|
|
|
CHANGE_TOPMOST_LAYER(NULL);
|
|
}
|
|
|
|
void GUIAPI UpdateTopmostLayer (const RECT* dirty_rc)
|
|
{
|
|
MSG msg = {0, MSG_PAINT, 0, 0, __mg_tick_counter};
|
|
|
|
if (!mgIsServer)
|
|
return;
|
|
|
|
CHANGE_TOPMOST_LAYER(mgTopmostLayer);
|
|
|
|
if (dirty_rc) {
|
|
RECT eff_rc;
|
|
RECT rcScr = GetScreenRect();
|
|
|
|
IntersectRect (&eff_rc, dirty_rc, &rcScr);
|
|
msg.wParam = MAKELONG (eff_rc.left, eff_rc.top);
|
|
msg.lParam = MAKELONG (eff_rc.right, eff_rc.bottom);
|
|
}
|
|
|
|
Send2Client (&msg, CLIENTS_TOPMOST);
|
|
|
|
PostMessage (HWND_DESKTOP, MSG_ERASEDESKTOP, 0, 0);
|
|
SendMessage (HWND_DESKTOP, MSG_PAINT, 0, 0);
|
|
}
|
|
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wzero-length-bounds"
|
|
int GUIAPI ServerGetTopmostZNodeOfType (MG_Layer* layer, DWORD type, int* cli)
|
|
{
|
|
ZORDERINFO* zi;
|
|
int topmost;
|
|
|
|
if (layer == NULL)
|
|
layer = mgTopmostLayer;
|
|
else if (!__mg_is_valid_layer (layer))
|
|
return -1;
|
|
|
|
if (type < ZOF_TYPE_LAUNCHER || type > ZOF_TYPE_TOOLTIP)
|
|
return -1;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
topmost = zi->first_in_levels [(ZOF_TYPE_TOOLTIP - type) >> 28];
|
|
if (topmost <= 0) {
|
|
return 0;
|
|
}
|
|
else if (cli) {
|
|
ZORDERNODE* nodes;
|
|
|
|
nodes = GET_ZORDERNODE(zi);
|
|
*cli = nodes [topmost].cli;
|
|
}
|
|
|
|
return topmost;
|
|
}
|
|
#pragma GCC diagnostic pop
|
|
|
|
int GUIAPI ServerGetNextZNode (MG_Layer* layer, int idx_znode, int* cli)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
int next = 0;
|
|
|
|
if (layer == NULL)
|
|
layer = mgTopmostLayer;
|
|
else if (!__mg_is_valid_layer (layer))
|
|
return -1;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
if (IS_INVALID_ZIDX_LOOSE (zi, idx_znode)) {
|
|
return -1;
|
|
}
|
|
|
|
nodes = GET_ZORDERNODE(zi);
|
|
if (idx_znode > 0) {
|
|
DWORD type = nodes [idx_znode].flags & ZOF_TYPE_MASK;
|
|
if (type < ZOF_TYPE_BOTTOMMOST || type > ZOF_TYPE_TOPMOST)
|
|
return -1;
|
|
}
|
|
|
|
next = __kernel_get_next_znode (zi, idx_znode);
|
|
if (next > 0 && cli) {
|
|
*cli = nodes [next].cli;
|
|
return next;
|
|
}
|
|
|
|
return next;
|
|
}
|
|
|
|
int GUIAPI ServerGetPrevZNode (MG_Layer* layer, int idx_znode, int* cli)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
int prev = 0;
|
|
|
|
if (layer == NULL)
|
|
layer = mgTopmostLayer;
|
|
else if (!__mg_is_valid_layer (layer))
|
|
return -1;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
if (IS_INVALID_ZIDX_LOOSE (zi, idx_znode)) {
|
|
return -1;
|
|
}
|
|
|
|
nodes = GET_ZORDERNODE(zi);
|
|
if (idx_znode > 0) {
|
|
DWORD type = nodes [idx_znode].flags & ZOF_TYPE_MASK;
|
|
if (type < ZOF_TYPE_BOTTOMMOST || type > ZOF_TYPE_TOPMOST)
|
|
return -1;
|
|
}
|
|
|
|
prev = __kernel_get_prev_znode (zi, idx_znode);
|
|
if (prev > 0 && cli) {
|
|
*cli = nodes [prev].cli;
|
|
return prev;
|
|
}
|
|
|
|
return prev;
|
|
}
|
|
|
|
BOOL GUIAPI ServerGetZNodeInfo (MG_Layer* layer, int idx_znode,
|
|
ZNODEINFO* znode_info)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
|
|
if (znode_info == NULL)
|
|
return FALSE;
|
|
|
|
if (layer == NULL)
|
|
layer = mgTopmostLayer;
|
|
else if (!__mg_is_valid_layer (layer))
|
|
return FALSE;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
if (IS_INVALID_ZIDX (zi, idx_znode)) {
|
|
return FALSE;
|
|
}
|
|
|
|
nodes = GET_ZORDERNODE(zi);
|
|
znode_info->type = (nodes [idx_znode].flags & ZOF_TYPE_FLAG_MASK);
|
|
znode_info->flags = nodes [idx_znode].flags;
|
|
znode_info->caption = nodes [idx_znode].caption;
|
|
znode_info->rc = nodes [idx_znode].rc;
|
|
znode_info->hwnd = nodes [idx_znode].hwnd;
|
|
znode_info->main_win = nodes [idx_znode].main_win;
|
|
znode_info->cli = nodes [idx_znode].cli;
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
// do not return mem_dc for this function
|
|
// znode_info->mem_dc = nodes [idx_znode].mem_dc;
|
|
znode_info->ct = nodes [idx_znode].ct;
|
|
znode_info->ct_arg = nodes [idx_znode].ct_arg;
|
|
#endif
|
|
znode_info->priv_data = nodes [idx_znode].priv_data;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
const ZNODEHEADER* GUIAPI ServerGetWinZNodeHeader (MG_Layer* layer,
|
|
int idx_znode, void** priv_data, BOOL lock)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
ZNODEHEADER* hdr;
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
PDC pdc;
|
|
#endif
|
|
|
|
if (layer == NULL)
|
|
layer = mgTopmostLayer;
|
|
else if (!__mg_is_valid_layer (layer))
|
|
return NULL;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
if (IS_INVALID_ZIDX (zi, idx_znode)) {
|
|
return NULL;
|
|
}
|
|
|
|
nodes = GET_ZORDERNODE(zi);
|
|
hdr = (ZNODEHEADER*)(nodes + idx_znode);
|
|
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
if (lock && (pdc = dc_HDC2PDC (hdr->mem_dc))) {
|
|
assert (pdc->surface->dirty_info);
|
|
|
|
if (hdr->lock_count == 0) {
|
|
if (pdc->surface->shared_header) {
|
|
// XXX: consider timeout.
|
|
__mg_lock_file_for_read(pdc->surface->fd);
|
|
}
|
|
|
|
hdr->dirty_age = pdc->surface->dirty_info->dirty_age;
|
|
hdr->nr_dirty_rcs = pdc->surface->dirty_info->nr_dirty_rcs;
|
|
hdr->dirty_rcs = pdc->surface->dirty_info->dirty_rcs;
|
|
}
|
|
|
|
hdr->lock_count++;
|
|
}
|
|
#endif /* defined _MGSCHEMA_COMPOSITING */
|
|
|
|
if (priv_data) {
|
|
*priv_data = nodes [idx_znode].priv_data;
|
|
}
|
|
|
|
return hdr;
|
|
}
|
|
|
|
int GUIAPI ServerGetPopupMenusCount (void)
|
|
{
|
|
ZORDERINFO* zi;
|
|
zi = (ZORDERINFO*)mgTopmostLayer->zorder_info;
|
|
|
|
return zi->nr_popupmenus;
|
|
}
|
|
|
|
const ZNODEHEADER* GUIAPI ServerGetPopupMenuZNodeHeader (int idx,
|
|
void** priv_data, BOOL lock)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* menu_nodes;
|
|
ZNODEHEADER* hdr;
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
PDC pdc;
|
|
#endif
|
|
|
|
zi = (ZORDERINFO*)mgTopmostLayer->zorder_info;
|
|
|
|
if (idx >= zi->nr_popupmenus)
|
|
return NULL;
|
|
|
|
menu_nodes = GET_MENUNODE(zi);
|
|
hdr = (ZNODEHEADER*)(menu_nodes + idx);
|
|
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
if (lock && (pdc = dc_HDC2PDC (hdr->mem_dc))) {
|
|
assert (pdc->surface->dirty_info);
|
|
|
|
if (hdr->lock_count == 0) {
|
|
if (pdc->surface->shared_header) {
|
|
// XXX: consider timeout
|
|
__mg_lock_file_for_read(pdc->surface->fd);
|
|
}
|
|
|
|
hdr->dirty_age = pdc->surface->dirty_info->dirty_age;
|
|
hdr->nr_dirty_rcs = pdc->surface->dirty_info->nr_dirty_rcs;
|
|
hdr->dirty_rcs = pdc->surface->dirty_info->dirty_rcs;
|
|
}
|
|
|
|
hdr->lock_count++;
|
|
}
|
|
#endif /* defined _MGSCHEMA_COMPOSITING */
|
|
|
|
if (priv_data) {
|
|
*priv_data = menu_nodes [idx].priv_data;
|
|
}
|
|
|
|
return hdr;
|
|
}
|
|
|
|
#ifdef _MGSCHEMA_COMPOSITING
|
|
BOOL GUIAPI ServerReleaseWinZNodeHeader (MG_Layer* layer, int idx_znode)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
ZNODEHEADER* hdr;
|
|
PDC pdc;
|
|
|
|
if (layer == NULL)
|
|
layer = mgTopmostLayer;
|
|
else if (!__mg_is_valid_layer (layer))
|
|
return FALSE;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
if (IS_INVALID_ZIDX (zi, idx_znode)) {
|
|
return FALSE;
|
|
}
|
|
|
|
nodes = GET_ZORDERNODE(zi);
|
|
hdr = (ZNODEHEADER*)(nodes + idx_znode);
|
|
if ((pdc = dc_HDC2PDC (hdr->mem_dc)) && hdr->lock_count > 0) {
|
|
|
|
hdr->lock_count--;
|
|
if (hdr->lock_count == 0) {
|
|
if (pdc->surface->shared_header)
|
|
__mg_unlock_file_for_read(pdc->surface->fd);
|
|
|
|
hdr->dirty_age = 0;
|
|
hdr->nr_dirty_rcs = 0;
|
|
hdr->dirty_rcs = NULL;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GUIAPI ServerReleasePopupMenuZNodeHeader (int idx)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* menu_nodes;
|
|
ZNODEHEADER* hdr;
|
|
PDC pdc;
|
|
|
|
zi = (ZORDERINFO*)mgTopmostLayer->zorder_info;
|
|
if (idx >= zi->nr_popupmenus)
|
|
return FALSE;
|
|
|
|
menu_nodes = GET_MENUNODE(zi);
|
|
hdr = (ZNODEHEADER*)(menu_nodes + idx);
|
|
if ((pdc = dc_HDC2PDC (hdr->mem_dc)) && hdr->lock_count > 0) {
|
|
|
|
hdr->lock_count--;
|
|
if (hdr->lock_count == 0) {
|
|
if (pdc->surface->shared_header)
|
|
__mg_unlock_file_for_read(pdc->surface->fd);
|
|
|
|
hdr->dirty_age = 0;
|
|
hdr->nr_dirty_rcs = 0;
|
|
hdr->dirty_rcs = NULL;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif /* defined _MGSCHEMA_COMPOSITING */
|
|
|
|
BOOL GUIAPI ServerSetWinZNodePrivateData (MG_Layer* layer,
|
|
int idx_znode, void* priv_data)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
|
|
if (layer == NULL)
|
|
layer = mgTopmostLayer;
|
|
else if (!__mg_is_valid_layer (layer))
|
|
return FALSE;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
if (IS_INVALID_ZIDX (zi, idx_znode)) {
|
|
return FALSE;
|
|
}
|
|
|
|
nodes = GET_ZORDERNODE(zi);
|
|
nodes [idx_znode].priv_data = priv_data;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GUIAPI ServerSetPopupMenuZNodePrivateData (int idx, void* priv_data)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* menu_nodes;
|
|
|
|
zi = (ZORDERINFO*)mgTopmostLayer->zorder_info;
|
|
if (idx >= zi->nr_popupmenus)
|
|
return FALSE;
|
|
|
|
menu_nodes = GET_MENUNODE(zi);
|
|
menu_nodes [idx].priv_data = priv_data;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GUIAPI ServerDoZNodeOperation (MG_Layer* layer,
|
|
int idx_znode, int op_code, void* op_data, BOOL notify)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
DWORD type;
|
|
ZORDEROPINFO info;
|
|
|
|
if (layer == NULL)
|
|
layer = mgTopmostLayer;
|
|
else if (!__mg_is_valid_layer (layer))
|
|
return FALSE;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
if (IS_INVALID_ZIDX (zi, idx_znode)) {
|
|
return FALSE;
|
|
}
|
|
|
|
nodes = GET_ZORDERNODE(zi);
|
|
|
|
/* Since 5.0.0: handle fixed znodes */
|
|
type = nodes [idx_znode].flags & ZOF_TYPE_MASK;
|
|
if (type < ZOF_TYPE_BOTTOMMOST || type > ZOF_TYPE_TOPMOST)
|
|
return FALSE;
|
|
|
|
if (nodes [idx_znode].hwnd == HWND_NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
switch (op_code) {
|
|
case ZNOP_MOVE2TOP:
|
|
info.id_op = ID_ZOOP_MOVE2TOP;
|
|
info.idx_znode = idx_znode;
|
|
break;
|
|
case ZNOP_SETACTIVE:
|
|
info.id_op = ID_ZOOP_SETACTIVE;
|
|
info.idx_znode = idx_znode;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
__mg_do_zorder_operation (nodes[idx_znode].cli, &info, NULL, -1);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Since 5.0.0 */
|
|
BOOL GUIAPI ServerMoveClientToLayer (int cli, MG_Layer* dst_layer)
|
|
{
|
|
MG_Client* client = mgClients + cli;
|
|
|
|
if (!mgIsServer)
|
|
return FALSE;
|
|
|
|
if (client->layer == dst_layer) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (__mg_move_client_to_layer (client, dst_layer)) {
|
|
MSG msg = { HWND_DESKTOP,
|
|
MSG_LAYERCHANGED, (WPARAM)dst_layer,
|
|
(LPARAM)dst_layer->zorder_shmid, __mg_tick_counter };
|
|
|
|
__mg_send2client (&msg, client);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|