
/*
 * PUBMAN.C
 * (c) 1992 J.Harper
 *
 * Public Screen Manager.
 *
 * usage:
 *	pubman {<command>}
 */

#include <exec/ports.h>
#include <exec/memory.h>
#include <intuition/screens.h>
#include <graphics/displayinfo.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <stdio.h>
#include <string.h>

__stkargs VOID	 LockAddr(LONG *);	/* These 2 are DICE specific */
__stkargs VOID	 UnlockAddr(LONG *);

	  VOID	 usage(VOID);
	  STRPTR savestring(STRPTR);
	  VOID	 NewList(struct List *);
	  VOID	 exit(LONG);

typedef struct
{
    struct MsgPort psm_Port;
    struct List	   psm_Screens;
    ULONG	   psm_DisplayID;
    UWORD	   psm_Width;
    UWORD	   psm_Height;
    LONG	   psm_Depth;
    LONG	   psm_Lock[2];
} PSM;

typedef struct
{
    struct Node	   sn_Node;
    struct Screen *sn_Screen;
} SNODE;

const UBYTE NoMem[]   = "error: no memory";
const UBYTE PSMPort[] = "PSMPort";

LONG
main(LONG ac, STRPTR *av)
{
    PSM *me;
    LONG rc = 0;
    if(ac == 1)
    {
	usage();
	exit(5);
    }
    Forbid();
    if(!(me = (PSM *)FindPort(PSMPort)))
    {
	struct Screen wbcopy;
	if(!(me = AllocMem(sizeof(PSM), MEMF_CLEAR | MEMF_PUBLIC)))
	{
	    Permit();
	    puts(NoMem);
	    exit(10);
	}
	if(!(me->psm_Port.mp_Node.ln_Name = savestring(PSMPort)))
	{
	    FreeMem(me, sizeof(PSM));
	    Permit();
	    puts(NoMem);
	    exit(10);
	}
	me->psm_Port.mp_Node.ln_Pri  = 1;
	me->psm_Port.mp_Node.ln_Type = NT_MSGPORT;
	me->psm_Port.mp_Flags	     = PA_IGNORE;
	NewList(&me->psm_Screens);
	me->psm_DisplayID	     = HIRES_KEY;
	me->psm_Depth		     = 2;
	if(GetScreenData(&wbcopy, sizeof(struct Screen), WBENCHSCREEN, NULL))
	{
	    me->psm_Width = wbcopy.Width;
	    me->psm_Height = wbcopy.Height;
	}
	else
	{
	    me->psm_Width = 640;
	    me->psm_Height = 256;
	}
	AddPort(&me->psm_Port);
    }
    Permit();
    LockAddr(me->psm_Lock);

    ac--;
    av++;
    while(ac && !rc)
    {
	STRPTR cmd = *av;
	switch(((*cmd++) << 8) | (*cmd++))
	{
	    case 'li':
		struct PubScreenNode *psn;
		psn = (struct PubScreenNode *)(LockPubScreenList()->lh_Head);
		while(psn->psn_Node.ln_Succ)
		{
		    printf("%s (%ld visitors)\n", psn->psn_Node.ln_Name, psn->psn_VisitorCount);
		    psn = (struct PubScreenNode *)psn->psn_Node.ln_Succ;
		}
		UnlockPubScreenList();
		break;
	    case 'dp':
		if(*cmd)
		    SetDefaultPubScreen(cmd);
		else
		{
		    UBYTE buff[MAXPUBSCREENNAME];
		    GetDefaultPubScreen(buff);
		    puts(buff);
		}
		break;
	    case 'md':
		UWORD modes = SetPubScreenModes(0);
		switch(*cmd++)
		{
		    case 's':
			if(*cmd == '+')
			    modes |= SHANGHAI;
			else
			    modes &= ~SHANGHAI;
			break;
		    case 'p':
			if(*cmd == '+')
			    modes |= POPPUBSCREEN;
			else
			    modes &= ~POPPUBSCREEN;
			break;
		    default:
			printf("%lx\n", modes);
			break;
		}
		SetPubScreenModes(modes);
		break;
	    case 'di':
		if(*cmd)
		{
		    APTR dummy;
		    me->psm_DisplayID = strtol(cmd, &dummy, 0);
		}
		else
		    printf("%lx\n", me->psm_DisplayID);
		break;
	    case 'de':
		if(*cmd)
		{
		    APTR dummy;
		    me->psm_Depth = strtol(cmd, &dummy, 0);
		}
		else
		    printf("%lx\n", me->psm_Depth);
		break;
	    case 'si':
		if(*cmd)
		{
		    STRPTR ptr;
		    me->psm_Width = strtol(cmd, &ptr, 0);
		    me->psm_Height = strtol(ptr + 1, &ptr, 0);
		}
		else
		    printf("%lx/%lx\n", me->psm_Width, me->psm_Height);
		break;
	    case 'op':
		if(*cmd)
		{
		    SNODE *sn;
		    static UWORD penarray[] = { ~0 };
		    ULONG error;
		    if(!(sn = AllocMem(sizeof(SNODE), MEMF_PUBLIC | MEMF_CLEAR)))
		    {
			puts(NoMem);
			rc = 5;
			continue;
		    }
		    if(!(sn->sn_Node.ln_Name = savestring(cmd)))
		    {
			FreeMem(sn, sizeof(SNODE));
			puts(NoMem);
			rc = 5;
			continue;
		    }
		    if(!(sn->sn_Screen = OpenScreenTags(NULL,
			SA_Width, me->psm_Width,
			SA_Height, me->psm_Height,
			SA_Depth, me->psm_Depth,
			SA_DisplayID, me->psm_DisplayID,
			SA_Overscan, OSCAN_TEXT,
			SA_Title, sn->sn_Node.ln_Name,
			SA_AutoScroll, TRUE,
			SA_PubName, sn->sn_Node.ln_Name,
			SA_Pens, penarray,
			SA_ErrorCode, &error,
			TAG_END)))
		    {
			FreeVec(sn->sn_Node.ln_Name);
			FreeMem(sn, sizeof(SNODE));
			switch(error)
			{
			    case OSERR_NOMONITOR:
				puts("error: monitor not available");
				break;
			    case OSERR_NOCHIPS:
				puts("error: newer custom chips are required");
				break;
			    case OSERR_NOMEM:
				puts(NoMem);
				break;
			    case OSERR_NOCHIPMEM:
				puts("error: no chip memory");
				break;
			    case OSERR_PUBNOTUNIQUE:
				puts("error: screen of that name already exists");
				break;
			    case OSERR_UNKNOWNMODE:
				puts("error: unknown display mode");
				break;
			    default:
				puts("error: can't open screen");
				break;
			}
			rc = 10;
			continue;
		    }
		    AddTail(&me->psm_Screens, &sn->sn_Node);
		    PubScreenStatus(sn->sn_Screen, 0);
		}
		break;
	    case 'cl':
		if(*cmd)
		{
		    SNODE *sn;
		    if(sn = (SNODE *)FindName(&me->psm_Screens, cmd))
		    {
			if(PubScreenStatus(sn->sn_Screen, PSNF_PRIVATE) & 1)
			{
			    Remove(&sn->sn_Node);
			    CloseScreen(sn->sn_Screen);
			    FreeVec(sn->sn_Node.ln_Name);
			    FreeMem(sn, sizeof(SNODE));
			}
			else
			{
			    puts("error: visitor windows open on screen");
			    rc = 5;
			}
		    }
		    else
		    {
			puts("error: no screen of that name");
			rc = 5;
		    }
		}
		break;
	    default:
		puts("error: unknown command");
		usage();
		rc = 10;
	}
	ac--;
	av++;
    }
    UnlockAddr(me->psm_Lock);
    exit(rc);
}

VOID
usage(VOID)
{
    puts("usage: pubman {<command>}");
    puts("command:");
    puts("\tli");
    puts("\tdp[name]");
    puts("\tmd[s+|s-|p+|p-]");
    puts("\tdi[display ID #]");
    puts("\tde[bpl #]");
    puts("\tsi[w/h]");
    puts("\top<name>");
    puts("\tcl<name>");
}

STRPTR
savestring(STRPTR str1)
{
    STRPTR str2;
    if(str2 = AllocVec(strlen(str1) + 1, MEMF_PUBLIC))
	strcpy(str2, str1);
    return(str2);
}

struct Screen *
OpenScreenTags(struct NewScreen *ns, ULONG tag1)
{
    return(OpenScreenTagList(ns, (struct TagItem *)&tag1));
}

VOID
NewList(struct List *list)
{
    list->lh_Head = (struct Node *)&list->lh_Tail;
    list->lh_Tail = NULL;
    list->lh_TailPred = (struct Node *)&list->lh_Head;
}

