/*
    GSK - a library to write servers
    Copyright (C) 1999-2000 Dave Benson

    This library is free software; you can redistribute it and/or
    modify it 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.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA

    Contact:
        daveb@ffem.org <Dave Benson>
*/

#ifndef __GSK_MAIN_LOOP_H_
#define __GSK_MAIN_LOOP_H_

/*
 * Abstract the process of waiting for the usual things.
 */

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include "gskgtk.h"

GtkType gsk_main_loop_get_type();
#define GSK_TYPE_MAIN_LOOP		(gsk_main_loop_get_type ())
#define GSK_MAIN_LOOP(obj)              (GTK_CHECK_CAST ((obj), GSK_TYPE_MAIN_LOOP, GskMainLoop))
#define GSK_MAIN_LOOP_CLASS(klass)      (GTK_CHECK_CLASS_CAST ((klass), GSK_TYPE_MAIN_LOOP, GskMainLoopClass))
#define GSK_MAIN_LOOP_GET_CLASS(obj)    (GSK_MAIN_LOOP_CLASS(GTK_OBJECT(obj)->klass))
#define GSK_IS_MAIN_LOOP(obj)           (GTK_CHECK_TYPE ((obj), GSK_TYPE_MAIN_LOOP))
#define GSK_IS_MAIN_LOOP_CLASS(klass)   (GTK_CHECK_CLASS_TYPE ((klass), GSK_TYPE_MAIN_LOOP))

typedef struct _GskMainLoop GskMainLoop;
typedef struct _GskMainLoopClass GskMainLoopClass;

#include "gskactor.h"

/* Undefined type -- to be managed by the main loop implementation. */
typedef struct _GskSource GskSource;

/* GskMainLoopWaitInfo:  Information about a reaped process.  */
typedef struct _GskMainLoopWaitInfo GskMainLoopWaitInfo;
struct _GskMainLoopWaitInfo
{
  int               pid;
  gboolean          exited;         /* exit(2) or killed by signal? */
  union {
    int             signal;         /* !exited */
    int             exit_status;    /*  exited */
  } d;
  gboolean          dumped_core;
};

/* --- Callback function typedefs. --- */

/* callback for child-process termination */
typedef void     (*GskMainLoopWaitPidFunc)(GskMainLoopWaitInfo  *info,
                                           gpointer              user_data);

/* callback for an "idle" function -- it runs after all events
 * have been processed.
 */
typedef gboolean (*GskMainLoopIdleFunc)   (gpointer              user_data);

/* callback for receiving a signal */
typedef gboolean (*GskMainLoopSignalFunc) (int                   sig_no,
                                           gpointer              user_data);

/* callback for a period */
typedef gboolean (*GskMainLoopTimeoutFunc)(gpointer              user_data);

/* callback for input or output on a file descriptor */
typedef gboolean (*GskMainLoopIOFunc)     (int                   fd,
                                           GIOCondition          condition,
				           gpointer              user_data);

/* callback for running code in another thread;
 * the run function is called in another thread,
 * and the result function gets its return value in the current
 * thread.
 */
typedef gpointer (*GskMainLoopRunFunc)    (gpointer              run_data);
typedef void     (*GskMainLoopResultFunc) (gpointer              result_data,
                                           gpointer              run_data);
                                  

/* --- structures --- */
struct _GskMainLoopClass
{
  GtkObjectClass object_class;
  gboolean   (*setup)         (GskMainLoop           *main_loop);
  guint      (*count)         (GskMainLoop           *main_loop);
  guint      (*run)           (GskMainLoop           *main_loop,
                               gint                   timeout,
			       guint                 *time_blocked_out);
  GskSource *(*add_idle)      (GskMainLoop           *main_loop,
		               GskMainLoopIdleFunc    source_func,
		               gpointer               user_data,
			       GDestroyNotify         destroy);
  GskSource *(*add_signal)    (GskMainLoop           *main_loop,
                               int                    signal_number,
		               GskMainLoopSignalFunc  signal_func,
		               gpointer               user_data,
			       GDestroyNotify         destroy);
  GskSource *(*add_waitpid)   (GskMainLoop           *main_loop,
                               int                    process_id,
		               GskMainLoopWaitPidFunc signal_func,
		               gpointer               user_data,
			       GDestroyNotify         destroy);
  GskSource *(*add_timeout)   (GskMainLoop           *main_loop,
                               int                    millis_expire,
                               int                    millis_period,
		               GskMainLoopTimeoutFunc source_func,
		               gpointer               user_data,
			       GDestroyNotify         destroy);
  void       (*adjust_timeout)(GskMainLoop           *main_loop,
			       GskSource             *source,
                               int                    millis_expire,
                               int                    millis_period);
  GskSource *(*add_io)        (GskMainLoop           *main_loop,
                               int                    fd,
                               guint                  events,
		               GskMainLoopIOFunc      source_func,
		               gpointer               user_data,
			       GDestroyNotify         destroy);
  void       (*adjust_io)     (GskMainLoop           *main_loop,
                               GskSource             *source,
                               guint                  events);
  void       (*remove)        (GskMainLoop           *main_loop,
                               GskSource             *source);

  /*< signals >*/
  void       (*add_actor)     (GskMainLoop           *main_loop,
			       GskActor              *actor);
  void       (*quit)          (GskMainLoop           *main_loop);
};

struct _GskMainLoop
{
  GtkObject object;

  /* the time, the last time the main loop was polling. */
  GTimeVal current_time;

  /* Flags. */
  unsigned  is_running : 1;
  unsigned  quit : 1;

