[comp.sys.next] communication between programs with Speaker and Listener

dz@orange.ucsb.edu (Daniel James Zerkle) (08/17/89)

You know the way that Digital Librarian can communicate with WriteNow
to display certain documents?  Ever notice that the print panel does
the same thing with Preview in many applications?  I am trying to do
something similar, but I am having no luck at all.  I want to be able
to send a message to Preview to display certain PostScript documents.
I have not gotten this to work, so if anything I say is clearly wrong,
let me know, as I may have some misconceptions.  Anyway, this is what I
understand so far:

The standard way to do inter-process communication  on a NeXT is with
the Speaker and Listener objects.  An application that wants to be able
to receive messages from other programs to do such useful procedures as
display or print files has a listener object that gets the messages and
passes them off to some sort of delegate, which takes care of the
actual work.  Preview should have one of these objects.  If a program
wants to *send* messages to other programs, it has a Speaker object for
each such program with which it wants to communicate.  You message the
Speaker when you want something interesting to happen at the other end.

As I'm not familiar with the special features of Mach over UNIX, this
part is where I'm a bit fuzzy.  The Listener listens and the Speaker
speaks over something called a "port".  When a Listener object is
started up, it creates the port and registers it with the Mach kernel.
A program with a speaker object can find that port somehow by looking
it up with the kernel.  Once it is found, there is an object method to
set that port as the one used.

Anyway, this is the code given as a typical use of the Speaker object
(this is in the documentation for Speaker):

/*****************************/
SpeakerId = [Speaker new];

port = NXPortFromName("someapp",NULL);
	// get port for "someapp" 
if (port != PORT_NULL)  
	[SpeakerId setSendPort:port]; 
	ec = [SpeakerId openFile:"/usr/foo" ok:&returnOk];
	// any method 
	if (ec == 0) 
		// fiddle with return results, like returnOk above 


[SpeakerId free]
	// when we quit
/*****************************/

This is the code I made that is utterly failing to work:

/*****************************/
- doDisplay:sender
{
    printf("Displayer is messaged...\n");
    [inputForm selectTextAt:0];
    
    SpeakerId = [Speaker new];
    port = NXPortFromName("/NextDeveloper/Demos/Preview",NULL);
    strcpy(dfile, [inputForm stringValueAt:0]);
    printf("File name is %s.\n",dfile);
    if (port != PORT_NULL)
      {
      printf("messaging the speaker\n");
      [SpeakerId openFile:dfile ok:&returnOk];
      }
    [SpeakerId free];
    return self;
}
/*****************************/

This bit of code is obviously just to test things out.  I'll clean up
later.  The assorted printf's (I don't grok gdb yet) show that the
openFile method does not get called (the file name and all else seem
correct).  Thusly, port must in fact == PORT_NULL (whatever that is).
The problem is, therefore, in the NXPortFromName() call.  Unfortunately,
I could find no online documentation anywhere for this call.  The best
I could do was the following line from /usr/include/appkit/Listener.h:

extern port_t NXPortFromName(const char *portName, const char *hostName);

