/********************************************************************
 *																	*
 *	Drag and drop protocol routines									*
 *	for MiNT and MultiTOS desktops only								*
 *																	*
 *	BUGS/CAVEATS:													*
 *	This code is not re-entrant (it uses a static variable for the	*
 *	pipe name and for saving the SIGPIPE signal handler)			*
 *																	*
 *	While doing the drag and drop, the SIGPIPE signal (write on an	*
 *	empty pipe) is ignored											*
 *																	*
 *	Copyright (C) 1992, Atari Corporation							*
 *																	*
 *	Since these are Atari's routines, these will not be modified.	*
 *	WinLIB PRO's Drag-and-drop protocol will be used in DDWINLIB.C	*
 *																	*
 ********************************************************************/

#include <tos.h>
#include <string.h>
#include "winlib.h"

#ifndef Word
#define Word short
#define Long long
#endif

#ifndef EACCDN
#define EACCDN -36
#endif

#ifndef SIGPIPE
#define SIGPIPE 13
#endif

#ifndef SIG_IGN
#define SIG_IGN 1L
#endif

#ifndef WF_OWNER
#define WF_OWNER 20
#endif

#ifndef FA_HIDDEN
#define FA_HIDDEN 0x2
#endif

extern int gl_apid;

static char pipename[] = "U:\\PIPE\\DRAGDROP.AA";
static long oldpipesig;

#ifdef DEBUGGING
#define debug_alert(x, y) form_alert(x, y)
#else
#define debug_alert(x, y)
#endif

/* Code for originator */

/*
 *	Create a pipe for doing the drag-and-drop, and send an AES
 *	message to the recipient application telling it about the drag-
 *	and-drop operation.
 *
 *	Input Parameters:
 *	apid:		AES id of the window owner
 *	winid:		target window (0 for background)
 *	msx, msy:	mouse X and Y position (or -1, -1 if a fake drag-and-
 *				drop)
 * 	kstate:		shift key state at time of event
 *
 *	Output Parameters:
 * 	exts:	A 32 byte buffer into which the receipient's 8 favorite
 *			extensions will be copied.
 *
 *	Returns:
 *	A positive file descriptor (of the opened drag-and-drop pipe) on
 *	success.
 *	-1 if the receipient doesn't respond or returns DD_NAK
 *	-2 if appl_write fails
 */

GLOBAL int ddcreate(int apid, int winid, int msx, int msy, int kstate, char exts[])
{
	int fd, i;
	int msg[8];
	long fd_mask;
	char c;

	pipename[17] = pipename[18] = 'A';
	fd = -1;
	do {
		pipename[18]++;
		if (pipename[18] > 'Z') {
			pipename[17]++;
			if (pipename[17] > 'Z')
				break;
		}
/* FA_HIDDEN means "get EOF if nobody has pipe open for reading" */
		fd = (int)(Fcreate(pipename, FA_HIDDEN));
	} while (fd == EACCDN);

	if (fd < 0) {
		debug_alert(1, "[1][Fcreate error][OK]");
		return fd;
	}

/* Construct and send the AES message */
	msg[0] = AP_DRAGDROP;
	msg[1] = gl_apid;
	msg[2] = 0;
	msg[3] = winid;
	msg[4] = msx;
	msg[5] = msy;
	msg[6] = kstate;
	msg[7] = (pipename[17] << 8) | pipename[18];
	i = appl_write(apid, 16, msg);
	if (i == 0) {
		debug_alert(1, "[1][appl_write error][OK]");
		Fclose(fd);
		return -2;
	}

/* Now wait for a response */
	fd_mask = 1L << fd;
	i = Fselect(DD_TIMEOUT, &fd_mask, 0L, 0L);
	if (!i || !fd_mask) {	/* Timeout happened */
		debug_alert(1, "[1][ddcreate: Fselect timeout][OK]");
abort_dd:
		Fclose(fd);
		return -1;
	}

/* Read the 1 byte response */
	i = (int)(Fread(fd, 1L, &c));
	if (i != 1 || c != DD_OK) {
		if (i != 1)
			debug_alert(1, "[1][ddcreate: read error][OK]");
		else
			debug_alert(1, "[1][ddcreate: DD_NAK][OK]");
		goto abort_dd;
	}

/* Now read the "preferred extensions" */
	i = (int)(Fread(fd, DD_EXTSIZE, exts));
	if (i != DD_EXTSIZE) {
		debug_alert(1, "[1][Error reading extensions][OK]");
		goto abort_dd;
	}

	oldpipesig = Psignal(SIGPIPE, SIG_IGN);
	return fd;
}


/*
 *	See if the recipient is willing to accept a certain type of data
 *	(as indicated by "ext")
 *
 *	Input parameters:
 *	fd		file descriptor returned from ddcreate()
 *	ext		pointer to the 4 byte file type
 *	name	pointer to the name of the data
 *	size	number of bytes of data that will be sent
 *
 *	Output parameters: none
 *
 *	Returns:
 *	DD_OK	if the receiver will accept the data
 *	DD_EXT	if the receiver doesn't like the data type
 *	DD_LEN	if the receiver doesn't like the data size
 *	DD_NAK	if the receiver aborts
 */

