[net.micro.amiga] shared IDCMP ports; verify messages

jimm@amiga.UUCP (Jim Mackraz) (03/14/86)

    Two Commonly Sought Intuition Tricks.

    Many people ask how they can receive IDCMP message for several windows
    through a single port.  This is feasible because Intuition tags each
    message with the address of the window to which it was directed.  The
    discussion and routines below, due largely to Neil Katin, show how this
    can be done.

    Another frequently needed trick comes in the use of the VERIFY IDCMP
    messages.  These turn out to be a bit trickier to use than we would have
    liked, and discussion and code follow the shared port section.

 I.   Using Multiple windows with one IDCMP.

    1- get your own port to use.
	method A: Open one window, and use the port Intuition supplies
	method B: Call CreatePort(name, pri) (part of the exec_support
	  library found in amiga.lib.)  Name is optional, which will cause
	  an AddPort() to be done if non-null.  Priority 0 is fine.

	If you use either method, you may deallocate the port in two ways:
	A: call DeletePort()
	B: let intuition free the port when it closes your LAST window
	    (that is, do not use CloseWindowSafely(), below).

    2- Get your windows open, with your common port installed
	OpenWindow with IDCMPFLags == 0.  (Intuition will then not CreatePort())
	Set mywindow->UserPort = myportaddress (your port from part 1)
	ModifyIDCMP(mywindow, FlagsIReallyWant);  Intuition will use the port
	you have stuck in mywindow->UserPort.

    3- Closing windows.  For all but last, use CloseWindowSafely() below.
	For last, you may use CloseWindowSafely() and then free your port
	yourself (above), or just CloseWindow(), and let Intuition() free
	your port.

    /** CloseWindowSafely.c ** (Neil) **/
    #include "exec/types.h"
    #include "exec/nodes.h"
    #include "exec/lists.h"
    #include "exec/ports.h"
    #include "intuition/intuition.h"

    /* this function closes an intuition window that shares a port with
     * other intuition windows.
     *
     * It is careful to set the UserPort to null before closing, and to
     * free any messages that it might have been sent.
     */

    CloseWindowSafely( win )
    struct Window *win;
    {
	/* we forbid here to keep out of race conditions with intuition */
	Forbid();

	/* send back any messages for this window 
	 * that have not yet been processed
	 */
	StripIntuiMessages( win->UserPort, win );

	/* clear UserPort so intuition will not free it */
	win->UserPort = NULL;

	/* tell inuition to stop sending more messages */
	ModifyIDCMP( win, 0 );

	/* turn tasking back on */
	Permit();

	/* and really close the window */
	CloseWindow( win );
    }

    StripIntuiMessages( mp, win )
    struct MsgPort *mp;
    struct Window *win;
    {
	struct IntuiMessage *msg;
	struct IntuiMessage *succ;

	msg = mp->mp_MsgList.lh_Head;

	while( succ = msg->ExecMessage.mn_Node.ln_Succ ) {

	    if( msg->IDCMPWindow ==  win ) {
		/* intuition is about to rudely free this message.
		 * Make sure that we have politely sent it back.
		 */
		Remove( msg );

		ReplyMsg( msg );
	    }
		
	    msg = succ;
	}
    }

 II.  IDCMP VERIFY functions

    In version 1.0 and 1.1 Intuition, using the MENUVERIFY, REQVERIFY, or
    SIZEVERIFY IDCMPFlags can easily lead to deadlocks, since Intuition
    will Wait() for you to reply to the Verify request.  (It is important to
    know that part of Intuition runs on the schedule of the Input Device task,
    while functions called by your program run on the schedule of your task.)

    Future releases of Intuition will make these features easier to use,
    but for now you must be very careful not to get into a situation where
    Intuition is waiting for you, but you are (perhaps indirectly) waiting
    for Intuition.

    This can happen if:
	You call AutoRequest().  Note that this might happen when you
	make any file I/O call, since the DOS may post an AutoRequest
	and will not return to you until the user clicks some gadget.
	The Intuition manual makes reference to adjusting your IDCMPFlags when
	you call AutoRequest(), but this problem is not avoided in the
	current release of software.

	CloseWindow() and ModifyIDCMP() can hang if there is a VERIFY message
	in your queue when you call them.  This is a bug to be fixed in
	Intuition, but you can work around them with the code provided.

	ClearMenuStrip() can, in rare circumstances, cause a hang.  It
	is advised that you use the code provided below to clear the 
	menu strip when using the MENUVERIFY flag.

    What you can do:
	The general idea is to safely turn off the verify IDCMPFlags before
	doing ANYTHING which may cause your input handling to be terminated
	for a long time or may depend on Intuition being operational to
	complete.

	When turning off verify IDCMPFlags, one most likely wants to turn
	off the functions they verify.  This is the approach in the code
	example below.  One turns off Menus using the function
	ClearMenuStrip().  Double Mouse Requesters (DMR's) are turned off by
	ClearDMRequest() (note that DMR's are the only requester that might
	generate a REQVERIFY message.  Window sizing cannot, today, be turned
	off.  It is thus wise to avoid the use of SIZEVERIFY unless you
	understand how you can handle this.

	In the code below, note that all Verify IDCMPFlags are cleared, and
	Menus and the DMR are turned off.  Modify to suit.  The code below only
	works if you have some additional IDCMPFlags set.  Otherwise, turing
	off the VERIFY IDCMPFlags causes the UserPort to be deallocated, before
	you have poked around for messages.  Handling the case where the
	verify flags are the *only* IDCMP flags you use is left as an
	exercise.

    /* CODE FRAGMENT TO TURN VERIFY FUNCTIONS OFF
     * Note:
     *	you can turn off SIZEVERIFY but you can't
     * 		turn off sizing.
     * 	this trick only works if there are IDCMPFlags in
     *		addition to the VERIFY flags. (so your port is not closed)
     *	See that ClearMenuStrip() must come after verify message is replied or
     *		you can get a deadlock (if window not active)
     *	See that ClearDMRequest() must come before replying in order to keep
     *		the requester from coming up if the user has already stroked
     *		the menu button twice
     *	to renable, ModifyIDCMP() to initial flag values and
     *		SetMenuStrip() and SetDMRequest() all three under Forbid()
     *
     *	THIS WILL NOT WORK IF THE VERIFY FLAGS ARE THE ONLY
     *	SET BITS IN THE IDCMP FLAGS (Intuition will close your port and
     *	free the messages; you will crash or intuition will hang.)
     */
    #include <exec/types.h>
    #include <intuition/intuition.h>
    /* intuition.h brings in other needed exec/ includes */

    KillVerify(window)
    struct Window *window;
    {
    struct Node *l, *next;

	Forbid();

	/* turn DMR off */
	/* will return false if request active */

	ClearDMRequest(window);	/* do before clearing REQVERIFY messages */
	/** REMOVE ABOVE LINE IF INAPPLICABLE **/

	/* stop verify messages */
	ModifyIDCMP(window, window->IDCMPFlags  &
	    ~(MENUVERIFY | REQVERIFY | SIZEVERIFY));

	/* clear messages (forbidden)
	 * note loop termination condition for Amiga exec list
	 * note also that 'l' is not used after ReplyMsg(l) (instead,
	 * we use 'next' in increment step  (and assume NULL == FALSE)
	 */
	for (l =  window->UserPort->mp_MsgList.lh_Head;
	    (next = l->ln_Succ); l =  next)
	{
	    switch ( ((struct IntuiMessage *)l)->Class)
	    {
	    case MENUVERIFY:
		((struct IntuiMessage *)l)->Code = MENUCANCEL;
		/* no break; */
	    case REQVERIFY:
	    case SIZEVERIFY:
		Remove(l);
		ReplyMsg((struct Message *) l);
		break;
	    }
	}
	/* turn menus off */
	ClearMenuStrip(window);	/* must be AFTER clearing MENUVERIFY messages */

	Permit();
    }

	go wild.
	jimm