Seeing "hostName", I tried changing the NULL to both "." and "pab" (our NeXT
is christened "pab", and it isn't my fault).  That had no effect whatsoever.

These are some good questions to answer:

1. How do I get the proper port identification, when all I know is the
   name of the program I want to use? Will this work if I don't even have
   the full pathname?  Should I use just the name and not the full path?

2a.Applications such as Digital Librarian and WriteNow (not to mention
   the Workspace Manager) will start up an application they need if it is
   not already started.  Is this automatic, or do I need to do it myself
   upon receipt of some error saying that there is no port registered
   under the desired application's name?

2b.How do I get this error message?  How do I know that the receiving
   program is ready (it may take awhile to start up)?

3. Who takes care of these sorts of things?  Should I be talking to the
   Workspace Manager, or directly to the kernel?  Is there more than one
   option?  If so, what is better under which circumstances?

4. Will the documentation be any better under 1.0?  It was rather a cheap
   shot to bring up NXPortFromName() and not have it documented anywhere.

5. Just what exactly do ports do?  How do they do it?  Chapter 15 of the
   technical manual talks about them but it is rather hard to follow.

6. Is there anything else I should know?

Obviously, the most useful thing for me right now would be a little piece
of Obj-C that would tell Preview what to do when I need it.  If you can
answer a couple of these questions, I'll probably be able to figure out
the code for myself.  (Just wish I had the source....)

Many thanks (in advance, of course).  May your family be blessed for
generations to come if you actually figure this out.

| Dan Zerkle home:(805) 968-4683 morning:961-2434 afternoon:687-0110  |
| dz@cornu.ucsb.edu dz%cornu@ucsbuxa.bitnet ...ucbvax!hub!cornu!dz    |
| Snailmail: 6681 Berkshire Terrace #5, Isla Vista, CA  93117         |
| Disclaimer: If it's wrong or stupid, pretend I didn't do it.        |

dz@orange.ucsb.edu (Daniel James Zerkle) (08/17/89)

Oops.  In my previous example code, I left out the line after the
last printf which should read:  [SpeakerId setSendPort:port];
This is not the bug I'm worried about.  Pretend I didn't do it.

| Dan Zerkle home:(805) 968-4683 morning:961-2434 afternoon:687-0110  |
| dz@cornu.ucsb.edu dz%cornu@ucsbuxa.bitnet ...ucbvax!hub!cornu!dz    |
| Snailmail: 6681 Berkshire Terrace #5, Isla Vista, CA  93117         |
| Disclaimer: If it's wrong or stupid, pretend I didn't do it.        |

duggie@Jessica.stanford.edu (Doug Felt) (08/18/89)

In article <2220@hub.UUCP> dz@cornu.ucsb.edu (Daniel James Zerkle) writes:

[description of app launching via Speaker/Listener omitted ]

>This is the code I made that is utterly failing to work:
>
>/*****************************/
>- doDisplay:sender
>{
>    printf("Displayer is messaged...\n");
>    [inputForm selectTextAt:0];
>    
>    SpeakerId = [Speaker new];
>    port = NXPortFromName("/NextDeveloper/Demos/Preview",NULL);
>    strcpy(dfile, [inputForm stringValueAt:0]);
>    printf("File name is %s.\n",dfile);
>    if (port != PORT_NULL)
>      {
>      printf("messaging the speaker\n");
>      [SpeakerId openFile:dfile ok:&returnOk];
>      }
>    [SpeakerId free];
>    return self;
>}
>/*****************************/
>
>...  Thusly, port must in fact == PORT_NULL (whatever that is).
>The problem is, therefore, in the NXPortFromName() call.  Unfortunately,
 ...
>Seeing "hostName", I tried changing the NULL to both "." and "pab" (our NeXT
>is christened "pab", and it isn't my fault).  That had no effect whatsoever.
>
>These are some good questions to answer:
>
>1. How do I get the proper port identification, when all I know is the
>   name of the program I want to use? Will this work if I don't even have
>   the full pathname?  Should I use just the name and not the full path?

It depends on how the application you want to messaage has registered
itself.  This is done in the main program that Interface Builder
generates, in the setAppName: call to the newly created app.  I think
an app can register itself as anything (if the Workspace is smart).  I
don't know how you can find out what an app registers itself as.

You should use NULL as the second parameter to talk to an application
registered on your own system.  Using your own hostname will also
work, but there is no reason to.

>2a.Applications such as Digital Librarian and WriteNow (not to mention 
> the Workspace Manager) will start up an application they need if it is 
> not already started.  Is this automatic, or do I need to do it myself 
> upon receipt of some error saying that there is no port registered 
> under the desired application's name?  

If the application is not running, it is not registered.  To launch
the application you need to ask the Workspace to do it, like this:

Speaker speak; /* created somewhere else */
port_t port;
int ec;
int ok;

  if ((port = NXPortFromName(NX_WORKSPACEREQUEST,NULL)) != PORT_NULL) {
    [speak setSendPort: port];
    if (((ec = [speak launchProgram: "TargetApp" ok: &ok]) == 0) && ok) {
      /* ... program launched... */
    }
  }

>2b.How do I get this error message?  How do I know that the receiving 
>program is ready (it may take awhile to start up)?  

I think you just get PORT_NULL back when you try to get its port.  The
app can terminate between messages, too, which will also cause requests
for its port to fail.  I myself would like to know if there is a better
option than just going to sleep and polling for the port every once
in awhile.

Additionally, there is a chance that an app can terminate between the
time you get port rights to it and the time you message it.  I'd like
to know myself if there is any way to get notified when a port that
you have send rights to is destroyed.  Otherwise you might as well
cache the port rather than using NXPortFromName each time, except for
the greater chance that the port would get reused by another app in
that window.  Ali?

>3. Who takes care of these sorts of things?  Should I be talking to the 
> Workspace Manager, or directly to the kernel?  Is there more than one 
> option? If so, what is better under which circumstances?  

I'll let Ali answer this one.

>4. Will the documentation be any better under 1.0?  It was rather a cheap 
> shot to bring up NXPortFromName() and not have it documented anywhere.  

That's beta software for you... :-)

>5. Just what exactly do ports do?  How do they do it?  Chapter 15 of the 
> technical manual talks about them but it is rather hard to follow.  

Reread it a few times and parts will start to make sense.  It could
use a few more examples, I agree.  Try to fork some tasks and pass
port rights back and forth.  You can pass rights using the
Speaker/Listener classes too.

>6. Is there anything else I should know?  

I don't think you can message the Workspace on other machines.  To talk
to an app on another machine, it has to be running and registered.  This
is probably for security.

If you use Speaker/Listener to message yourself, you will hang.  You'd
think separate threads could handle this, but I don't know how the
messaging works.  

I've just been experimenting with this stuff myself, and have no
special knowledge other than what I've been able to figure out.  Take
everything I say with a giant rock of salt.

>| Dan Zerkle

Doug Felt
duggie@jessica.stanford.edu

eht@f.word.cs.cmu.edu (Eric Thayer) (08/18/89)

In article <4594@portia.Stanford.EDU> duggie@Jessica.UUCP (Doug Felt) writes:
>I don't think you can message the Workspace on other machines.  To talk
>to an app on another machine, it has to be running and registered.  This
>is probably for security.
>
You can Speak to Workspace on another host, but its name is Workspace$x, where
x is some large number.  So you need to find out that number somehow, but
after that you are home free.

...eric
-- 
Eric H. Thayer      School of Computer Science, Carnegie Mellon
(412) 268-7679      5000 Forbes Ave, Pittsburgh, PA 15213

dz@orange.ucsb.edu (Daniel James Zerkle) (08/18/89)

In article <2220@hub.UUCP> I write:
>You know the way that Digital Librarian can communicate with WriteNow
>to display certain documents?  Ever notice that the print panel does
>the same thing with Preview in many applications?  I am trying to do
>something similar, but I am having no luck at all.  I want to be able
>to send a message to Preview to display certain PostScript documents.

The following code will do the job.  Types declared in the interface are
port_t port; id SpeakerId, inputForm; int returnOk; char dfile[500];

- doDisplay:sender
{
    [inputForm selectTextAt:0]; /* I'm getting it from a Form */
    
    returnOk=YES;  /* this probably is not necessary */
    SpeakerId = [Speaker new];
    port = NXPortFromName("Preview",NULL);
    strcpy(dfile, [inputForm stringValueAt:0]); /* dfile is full path of doc */
    if (port != PORT_NULL)
      {
      [SpeakerId setSendPort:port];
      [SpeakerId openFile:dfile ok:&returnOk];
      }
    [SpeakerId free];
    return self;
}

>1. How do I get the proper port identification, when all I know is the
>   name of the program I want to use? Will this work if I don't even have
>   the full pathname?  Should I use just the name and not the full path?

The reason my code wasn't working is because I was using the full
pathname for Preview.  NXPortFromName is now returning the proper
port, somehow, with just "Preview", and not "/NextDeveloper/Demos/Preview".

>2a.Applications such as Digital Librarian and WriteNow (not to mention
>   the Workspace Manager) will start up an application they need if it is
>   not already started.  Is this automatic, or do I need to do it myself
>   upon receipt of some error saying that there is no port registered
>   under the desired application's name?

NXPortFromName() will automatically start up the Preview application,
if it is not already started.  If you kill Preview, it will re-start
it the next time.  This would seem to indicate that Preview does NOT
need to be registered anywhere.  Note, however, that I create a new
instance of a Speaker every time I use this method, and therefore get
the port anew each time.  If I retain the port, and don't re-get it, it
is possible for Preview to die before the next time I use it.

By the way, I have developed a slight case of athelete's tongue (foot
in mouth disease).  About ten minutes after I posted my second message,
I discovered "don't use the full path, dummy".  I do, however, greatly
appreciate the people who have attempted to answer my questions, as all
I really know is that the above code works, not *why* it works (which is
far more important).  The bit about messaging the Workspace directly
could prove to be very, very useful.  Thanks for the help.

My code above is really trivial.  It's wonderful that NeXT provided such
powerful tools to make programming easier.  Unfortunately, they didn't
document carefully enough.  They will have a whole lot of programs out
there if release 1.0 can document more carefully and *let* people write
code without being either psychic or priveledged to another, capable infor-
mation source (such as this net).  Are the tech writers listening?

| Dan Zerkle home:(805) 968-4683 morning:961-2434 afternoon:687-0110  |
| dz@cornu.ucsb.edu dz%cornu@ucsbuxa.bitnet ...ucbvax!hub!cornu!dz    |
| Snailmail: 6681 Berkshire Terrace #5, Isla Vista, CA  93117         |
| Disclaimer: If it's wrong or stupid, pretend I didn't do it.        |