GLOBAL int ddstry(int fd, char *ext, char *name, long size)
{
	Word hdrlen, i;
	char c;

/* 4 bytes for extension, 4 bytes for size, 1 byte for trailing 0 */
	hdrlen = 9 + strlen(name);
	i = Fwrite(fd, 2L, &hdrlen);

/* Now send the header */
	if (i != 2) return DD_NAK;
	i = Fwrite(fd, 4L, ext);
	i += Fwrite(fd, 4L, &size);
	i += Fwrite(fd, (long)strlen(name)+1, name);
	if (i != hdrlen) return DD_NAK;

/* Wait for a reply */
	i = Fread(fd, 1L, &c);
	if (i != 1) return DD_NAK;
	return c;
}

/*
 * Close a drag-and-drop operation
 */
GLOBAL void ddclose(int fd)
{
	(void)Psignal(SIGPIPE, oldpipesig);
	(void)Fclose(fd);
}

/*
 *	open a drag-and-drop pipe
 *
 *	Input Parameters:
 *	ddnam:		the pipe's name (from the last word of
 *				the AES message)
 *	preferext:	a list of DD_NUMEXTS 4 byte extensions we understand
 *				these should be listed in order of preference
 *				if we like fewer than DD_NUMEXTS extensions, the
 *				list should be padded with 0s
 *
 *	Output Parameters: none
 *
 *	Returns:
 *	A (positive) file handle for the drag & drop pipe, on success
 *	-1 if the drag & drop is aborted
 *	A negative error number if an error occurs while opening the
 *	pipe.
 */
GLOBAL int ddopen(int ddnam, char *preferext)
{
	int fd;
	char outbuf[DD_EXTSIZE+1];

	pipename[18] = ddnam & 0x00ff;
	pipename[17] = (ddnam & 0xff00) >> 8;

	fd = (int)(Fopen(pipename, 2));
	if (fd < 0) return fd;

	outbuf[0] = DD_OK;
	strncpy(outbuf+1, preferext, DD_EXTSIZE);

	oldpipesig = Psignal(SIGPIPE, SIG_IGN);

	if (Fwrite(fd, (long)DD_EXTSIZE+1, outbuf) != DD_EXTSIZE+1) {
		ddclose(fd);
		return -1;
	}

	return fd;
}

/*
 *	ddrtry: Get the next header from the drag-and-drop originator
 *
 *	Input Parameters:
 *	fd:		the pipe handle returned from ddopen()
 *
 *	Output Parameters:
 *	name:		a pointer to the name of the drag & drop item
 *				(note: this area must be at least DD_NAMEMAX bytes long)
 *	whichext:	a pointer to the 4 byte extension
 *	size:		a pointer to the size of the data
 *
 *	Returns:
 *	0 on success
 *	-1 if the originator aborts the transfer
 *
 *	Note: it is the caller's responsibility to actually send the
 *	DD_OK byte to start the transfer, or to	send a DD_NAK, DD_EXT, or
 *	DD_LEN reply with ddreply().
 */

GLOBAL int ddrtry(int fd, char *name, char *whichext, long *size)
{
	Word hdrlen;
	int i;
	char buf[80];

	i = (int)(Fread(fd, 2L, &hdrlen));
	if (i != 2) {
		return -1;
	}
	if (hdrlen < 9) {	/* This should never happen */
		return -1;
	}
	i = (int)(Fread(fd, 4L, whichext));
	if (i != 4) {
		return -1;
	}
	whichext[4] = 0;
	i = (int)(Fread(fd, 4L, size));
	if (i != 4) {
		return -1;
	}
	hdrlen -= 8;
	if (hdrlen > DD_NAMEMAX)
		i = DD_NAMEMAX;
	else
		i = hdrlen;
	if (Fread(fd, (long)i, name) != i) {
		return -1;
	}
	hdrlen -= i;

/* Skip any extra header */
	while (hdrlen > 80) {
		Fread(fd, 80L, buf);
		hdrlen -= 80;
	}
	if (hdrlen > 0)
		Fread(fd, (long)hdrlen, buf);

	return 0;
}

/*
 *	send a 1 byte reply to the drag & drop originator
 *
 *	Input Parameters:
 *	fd:		file handle returned from ddopen()
 *	ack:	byte to send (e.g. DD_OK)
 *
 *	Output Parameters: none
 *
 *	Returns: 0 on success, -1 on failure
 *	in the latter case the file descriptor is closed
 */

GLOBAL int ddreply(int fd, int ack)
{
	char c = ack;

	if (Fwrite(fd, 1L, &c) != 1L) {
		Fclose(fd);
	}
	return 0;
}