/*
 ******************************************************************************
 *
 *	ParcPlace Systems, Inc.
 *	Copyright (c) 1990, ParcPlace Systems, Inc. USA   All rights reserved.
 *
 ****************************************************************************
 *
 *	File:	dispatch.H
 *
 *	Functions(s):
 *
 *	Description:
 *		Object Interface dispatch table header
 *		The dispatch table is used to define a mapping between XEvents
 *		and procedures to call when those events occur.
 *
 *	RCSid = "$Id: dispatch.H,v 4.17.1.1 1993/06/01 22:53:58 mth Exp $"
 *
 ****************************************************************************
 */

#ifndef OI_dispatch_H
#define OI_dispatch_H

#ifndef OI_util_H
#include <OI/util.H>
#endif /* OI_util_H */

#ifndef OI_array_H
#include <OI/array.H>
#endif /* OI_array_H */

#ifndef OI_cb_inf_H
#include <OI/cb_inf.H>
#endif /* OI_cb_inf_H */

#ifndef OI_fd_set_H
#include <OI/fd_set.H>
#endif /* OI_fd_set_H */

	// class forward reference declarations
	class OI_dispatch;
	class OI_dispatch_syn_ev;
	class OI_dispatch_syn_cb;

 /* constants effecting layout and performance of dispatch table */
#define	OI_X_WIN_INC		256	/* # X windows we will get events for initially, and incremental table size */
#define	OI_AVG_EVENTS_WIN	5	/* avg # events to catch per X window */

	class OI_dispatch_event : public OI_cb {					/* per event dispatch table */
			int			ev ;			/* X event to look for */
			unsigned long		ev_mask ;		/* X event mask to select the event */

		friend	void			dmpOI_dispatch_event(OI_dispatch_event*) ;	/* diagnostic dump, dispatch table event */
		 public:
						OI_dispatch_event() ;
						OI_dispatch_event(OI_callback*, OI_memfnp, void* =NULL) ;
						OI_dispatch_event(OI_fnp, void* =NULL) ;
		virtual				~OI_dispatch_event() ;
			int			event()					{ return(ev); }
			unsigned long		event_mask()				{ return(ev_mask); }
			void			set_event(int e, unsigned long em)	{ ev=e; ev_mask=em; }
	} ;

#ifdef CPLUSPLUS20
#define OI_EVENT_MEMFN_CAST(mfn)	/*(OI_event_memfnp)*/(mfn)
#else
#define OI_EVENT_MEMFN_CAST(mfn)	(OI_event_memfnp)(mfn)
#endif
#ifdef TYPEDEF_MEMF
	typedef	void	(OI_syn_event_end_fn)(void*,void*) ;			/* function to handle end of synthetic event processing */
	typedef	void	(OI_callback::OI_syn_event_memfn)(void*,void*) ;	/* member function to handle end of synthetic event processing */
#endif /* TYPEDEF_MEMFN */
	typedef	void	(*OI_syn_event_fnp)(void*,void*) ;			/* function to handle end of synthetic event processing */
	typedef	void	(OI_callback::*OI_syn_event_memfnp)(void*,void*) ;	/* member function to handle end of synthetic event processing */

	struct OI_dispatch_entry {		/* per event dispatch table */
			int			event ;			/* X event to look for */
		unsigned long			event_mask ;		/* X event mask to select the event */
			OI_event_fnp		fnp ;			/* function callback to handle X events */
			OI_callback		*objp ;			/* object for member function callback */
			OI_event_memfnp		memfnp ;		/* member function callback to handle X events */ 
			void			*argp ;			/* ptr to arg to pass to callback */
	} ;

