% CWEB source for FrontPubScreen
% 12 Apr 1992 john fieber (jfieber@@sophia.smith.edu)

\nocon % omit table of contents
\datethis % print date on listing
\def\progname{\.{FrontPubScreen}}

@* FrontPubScreen. This program is for AmigaDOS 2.0 or later. It does
one simple and useful thing when you are working with public screens:
if the frontmost screen is a public screen, it returns the name of
that screen.  Since the the front screen is generally the one you are
working on, it effectively returns the name of the screen you are
working on.

A good example of how this can be used is with a ``hotkey'' program
for opening pop-up utilities, command shells, editors or whatever. To
illustrate, in this CLI command: $$\hbox{\tt NewCLI
CON:20/50/540/180/AmigaShell/AUTO/SCREEN`\progname`}$$
\.{`\progname`} will be replaced by the output of \progname.  Since
\progname\ displays the name of the front screen, the result the
above command is a CLI window opening up on whatever screen you
happen to be working on.  This command attached to a hot-key provides
an instant CLI on whatever screen you happen to be working on.  Other
programs that allow you to specify a screen to open on can use the
same technique.  For TurboText users, try: $$\hbox{\tt ttx screen
`progname`}$$ attached to a hotkey.

Unfortunately this nifty feature only works if the front screen is
indeed a public screen.  Fortunately there are a growing number of
programs that do open public screens.  Also, you can create your own
using one of the various public screen managers roaming around the
``net''.

@ {\bf Copying.}  Feel free to copy and distribute this program but
please don't distribute the executable without this source code.
Also, feel free to make changes but please make and document the
changes in a cweb change file, not the original.  Also, I encourage
people who are writing little pop-up type applications to stick this
little bit of code in.  Public screens are nifty things so use them
and make them useful!

If you do make a change file, use the code in your own program, or
otherwise find this program useful I would like to hear about it.
Send email to John Fieber at \.{jfieber@@sophia.smith.edu}.

@ Okay, enough description, now on to the program, starting with a
little thing to teach \.{cweave} about one of the special Amiga
types\dots

@f ULONG int

@ All good AmigaDOS programs should have string such as the following
embedded somewhere in the program.  The CLI command \.{Version} will
look for this string and display it if found.  This approach is much
easier than writing in code to provide a command line switch and to
display the version and much more consistant from the user's point of
view.

@c
char version[] = "\0$VER: FrontPubScreen 1.0 (4.12.92)";

@ Here is the general layout of the program.  We first check the
value of |argc| to determine if the program was run from a command
shell or from the Workbench.  If |argc == 0| then \progname\ was
started from the Workbench. Since it has no useful function in that
context, we just exit.  If |argc >= 1| then we move on and try the
name of a useful public screen.

@c
@<Include files@>@;

@<Function prototypes@>@;

@<Global variables@>@;

main(argc, argv)
int argc;
char *argv[];
{
	if (argc) {
		@<Open system libraries@>;
		@<Get screen name@>;
		@<Output screen name@>;
		@<Close system libraries@>@;
	}
}

@ We don't really need to include the extra baggage of \.{stdio.h}
either during compilation or linking since AmigaDOS provides for
writing things out to the console. But we {\it do\/} need to include
the headers to get at those system functions.

@<Include files@>=
#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <dos/dos.h>

@ We also want to include the the Commodore function prototypes to be
ANSI compliant and the \#{\bf pragma}'s to bypass the library access
functions in \.{amiga.lib} that make the code unnecessairly large and
slow.

@<Function prototypes@>=

#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/exec.h>


@ The variable |IntuitionBase| is a pointer to base address of the
Intuition Library.  The value returned by the |OpenLibrary()| call
will be placed here. Also needed is a place to store the name of the
public screen that the program will return.  The value for
|MAXPUBSCREENNAME| is defined in \.{screens.h} (which is included
with \.{intuition.h}). Finally, we have a pointer to a screen
structure.

@<Global variables@>=

struct IntuitionBase *IntuitionBase;
char public_screen_name[MAXPUBSCREENNAME];
struct Screen *public_screen_address;

@ Now we must open the Intuition library so we can get at the nifty
system functions for twiddling with screens. If the call to
|OpenLibrary()| fails, it will return |NULL|.  We must exit
gracefully in the event that this occurs.

