/** pubscr.c
*
*   These are the screen management routines
*
**/
#include <exec/exec.h>
#include <stdio.h>
#include <functions.h>
#include "pubscr.h"
/*
*   The following two strings must exist.
*/
char SSHname[] = "screenshare.library";
char SSHid[]   = "screenshare 1.2 (Sep 89)\r\n";
UWORD SSHrevision = 2;
/*
*   ScreenList is the only static variable
*/
static struct List *ScreenList = NULL;

static struct ScreenNode *ScrListAlloc(), *ScrListFree(), *ScrListFind();
static struct Library *ScrSharBase;
                         
/**
*
*    NAME
*
*	GetPubScrList
*	=============
*
*    SYNOPSIS
*
*	struct List *ScreenList = GetPubScrList()
*	D0
*
*    FUNCTION
*
*	This function returns a pointer to the Exec-style
*	list of shared screen nodes.
*
*    INPUTS
*
*	None
*
*    RESULT
*
*	Pointer to the first node.
*
*    ADDITIONAL	CONSIDERATIONS
*
*	This routine is not normally used in applications.
*	The ScreenNode structure is described in pubscr.h.
*
*    BUGS
*
*	None known.
*
*    AUTHOR
*
*	W.G.J. Langeveld (WGL)
*
**/
struct List *GetPubScrList()
{
   return(ScreenList);
}

/**
*
*    NAME
*
*	PublicScreen
*	============
*
*    SYNOPSIS
*
*	int result = PublicScreen(Name, ScreenAddress)
*       D0                        A0    A1
*
*    FUNCTION
*
*	This function adds a named screen to the public screen list.
*
*    INPUTS
*
*	Name		Name to be assigned to the public screen.
*	ScreenAddress	Pointer to the Screen structure.
*
*    RESULT
*
*	0 on failure, 1 on success.
*	Note: the return code is a 16 bit int (a short).
*
*    ADDITIONAL	CONSIDERATIONS
*
*	This routine should be called by an application ("host") if it wants
*	to make its custom screen accessible to the outside world ("symbionts").
*	When an application calls this function, it promises in particular to 
*	NOT CLOSE this screen until it has ascertained that the screen is no
*	longer occupied by symbionts through calling PubScrLocked(), and until
*	it has reverted the status of the screen to private by calling 
*	PrivateScreen().
*
*	If a screen with the same name already exists, the screen pointer is
*	compared with that of the existing public screen. The function only
*	returns successfully in that case if the two pointers are identical.
*	In such a case, the symbiont open count is not affected.
*	Otherwise, the function returns 0 and performs no action.
*
*    BUGS
*
*	None known.
*
*    AUTHOR
*
*	W.G.J. Langeveld (WGL)
*
**/
int PublicScreen(name, address)
char *name;
struct Screen *address;
{
   struct ScreenNode *olditem = NULL;
   int res;
/*
*   Can't interrupt this
*/
   Forbid();
   res = 0;
/*
*   First try to find this one. If found, and the addess is the same, return okay
*/
   if (olditem = ScrListFind(name)) {
      if (olditem->sn_Address == address) res = 1;
   }
/*
*   Else allocate a new node and open ourselves so we don't go away
*/
   else {
      if (ScrListAlloc(name, address)) {
         ScrSharBase = OpenLibrary("screenshar.library", 0L);
         res = 1;
      }
   }
   Permit();
   return(res);
}