  /* max threads in the thread pool */
  int max_workers;

  /* private: thread pool */
  gpointer thread_pool;
};

/* --- prototypes --- */

/* Create a new main loop.
 * (Note that this returns the best derived type -- not
 *  an instance of GskMainLoop, which is abstract.)
 */

/* Create a main loop with selected options. */
typedef enum
{
  GSK_MAIN_LOOP_NEEDS_THREADS = (1 << 0)
} GskMainLoopCreateFlags;

GskMainLoop     *gsk_main_loop_new       (GskMainLoopCreateFlags create_flags);

guint            gsk_main_loop_count_sources(GskMainLoop       *main_loop);

long             gsk_main_loop_current_time (GskMainLoop       *main_loop);

/* TIMEOUT is the maximum number of milliseconds to wait,
 * or pass in -1 to block forever.
 */
guint            gsk_main_loop_run          (GskMainLoop       *main_loop,
                                             gint               timeout,
					     guint             *t_waited_out);
GskSource       *gsk_main_loop_add_idle     (GskMainLoop       *main_loop,
					     GskMainLoopIdleFunc source_func,
					     gpointer           user_data,
					     GDestroyNotify     destroy);
GskSource       *gsk_main_loop_add_signal   (GskMainLoop       *main_loop,
					     int                signal_number,
					     GskMainLoopSignalFunc signal_func,
					     gpointer           user_data,
					     GDestroyNotify     destroy);
GskSource       *gsk_main_loop_add_waitpid  (GskMainLoop       *main_loop,
					     int                process_id,
					   GskMainLoopWaitPidFunc waitpid_func,
					     gpointer           user_data,
					     GDestroyNotify     destroy);
GskSource       *gsk_main_loop_add_io       (GskMainLoop       *main_loop,
					     int                fd,
					     guint              events,
					     GskMainLoopIOFunc  io_func,
					     gpointer           user_data,
					     GDestroyNotify     destroy);
void             gsk_main_loop_adjust_io    (GskMainLoop       *main_loop,
					     GskSource         *source,
					     guint              events);
GskSource       *gsk_main_loop_add_timer    (GskMainLoop       *loop,
                                             GskMainLoopTimeoutFunc timer_func,
                                             gpointer           timer_data,
                                             GDestroyNotify     timer_destroy,
                                             int                millis_expire,
                                             int                milli_period);
GskSource       *gsk_main_loop_add_timer_absolute
                                            (GskMainLoop       *loop,
                                             GskMainLoopTimeoutFunc timer_func,
                                             gpointer           timer_data,
                                             GDestroyNotify     timer_destroy,
                                             int                unixtime,
                                             int                unixtime_micro);
void             gsk_main_loop_adjust_timer (GskMainLoop       *loop,
                                             GskSource         *timer_source,
                                             int                millis_expire,
                                             int                milli_period);
void             gsk_main_loop_remove       (GskMainLoop       *main_loop,
					     GskSource         *source);
void             gsk_main_loop_quit         (GskMainLoop       *main_loop);

/*
 * In a background thread, run
 *      thread_result = (*run_func)(run_data);
 *
 * When it is done run in the original thread:
 *      (*result_func)(thread_result, result_data);
 */
gboolean         gsk_main_loop_run_background(GskMainLoop      *loop,
                                              GskMainLoopRunFunc run_func,
                                              gpointer          run_data,
                                              GskMainLoopResultFunc result_func,
                                              gpointer          result_data);

/* for debugging mostly -- a way to just block with the 
 * same interface as ``run_background''. */
gboolean         gsk_main_loop_run_foreground(GskMainLoop      *loop,
                                              GskMainLoopRunFunc run_func,
                                              gpointer          run_data,
                                              GskMainLoopResultFunc result_func,
                                              gpointer          result_data);


gboolean         gsk_main_loop_should_continue
                                            (GskMainLoop       *main_loop);

/*
 * Compatibility notes:
 *    replace:
 *         int          gsk_current_time();
 * with    int          gsk_main_loop_current_time(main_loop);
 *
 * gsk_main_loop_add_idle() takes another argument (NULL should be added)
 */    

/* Additional macros we want to get rid of someday. */
#ifndef GSK_DISABLE_COMPAT
#define gsk_main_loop_run_once(main_loop)		\
        ((gsk_main_loop_run ((main_loop), -1, NULL)),	\
	 !((main_loop)->quit))
#define gsk_main_loop_is_empty(main_loop)		\
	(gsk_main_loop_count_sources (main_loop) == 0)
#define gsk_main_loop_add_wait_handler(main_loop, process_id, func, udata) \
	(gsk_main_loop_add_waitpid (main_loop, process_id, func, udata, NULL))
#define gsk_main_loop_remove_timer(main_loop, timer_id)	\
	gsk_main_loop_remove (main_loop, timer_id)
#define gsk_main_loop_add_actor(main_loop, actor)	\
        _gsk_actor_addto_mainloop(GSK_ACTOR (actor), GSK_MAIN_LOOP (main_loop))
#define gsk_main_loop_remove_actor(main_loop, actor)	\
	gsk_actor_set_main_loop (actor, NULL)
#define GskRunFunc GskMainLoopRunFunc
#define GskResultFunc GskMainLoopResultFunc
#endif


/* --- protected:  for use in derived classes only --- */
void             gsk_main_loop_update_current_time (GskMainLoop *main_loop);

/* use -1 to waitpid for any process; this function will never block;
 * but will return FALSE if there was no process to wait for.
 */
gboolean         gsk_main_loop_do_waitpid          (int pid,
						    GskMainLoopWaitInfo *info);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif
