Files
cdesktop/cde/programs/dtappbuilder/src/libABobj/obj_notify.c
2018-06-28 04:08:35 +01:00

1274 lines
31 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
* $XConsortium: obj_notify.c /main/4 1996/10/02 16:03:34 drk $
*
* @(#)obj_notify.c 1.41 02 Feb 1995 cde_app_builder/src/libABobj
*
* RESTRICTED CONFIDENTIAL INFORMATION:
*
* The information in this document is subject to special
* restrictions in a confidential disclosure agreement between
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
* document outside HP, IBM, Sun, USL, SCO, or Univel without
* Sun's specific written approval. This document and all copies
* and derivative works thereof must be returned or destroyed at
* Sun's request.
*
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
*
*/
/*
* obj_notify.c - object change notification
*
* ALL FUNCTIONS THAT ACTUALLY MODIFY, UPDATE, SEND OR QUEUE NOTIFICATIONS
* ARE IN THIS FILE. NO NOTIFICATION CODE SHOULD EXIST OUTSIDE OF THIS
* FILE!
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "objP.h" /* include this first! */
#include <ab_private/obj_notify.h>
#include "obj_notifyP.h"
/*************************************************************************
** **
** Constants (#define and const) **
** **
**************************************************************************/
#define MAX_MODE_DEPTH 100
#define MAX_EVENT_QUEUE_SIZE 5000
/*************************************************************************
** **
** Types **
** **
**************************************************************************/
typedef int (*GenericCallback)(void *);
typedef struct
{
GenericCallback callback;
OBJEV_ATT_FLAGS atts;
ISTRING name;
} NAMED_CALLBACK, *NamedCallback;
typedef NamedCallback NamedCallbackList;
/*************************************************************************
** **
** Private Function Declarations **
** **
**************************************************************************/
static int call_callbacks_in_list(NamedCallbackList list,
void *data, ObjEvent event);
static int add_to_callback_list(NamedCallbackList *list,
GenericCallback callback, OBJEV_ATT_FLAGS atts,
ISTRING name);
static int objP_dispatch_event(ObjEvent event);
static int objP_dispatch_event_batched(ObjEvent event);
static int objP_dispatch_event_now(ObjEvent event);
static int objP_send_event(ObjEvent event);
static int flush_queue(void);
static int force_flush_queue(void);
static int event_destruct(ObjEvent, BOOL wasBatched);
static int cleanup_ev_att_change(ObjEvent event);
static int cleanup_ev_destroy(ObjEvent event, BOOL wasBatched);
static int cleanup_ev_update_with_data(ObjEvent event);
static BOOL event_is_in_queue(ObjEvent event);
static BOOL events_equal(ObjEvent event1, ObjEvent event2);
#define should_print_callbacks() \
((forcePrintCallbacks) || (debug_level() >= 3))
#define current_notify_mode() (modeStack[modeStackIndex])
#define queue_next_index(i) (((i) >= (MAX_EVENT_QUEUE_SIZE-1))? 0:(i)+1)
#define queue_is_empty() (firstEventIndex == lastEventIndex)
#define queue_is_full() (queue_next_index(lastEventIndex) == firstEventIndex)
/*
* Pops a mode from the mode stack, but won't flush the queue
*/
#define objP_notify_pop_mode_dont_flush() \
((modeStackIndex <= 0)? ERR_CLIENT : --modeStackIndex)
/*************************************************************************
** **
** Data **
** **
**************************************************************************/
static BOOL ignoreEvents= FALSE; /* critical error conditions*/
static BOOL forcePrintCallbacks= FALSE;
static BYTE modeStack[MAX_MODE_DEPTH]= {OBJEV_MODE_NORMAL };
static int modeStackIndex= 0;
static int recurseLevel= 0;
OBJ_EVENT eventQueue[MAX_EVENT_QUEUE_SIZE];
int firstEventIndex= 0;
int lastEventIndex= 0;
NamedCallbackList callbacksForAllowGeometryChange= NULL;
NamedCallbackList callbacksForAllowReparent= NULL;
NamedCallbackList callbacksForAttChange= NULL;
NamedCallbackList callbacksForCreate= NULL;
NamedCallbackList callbacksForDestroy= NULL;
NamedCallbackList callbacksForReparent= NULL;
NamedCallbackList callbacksForUpdate= NULL;
NamedCallbackList callbacksForUpdateWithData= NULL;
/*************************************************************************
** **
** PUBLIC FUNCTIONS for callback lists **
** **
**************************************************************************/
int
obj_add_allow_geometry_change_callback(ObjAllowGeometryChangeCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForAllowGeometryChange,
(GenericCallback)cb, OBJEV_ATT_NONE,
istr_create(debug_name));
}
int
obj_add_allow_reparent_callback(ObjAllowReparentCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForAllowReparent,
(GenericCallback)cb, OBJEV_ATT_NONE,
istr_create(debug_name));
}
int
obj_add_create_callback(ObjCreateCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForCreate,
(GenericCallback)cb, OBJEV_ATT_NONE,
istr_create(debug_name));
}
int
obj_add_destroy_callback(ObjDestroyCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForDestroy,
(GenericCallback)cb, OBJEV_ATT_NONE,
istr_create(debug_name));
}
int
obj_add_geometry_change_callback(ObjAttChangeCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForAttChange,
(GenericCallback)cb,
OBJEV_ATT_GEOMETRY | OBJEV_ATT_POSITION | OBJEV_ATT_SIZE,
istr_create(debug_name));
}
int
obj_add_rename_callback(ObjAttChangeCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForAttChange,
(GenericCallback)cb, OBJEV_ATT_NAME,
istr_create(debug_name));
}
int
obj_add_reparent_callback(ObjReparentCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForReparent,
(GenericCallback)cb, OBJEV_ATT_NONE,
istr_create(debug_name));
}
int
obj_add_selected_change_callback(ObjAttChangeCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForAttChange,
(GenericCallback)cb, OBJEV_ATT_SELECTED,
istr_create(debug_name));
}
int
obj_add_update_callback(ObjUpdateCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForUpdate,
(GenericCallback)cb, OBJEV_ATT_NONE,
istr_create(debug_name));
}
int
obj_add_update_with_data_callback(ObjUpdateWithDataCallback cb,
STRING debug_name)
{
return add_to_callback_list(&callbacksForUpdateWithData,
(GenericCallback)cb, OBJEV_ATT_NONE,
istr_create(debug_name));
}
/*************************************************************************
** **
** Function Definitions **
** **
**************************************************************************/
#define event_is_allow(ev) \
( ((ev)->type == OBJEV_ALLOW_GEOMETRY_CHANGE) \
|| ((ev)->type == OBJEV_ALLOW_REPARENT) )
#define event_is_abortable(ev) (event_is_allow(ev))
/*************************************************************************
** **
** Mode stack **
** **
** The bottom stack entry is MODE_NORMAL, and is always there. **
** **
**************************************************************************/
int
objP_notify_push_mode(void)
{
if (modeStackIndex >= (MAX_MODE_DEPTH-1))
{
return ERR_NO_MEMORY;
}
++modeStackIndex;
modeStack[modeStackIndex]= modeStack[modeStackIndex-1];
return 0;
}
int
objP_notify_set_mode(OBJ_EVENT_MODE newMode)
{
modeStack[modeStackIndex] |= newMode;
return 0;
}
int
objP_notify_clear_mode(OBJ_EVENT_MODE deadMode)
{
modeStack[modeStackIndex] &= ~deadMode;
return 0;
}
/*
* Pops a mode from the mode stack. If a non-batching mode is entered,
* the notify queue is flushed.
*/
int
objP_notify_pop_mode(void)
{
objP_notify_pop_mode_dont_flush();
/*
* If we've popped into a non-batching context, send the events!
*/
if (!objP_notify_mode_is_set(OBJEV_MODE_BATCH_NOTIFY_EVS))
{
flush_queue();
}
return 0;
}
OBJ_EVENT_MODE
objP_notify_get_mode(void)
{
return current_notify_mode();
}
/*************************************************************************
** **
** SHARED send_event functions **
** **
**************************************************************************/
int
objP_notify_send_allow_geometry_change(ABObj obj, int x, int y, int w, int h)
{
OBJ_EVENT event;
event.type= OBJEV_ALLOW_GEOMETRY_CHANGE;
/* event.info.allow_geometry_change.abortable= TRUE; */
event.info.allow_geometry_change.obj= obj;
event.info.allow_geometry_change.new_x= x;
event.info.allow_geometry_change.new_y= y;
event.info.allow_geometry_change.new_width= w;
event.info.allow_geometry_change.new_height= h;
return objP_dispatch_event(&event);
}
int
objP_notify_send_allow_reparent(ABObj obj, ABObj newParent)
{
OBJ_EVENT event;
event.type= OBJEV_ALLOW_REPARENT;
/* event.info.allow_reparent.abortable= TRUE; */
event.info.allow_reparent.obj= obj;
event.info.allow_reparent.new_parent= newParent;
return objP_dispatch_event(&event);
}
int
objP_notify_send_att_change(ABObj obj, OBJEV_ATT_FLAGS atts)
{
OBJ_EVENT event;
event.type= OBJEV_ATT_CHANGE;
event.info.att_change.obj= obj;
event.info.att_change.atts= atts;
return objP_dispatch_event(&event);
}
int
objP_notify_send_create(ABObj obj)
{
OBJ_EVENT event;
event.type= OBJEV_CREATE;
/* event.info.create.abortable= FALSE; */
event.info.create.obj= obj;
return objP_dispatch_event(&event);
}
int
objP_notify_send_destroy(ABObj obj)
{
OBJ_EVENT event;
event.type= OBJEV_DESTROY;
/* event.info.destroy_info.abortable= FALSE; */
event.info.destroy_info.obj= obj;
return objP_dispatch_event(&event);
}
int
objP_notify_send_geometry_change(ABObj obj,
int oldX, int oldY, int oldWidth, int oldHeight)
{
OBJ_EVENT event;
OBJEV_ATT_FLAGS geomflag = (OBJEV_ATT_FLAGS)0;
if (oldX != obj->x || oldY != obj->y)
geomflag |= OBJEV_ATT_POSITION;
if (oldWidth != obj->width || oldHeight != obj->height)
geomflag |= OBJEV_ATT_SIZE;
event.type= OBJEV_ATT_CHANGE;
event.info.att_change.atts = geomflag;
event.info.att_change.obj = obj;
event.info.att_change.old_name = NULL;
return objP_dispatch_event(&event);
}
int
objP_notify_send_rc_geometry_change(ABObj obj)
{
OBJ_EVENT event;
event.type= OBJEV_ATT_CHANGE;
event.info.att_change.atts = OBJEV_ATT_SIZE;
event.info.att_change.obj = obj;
event.info.att_change.old_name = NULL;
return objP_dispatch_event(&event);
}
int
objP_notify_send_name_change(ABObj obj, ISTRING old_name)
{
OBJ_EVENT event;
event.type= OBJEV_ATT_CHANGE;
event.info.att_change.atts = OBJEV_ATT_NAME;
event.info.att_change.obj = obj;
event.info.att_change.old_name = istr_dup(old_name);
return objP_dispatch_event(&event);
}
int
objP_notify_send_reparent(ABObj obj, ABObj oldParent)
{
OBJ_EVENT event;
event.type= OBJEV_REPARENT;
event.info.reparent.obj= obj;
/* event.info.reparent.abortable= FALSE; */
event.info.reparent.old_parent= oldParent;
return objP_dispatch_event(&event);
}
int
objP_notify_send_selected_change(ABObj obj)
{
OBJ_EVENT event;
event.type = OBJEV_ATT_CHANGE;
event.info.att_change.atts = OBJEV_ATT_SELECTED;
event.info.att_change.obj= obj;
event.info.att_change.old_name = NULL;
return objP_dispatch_event(&event);
}
int
objP_notify_send_update(ABObj obj, BOOL update_subtree)
{
OBJ_EVENT event;
event.type= OBJEV_UPDATE;
event.info.update.obj= obj;
event.info.update.update_subtree= update_subtree;
return objP_dispatch_event(&event);
}
int
objP_notify_send_update_with_data(
ABObj obj,
BOOL update_subtree,
int update_code,
void *update_data,
UpdateDataFreeFunc update_data_free_func
)
{
OBJ_EVENT event;
event.type= OBJEV_UPDATE_WITH_DATA;
event.info.update_with_data.obj= obj;
event.info.update_with_data.update_subtree= update_subtree;
event.info.update_with_data.update_code= update_code;
event.info.update_with_data.update_data= update_data;
event.info.update_with_data.update_data_free_func= update_data_free_func;
return objP_dispatch_event(&event);
}
/*************************************************************************
** **
** PRIVATE functions to deal with dispatching, queueing events **
** **
**************************************************************************/
static int
objP_dispatch_event(OBJ_EVENT *event)
{
int iReturn= 0;
BOOL eventIsNotify= !event_is_allow(event);
if (ignoreEvents)
{
/*
* A critical error has occurred (e.g., an event loop). Just
* pretend everything is hunky-dory and ignore dispatch requests.
*/
iReturn= OK;
goto epilogue;
}
if (objP_notify_mode_is_set(OBJEV_MODE_DISALLOW_ALL_EVS))
{
iReturn= ERR_NOT_ALLOWED;
goto epilogue;
}
if ( eventIsNotify
&& (!(objP_notify_mode_is_set(OBJEV_MODE_SEND_NOTIFY_EVS))) )
{
/* we're not sending notify events - assume OK */
iReturn= OK;
goto epilogue;
}
if ( (!eventIsNotify)
&& (!(objP_notify_mode_is_set(OBJEV_MODE_SEND_ALLOW_EVS))) )
{
/* we're not sending allow events - assume OK */
iReturn= OK;
goto epilogue;
}
/*
* Actually send an event! (or batch it...)
*/
if ( eventIsNotify
&& objP_notify_mode_is_set(OBJEV_MODE_BATCH_NOTIFY_EVS) )
{
iReturn= objP_dispatch_event_batched(event);
}
else
{
iReturn= objP_dispatch_event_now(event);
}
epilogue:
return iReturn;
}
/* Effects: queues an event for later delivery
* Assumes: mode NO_NOTIFY is not in effect
* Modifies: adds event to global queue
*/
static int
objP_dispatch_event_batched(ObjEvent event)
{
int iReturn= OBJ_NOTIFY_BATCHED; /* must return this! */
if (event_is_in_queue(event))
{
goto epilogue; /* it's already there! */
}
if (queue_is_full())
{
iReturn= ERR_DATA_SPACE_FULL;
goto epilogue;
}
eventQueue[lastEventIndex]= *event;
lastEventIndex= queue_next_index(lastEventIndex);
epilogue:
return iReturn;
}
/* Effects: sends an event immediately. Flushes all events resulting from
* callbacks
* Assumes: NO_NOTIFY is not in effect
*/
static int
objP_dispatch_event_now(ObjEvent event)
{
int iReturn= 0;
int iRC= 0;
iRC= objP_send_event(event);
event_destruct(event, FALSE);
if (iRC < 0)
{
iReturn= iRC;
}
/*
* If we've popped into a non-batching context, send the events!
*/
if (!objP_notify_mode_is_set(OBJEV_MODE_BATCH_NOTIFY_EVS))
{
iRC = flush_queue();
}
if (iRC < 0)
{
iReturn= iRC;
}
/* epilogue: */
return iReturn;
}
/* Effects: sends an event immediately to all registered clients
* Assumes: mode NO_NOTIFY is not in effect
* Modifies:
*
* Will not flush and messages resulting from the callbacks
*/
static int
objP_send_event(ObjEvent event)
{
int iReturn= 0;
int iRC= 0; /* return code */
NamedCallbackList callbackList= NULL;
void *data= NULL;
switch (event->type)
{
case OBJEV_ALLOW_GEOMETRY_CHANGE:
callbackList= callbacksForAllowGeometryChange;
data= (void *)&(event->info.allow_geometry_change);
break;
case OBJEV_ALLOW_REPARENT:
callbackList= callbacksForAllowReparent;
data= (void *)&(event->info.allow_reparent);
break;
case OBJEV_ATT_CHANGE:
callbackList= callbacksForAttChange;
data= (void *)&(event->info.att_change);
break;
case OBJEV_CREATE:
callbackList= callbacksForCreate;
data= (void *)&(event->info.create);
break;
case OBJEV_DESTROY:
callbackList= callbacksForDestroy;
data= (void *)&(event->info.destroy_info);
break;
case OBJEV_REPARENT:
callbackList= callbacksForReparent;
data= (void *)&(event->info.reparent);
break;
case OBJEV_UPDATE:
callbackList= callbacksForUpdate;
data= (void *)&(event->info.update);
break;
case OBJEV_UPDATE_WITH_DATA:
callbackList= callbacksForUpdateWithData;
data= (void *)&(event->info.update_with_data);
break;
default:
iReturn= ERR_INTERNAL;
goto epilogue;
/* break; */
}
if (recurseLevel > 0)
{
iReturn= ERR_RECURSION;
goto epilogue;
}
/*
* Batch all events that are generated by callback
*/
objP_notify_push_mode();
objP_notify_set_mode( OBJEV_MODE_SEND_ALLOW_EVS
| OBJEV_MODE_SEND_NOTIFY_EVS
| OBJEV_MODE_BATCH_NOTIFY_EVS);
if (event_is_allow(event))
{
objP_notify_set_mode(OBJEV_MODE_DISALLOW_ALL_EVS);
}
iReturn= iRC= call_callbacks_in_list(callbackList, data, event);
objP_notify_pop_mode_dont_flush();
epilogue:
return iReturn;
}
/*
* Effects: flushes all events in the queue, including any new events
* generated while sending. Guarantees empty queue on return.
*/
static int
flush_queue(void)
{
int iReturn= 0;
int numEventsProcessed= 0;
ObjEvent event= NULL;
while (!queue_is_empty())
{
event= &(eventQueue[firstEventIndex]);
objP_send_event(event);
event_destruct(event, TRUE);
firstEventIndex= queue_next_index(firstEventIndex);
++numEventsProcessed;
if (numEventsProcessed >= MAX_EVENT_QUEUE_SIZE)
{
/*
* an event cycle has been detected.
*/
iReturn= force_flush_queue();
}
}
return iReturn;
}
/*
* Guarantees that the event queue will be empty upon return. Turns
* off queuing of events from the callbacks
*
* Allows 20 or so more events to be queued, while printing them out
*/
static int
force_flush_queue(void)
{
int iReturn= ERR_RECURSION;
int numEventsProcessed= 0;
ObjEvent event= NULL;
/*
* We have probably run into an endless event cycle. To fix this,
* we're going ignore any more events generated while flushing the
* queue.
*
* If we're verbose, we're going to print out the remainder of the
* events in the queue as they are dispatched.
*/
ignoreEvents= TRUE;
#ifdef DEBUG
if (debugging())
{
ignoreEvents= FALSE; /* for debugging, allow some events */
forcePrintCallbacks= TRUE;
fprintf(stderr,
"\n\nAPPARENT EVENT CYCLE DETECTED. STARTING EVENT TRACING.\n\n");
fflush(stderr);
sleep(3);
}
#endif /* DEBUG */
while (!queue_is_empty())
{
event= &(eventQueue[firstEventIndex]);
objP_send_event(event);
event_destruct(event, TRUE);
firstEventIndex= queue_next_index(firstEventIndex);
++numEventsProcessed;
if (numEventsProcessed > 20)
{
ignoreEvents= TRUE;
}
}
ignoreEvents= FALSE;
forcePrintCallbacks= FALSE;
return iReturn;
}
/*
* Effects: invalidates the event, freeing any resources it is holding
*/
static int
event_destruct(ObjEvent event, BOOL wasBatched)
{
int return_value = 0;
switch (event->type)
{
case OBJEV_ATT_CHANGE:
return_value = cleanup_ev_att_change(event);
break;
case OBJEV_DESTROY:
return_value = cleanup_ev_destroy(event, wasBatched);
break;
case OBJEV_UPDATE_WITH_DATA:
return_value= cleanup_ev_update_with_data(event);
break;
}
return return_value;
}
/*
* cleanup_att_change
*/
static int
cleanup_ev_att_change(ObjEvent event)
{
if ((event->info.att_change.atts) & OBJEV_ATT_NAME)
{
istr_destroy(event->info.att_change.old_name);
}
return 0;
}
/*
* Effects: Completes a destroy operation that was deferred, waiting for
* notification.
*
* Assumes: event is of type destroy.
*/
static int
cleanup_ev_destroy(ObjEvent event, BOOL wasBatched)
{
int iReturn= 0;
if (!wasBatched)
{
/* if we weren't batched, then the caller will clean up the */
/* object */
return 0;
}
/*
* We must push a batching context. Otherwise, when the called routines
* push and then pop a mode the new mode (the one we're in) is non-
* batching, and the queue will be flushed. Since we are currently
* flushing the queue, that's us! (it will recurse back here).
*/
objP_notify_push_mode();
objP_notify_set_mode(OBJEV_MODE_BATCH_NOTIFY_EVS);
iReturn= objP_actually_destroy_one(event->info.destroy_info.obj);
objP_notify_pop_mode_dont_flush();
return iReturn;
}
/*
* Effects: - frees the data for an update_with_data, if it is no longer
* needed
* Assumes: - queue is not empty
* - event is of type UPDATE_WITH_DATA
*
* If any other events in the queue are using this data, it is not freed.
*/
static int
cleanup_ev_update_with_data(ObjEvent event)
{
int iReturn= 0;
int eventIndex= firstEventIndex;
void *update_data= event->info.update_with_data.update_data;
UpdateDataFreeFunc free_func=
event->info.update_with_data.update_data_free_func;
BOOL dataStillInUse= FALSE;
if (free_func == NULL)
{
goto epilogue;
}
eventIndex= queue_next_index(eventIndex);
while (eventIndex != lastEventIndex)
{
if ( (eventQueue[eventIndex].type == OBJEV_UPDATE_WITH_DATA)
&& (eventQueue[eventIndex].info.update_with_data.update_data
== update_data) )
{
dataStillInUse= TRUE;
break;
}
}
if (!dataStillInUse)
{
free_func(event->info.update_with_data.update_code,
update_data);
update_data= NULL;
event->info.update_with_data.update_data= NULL;
}
epilogue:
return iReturn;
}
static int
event_print(OBJ_EVENT *event, FILE *outFile, BOOL addNewline, STRING name)
{
ABObj obj= NULL;
char eventName[1024];
char eventParams[1024];
char *eventParamsPtr= eventParams;
char buf1[256];
char buf2[256];
*eventName= 0;
*eventParams= 0;
*buf1= 0;
*buf2= 0;
strcpy(eventName, "EVENT ");
switch (event->type)
{
case OBJEV_ALLOW_GEOMETRY_CHANGE:
strcat(eventName, "AllowGeometryChange");
obj= event->info.allow_geometry_change.obj;
sprintf(eventParams, "(%s %d %d %d %d)",
obj_get_safe_name(obj, buf1, 256),
event->info.allow_geometry_change.new_x,
event->info.allow_geometry_change.new_y,
event->info.allow_geometry_change.new_width,
event->info.allow_geometry_change.new_height);
break;
case OBJEV_ALLOW_REPARENT:
strcat(eventName, "AllowReparent");
obj= event->info.allow_reparent.obj;
sprintf(eventParams, "(%s %s)",
obj_get_safe_name(obj, buf1, 256),
obj_get_safe_name(event->info.allow_reparent.new_parent,
buf2, 256));
break;
case OBJEV_ATT_CHANGE:
strcat(eventName, "AttChange");
obj= event->info.att_change.obj;
sprintf(eventParams, "(%s", obj_get_safe_name(obj, buf1, 256));
eventParamsPtr= eventParams + strlen(eventParamsPtr);
switch (event->info.att_change.atts)
{
case OBJEV_ATT_GEOMETRY:
{
int x, y, w, h;
obj_get_geometry(obj, &x, &y, &w, &h);
strcat(eventName, "/Geometry");
sprintf(eventParamsPtr, "x:%d y:%d w:%d h:%d",
x, y, w, h);
}
break;
case OBJEV_ATT_LABEL:
strcat(eventName, "Label");
sprintf(eventParamsPtr, "%s",
util_strsafe(obj_get_label(obj)));
break;
case OBJEV_ATT_HSCROLL:
strcat(eventName, "HScroll");
sprintf(eventParamsPtr, "%s",
util_cvt_bool_to_string(
obj_has_hscrollbar(obj), buf1, 256));
break;
case OBJEV_ATT_SELECTED:
strcat(eventName, "Selected");
sprintf(eventParamsPtr, "%s",
util_cvt_bool_to_string(
obj_is_selected(obj), buf1, 256));
break;
case OBJEV_ATT_VSCROLL:
strcat(eventName, "VScroll");
sprintf(eventParamsPtr, "%s",
util_cvt_bool_to_string(
obj_has_vscrollbar(obj), buf1, 256));
break;
}
strcat(eventParams, ")");
break;
case OBJEV_CREATE:
strcat(eventName, "Create");
obj= event->info.create.obj;
break;
case OBJEV_DESTROY:
strcat(eventName, "Destroy");
obj= event->info.destroy_info.obj;
break;
case OBJEV_REPARENT:
strcat(eventName, "Reparent");
obj= event->info.reparent.obj;
sprintf(eventParams, "(%s old_parent:%s)",
obj_get_safe_name(obj, buf1, 256),
obj_get_safe_name(event->info.reparent.old_parent,
buf2, 256));
break;
case OBJEV_UPDATE:
strcat(eventName, "Update");
obj= event->info.update.obj;
break;
case OBJEV_UPDATE_WITH_DATA:
strcat(eventName, "UpdateWithData");
obj= event->info.update_with_data.obj;
sprintf(eventParams, "(%s %d 0x%08lx)",
obj_get_safe_name(obj, buf1, 256),
event->info.update_with_data.update_code,
(unsigned long) event->info.update_with_data.update_data);
break;
default:
obj= event->info.reparent.obj;
sprintf(eventName + strlen(eventName), "%d", event->type);
break;
}
if (*eventParams == 0)
{
char objName[1024];
sprintf(objName, "(%#lx", (unsigned long) obj);
if (obj_get_name(obj) != NULL)
{
strcat(objName, " = ");
strcat(objName, obj_get_name(obj));
}
strcat(objName, ")");
sprintf(eventParams, "(%s)", objName);
}
if (name != NULL)
{
strcat(eventName, ": ");
strcat(eventName, name);
}
fprintf(outFile, "%s%s", eventName, eventParams);
if (addNewline)
{
fprintf(outFile, "\n"); fflush(outFile);
}
return 0;
}
static int
event_queue_print(void)
{
int oldFirst= firstEventIndex;
int oldLast= lastEventIndex;
if (queue_is_empty())
{
printf("queue is empty!\n");
return 0;
}
printf("Event queue:\n");
while (!queue_is_empty())
{
event_print(&(eventQueue[firstEventIndex]), stdout, TRUE, NULL);
firstEventIndex= queue_next_index(firstEventIndex);
}
printf("\n");
firstEventIndex= oldFirst;
lastEventIndex= oldLast;
return 0;
}
/*************************************************************************
** **
** PRIVATE FUNCTIONS for Callback lists **
** **
**************************************************************************/
static int
add_to_callback_list(NamedCallbackList *ppsList,
GenericCallback callback,
OBJEV_ATT_FLAGS atts,
ISTRING callbackName)
{
#define list (*ppsList)
int iNumEntries;
NamedCallbackList newList= NULL;
if (list == NULL)
{
list= (NamedCallbackList)util_malloc(1 * sizeof(NAMED_CALLBACK));
if (list == NULL)
{
return ERR_NO_MEMORY;
}
list[0].callback= NULL;
}
for (iNumEntries= 0; list[iNumEntries].callback != NULL; )
{
++iNumEntries;
}
iNumEntries+= 2;
newList= (NamedCallbackList)
realloc(list, iNumEntries * sizeof(NAMED_CALLBACK));
if (newList == NULL)
{
return ERR_NO_MEMORY;
}
list= newList;
list[iNumEntries-2].callback= callback;
list[iNumEntries-2].atts= atts;
list[iNumEntries-2].name= callbackName;
list[iNumEntries-1].callback= NULL;
return 0;
#undef list
}
static int
call_callbacks_in_list(NamedCallbackList list, void *data, ObjEvent event)
{
int iRC= 0; /* return code */
int iReturn= 0; /* our return value */
int i= 0;
GenericCallback func= NULL;
if (list == NULL)
{
return 0;
}
for (i= 0; list[i].callback != NULL; ++i)
{
/*
* Only call attribute callbacks where masks match
*/
if ( (event->type == OBJEV_ATT_CHANGE)
&& ((event->info.att_change.atts & list[i].atts)
!= event->info.att_change.atts) )
{
continue;
}
if (should_print_callbacks())
{
event_print(event, stderr, FALSE, istr_string(list[i].name));
}
func= list[i].callback;
iRC= func(data);
if ((iRC < 0) && (iReturn >= 0))
{
iReturn= iRC;
}
if (should_print_callbacks())
{
fprintf(stderr, " -> %d\n", iRC); fflush(stderr);
}
}
return iReturn;
}
static BOOL
event_is_in_queue(ObjEvent event)
{
int eventIndex= firstEventIndex;
BOOL foundIt= FALSE;
if (queue_is_empty())
{
return FALSE;
}
eventIndex= queue_next_index(eventIndex);
for (; (!foundIt) && (eventIndex != lastEventIndex);
eventIndex= queue_next_index(eventIndex))
{
foundIt= events_equal(event, &(eventQueue[eventIndex]));
}
return foundIt;
}
static BOOL
events_equal(ObjEvent event1, ObjEvent event2)
{
BOOL equal= FALSE;
if (event1->type != event2->type)
{
return FALSE;
}
switch (event1->type)
{
case OBJEV_ATT_CHANGE:
{
if ((event1->info.att_change.obj != event2->info.att_change.obj)||
(event1->info.att_change.atts != event2->info.att_change.atts)
)
{
break;
}
switch (event1->info.att_change.atts)
{
case OBJEV_ATT_GEOMETRY:
case OBJEV_ATT_POSITION:
case OBJEV_ATT_SIZE:
break;
case OBJEV_ATT_HSCROLL:
break;
case OBJEV_ATT_LABEL:
break;
case OBJEV_ATT_SELECTED:
break;
case OBJEV_ATT_VSCROLL:
break;
}
}
break;
case OBJEV_CREATE:
equal= (event1->info.create.obj == event2->info.create.obj);
break;
case OBJEV_DESTROY:
equal= (event1->info.destroy_info.obj
== event2->info.destroy_info.obj);
break;
case OBJEV_REPARENT:
equal= (event1->info.reparent.obj == event2->info.reparent.obj);
break;
case OBJEV_UPDATE:
equal= (event1->info.update.obj == event2->info.update.obj);
break;
case OBJEV_UPDATE_WITH_DATA:
{
OBJEV_UPDATE_WITH_DATA_INFO *info1=
&(event1->info.update_with_data);
OBJEV_UPDATE_WITH_DATA_INFO *info2=
&(event2->info.update_with_data);
equal= ( (info1->obj == info2->obj)
&& (info1->update_data == info2->update_data)
&& (info1->update_data_free_func
== info2->update_data_free_func) );
}
break;
}
return equal;
}