#define			OI_DISPATCH_EVENTS_REALLOCD		0x01	/* eventp has been reallocated, start over at beginning */
#define			OI_DISPATCH_EVENTS_WL_OWNS		0x02	/* a windowless object owns this event */
#define			OI_DISPATCH_EVENT_HANDLED		0x04	/* a particular event was handled by a callback */

	class OI_dispatch_window {					/* per X window dispatch table entry */
			Window			win ;			/* X window id waiting for event, NULL => unused except for overflow field */
			OI_number		scn ;			/* screen number window is on */
			OI_number		hits ;			/* # of entries which should map to this entry */
			OI_number		max_event ;		/* max # events in *eventp */
			OI_number		n_event ;		/* # events actually in use in *eventp */
			OI_number		n_empty ;		/* # events marked as empty in *eventp */
			OI_dispatch_event	*eventp ;		/* ptr to vector of events for this window; may have holes */
			unsigned long		event_mask ;		/* X event mask for all events selected */
			OI_dispatch_window	*mapsto ;		/* Index to where this entry should map to, 0-tbl_size */
			unsigned long		lvl_mask ;		/* 1 bit on => active for corresponding level */
			unsigned long		ctl_bits ;		/* control bits */

		friend	class OI_dispatch ;
		friend	void			dmpOI_dispatch(OI_dispatch*) ;	/* diagnostic dump procedure */
		friend	void			dmpOI_dispatch_window(OI_dispatch*,OI_dispatch_window*) ;	/* dump dispatch info for a window */
		 public:
						OI_dispatch_window() ;			/* constructor */
						~OI_dispatch_window() ;			/* destructor */
			void			init(Window w=NULL)
							{win=w; scn= -1; hits=0; max_event=0; n_event=0; eventp=NULL; event_mask=0;
								mapsto=NULL; lvl_mask=0; ctl_bits=0; }
			void			set_waiting(int) ;	/* set waiting flag for all events */
			void			void_eventp()		{ ctl_bits |= OI_DISPATCH_EVENTS_REALLOCD; }
			void			set_eventp_ok()	{ ctl_bits &= ~OI_DISPATCH_EVENTS_REALLOCD; }
			OI_bool			is_eventp_ok()	{ return((ctl_bits&OI_DISPATCH_EVENTS_REALLOCD) ? OI_NO : OI_YES); }
			void			clear_waiting(const XEvent*) ;	/* set waiting flag for all events */
			OI_number		screen()	{return(scn);}
			void			adjust(Display *);
			void			allow_wl_owns()		{ ctl_bits |= OI_DISPATCH_EVENTS_WL_OWNS; }
			void			disallow_wl_owns()	{ ctl_bits &= ~OI_DISPATCH_EVENTS_WL_OWNS; }
			OI_bool			is_wl_owns()		{ return ((ctl_bits&OI_DISPATCH_EVENTS_WL_OWNS) ? OI_YES : OI_NO); }
			void			allow_event_handled()		{ ctl_bits |= OI_DISPATCH_EVENT_HANDLED; }
			void			disallow_event_handled()	{ ctl_bits &= ~OI_DISPATCH_EVENT_HANDLED; }
			OI_bool			is_event_handled()		{ return ((ctl_bits&OI_DISPATCH_EVENT_HANDLED) ? OI_YES : OI_NO); }
			Window			window()		{ return(win); }
	} ;

	/*
	 * More than one synthetic event may be generated for a particular "thing"
	 * In practice, a "thing" is a windowless graphic object used as a reference point for painting one or more other windowless objects
	 * Only one done callback object (OI_dispatch_syn_cb) will be generated for all of the synthetic events associated with a single real X event.
	 */
	class OI_dispatch_syn_cb : public OI_cb {			/* synthetic event done callback */
			void			*id ;			/* id for events corresponding to this "thing" */
			OI_dispatch_syn_cb	*nxtp ;			/* ptr to next callback on chain */
	 public:
						OI_dispatch_syn_cb(void*) ;
	virtual					~OI_dispatch_syn_cb();
			void			*ident()		{ return(id); }
			OI_dispatch_syn_cb	*next()			{ return(nxtp); }
			void			set_next(OI_dispatch_syn_cb *p)	{ nxtp=p; }
	} ;

	class OI_dispatch_syn_ev {					/* synthetic event data package */
			XEvent			ev ;			/* X event */
			OI_dispatch_syn_ev	*nxtp ;			/* ptr to next list on chain */
			void			*ap ;			/* ptr to user data packet to as 2nd arg in event callback */
			Bool			sid ;			/* modified send_event flag in event put back on input queue */
			Bool			send_ev_org ;		/* send_event flag from original event */
	 public:
						OI_dispatch_syn_ev(Bool,const XEvent*,void*) ;
			void			*argp()			{ return(ap); }
			OI_dispatch_syn_ev	*next()			{ return(nxtp); }
			Bool			send_event()		{ return(send_ev_org); }
			void			set_next(OI_dispatch_syn_ev*p)	{ nxtp=p; }
			Bool			syn_ident()		{ return(sid); }
			XEvent			*x_event()		{ return(&ev); }
	} ;

	class OI_dispatch {			/* dispatch table */
			Display			*dpy ;			/* ptr to X connection dispatch table belongs to */
			OI_dispatch_window	*tblp ;			/* ptr to dispatch table */
			OI_cb			*lvl_cbp ;		/* ptr to discard function table for levels */
			OI_dispatch_syn_ev	*syn_lstp ;		/* ptr to que of synthetic events waiting to be pushed back onto event queue */
			OI_dispatch_syn_cb	*syn_cbp ;		/* ptr to list of synthetic event done callbacks */
			unsigned long		cur_lvl_mask ;		/* mask for current level */
			OI_number		tbl_size ;		/* # slots in table, forced to power of 2 */
			OI_number		hash_mask ;		/* mask for hash function, tbl_size-1 */
			OI_number		lvl ;			/* current dispatch level */
			OI_number		mx_lvl ;		/* max # entries allocated in lvl_fnp */
			OI_number		ref_cnt ;		/* reference count */
			OI_number		syn_cnt ;		/* count of events processed */
			Bool			syn_id ;		/* send_event id for next synthetic event */

	 private:
			OI_dispatch_window	*hash(Window) ;				/* hash window & event into dispatch table */
			OI_dispatch_event	*insert_cb(Window,OI_number,int,unsigned long,OI_event_fnp,OI_callback*,OI_event_memfnp,
								void*,OI_number= -1) ;
			void			push_cb(OI_fnp, OI_callback*, OI_memfnp, void*) ;
			void			remove_cb(Window,int,unsigned long,OI_event_fnp,OI_callback*,OI_event_memfnp,void*) ;
			OI_dispatch_window	*seek_window(Window) ;			/* locate slot for window in dispatch table */
			void			set_synthetic_done_cb(void*,OI_syn_event_fnp,OI_callback*,OI_syn_event_memfnp,void*) ;
			void			verify_window(OI_dispatch_window*) ;

		friend	void			dmpOI_dispatch_window(OI_dispatch*,OI_dispatch_window*) ;	/* dump dispatch info for a window */
		friend	void			dmpOI_dispatch(OI_dispatch*) ;					/* diagnostic dump proc */
	 public:
						OI_dispatch(OI_number,Display*) ;
						~OI_dispatch() ;
			int			allow_input(Window,OI_bool=OI_NO) ;	/* allow input on window at current level */
			void			call_function(OI_dispatch_window*,OI_dispatch_event*,const XEvent*,OI_dispatch_syn_ev*) ;
			void			deref() ;
			void			disallow_input(Window) ;		/* disallow input on window at current level */
			void			discard(Window, OI_callback* =NULL) ;	/* remove all entries for a window */
			OI_dispatch_event	*find(Window,const XEvent*,OI_dispatch_event*) ;	/* find entry in dispatch table */
			OI_dispatch_event	*find_event(OI_dispatch_window*,const XEvent*,OI_dispatch_event*) ;
			OI_dispatch_syn_ev	*find_synthetic_event(const XEvent*) ;
			OI_dispatch_window	*find_window(Window) ;			/* locate window in dispatch table */
			void			group_insert(Window,OI_number,OI_number,OI_dispatch_entry*) ;
			void			group_remove(Window,OI_number,OI_dispatch_entry*) ;	/* remove entries from dispatch table */
			void			ignore(const XEvent*) ;			/* ignore anyone else registered for an event */
			OI_dispatch_event	*insert(Window w,OI_number sn,int e,unsigned long em,OI_event_fnp fp,void *argp=NULL, OI_number p= -1) ;
			OI_dispatch_event	*insert(Window w,OI_number sn,unsigned long em,OI_event_fnp fp,void *argp=NULL,OI_number p= -1) ;
			OI_dispatch_event	*insert(Window w, OI_number sn, int e, unsigned long em,
							OI_callback *objp, OI_event_memfnp mfp,void *argp=NULL, OI_number p= -1);
			OI_dispatch_event	*insert(Window w,OI_number sn,unsigned long em,OI_callback *objp,OI_event_memfnp mfp,
								void *argp=NULL, OI_number p= -1);
			void			pop() ;					/* return to previous input level */
			void			push(OI_callback *objp,OI_memfnp mfp,void *argp=NULL) ;
			void			push(OI_fnp fp=NULL,void *argp=NULL) ;
			void			que_synthetic_event(void *id,const XEvent*,void *argp=NULL) ;
			void			ref() ;
			void			release_synthetic_events() ;
			void			remove(Window w,int e,unsigned long em,OI_event_fnp fp,void *argp=NULL) ;
			void			remove(Window w,unsigned long em,OI_event_fnp fp,void *argp=NULL) ;
			void			remove(Window w,int e,unsigned long em,OI_callback *objp,OI_event_memfnp mfp, void *argp=NULL);
			void			remove(Window w,unsigned long em,OI_callback *objp,OI_event_memfnp mfp,void *argp=NULL);
			void			remove_synthetic_done(void*) ;
			void			set_synthetic_done(void*,OI_syn_event_fnp,void* =NULL) ;
			void			set_synthetic_done(void*,OI_callback*,OI_syn_event_memfnp,void* =NULL) ;
			OI_dispatch_syn_cb	*synthetic_callback(void*) ;		/* find / allocate new synthetic event callback */
			void			synthetic_deref()		{ if (syn_cnt) syn_cnt--; }
			void			synthetic_done() ;			/* perform synthetic event done callbacks, clean up syn events */
			OI_number		synthetic_ref_count()		{ return(syn_cnt); }
			OI_bool			will_dispatch(Window,const XEvent*) ;	/* chk if event is registered and will dispatch for window */
	} ;

 /* Structures and classes used in this file for handling user procedure dispatches on non X fd's */
