Be Developers' Conference Approaching BeOS Messaging |
Doug Wright: Hello. Welcome to Approaching Messaging and Scripting. I'm Doug Wright, hopefully most of all of you heard me speak earlier at Approaching Applications.
One of the things a couple people mentioned was that I was trailing off the end of my sentences, so if anybody can't hear me, just let me know during the presentation instead of waiting till afterwards to tell me. All right?
So we are going to talk about the basic messaging architecture in the BeOS, and the scripting architecture and some of the basics of the scripting, and I'm going to tell you a little bit about the built-in scripting stuff that is in all the BeOS interface kits, et cetera.
So messaging, the way applications, the way we communicate with each other, the main way we use to communicate in the BeOS is the BMessage, because our messaging architecture is so pervasive, it's all throughout our interface kit, et cetera, it's really easy to do scripting using that, the same messaging architecture.
Then I'm going to tell you a little bit about something that involves BMessages, called Replicants, that allows you to take little pieces of your application and, views in particular, and put them inside other people's applications. Kind of, you know, not OpenDoc, but it's a lightweight, you know, component framework so you can take little pieces of applications and put them inside other applications.
So, messaging and threading. Messaging is obviously a big part of the BeOS, we have windows and application objects and views and we need to be able to send messages back and forth, especially with all these different threads running. The easiest way, if you are doing something old school, sequential programming, one thing has to happen before the other, messaging isn't so important, but with different threads all executing at different times on different processors, you need a message to send information back and forth between threads. I just spoke about multi-threading, lots of threads going on, so you need these two things. You combine these two things and you wind up with our basic model of an application.
Any time you use an application, the application framework of the BeOS, you wind up taking advantage of all these things.
So there are several types of messaging on the BeOS. We have ports, kernel ports where there is a distinct port that anybody in the system can write to using the port ID. We have pipes, the familiar POSIX information stream type of messaging system. We have thread messages, so every thread that exists actually has its own little message buffer that it can receive data on. That's where we put things like signals.
Then we have the BMessage, which obviously is going to be using these things, but at the higher level it's what you use in your application in order to communicate between windows, other applications, our scripting system, et cetera. So what's in the BMessage class? What does it get you? Where do you go with it?
The first thing you have is a description of what the message is about. We appropriately called that the What field in the BMessage. When you get a message you take a look at what the What is. I love saying that. To figure out what the message is all about. It's also a collection of name value pairs that contain data. So every entry in this name value pair collection has an associated type. And any entry, named entry, can also contain more than one value of that same type. So you can't mix types under the same name, but you can have multiple values associated with that one name, so it winds up being like an array.
Here is some example code. You have a message, we are adding a couple of integers under the sum name, so now we have two integers in there and you can go in there and get those out.
We have a string name comment, then we try to add an integer in the same name comment. We can't do that. That is an error. So you can extend this, you don't have to use only the Be defined types, you can stick your own data inside a message, any size, you just need to know how big the data that you are sticking in there is. And of course on the opposite end, whoever you are sending this message to had better know how to pull that data out. If this is inside your application, it's easy.
If you want to do that, you know, if you want to send messages with custom data types to another application you need to publish that API so other applications will know how to take advantage of that data, what to do with it when it arrives.
How does the API work with the Bmessages? We have this thing called a BLooper, and that's actually the class that our windows and our applications both inherit from, that's where they get their event loop, and the thread that that event operates in.
That event loop pulls messages out of the queue and sends them to handlers, a BHandler is actually what a looper inherits from, so any looper is also a handler, which just makes sense. So this object that runs the message loop can also handle messages itself or hand them off to other BHandler objects.
So your BApplication, your BWindow are loopers, they are also handlers, and so are BView, they inherit from BHandler. BHandler can handle system events or your own custom messages, things like KeyDown in a BView, or MouseDown, MouseMoved, all get handled by a BHandler.
Your own custom types get dealt with in a function called MessageReceived. And you override that function and add a switch statement that deals with any type of message that you send to the handler.
So here is a class diagram illustrating the inheritance hierarchy, to make it all clear.
So let's hop out and look at a little bit of sample code.
Is this big enough? Can everybody see that? Okay.
So this is a view, and it's a simple MessageReceived, and here is where you have your switch statement on the What. And this view is looking for a couple of different messages that it thinks it can handle; one is called Effect Button Message and the other is called Effect Jack Message. And if it receives either of those messages, it does whatever it wants to do. Now you can look in the Effect Button Message case, it actually attempts to find a pointer to an effect. And if it's successful in finding that pointer it then goes inside that message and one of the functions of message was dropped. That will tell you whether or not that message was dropped on to your View. And then it checks to see if it was dropped and if it was dropped it goes into the message and finds the dropped point and sticks that in the value that was declared somewhere that got deleted in order to make this code snippet simple.
And you will notice that after those two cases if it doesn't find an Effect Button Message or Effect Jack Message, it then called in default the BView MessageReceived. Now we refer to this BView as inherited, they are actually, has been in the past a keyword inherited that you could use, but we are discouraging people from using that. For compiler transportability and handling so-called BView MessageReceived, that message gets passed on, so maybe the BView that you inherited or some other view, if that happened to be the case, can handle the message and you can't.
So back to the presentation.
So how do these BMessages get delivered? There are two situations, or two, you know, contexts in which you want to deliver a BMessage, the intra-application, inside your own application you want to take a BMessage and give it from this window or that window or this view to that window or something like that.
Then there are inter-application, where you want to get it from your application and you want to send it over to somebody else's application, or maybe it's your own but it's a separate application running somewhere else. And there are two mechanisms to do that, and the underlying mechanics of those are the same, but there are two different situations where you use that.
BLooper PostMessage is the first one, that's the primary one you are going to use inside your own application. So anything, any object that's a BLooper has a PostMessage function, and when you call PostMessage you pass a message along with that.
And then there is BMessenger, SendMessage, and I haven't told you about BMessenger, I will tell you about that in a little bit. The intra-app situation can use either, inter-app has to use a BMessenger, because when you are doing PostMessage that takes that message, and say it's a window, it takes that message and looks for a handler to give that message to. You can specify a specific target when you call a PostMessage, and look for that target, that target is a pointer to a BHandler, and unfortunately you can't pass a pointer to a BHandler in some other application because we have this marvelous thing called protected memory that you have heard so much about. So that means you can't use pointers to other application's spaces in your own application, so you have to have this other mechanism. That mechanism is a BMessenger, and that BMessenger is basically a token that drives this message from your application space into another application space, and it points at a handler/looper pair because the message needs to get stuck in some looper's message loop so it gets handled, and then the handler specifies a particular handler that that looper will then give the message to. So that obviously it works across application address spaces.
So how do you get a BMessenger to somebody else's app, you wonder? Two basic ways to construct a messenger. One is to create a messenger for your own application and then give that to somebody else, to some other application, so if you want another application to send messages to you, you construct a BMessenger and you hand it, you hand that BMessenger back to another application, usually because you already have a BMessenger to that application.
So you really need a second one, too, and that second one using the MIME signature of the other application to create a BMessenger object to that app. And that really is the key to inter-application messaging, because then that's the only way to establish a link to another application is to use that second.
So because we can communicate so easily between applications, it leads us straight into our scripting architecture, because it means you can get a messenger and then our scripting architecture allows you to navigate down to a very specific handler inside another application by using scripting messages. So you get an application from a messenger to an application, then you ask that application for a messenger to a specific handler inside a view, say, inside of a window, and then you can send messages straight to a view inside of somebody else's application.
So we have arrived very quickly in scripting. It obviously uses and leverages off of our existing messaging model, because we have built in inter-application communication. It's really easy, you can see where scripting would follow really easily from that. Any app can use it to control another application. And any scripting language could be written to create these messages and pass them back and forth between applications. And all the...
Yes?
A Speaker: I'm sorry to interrupt, I wanted to look into the concept of a dictionary. Because in, for example, an Apple script, any application that has... is scriptable, has a dictionary that explains to applications that can request information that it supports in terms of the object model. And I was wondering if there is any sort of dictionary now, so that when you make a request to an application you can say exactly what can I ask you to do? So there is that.
Doug Wright: Yes. I'm about to get to that, inside the basics of scripting. Beautiful. Beautiful segue.
So all of our components have some built-in scripting messages that they understand and you can read about those in the Be Book, but how does your app find out what a particular object supports?
As I said, beautiful segue. So first we need to learn some scripting lingo on BeOS, then I can fully explain how it comes to know what other objects understand.
First, any scriptable object has what we call properties. So applications have windows, and documents, not... this is, you know, short examples of what an application could have. So a window can have views, controls can have labels and values, and there are commands that we send in scripting messages, the scripting messages that say what do you want to do? Do you want to get a property? Do you want to set a property? Do you want to create one? Delete one? Et cetera. Count the number of properties that an object has? And these properties are addressable in scripting message by specifier.
So the name, the index, the range, in the case of something that has multiple, and it makes sense to specify a range. So like in particular the Tracker supports selecting a range of files from a window. So you can say to Tracker I want this range of files that are being displayed in this window, and that window happens to be a window to a directory. So each property defines a set of specifiers that it accepts.
For example, windows can be referenced by name or by index. So you can say I want this application's second window, or I want this application's main document window, if that happened to be named that. And the specifiers direct the BMessage to its target by adding to the explanation of what it is you are looking for.
So as an example, the frame of the view named George of window 2 contains three specifiers. It contains the frame, that's a direct specifier, that means there is only one frame of a view. So there is... you don't need to describe which kind of a frame, you just say I want the frame. And you say of view, George. So George is a specifier of a specific view. And then window 2 is, 2 is an index specifier into some application. This actually is missing some application text edit.
In order to build a scripting message you need a messenger in order to send it to another application, so here we have a BMessenger created using the MIME string, and we create a BMessage with a B_SET_PROPERTY message, so we are going to change some property of another application.
Then we are going to add a specifier, here you can see how you add the specifiers we talked about. Frame is a direct specifier, so that's all that's added to that specifier.
The next one says View, George. So we are trying to find a view. And the second one is a name, obviously, George. And then the next AddSpecifier is Window 2. So it's really simple to build these messages up.
Then we are going to add the rectangle that when you want to set a frame you are obviously going to provide a rectangle that you are going to set it to. We add that as data and the rectangle object. And then we send the message. It's all pretty simple.
Here you can see how that progression goes, so here is the frame of view in this diagram, and we want to set it and we want to set it to this rectangle, so we send this message to some application, it gets it and it says there is a function actually called RESOLVE_SPECIFIER. And in that specifier, in that function RESOLVE_SPECIFIER it says okay, you are looking for a window and you are looking for my second window, so I'm going to find the second window and give it the rest of the message.
Then RESOLVE_SPECIFIER is getting a call in the window, and it's going to say okay, you are looking for a view named George. Let me find my view named George, I will pass the message on to that one. And the view gets the message, it says okay, you want to set my frame, and here is the data, great, okay, I will change my frame to this. So, you know, it drops really nicely down from one thing into the next.
So let me show you a little demo of this in realtime action. Doo is a little command line scripting language partially that Peter Potrebic wrote, he is the guy that architected this whole thing, and it just takes really simple strings and parses them into these commands. Now one of the things you have to worry about in Doo, if you misspell something it can get a little wacky because all he is doing is parsing.
See, I have this window here called dugzdemos. Again I'm going to do a simple GET-FRAME of window dugzdemos, Doo Tracker, that's the name of the app this is going to get sent to, that's our browser, our Tracker. And we are going to get the frame of window dugzdemo and that tells me just where this frame is located in space. And if I do a set, it moves that window on screen just like that. So the Tracker is completely scriptable.
It looks like I am missing a slide. The last piece of our scripting lingo is called suites and a suite is what we used, we used the MIME format to describe what kind of scripting messages any particular object understands. So the function that gets called is get supported suites. And that's actually the scripting message that gets supported suites, it returns you a series of MIME suites, /x-vnd.b in this application.
For the suites that are application object supports or window or view, there are specific sets of suites that any one of these objects supports. And those are built into those objects, the support for those suites.
So as you saw this window, that's a regular old BWindow, it supports finding a view and the window supports changing its frame so it can just be moved around. So any BWindow that you create, somebody else can script it to move it around. And it's really easy to get these suites as a programmer, if you want to make your application scriptable, all you have to do is create properties that you allow to be get or set. And the type of specifier that they use in order to get or set it.
So if you had, say, a dictionary, as your example was, and your property was get, you know the definition of word blah, and you had to provide that word as part of the data, so the word is data that you passed, get definition is a... sorry... get property and then you would say of string probably and you name the definition, then you are thinking of return, a message with this string and it would be the definition of that word or something.
So I don't have, unfortunately, our next suite slide I can show you to explain this better.
Are there any questions about how the suites work?
Okay.
A Speaker: Well, so I've gone through my application and decided which portions of the application I want to be scriptable by someone outside.
Doug Wright: Right.
A Speaker: Right? Then I create the suites.
Doug Wright: A suite. You create a suite of commands that your... or properties that can be changed, get or set.
A Speaker: When I get property, it's going to send a whole packet of suites at once.
Doug Wright: When they get supported suites it's going to send them the list of suites you support. You are going to have to document somewhere what those suites are. But along with the list of MIME strings of suites it also gives you a list of the get property and set property and the names of those properties that you can get and set. So you can actually figure it out or display it right there in the terminal if you use...
A Speaker: Could you do it with Doo?
A Speaker: Go back to your little code and say Doo Tracker get suite, just change frame to suite?
Doug Wright: Yes, I can just do this.
A Speaker: Okay, type suites.
Doug Wright: It's suites, yes. Okay. We just sent a command get suites of windows, here are the supported suites of this window right here. Suite/vnd.bewindow and suite/vnd.be-handler. Here are the properties you can play with and here are the properties these commands respond to. These are backwards, it's PSet and PGet. It's backwards on Intel, it's forward on PowerPC. But it's still legible if you just twist your brain. You have to do swapping on the fly, so to speak.
So we can get and set the frame, get and set the title, get and set the work spaces. So here we can illustrate this really quickly. So let's get... let's set the title of window dugzdemos to... do you have to type to...
Peter Potrebic: Yes.
Doug Wright: To dugzfun. So there, the title changed. You can really easily figure out just using this little tool what other... and you can do this programmatically, too, you can send this message and get the information back and your application can use it to whatever advantage you need.
Thanks Peter. So there you go. All right.
So I'm sorry, go ahead.
A Speaker: I was curious, does this work with the center in, center out, like Unix? Can you take a script and chmod it to an executable and run it and then type it into something?
Doug Wright: The Doo tool isn't distributed with BeOS right now; is that right, Peter?
Peter Potrebic: Right.
Doug Wright: If you wrote a little parser like this, yes, you could write shell scripts really easily that would send the messages anywhere.
A Speaker: Somebody has written another tool called Hey.
Doug Wright: Hey, that's right.
A Speaker: What's it called?
Doug Wright: Hey. H-e-y.
So as you saw in the Doo, it's Doo Tracker, so Hey, it's Hey Tracker, get blah. Yes, you can easily write scripts that do all that.
So I'm going to tell you about another technology that goes along, another thing that came out of our messaging.
Wait a minute, one more question. I'm sorry.
A Speaker: Just by using the standard kits do I have a couple of suites available by virtue of using the kits, or do I have to publish them?
Doug Wright: No. If you use the kits you get the suites that come with those kits. So any descendent of BHandler understands the suite BHandler, and any descendent of BView understands suite BView, and different objects can be scripted. BControl is also a suite underneath that, like BButton and BSlider, et cetera, all understand their own scripting messages. I'm pretty sure every interface kit has its own suite of things it understands or deals with. If you had a whole bunch of sliders you could script all the sliders, move around, that kind of thing.
So on to Replicants. They are another technology that came out of the messaging system and the architecture. It's kind of a new technology for sharing objects between halves, it's been in the BeOS since PR2, that allows you to do... I will show you really quick because it's so much easier to illustrate this way.
Here is our standard clock app. If you see on the BMenu there is a thing called Show Replicant. When you do that, this little brown hand shows up in the corner of the clock. You can grab that brown hand and drag it down here and drop that clock face on the Tracker.
I'm going to quit the clock application. Notice it's running over here right now, and so the clock app has quit. We still have a fully functional clock view here that acts like a clock and keeps time like a clock, but the clock app isn't running. So it allows you to archive. What a Replicant is or does is allows you to archive an object and you can archive a window or a view and then it sticks it basically inside of a message. It then can be rehydrated into that object.
Now it needs to be able to find the app. The original application code is somewhere on the machine, so if I delete the clock app, the clock Replicant will be broken. But it loads the code basically dynamically when it rehydrates that, it's calling an archive which is a symbol in your code, and allows the clock view to exist inside another application, like the Tracker. And you can do that with, you know, more complicated views as well. Like NetPositive, for instance, happens to have its own dragger. So now we have fully operative NetPositive view living in the Tracker. And you can scroll it and connect to links and here we are ordering BeOS for Intel right here on the desktop. So obviously this is enabled by the fact that the BeOS supports DLLs or add-ons or code that's dynamically loaded while, you know, from another application.
So this resulted in the ability to do as you saw, a really live desktop. Some other OSes would love us. We just barely scratched the surface of services, surface of BMessages and the messages architecture. We didn't mention the clipboard, which also uses BMessages, so when you copy something and you stick it on the clipboard, you are basically sticking that data in a message and giving that message to the clipboard. Then when you paste something you get the message from the clipboard and extract the data and do whatever you do with it. And you can actually create multiple named... you can create our own BClipboard objects inside your application so there is one system-wide clipboard and you can create your own clipboard so you can have multiple clipboards in your app.
Definitely, definitely, definitely read the Be Book, read the messaging section and the application kit, the scripting section, check out BMessage, BMessenger, BLooper, BHandler, they are all really pretty self-explanatory. One of the things... I mean first they were engineered well. Then we have a really great documentation team that helps go through and makes sure the names are consistent, clear, make sense. It really is a joy to look at and read, whether you do it in HTML or get the hard copy.
And of course as always, being a developer technical support engineer, if you need more information, don't hesitate to ask me. Dougsupport@be.com.
Anybody else have any questions? Yes?
A Speaker: It doesn't look like there is a system-wide scripting language like AppleScript on the Mac. Is that intentional?
Doug Wright: You mean a user level scripting language?
A Speaker: System-wide kind of thing that's external to all the applications.
Doug Wright: No, it's definitely a built-in part of every application. The scripting system is a part of the architecture that makes every object scriptable, and you could, as you saw, really easily...
What, did you write that in an afternoon, Doo?
Peter Potrebic: Yes. But he was asking a real scripting language that we ship.
A Speaker: Are you going to have an official Doo like an official Apple?
Doug Wright: That's a Peter question.
Peter Potrebic: We didn't want to invent a language, first of all. And we do have plans or ideas about incorporating, adding extensions to an existing popular scripting language, perhaps Python, so that's something we have thought about. We don't have it planned particularly for release 3 or release 4 or release 5. But it's something we are thinking about. It's also an easy third party opportunity, I think, to incorporate, add extensions to Python to allow you to construct a message, build those specifiers, get back the data, display the suite in a nice human readable format, and so forth.
So you could add... the idea is that any scripting language with some extensions could operate. So we didn't want to bless one scripting language as the language.
Doug Wright: There you go. Yes?
A Speaker: How difficult is it to use this Replicant technology?
Doug Wright: The Replicant technology is really simple. You do have to worry about a couple of things. You have to export a couple of symbols, your class, particularly a function called instantiate. And then you have to implement archive and unarchive. So if you have a view that has a bunch of data that it needs in order to function in your archive, you have to put that stuff away. A BView and our classes already know how to archive themselves, but if you add more functionality you need to figure out what you need to do in order to have all of that information when you are unarchived.
Peter Potrebic: As an example, to make the... back in whatever release we first added the Replicant, to make the desktop a container, Doug just showed you one example of a container view, our system has a couple, has at least one other example, but in the Tracker the author of Tracker had to add seven lines of code for it to be a container view.
There is kind of two parts of the Replicant stuff. There is the thing that you replicate, then a container view. So being a container view at the simplest level is very, very simple and there is more sophistication for preventing certain Replicants and the Replicant thing is scriptable as well, so you can do things dynamically. And so it's very simple at the beginning.
And if you want to expand on the functionality, I have a talk tomorrow where I give some more sophisticated examples of how you can use Replicants. So you can see how it could extend to doing something like a tool pallet interface using Replicants.
Doug Wright: Peter's session is Advanced Messaging and Scripting and Replicants tomorrow. Definitely if you are at all interested by this architecture, and you should really because it's the key to writing bool applications, you should check that out.
A Speaker: One of the popular things to do in scripting is sort of demo playback kind of things. Is there any way to convince an application to tell you what the user is doing so you could record all these different messages that you need to tell it to do to, play it back?
Doug Wright: I do not know the answer to that at all. That's another...
Peter Potrebic: We have thought about it. We don't have anything implemented or ready to talk about now. It's something that we have thought about and know that it's...
Doug Wright: Desirable.
Peter Potrebic: ...a useful thing.
Whenever someone asks a good question like that that is worthy of being implemented I also always add at Be we always try to do things, instead of trying to go for the whole shebang up front that takes longer, makes things more complicated, and try to rush and get everything done, we try to take things slowly, do something, build on top of it, expand the functionality, which means you do get, you know, there are some useful things that aren't there right now.
A Speaker: But you could certainly implement it even with the current version of scripting someone can send a message to your application I want to record, and then once it got that message every time something happened it would send a message back saying I did this?
Peter Potrebic: Right. But the application would kind of have to implement all of that on their own. There is nothing kind of built into the kit to help you record things, or a framework in which to say this is an event you should record, this is an event that doesn't need to be recorded, there is no kind of framework.
Doug Wright: It would definitely be an application-by-application basis.
Peter Potrebic: But it could implement it itself if it wanted to.
A Speaker: Right.
A Speaker: Is Be going to have any place to register stuff that your application can respond to in ways that can be scripted? Are you going to coordinate that at all?
Doug Wright: We haven't done any of that yet. The kind of party line is sending it to dev support, we are going to figure out what to do with it. We don't have a massive system in place to register it. The things you need to establish, those things are publish your API so other people can figure it out. Put it on your web page, say this is what you can do to my app. That's the most basic step.
If you want to collaborate with other developers for pointing out some kind of standard scripting architecture, for certain applications, there is a lot of organizations of other developers that goes on there. Be isn't actively doing that, but we are certainly willing to help you, put you in contact with other people that are working on something.
All right. Thanks again for coming.
(Applause.)
Transcription provided by: