[comp.windows.x] How to simulate a blocked i/o in an event driven program?

veerabad@buster.cps.msu.edu (Vibhu Veerabadrappa) (02/24/91)

Hi,
	Here is a question on X window programming:

	I have a program in which I pop up a dialog window from which
I expect the user to input some data which I need, later in the program.
But since X window programs are event driven, the program does not "Block"
ubtil the user types in the required data, but goes on. 

		  .
		  .
		  .
            popupwindow ();
	    /* 
	      some processing
	      which requires 
	      the user's data ...
	    */
		  .
		  .
		  .
	I tried to have a loop after the pop up, waiting until the data is
input. But the pop up window was never popped up !!

	Young's book has an example in which the program does not block,
and he also says that it is difficult to simulate "blocked" i/o ... but 
does not say it is impossible !! So there must be a way out.

	Can anyone provide me with a hint how it can be done? 

	Thank you very much.

	My e_mail address is: veerabad@buster.cps.msu.edu

------Vibhu.

cs@cbnewsh.att.com (cetin.seren) (02/25/91)

In article <1991Feb23.213455.19457@msuinfo.cl.msu.edu>, veerabad@buster.cps.msu.edu (Vibhu Veerabadrappa) writes:
> 
> Hi,
> 	Here is a question on X window programming:
> 
> 	I have a program in which I pop up a dialog window from which
> I expect the user to input some data which I need, later in the program.
> But since X window programs are event driven, the program does not "Block"
> ubtil the user types in the required data, but goes on. 
> 
> 		  .
> 		  .
> 		  .
>             popupwindow ();
> 	    /* 
> 	      some processing
> 	      which requires 
> 	      the user's data ...
> 	    */
> 		  .
> 		  .
> 		  .
> 	I tried to have a loop after the pop up, waiting until the data is
> input. But the pop up window was never popped up !!
> 
> 	Young's book has an example in which the program does not block,
> and he also says that it is difficult to simulate "blocked" i/o ... but 
> does not say it is impossible !! So there must be a way out.
> 
> 	Can anyone provide me with a hint how it can be done? 
> 
> 	Thank you very much.
> 
> 	My e_mail address is: veerabad@buster.cps.msu.edu
> 
> ------Vibhu.
> 


Disclaimer:  The only half-acceptable argument to doing something
             like what you want is that you want to port an old, dumb-
             terminal based application or application support library
             to a windowed environment.
             In case you are designing something from scratch,
             think carefully; you WILL find a BETTER paradigm to
             do what you want to accomplish.  The concurrency involved
             in multi-window style programming requires that the program
             designer approaches the problem differently.  As a hint,
             approach your problem with the question:
                -- What should happen once the user has decided to pick
                   one of the options available in the popupwindow??
             as opposed to:
                -- Now we have to get the users' choice...
             There has been a lot said and done on the style of
             multi-window style programming.  I can send you a list
             of references dating back to '85 or even earlier if you
             want.
Having said that, I admit I've had to deal with the problem; it pops up
when one tries to port an old, dumb-terminal based user interface support
library to a graphical widowing environment like X windows.  The programs
based on old support libraries count on the popupwindows to be blocking;
the popupwindow() call is expected to return the choice.  The solution I
came up with works fairly well, although if abused, it will come to a
point where some stack will overflow
and a core dump will follow.  Neverthless, it handles one problem fairly
well:  The event-processing necessary to keep the other windows operating,
especially the expose events that have to be dealt with right away.  A
conventional blocking call would cause all the expose events to be
queued, but not be processed; That is why poele

NOTE:  The code is for example only; you'll have fill in the blanks from
       the man pages.  Also, it is kind of backwards, so you'll have
       to read all of it before it makes sense:

AnyWay, here's how you do it:

/* define a global var. to keep reqired action */
int MainLoopContinue = 1;


/* write your own equivalent of XtMainLoop() function: */
 
   
void
MyXtMainLoop(............)
{
         while ( MainLoopContinue ) {
           /* wait until there's something to read at the X socket */
           select ( /* put the fd for the X connection socket in
                       the readfds field, set everything else to NULL
	               for sysV, use poll() instead...
	            */
	          );
	   /* since select broke, we have an event: */
	   XNextEvent(.....&event.....);
	   XtDispactEvent( .....&event......);
	}
	MainLoopContinue = 1;
}


/* now define your own popupwindow's callback.  Arrange your
** popupwindow so that this gets called when the user types
** something or select something in the popupwindow:
*/

void
popupwindowcallback(...../* the Xt args */......)
{
	/*
	** if you want, you can do some initial processing
	** of the user input here
	*/
	MainLoopContinue=0; /* next time the while in MyXtMainLoop() is
			    ** encountered, it will return..
			    */
}


/*
** now define your popupwindow() function that actually pops up the
** window:
*/

int /* or char or ZONK*, or whatever */
popupwindow(.................)
{
	/* set up your pop-up window widget, etc. suppose you call
	** your popupwin widget popeye:
	** also,  the popupwindowcallback()
	** function defined above  has to be defined as the
	** event callback function in your XtCreateManagedWidget()
	** call.
	*/
	.
	.
	.
	XtRealizeWidget(.......popeye.......);
	MyXtMainLoop(....); /* defined above */

	return /* whatever it is you want your popupwindow call
	       ** to actually return, after seemingly blocking
	       ** until the user types in something
	       */
}