/********************************
 *	OI_io_dispatch class	*
 ********************************/
#define		OI_IO_DEL		0x1L	/* entry needs to be deleted after dispatching */
#define		OI_IO_UNPROCESSED	0x2L	/* entry is waiting to be processed */

	class OI_io_dispatch {							/* user dispatch info */
			int			_fd ;				/* fd of interest */
			fd_set			_mask ;				/* fd converted to mask for select() */
			int			typ_mask ;			/* types of i/o of interest */
			OI_io_fnp		fnp ;				/* callback function when select() times out */
			OI_callback		*objp ;				/* ptr to object on behalf of whom to call proc */
			OI_io_memfnp		memfnp ;			/* callback memfn when select() times out */
			void			*argp ;				/* ptr to arg to pass to callback */
			long			ctl_bits ;			/* various control bits */

		friend	void			dmpio_disp() ;
		public:
						OI_io_dispatch(int, int, OI_io_fnp, OI_callback*, OI_io_memfnp, void *) ;
			OI_bool			del() ;
			OI_bool			pending_delete()	{ return((OI_bool)(((ctl_bits&OI_IO_DEL) != 0) ?
										OI_YES : OI_NO)); }
			OI_bool			unprocessed()		{ return((OI_bool)(((ctl_bits&OI_IO_UNPROCESSED) != 0) ?
										OI_YES : OI_NO)); }
			void			mark_unprocessed()	{ ctl_bits |= OI_IO_UNPROCESSED; }
			void			mark_processed()	{ ctl_bits &= ~OI_IO_UNPROCESSED; }
			int			fd()			{ return(_fd); }
			fd_set			*mask()			{ return(&_mask); }
			int			type_mask()		{ return(typ_mask); }
			void			callback()		{ if (objp) (objp->*memfnp)(_fd,argp);
										else (*fnp)(_fd,argp); }
	} ;