Notice that since Intuition has its own non-standard library
structure we must typecast the |OpenLibrary()| function which by
definition returns a pointer to a standard library structure.

@<Open system libraries@>=

IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",36L);
if (!IntuitionBase) {
	exit(20L);
}

@ To clean up at the end of the program, the Intuition library must
be closed.  Again since Intuition has a non-standard library structure
we need to typecast it so that the use of |CloseLibrary()| matches
its definition.

@<Close system libraries@>=

CloseLibrary((struct Library *) IntuitionBase);

@ Moving right along, here is the code that prints out the
(hopefully) useful information regarding a public screen name.  The
variable |public_screen_name| is assumed to contain the screen name
of the front public screen, or the default public screen.  Just to
check its validity, we try to lock the screen.  In the event that
fails we just return \.{Workbench} which should always work in some
way shape or form.

@<Output screen name@>=

public_screen_address = LockPubScreen(public_screen_name);
if (public_screen_address) {
	(void) PutStr(public_screen_name);
	(void) PutStr("\n");
	UnlockPubScreen(NULL, public_screen_address);
}
else {
	(void) PutStr("Workbench\n");
}

@ Now we must figure out some useful information for the previous
section to display.  A couple more variables must be defined to aid in the
search for the name of the front public screen.

@<Global variables@>=

ULONG intuition_lock;
struct Screen *front_screen_address;
struct List *public_screen_list;
struct PubScreenNode *public_screen_node;

@ The address of the front screen is easily found in the
|IntuitionBase| structure.  However, since this is a system structure
that we are not normally at liberty to examine, we must first get
permission from Intuition to examine it.  This is accomplished with
the |LockIBase()| function which sets up a semaphore.  This is much
friendlier than simply locking the whole system with |forbid()|
and |permit()| while we look at the structure.

With the address of the frontmost screen in hand, we check to see if
it is a public or Workbench screen.  If so, we search the public
screen list for a match.  The variable |public_screen_name| is set to
a |NULL| string at the start and then checked at the end.  If it is
still |NULL|, then the front screen was not found in the public
screen list so we ask the system for the name of the default public
screen.

@<Get screen name@>=

public_screen_name[0] = '\0';

intuition_lock = LockIBase(0L);
front_screen_address = IntuitionBase->FirstScreen;
if ((front_screen_address->Flags & PUBLICSCREEN) || (front_screen_address->Flags & WBENCHSCREEN)) {
	UnlockIBase(intuition_lock);
	@<Search public screen list@>;
}
else {
	UnlockIBase(intuition_lock);
}

if (public_screen_name[0] == '\0') {
	GetDefaultPubScreen(public_screen_name);
}


@ Here we lock the public screen list and examine it
looking for a match to |front_screen_address|.  If one is found,
we then copy the name of the screen into |public_screen_name| for
later use. Note that the AutoDocs mention that the screen pointer in
the |PubScreenNode| structure is not trustworthy. However, in this
case nothing catastrophic will happen though if it is wrong---the
program won't work right but it should not affect anything else.

@<Search public screen list@>=

public_screen_list = LockPubScreenList();
public_screen_node = (struct PubScreenNode *) public_screen_list->lh_Head;
while (public_screen_node) {
    if (public_screen_node->psn_Screen == front_screen_address) {
		strcpy(public_screen_name, public_screen_node->psn_Node.ln_Name);
        break;
	}
	public_screen_node = (struct PubScreenNode *) public_screen_node->psn_Node.ln_Succ;
}
UnlockPubScreenList();

@ And thus we are finished!

@ {\bf Other notes.} There are still a few problems that hopefully
won't be too problematic.  Basically, although a public screen may be
available when \progname\ runs, there is no guarantee that it will
still be around later when that screen is used by whatever
called \progname.  In order to ensure this we would have to maintain
a lock on the screen until the caller has locked the screen, either
with |LockPubScreen()| or by having a window open on it.

To do this properly would require communication between the
applications so you might as well just build the \progname\ right
in and not bother with this silly utility.  Alternately
\progname\ could start another process to hold the lock for a
while and possibly monitor the visitor count on the screen.  This
might help but the payoff would probably be much smaller than the
effort involved to implement it.

Also, if the comments and explainations of this program seem a bit
excessive it is because I'm just learning how to use this nifty
invention of D.~Knuth called \.{CWEB}.
\medskip\rightline{--john fieber}

@* Index.