/*
** Your MAIN() function would look like this:
** (I guess, since that is dependent on how you really want to use
** the stuff above.  Here's one scenario anyway:
*/

main( int argc, char** argv)
/* O.K., so I'm too used to C++ !! */
{
	/*
	** set up your initial widgets, etc...
	*/
	.
	.
	.
	MyXtMainLoop(....) /* defined above */
}

/*
** and I assume you would actually want to use the blocking
** popupwindow() functionthrough one of the other callbacks:
*/


void
SomeOtherCallBackThatGetsCalled(...........)
{
	/*
	** do your other stuff, computation, etc.,
	** and realize that you actually want to
	** use the popupwindow() function
	*/
	.
	.
	.
	
	
	ZONK* UserResponse = popupwindw(........);

	
	.
	.
	. some more computation, etc..
	.
	.
}


That's it.  The idea is to call MyXtMainLoop() function recursively
and use the the MainLoopContinue global variable as cotrol for
breaking out of MyXtMainLoop() at each level.

PitFall:  If too many blocking popupwindow() calls are made within
          each other, you will blow away the program stack (eventually,
	  anyway.  Never happened to me yet).
	
	
	
				Good Luck!!!
				Cetin Seren
				cs%speedy@att.com
				
				
				
				

mouse@lightning.mcrcim.mcgill.EDU (02/25/91)

>>             popupwindow ();
>> 	    /* some processing which requires the user's data ... */

> The only half-acceptable argument to doing something like what you
> want is that you want to port an old, dumb-terminal based application
> or application support library to a windowed environment.

> In case you are designing something from scratch, think carefully;
> you WILL find a BETTER paradigm to do what you want to accomplish.

This sounds to me like a comment coming from limited experience.  It's
true that this is *usually* the wrong paradigm, but there are
occasional cases where it's the least of the available evils.  (The
alternatives in such circumstances generally involve deeply-nested
control structure or large amounts of data in local variables; in all
of these cases, turning the control structure into something
event-driven results in painful code.)

To pick an example that is somewhat too simple but will perhaps give
some of the flavor of what I'm trying to say, suppose some code is
doing a three-dimensional convolution and finds some exceptional
condition that the user must resolve:

	for (var1=min1;var1<=max1;var1++)
	  for (var2=min2;var2<=max2;var2++)
	    for (var3=min3;var3<=max3;var3++)
	      { if (special(elt[var1][var2][var3]))
		  user_resolve(var1,var2,var3);
		do_voxel(var1,var2,var3);
	      }

Making this event-driven wrt user interaction involves breaking out of
the loops and then arranging to re-enter them later.  As I indicated,
in this example it is not excessively painful.  But it can get so.

> As a hint, approach your problem with the question:
> -- What should happen once the user has decided to pick one of the
>    options available in the popupwindow??

And if the answer is "pick up where we left off with <foo>", where
<foo> is complicated?

You are generally correct; I don't mean to give the impression that
blocking popups are always a good thing.  But they are not quite of as
little utility as you indicate.

Of course, this is all opinion.

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

josh@concept.viewlogic.com (Josh Marantz) (03/01/91)

The nested loop approach described by cs@cbnewsh.att.com is probably
the best way to approach this problem in C.  But when using Scheme,
which support lexical scoping and first class procedures, there
is a better way.  Instead of "blocking", have the popup routine take
a function as an argument, and run that function when the popup is
activated.

(define (SomeOtherCallBackThatGetsCalled ...)
  ; do your other stuff, computation, etc.,
  ; and realize that you actually want to
  ; use the popupwindow() function
  (popwindow ...
    (lambda (UserResponse)
       ; some other computation, etc.
       )))


(popwindow ...) returns immediately, but its procedure argument gets
called when the popup is terminated.  The body of the procedure gets
to use all the local variables in SomeOtherCallBackThatGetsCalled,
and generally behave as if it was code that followed a blocking call
to popwindow.  No nested main loops are required.

This can be done in C too, its just too clumsy to be used very much:

typedef struct {
    ... Local variable declarations for SomeOtherCallbackThatGetsCalled ...
} SomeOtherCallbackLocals;

static void continuation();

void
SomeOtherCallBackThatGetsCalled(...........)
{
        SomeOtherCallbackLocals args;

	/*
	** do your other stuff, computation, etc.,
	** and realize that you actually want to
	** use the popupwindow() function
	*/
	.
	.
	.
	
	
	ZONK* UserResponse = popupwindw(........, continuation, &args);
}

static void continuation(UserResponse, args)
     ZONK* UserResponse;
     SomeOtherCallbackLocals *args;
{
	.
	.
	. some more computation, etc..
	.
	.
}

	
-- 
Joshua Marantz
Viewlogic Systems, Inc.
josh@viewlogic.com
        Why not pass the time by playing a little solitaire?