/*
 * Maketool - GTK-based front end for gmake
 * Copyright (c) 1999-2003 Greg Banks
 * 
 * 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 "mqueue.h"
#include <gdk/gdkx.h>

CVSID("$Id: mqueue.c,v 1.4 2003/05/24 05:48:21 gnb Exp $");

typedef struct
{
    unsigned long msgid;
    unsigned long from;    	/* XID of window to reply to */
    unsigned long length;
} MessageHeader;

typedef struct
{
    unsigned long msgid;
    long code;
} ReplyHeader;


static GdkAtom message_atom;
static GdkAtom reply_atom;
static GdkWindow *self_window;
static GList *receiveq;
static GList *replyq;
static unsigned long last_msgid = 0;

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

void
mq_init(GdkWindow *self)
{
    message_atom = gdk_atom_intern("_MAKETOOL_MESSAGE", FALSE);
    reply_atom = gdk_atom_intern("_MAKETOOL_REPLY", FALSE);
    self_window = self;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

static GdkWindow *
lookup_window(unsigned long xid)
{
    GdkWindow *win;
    
    if ((win = gdk_window_lookup(xid)) != 0)
    	gdk_window_ref(win);
    else
    	win = gdk_window_foreign_new(xid);
	
    return win;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/


void
mq_send(GdkWindow *to, Message *msg)
{
    estring bits;
    MessageHeader hdr;
    
    msg->msgid = hdr.msgid = ++last_msgid;
    hdr.from = GDK_WINDOW_XWINDOW(self_window);
    hdr.length = msg->body.length;
	
    estring_init(&bits);
    estring_append_chars(&bits, (const char *)&hdr, sizeof(hdr));
    if (msg->body.length > 0)
	estring_append_chars(&bits, msg->body.data, msg->body.length);
    
    gdk_property_change(to,
    	    	    	message_atom,
			message_atom,
			/*format*/8,
			GDK_PROP_MODE_APPEND,
			(guchar *)bits.data, bits.length);
			
    estring_free(&bits);
}


Message *
mq_receive(void)
{
    Message *msg = 0;
    
    if (receiveq != 0)
    {
	msg = (Message *)receiveq->data;
	receiveq = g_list_remove_link(receiveq, receiveq);
    }
        
    return msg;    
}


gboolean
mq_message_event(GdkEvent *event)
{
    MessageHeader hdr;
    Message *msg;
    GdkAtom type = GDK_NONE;
    gint format = 0;
    gint length = 0;
    guchar *data = 0;
    int offset;

    /*
     * Check if this is the kind of event we want.
     */
    if (event->type != GDK_PROPERTY_NOTIFY ||
    	event->property.atom != message_atom ||
	event->property.state != GDK_PROPERTY_NEW_VALUE)
    	return FALSE;
	
    if (!gdk_property_get(
    	    self_window,
	    message_atom,   	    /* property */
	    message_atom,    	    /* type */
	    0,	    	    	    /* offset */
	    1024,   	    	    /* length */
	    TRUE,   	    	    /* pdelete */
	    &type,
	    &format,
	    &length,
	    &data))
	return TRUE;
	
    if (type != message_atom ||
    	format != 8 ||
	data == 0)
    {
    	if (data != 0)
	    g_free(data);
    	return TRUE;
    }

    /* Note that we always get back a handy extra trailing nul byte from XLib */
    length--;
    
    /* Parse the property data into a sequence of header/body pairs. */
    for (offset = 0 ; 
    	 offset+(int)sizeof(hdr) < length ; 
	 )
    {
    	/* deal with alignment issue by copying */
    	memcpy(&hdr, data+offset, sizeof(hdr));
	offset += sizeof(hdr);
	
	msg = mq_msg_new_d((const char *)data+offset, hdr.length);
    	msg->msgid = hdr.msgid;
	msg->from = lookup_window(hdr.from);
		
	receiveq = g_list_append(receiveq, msg);
	
	offset += hdr.length;
    }
    
    g_free(data);
    
    return TRUE;
}



/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

void
mq_reply(const Message *msg, long code)
{
    ReplyHeader reply;
    
    reply.msgid = msg->msgid;
    reply.code = code;
	
    gdk_property_change(msg->from,
    	    	    	reply_atom,
			reply_atom,
			/*format*/8,
			GDK_PROP_MODE_APPEND,
			(guchar *)&reply, sizeof(reply));
}


gboolean
mq_get_reply(const Message *msg, long *codep)
{
    GList *list;

    for (list = replyq ; list != 0 ; list = list->next)
    {
	ReplyHeader *reply = (ReplyHeader *)list->data;
	
	if (reply->msgid == msg->msgid)
	{
	    if (codep != 0)
	    	*codep = reply->code;
	    replyq = g_list_remove_link(replyq, list);
	    g_free(reply);
	    return TRUE;
	}
    }
    
    return FALSE;
}


gboolean
mq_reply_event(GdkEvent *event)
{
    GdkAtom type = GDK_NONE;
    gint format = 0;
    gint length = 0;
    guchar *data = 0;
    int offset;
    ReplyHeader reply;
    
    if (event->type != GDK_PROPERTY_NOTIFY ||
	event->property.atom != reply_atom ||
	event->property.state != GDK_PROPERTY_NEW_VALUE)
    	return FALSE;

    if (!gdk_property_get(
    	    self_window,
	    reply_atom,   	    /* property */
	    reply_atom,    	    /* type */
	    0,	    	    	    /* offset */
	    1024,   	    	    /* length */
	    TRUE,   	    	    /* pdelete */
	    &type,
	    &format,
	    &length,
	    &data))
	return TRUE;

    if (type != reply_atom ||
    	format != 8 ||
	data == 0)
    {
    	if (data != 0)
	    g_free(data);
    	return TRUE;
    }
    
    /* Parse the property data into a sequence of header/body pairs. */
    for (offset = 0 ; 
    	 offset < length ; 
	 offset += sizeof(reply))
    {
	replyq = g_list_append(replyq, g_memdup(data+offset, sizeof(reply)));
    }
    
#if DEBUG
    {
    	GList *list;
	
	fprintf(stderr, "mq_reply_event: replyq =");
	for (list = replyq ; list != 0 ; list = list->next)
	{
	    ReplyHeader *r = (ReplyHeader *)list->data;

	    fprintf(stderr, " { msgid=%ld code=%ld }", r->msgid, r->code);
	}
	fprintf(stderr, "\n");
    }   
#endif

    return TRUE;
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/

Message *
mq_msg_new_d(const char *data, int len)
{
    Message *msg;
    
    msg = g_new(Message, 1);
    memset(msg, 0, sizeof(*msg));
    
    if (len != 0)
    	estring_append_chars(&msg->body, data, len);
    
    return msg;
}

Message *
mq_msg_new_e(const estring *e)
{
    return (e == 0 ? mq_msg_new_d(0, 0) : mq_msg_new_d(e->data, e->length));
}

void
mq_msg_delete(Message *msg)
{
    if (msg->from != 0)
    	gdk_window_unref(msg->from);
    estring_free(&msg->body);
    g_free(msg);
}

/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/*END*/


syntax highlighted by Code2HTML, v. 0.9.1