mirror of
https://github.com/VincentWei/MiniGUI.git
synced 2026-10-02 18:22:55 +08:00
422 lines
12 KiB
C
422 lines
12 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/>.
|
|
*/
|
|
/*
|
|
** compsor-manager.c: the compositor manager module for MiniGUI.
|
|
**
|
|
** Create date: 2020-01-19
|
|
**
|
|
** Current maintainer: Wei Yongming.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#define _DEBUG
|
|
#include "common.h"
|
|
|
|
#if defined(_MGRM_PROCESSES) && defined(_MGSCHEMA_COMPOSITING)
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include "minigui.h"
|
|
#include "gdi.h"
|
|
#include "window.h"
|
|
#include "constants.h"
|
|
#include "zorder.h"
|
|
#include "dc.h"
|
|
|
|
#define MAX_NR_COMPOSITORS 8
|
|
#define LEN_COMPOSITOR_NAME 15
|
|
|
|
extern CompositorOps __mg_fallback_compositor;
|
|
|
|
static struct _compositors {
|
|
char name [LEN_COMPOSITOR_NAME + 1];
|
|
const CompositorOps* ops;
|
|
} compositors [MAX_NR_COMPOSITORS] = {
|
|
{ "fallback", &__mg_fallback_compositor },
|
|
};
|
|
|
|
static void* dl_handle;
|
|
|
|
static const CompositorOps* load_default_compositor (void)
|
|
{
|
|
const char* filename = NULL;
|
|
char buff [LEN_SO_NAME + 1];
|
|
const CompositorOps* (*ex_compsor_get) (const char*, const CompositorOps*);
|
|
char* error;
|
|
|
|
filename = getenv ("MG_DEF_COMPOSITOR_SO");
|
|
if (filename == NULL) {
|
|
memset (buff, 0, sizeof (buff));
|
|
if (GetMgEtcValue ("compositing_schema", "def_compositor_so",
|
|
buff, LEN_SO_NAME) < 0)
|
|
return NULL;
|
|
|
|
filename = buff;
|
|
}
|
|
|
|
dl_handle = dlopen (filename, RTLD_LAZY);
|
|
if (!dl_handle) {
|
|
_WRN_PRINTF ("Failed to open specified shared library for the default compositor: %s (%s)\n",
|
|
filename, dlerror());
|
|
return NULL;
|
|
}
|
|
|
|
dlerror(); /* Clear any existing error */
|
|
ex_compsor_get = dlsym (dl_handle, "__ex_compositor_get");
|
|
error = dlerror ();
|
|
if (error) {
|
|
_WRN_PRINTF ("Failed to get symbol: %s\n", error);
|
|
dlclose (dl_handle);
|
|
return NULL;
|
|
}
|
|
|
|
return ex_compsor_get (COMPSOR_NAME_DEFAULT, &__mg_fallback_compositor);
|
|
}
|
|
|
|
static void lock_znode_surface (PDC pdc, ZORDERNODE* node)
|
|
{
|
|
if (node->lock_count == 0) {
|
|
if (pdc->surface->shared_header) {
|
|
LOCK_SURFACE_SEM (pdc->surface->shared_header->sem_num);
|
|
}
|
|
|
|
node->dirty_age = pdc->surface->dirty_info->dirty_age;
|
|
node->nr_dirty_rcs = pdc->surface->dirty_info->nr_dirty_rcs;
|
|
node->dirty_rcs = pdc->surface->dirty_info->dirty_rcs;
|
|
|
|
}
|
|
|
|
node->lock_count++;
|
|
}
|
|
|
|
static void unlock_znode_surface (PDC pdc, ZORDERNODE* node)
|
|
{
|
|
if (node->lock_count > 0) {
|
|
node->lock_count--;
|
|
if (node->lock_count == 0) {
|
|
if (pdc->surface->shared_header)
|
|
UNLOCK_SURFACE_SEM (pdc->surface->shared_header->sem_num);
|
|
|
|
node->dirty_age = 0;
|
|
node->nr_dirty_rcs = 0;
|
|
node->dirty_rcs = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void __mg_composite_dirty_znodes (void)
|
|
{
|
|
MG_Layer* layer;
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
CompositorCtxt* ctxt;
|
|
const CompositorOps* ops = ServerSelectCompositor (NULL, &ctxt);
|
|
int i, next;
|
|
unsigned int changes_in_dc;
|
|
PDC pdc;
|
|
|
|
assert (ops);
|
|
|
|
zi = (ZORDERINFO*)mgTopmostLayer->zorder_info;
|
|
|
|
/* travel menu znodes on the topmost layer */
|
|
if (zi->nr_popupmenus > 0) {
|
|
nodes = GET_MENUNODE(zi);
|
|
for (i = 0; i < zi->nr_popupmenus; i++) {
|
|
pdc = dc_HDC2PDC (nodes[i].mem_dc);
|
|
assert (pdc->surface->dirty_info);
|
|
|
|
lock_znode_surface (pdc, nodes + i);
|
|
changes_in_dc = pdc->surface->dirty_info->dirty_age;
|
|
if (changes_in_dc != nodes[i].changes) {
|
|
ops->on_dirty_ppp (ctxt, i);
|
|
nodes[i].changes = changes_in_dc;
|
|
pdc->surface->dirty_info->nr_dirty_rcs = 0;
|
|
}
|
|
unlock_znode_surface (pdc, nodes + i);
|
|
}
|
|
}
|
|
|
|
/* travel win znodes on the topmost layer first */
|
|
nodes = GET_ZORDERNODE(zi);
|
|
next = 0;
|
|
while ((next = __kernel_get_next_znode (zi, next)) > 0) {
|
|
if (nodes [next].flags & ZOF_VISIBLE) {
|
|
pdc = dc_HDC2PDC (nodes[next].mem_dc);
|
|
assert (pdc->surface->dirty_info);
|
|
|
|
lock_znode_surface (pdc, nodes + next);
|
|
changes_in_dc = pdc->surface->dirty_info->dirty_age;
|
|
if (changes_in_dc != nodes[next].changes) {
|
|
ops->on_dirty_win (ctxt, mgTopmostLayer, next);
|
|
nodes[next].changes = changes_in_dc;
|
|
pdc->surface->dirty_info->nr_dirty_rcs = 0;
|
|
}
|
|
unlock_znode_surface (pdc, nodes + next);
|
|
}
|
|
}
|
|
|
|
/* then travel win znodes on other layers */
|
|
layer = mgLayers;
|
|
while (layer) {
|
|
if (layer != mgTopmostLayer) {
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
nodes = GET_ZORDERNODE(zi);
|
|
next = 0;
|
|
while ((next = __kernel_get_next_znode (zi, next)) > 0) {
|
|
if (nodes [next].flags & ZOF_VISIBLE) {
|
|
pdc = dc_HDC2PDC (nodes[next].mem_dc);
|
|
assert (pdc->surface->dirty_info);
|
|
|
|
lock_znode_surface (pdc, nodes + next);
|
|
changes_in_dc = pdc->surface->dirty_info->dirty_age;
|
|
if (changes_in_dc != nodes[next].changes) {
|
|
ops->on_dirty_win (ctxt, layer, next);
|
|
nodes[next].changes = changes_in_dc;
|
|
pdc->surface->dirty_info->nr_dirty_rcs = 0;
|
|
}
|
|
unlock_znode_surface (pdc, nodes + next);
|
|
}
|
|
}
|
|
}
|
|
|
|
layer = layer->next;
|
|
}
|
|
|
|
/* check wallpaper pattern */
|
|
pdc = dc_HDC2PDC (HDC_SCREEN);
|
|
if (pdc->surface->w > 0 && pdc->surface->h > 0) {
|
|
assert (pdc->surface->dirty_info);
|
|
lock_znode_surface (pdc, nodes);
|
|
changes_in_dc = pdc->surface->dirty_info->dirty_age;
|
|
if (changes_in_dc != nodes[0].changes) {
|
|
ops->on_dirty_wpp (ctxt);
|
|
nodes[0].changes = changes_in_dc;
|
|
pdc->surface->dirty_info->nr_dirty_rcs = 0;
|
|
}
|
|
unlock_znode_surface (pdc, nodes);
|
|
}
|
|
}
|
|
|
|
static void purge_znodes_private_data_in_layer (const CompositorOps* ops,
|
|
CompositorCtxt* ctxt, MG_Layer* layer)
|
|
{
|
|
ZORDERINFO* zi;
|
|
ZORDERNODE* nodes;
|
|
int i, next;
|
|
|
|
zi = (ZORDERINFO*)layer->zorder_info;
|
|
|
|
// travel popup menu znodes
|
|
if (zi->nr_popupmenus > 0) {
|
|
nodes = GET_MENUNODE(zi);
|
|
for (i = 0; i < zi->nr_popupmenus; i++) {
|
|
if (nodes[i].priv_data && ops->purge_ppp_data) {
|
|
ops->purge_ppp_data (ctxt, i, nodes[i].priv_data);
|
|
nodes[i].priv_data = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// travel window znodes
|
|
nodes = GET_ZORDERNODE(zi);
|
|
next = 0;
|
|
while ((next = __kernel_get_next_znode (zi, next)) > 0) {
|
|
if (nodes[next].priv_data && ops->purge_win_data) {
|
|
ops->purge_win_data (ctxt, layer, next, nodes[next].priv_data);
|
|
nodes[next].priv_data = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void purge_all_znodes_private_data (const CompositorOps* ops,
|
|
CompositorCtxt* ctxt)
|
|
{
|
|
MG_Layer* layer;
|
|
|
|
layer = mgLayers;
|
|
while (layer) {
|
|
purge_znodes_private_data_in_layer (ops, ctxt, layer);
|
|
layer = layer->next;
|
|
}
|
|
}
|
|
|
|
BOOL mg_InitCompositor (void)
|
|
{
|
|
const char* name = COMPSOR_NAME_FALLBACK;
|
|
const CompositorOps* ops;
|
|
CompositorCtxt* ctxt = NULL;
|
|
|
|
ops = load_default_compositor ();
|
|
if (ops && ServerRegisterCompositor (COMPSOR_NAME_DEFAULT, ops)) {
|
|
name = COMPSOR_NAME_DEFAULT;
|
|
}
|
|
|
|
ServerSelectCompositor (name, &ctxt);
|
|
return (ctxt != NULL);
|
|
}
|
|
|
|
void mg_TerminateCompositor (void)
|
|
{
|
|
CompositorCtxt* ctxt = NULL;
|
|
|
|
// Select fallback compositor and terminate the fallback compositor.
|
|
ServerSelectCompositor (COMPSOR_NAME_FALLBACK, &ctxt);
|
|
if (ctxt) {
|
|
purge_all_znodes_private_data (&__mg_fallback_compositor, ctxt);
|
|
__mg_fallback_compositor.terminate (ctxt);
|
|
}
|
|
|
|
if (dl_handle)
|
|
dlclose (dl_handle);
|
|
}
|
|
|
|
const CompositorOps* GUIAPI ServerGetCompositorOps (const char* name)
|
|
{
|
|
int i;
|
|
|
|
if (name == NULL || name[0] == 0)
|
|
return NULL;
|
|
|
|
for (i = 0; i < MAX_NR_COMPOSITORS; i++) {
|
|
if (strcmp (compositors [i].name, name) == 0) {
|
|
return compositors [i].ops;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
BOOL GUIAPI ServerRegisterCompositor (
|
|
const char* name, const CompositorOps* ops)
|
|
{
|
|
int i;
|
|
|
|
if (name == NULL || name[0] == 0 || ops == NULL)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < MAX_NR_COMPOSITORS; i++) {
|
|
if (compositors [i].name[0] == 0) {
|
|
strncpy (compositors [i].name, name, LEN_COMPOSITOR_NAME);
|
|
compositors [i].ops = ops;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL GUIAPI ServerUnregisterCompositor (const char* name)
|
|
{
|
|
int i;
|
|
const CompositorOps* curr_ops;
|
|
|
|
if (name == NULL || name[0] == 0 ||
|
|
strcmp (name, COMPSOR_NAME_FALLBACK) == 0)
|
|
return FALSE;
|
|
|
|
curr_ops = ServerSelectCompositor (NULL, NULL);
|
|
|
|
for (i = 0; i < MAX_NR_COMPOSITORS; i++) {
|
|
if (strcmp (compositors [i].name, name) == 0) {
|
|
if (curr_ops == compositors [i].ops)
|
|
return FALSE;
|
|
|
|
compositors [i].name[0] = 0;
|
|
compositors [i].ops = NULL;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const CompositorOps* GUIAPI ServerSelectCompositor (const char* name,
|
|
CompositorCtxt** the_ctxt)
|
|
{
|
|
static const CompositorOps* curr_ops;
|
|
static CompositorCtxt* curr_ctxt;
|
|
const CompositorOps* ops;
|
|
|
|
if (name == NULL || name [0] == 0) {
|
|
if (the_ctxt) *the_ctxt = curr_ctxt;
|
|
return curr_ops;
|
|
}
|
|
|
|
ops = ServerGetCompositorOps (name);
|
|
if (ops) {
|
|
CompositorCtxt* ctxt;
|
|
|
|
if (ops == curr_ops) {
|
|
if (the_ctxt) *the_ctxt = curr_ctxt;
|
|
return ops;
|
|
}
|
|
|
|
ctxt = ops->initialize (name);
|
|
if (ctxt) {
|
|
if (curr_ops) {
|
|
purge_all_znodes_private_data (curr_ops, curr_ctxt);
|
|
curr_ops->terminate (curr_ctxt);
|
|
}
|
|
curr_ops = ops;
|
|
curr_ctxt = ctxt;
|
|
curr_ops->refresh (ctxt);
|
|
}
|
|
|
|
if (the_ctxt) *the_ctxt = curr_ctxt;
|
|
return curr_ops;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* defined(_MGRM_PROCESSES) && defined(_MGSCHEMA_COMPOSITING) */
|
|
|