/**
*
*    NAME
*
*	PrivateScreen
*	=============
*
*    SYNOPSIS
*
*	int result = PrivateScreen(Name)
*       D0                         A0
*
*    FUNCTION
*
*	This function removes a named screen from the public screen list.
*
*    INPUTS
*
*	Name		Name of the public screen
*
*    RESULT
*
*	0 on failure, 1 on success.
*	Note: the return code is a 16 bit int (a short).
*
*    ADDITIONAL	CONSIDERATIONS
*
*	This routine should be called by an application that has made itself
*	the "host" to a named public screen through a call to PublicScreen().
*	Before calling PrivateScreen(), the application should verify that no
*	"symbionts" are currently present on the screen through a call to 
*	PubScrLocked().
*
*	If the Name is not found in the public screen list, the function
*	performs no operation and returns 0.
*	If the Name is found, the current symbiont open count is checked.
*	If the open count is non-zero, the function performs no operation
*	and returns 0.
*
*    BUGS
*
*	None known.
*
*    AUTHOR
*
*	W.G.J. Langeveld (WGL)
*
**/
int PrivateScreen(name)
char *name;
{
   struct ScreenNode *olditem = NULL;
   int res;
/*
*   Can't interrupt this
*/
   Forbid();
   res = 0;
/*
*   Try to find this name, if found, free node and close ourselves.
*/
   if (olditem = ScrListFind(name)) {
      if (olditem->sn_OpenCount == 0) {
         ScrListFree(olditem);
         if (ScrSharBase) CloseLibrary(ScrSharBase);
         res = 1;
      }
   }
   Permit();
   return(res);
}

/**
*
*    NAME
*
*	PubScrLocked
*	============
*
*    SYNOPSIS
*
*	int result = PubScrLocked(Name)
*       D0                       A0
*
*    FUNCTION
*
*	This function returns the number of symbionts still open on this
*	named screen.
*
*    INPUTS
*
*	Name		Name of the public screen
*
*    RESULT
*
*	Number of symbionts with windows still open on this screen.
*	Note: the return code is a 16 bit int (a short).
*
*    ADDITIONAL	CONSIDERATIONS
*
*	This function should be used by an application that has made itself
*	the "host" to a named public screen through a call to PublicScreen()
*	in order to determine whether it is safe to call PrivateScreen().
*
*	If a screen with the given Name is not found, the function returns 0.
*
*    BUGS
*
*	None known.
*
*    AUTHOR
*
*	W.G.J. Langeveld (WGL)
*
**/
int PubScrLocked(name)
char *name;
{
   struct ScreenNode *olditem = NULL;
   int res;
/*
*   Can't interrupt this
*/
   Forbid();
   res = 0;
   if (olditem = ScrListFind(name)) res = olditem->sn_OpenCount;
   Permit();

   return(res);
}

/**
*
*    NAME
*
*	LockPubScreen
*	=============
*
*    SYNOPSIS
*
*	struct Screen *ScreenPtr = LockPubScreen(Name)
*       D0                                       A0
*
*    FUNCTION
*
*	This function returns the address of a named public screen, and 
*	increments the screenshare.library's symbiont counter for this
*	screen.
*
*    INPUTS
*
*	Name		Name of the public screen
*
*    RESULT
*
*	Pointer to the public screen, if found. NULL otherwise.
*
*    ADDITIONAL	CONSIDERATIONS
*
*	This function should be used by the symbiont in order to find
*	the address of the Screen structure of the application that has declared
*	its screen public. The symbiont should use UnlockPubScreen() whenever
*	it has closed its window(s) on that screen, and not open any windows
*	on that screen until after a call to LockPubScreen().
*
*    BUGS
*
*	None known.
*
*    AUTHOR
*
*	W.G.J. Langeveld (WGL)
*
**/
struct Screen *LockPubScreen(name)
char *name;
{
   struct Screen *res;
   struct ScreenNode *olditem;
/*
*   Can't interrupt this
*/
   Forbid();
   res = 0L;
/*
*   Find the screen. If found, increment its counter. Return the address.
*/
   if (olditem = ScrListFind(name)) {
      olditem->sn_OpenCount++;
      res = olditem->sn_Address;
   }
   Permit();
   return(res);
}

