/*
 * ButtonBox - a program launcher
 *
 * Copyright (C) 2000 Paul Collins
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "assertions.h"
#include "dispatcher.h"



static WindowInformation* get_free_slot (Dispatcher*);



Dispatcher* dispatcher_new (Display* display)
{
	Dispatcher* new_disp = NULL;
	
	I(display != NULL);
	new_disp = malloc (sizeof (Dispatcher));
	I(new_disp != NULL);
	new_disp->display = display;
	new_disp->num_windows = 0;
	new_disp->num_windows_allocated = 0;
	new_disp->windows = NULL;
	new_disp->default_procedure = NULL;
	new_disp->loop_running = False;
	new_disp->reap_zombies = False;
	return new_disp;
};




void
dispatcher_register (Dispatcher* dispatcher, Window w, WindowProcedure p, void* d)
{
	WindowInformation* wi = NULL;
	
	I (dispatcher != NULL);
	wi = get_free_slot (dispatcher);
	I (wi != NULL);
	wi->wi_window = w;
	wi->wi_procedure = p;
	wi->wi_data = d;
	dispatcher->num_windows += 1;
}




void
dispatcher_reap_zombies (Dispatcher* dispatcher, Bool reap_p)
{
	dispatcher->reap_zombies = reap_p;
}



void dispatcher_run_loop (Dispatcher* dispatcher)
{
	XEvent e;
	pid_t child_pid;

	dispatcher->loop_running = True;
	if (dispatcher->reap_zombies) {
		while ((child_pid = waitpid (WAIT_ANY, NULL, WNOHANG)) > 0) {
			L ("dispatcher_run_loop: reaped child %d\n", child_pid);
		}
	}
	while (dispatcher->loop_running) {
		int i = 0;
		Bool found = False;
		XNextEvent (dispatcher->display, &e);
		for (; i < dispatcher->num_windows; ++i) {
			if (dispatcher->windows[i].wi_window == e.xany.window) {
				Bool b = False;
				L("Found window 0x%08x for event with serial = %d\n", e.xany.window, e.xany.serial);
				found = True;
				b = dispatcher->windows[i].wi_procedure (dispatcher,
									 &e,
									 dispatcher->windows[i].wi_data);
				L("Called WindowProcedure for window 0x%08x; returned %s\n",
				  e.xany.window, b ? "True" : "False");
				break;
			}
		}
		if (!found) {
			L("No WindowProcedure found for window 0x%08x\n", e.xany.window);
			if (dispatcher->default_procedure) {
				Bool b = False;
				L("calling default_procedure for window 0x%08x\n", e.xany.window);
				b = dispatcher->default_procedure (dispatcher, &e, NULL);
				L("Called default_procedure for window 0x%08x; returned %s\n",
				  e.xany.window, b ? "True" : "False");
			}
		}
		if (dispatcher->reap_zombies) {
			while ((child_pid = waitpid (WAIT_ANY, NULL, WNOHANG)) > 0) {
				L ("dispatcher_run_loop: reaped child %d\n", child_pid);
			}
		}
	}
		
}




Display*
dispatcher_get_display (Dispatcher* dispatcher)
{
	return dispatcher->display;
}




void dispatcher_request_loop_exit (Dispatcher* dispatcher)
{
	L("loop_running for dispatcher 0x%08x set to False\n", (unsigned int)dispatcher);
	dispatcher->loop_running = False;
}




/* NOTE: We do not adjust num_windows; we leave that to the caller, in
   case they do not use the WindowInformation for some reason. */
static WindowInformation*
get_free_slot (Dispatcher* dispatcher)
{
	if (dispatcher->num_windows_allocated == 0) {
		dispatcher->windows = malloc (4 * sizeof (WindowInformation));
		I (dispatcher->windows != NULL);
		dispatcher->num_windows_allocated = 4;
	}
	if (dispatcher->num_windows == dispatcher->num_windows_allocated) {
		dispatcher->windows = realloc (dispatcher->windows,
					       (dispatcher->num_windows_allocated
						* 2
						* sizeof (WindowInformation)));
		dispatcher->num_windows_allocated = dispatcher->num_windows_allocated * 2;
	}
	I (dispatcher->num_windows < dispatcher->num_windows_allocated);
	return &(dispatcher->windows[dispatcher->num_windows]);
}

