/* * 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 #include #include #include #include "objP.h" /* include this first! */ #include #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; }