/**
*
*    NAME
*
*	UnlockPubScreen
*	===============
*
*    SYNOPSIS
*
*	struct Screen *ScreenPtr = UnlockPubScreen(Name)
*       D0                                         A0
*
*    FUNCTION
*
*	This function decrements the symbiont counter of the named screen.
*
*    INPUTS
*
*	Name		Name of the public screen
*
*    RESULT
*
*	Guaranteed NULL.
*
*    ADDITIONAL	CONSIDERATIONS
*
*	This function should be used by the symbiont in order to decrement the
*	the symbiont counter of the named screen to signal it has closed its
*	window(s) on that screen. The symbiont should use LockPubScreen()
*	before opening any windows on that screen, and not call UnlockPubScreen()
*	until it has closed its window(s) on that screen.
*
*    BUGS
*
*	None known.
*
*    AUTHOR
*
*	W.G.J. Langeveld (WGL)
*
**/
struct Screen *UnlockPubScreen(name)
char *name;
{
   struct Screen *res;
   struct ScreenNode *olditem;
/*
*   Can't interrupt this
*/
   Forbid();
   res = 0L;
/*
*   First find the screen. If found, decrement its counter.
*/
   if (olditem = ScrListFind(name)) {
      if (olditem->sn_OpenCount != 0) olditem->sn_OpenCount--;
      res = 0L;
   }
   Permit();
   return(res);
}



/**
*
*   The following are local routines, not accessible from the outside world
*   =======================================================================
*
*   Routine to add another node to the Screen list.
*
**/
#define SNSIZE ((long) sizeof(struct ScreenNode))
#define SLSIZE ((long) sizeof(struct List))

static struct ScreenNode *ScrListAlloc(s, scr)
char *s;
struct Screen *scr;
{
   struct ScreenNode *sn;

/*
*   If no list exists, create one
*/
   if (ScreenList == NULL) {
      ScreenList = (struct List *) AllocMem(SLSIZE, MEMF_PUBLIC | MEMF_CLEAR);
      if (!ScreenList) return(NULL);
      NewList(ScreenList);
   }
/*
*   Create a node
*/
   sn = (struct ScreenNode *) AllocMem(SNSIZE, MEMF_PUBLIC | MEMF_CLEAR);
   if (sn) {
/*
*   Allocate room for the name
*/
      sn->sn_Node.ln_Name = (char *)
         AllocMem((long) (strlen(s) + 1), MEMF_PUBLIC | MEMF_CLEAR);
      if (sn->sn_Node.ln_Name) {
/*
*   ...and copy the name
*/
         strcpy(sn->sn_Node.ln_Name, s);
/*
*   Copy the address and zero the open count.
*/
         sn->sn_Address = scr;
         sn->sn_OpenCount = 0;
/*
*   Add this one to the list
*/
         AddTail(ScreenList, sn);
      }
   }
   return(sn);
}

/**
*
*   Free a ScreenNode node
*
**/
static struct ScreenNode *ScrListFree(item)
struct ScreenNode *item;
{
   if (item == 0L)       return(0L);
   if (ScreenList == 0L) return(0L);
/*
*   Isolate this item from the list.
*/
   Remove(item);
/*
*   Free the string
*/
   if (item->sn_Node.ln_Name) 
      FreeMem(item->sn_Node.ln_Name, (long) (strlen(item->sn_Node.ln_Name) + 1));
/*
*   Free the item
*/
   FreeMem(item, SNSIZE);
/*
*   If this is the last item, free the list.
*/
   if (ScreenList->lh_TailPred == ScreenList) {
      FreeMem(ScreenList, SLSIZE);
      ScreenList = NULL;
   }

   return(0L);
}

/**
*
*   Find a screen with a certain name
*
**/
static struct ScreenNode *ScrListFind(name)
char *name;
{
/*
*   If the name is null or there is no header, return
*/
   if (name == 0L)       return(0L);
   if (ScreenList == 0L) return(0L);
/*
*   Find it
*/
   return((struct ScreenNode *) FindName(ScreenList, name));
}