// OI_cb_deletable is the base class for all callbacks that can be put into a table.  If you can put a callback into a table, then you
// can delete it at any time.  If the table is currently marked as busy, these guys know how to mark themselves as needs to be
// deleted.  Also, if you add a callback in a critical section, then it will be marked as being new until the critical section is
// over.
//
// bits for ctl_bits
	const	unsigned long		OI_cb_deletable_pending_delete	= 0x00000001;	// cb deleted in critical section, so ignore.
	const	unsigned long		OI_cb_deletable_new_callback 	= 0x00000002;	// cb added in critical section, so ignore.

	class OI_cb_deletable : public OI_cb {
	 private:
		unsigned long		ctl_bits;
	 private:
		void			construct();
	 public:
					OI_cb_deletable() : OI_cb() { construct(); }
					OI_cb_deletable(OI_fnp fp, void *argp) : OI_cb( fp, argp ) { construct(); }
					OI_cb_deletable(OI_callback *objp, OI_memfnp mfp, void *argp) : OI_cb( objp, mfp, argp ) { construct(); }
	virtual				~OI_cb_deletable();
		void			allow_new_callback()		{ ctl_bits |= OI_cb_deletable_new_callback; }
		void			allow_pending_delete()		{ ctl_bits |= OI_cb_deletable_pending_delete; }
		void			disallow_new_callback()		{ ctl_bits &= ~OI_cb_deletable_new_callback; }
		void			disallow_pending_delete()	{ ctl_bits &= ~OI_cb_deletable_pending_delete; }
		OI_bool			is_new_callback()		{ return( (ctl_bits & OI_cb_deletable_new_callback) ? OI_YES : OI_NO ); }
		OI_bool			is_pending_delete()		{ return( (ctl_bits & OI_cb_deletable_pending_delete) ? OI_YES : OI_NO ); }
	};

	class OI_idle_timeout_cb : public OI_cb_deletable {
	 protected:
		long			ms ;			/* # milliseconds between callbacks */
		long			ms_togo ;		/* # milliseconds remaining before next callback */
	 public:
					OI_idle_timeout_cb( OI_timeout_fnp, void *, long );
					OI_idle_timeout_cb( OI_callback *, OI_timeout_memfnp, void *, long );
	virtual				~OI_idle_timeout_cb();
	        void			callback_if_expired( long );
	        void			set_cb(OI_timeout_fnp, void *, long) ;
	        void			set_cb(OI_callback *, OI_timeout_memfnp, void *, long) ;
	        void			set_interval(long delta)	{ ms = delta; ms_togo = delta; }
		long			time_left()			{ return (ms_togo); }
	};

	class OI_wall_timeout_cb : public OI_cb_deletable {
		long			ms ;			/* # milliseconds between callbacks */
	struct	timeval			expires ;		/* When does this callback expire */
	 protected:
		void			calc_next( struct timeval * );
	 public:
					OI_wall_timeout_cb( OI_timeout_fnp, void *, long );
					OI_wall_timeout_cb( OI_callback *, OI_timeout_memfnp, void *, long );
	virtual				~OI_wall_timeout_cb();
		void			callback_if_expired( struct timeval * );
	struct	timeval			*expire_time()				{ return( &expires ); }
	        void			set_cb(OI_timeout_fnp, void *, long) ;
	        void			set_cb(OI_callback *, OI_timeout_memfnp, void *, long) ;
	        void			set_interval(long delta)		{ ms = delta; }
	};

	class OI_cb_table : public OI_pointer_array {
	 protected:
		int			critical_count;			/* >0 => in a critical section, can't fire callback */
	 protected:
		void			remove( const unsigned int );
	 public:
					OI_cb_table() : OI_pointer_array()	{ critical_count = 0; }
	virtual				~OI_cb_table();
		OI_cb_deletable		*element( const unsigned int i)		{ return(((OI_cb_deletable **)array)[i]); }
		void			enter_critical_section();
		OI_bool			in_critical_section()			{ return (critical_count > 0 ? OI_YES : OI_NO); }
		void			leave_critical_section();
		void			remove( OI_fnp, void * );
		void			remove( OI_callback *, OI_memfnp, void * );
	};

	class OI_wall_timeout_table : public OI_cb_table {
	 public:
					OI_wall_timeout_table();
					~OI_wall_timeout_table();
		unsigned long		append( long, OI_timeout_fnp, void * );
		unsigned long		append( long, OI_callback *, OI_timeout_memfnp, void * );
		void			make_callbacks( struct timeval * );
		OI_wall_timeout_cb	*element( const unsigned int i)		{ return(((OI_wall_timeout_cb **)array)[i]); }
	struct	timeval			*soonest_timeout( struct timeval *, long );
	};

	class OI_idle_timeout_table : public OI_cb_table {
	 protected:
		OI_bool			cb_enable;
	 public:
					OI_idle_timeout_table();
					~OI_idle_timeout_table();
		unsigned long		append( long, OI_timeout_fnp, void * );
		unsigned long		append( long, OI_callback *, OI_timeout_memfnp, void * );
		void			make_callbacks( struct timeval * );
		long			delta();
		OI_idle_timeout_cb	*element( const unsigned int i)		{ return(((OI_idle_timeout_cb **)array)[i]); }
		OI_bool			is_cb_enable()				{ return (cb_enable); }
		void			allow_cb_enable();
		void			disallow_cb_enable();
	struct	timeval			*soonest_timeout( struct timeval *, long );
	};

#endif /* OI_DISPATCH_H */
