Be Developers' Conference Writing Scriptable Apps Peter Potrebic |
Peter Potrebic: Hi. Welcome. Welcome to this session on writing scriptable applications in the BeOS. My name is Peter Potrebic and I've been an engineer and worked at Be for the last four years and always excited to get a chance to talk with you guys about stuff that hopefully excites me as much as it excites you guys.
(Laughter.)
Peter Potrebic: Excite in a good way, not anything perverse.
What we're going to be talking about today is scripting in the BeOS. I'm going to start with an introduction to some of the concepts in the scripting architecture and this is in the extended track session, so it's going to bend -- I'm going to dive into some more in-depth look into what you do if you want your application to be scriptable.
So we'll look at some code and I'll give you some examples of how you would work with existing functionality that's built into the BeOS, and there is quite a bit of built-in functionality that you get just by using our kits, and how you could extend that functionality as appropriate in your own applications.
So in the BeOS there's a couple key classes in our API that you'll become familiar with just in general as you're writing BApplications and then specific to messaging and scriptibility. And the key class is the BMessage class and that's generic container object for managing different types of data and that's used all over our system, in the messaging architecture, the clipboard, inter and intra application communication and it's also central to scripting.
And a couple other classes are the BLooper class and the BHandler class. The BLooper class is an encapsulation of a thread running an event loop, and this thread is getting messages and then processing these messages and it's just an iterative process.
A BHandler is an object which actually handles a message, so the looper receives a message and then hands it off to the appropriate handler for that message and that's how things get done in a BApplication, and those concepts are again central to the scripting architecture.
And as I had mentioned, the message object is used in many other places outside of scripting and messaging, the clipboard. It's also used as a basis for our archiving capabilities and the replicant technology that you might have heard.
So building -- understanding scripting really builds on things that you already know. If you're using a window, you're already using a looper and a handler, so it adds scriptibility to it. It doesn't really add a whole lot of complexity to the system.
And what the scripting allows you to do is take a particular message and target it to some object in the system, be it in your own application or some object inside of another application. If you wanted to send a particular message to some view in a window in some other application, the scripting architecture gives you that capability.
And as -- as I had mentioned also, the framework allows you to extend that capability as well and new in R3 there's a facility for advertising the functionality that your application has in terms of scripting, and I'll be giving you a demonstration of that later on, how you can probe an application and find out what messages it understands.
Some of the terminology I'm going to go into now. First we have something we call commands. It's basically just the action of the message. There's a predesigned set that Be defines, and you'll find it in one of our header files, for things like set or get, create, delete, execute, standard actions that you might want to initiate on some object.
But any message in the Be Operating System can be part of a scripting message, so a B_QUIT_REQUESTED message, which if you program the BeOS already you're familiar with, well, you can take that message and, using the scripting architecture, send it to a particular object in some other application.
So really any message in the Be Operating System is or can be a scripting message, so it's -- there's really no difference between a message that you might receive through some user-initiated action or because of one of the servers sending you a message, there's really no difference with the scripting messages.
Another concept we have is the concept of properties, and it's basically a name/value pair and it's similar to the concept that you might find in Apple Scripts or Apple Events in the MacOS, and an object defines the set of properties that it has or it understands or it implements.
For instance, a window has views inside of it, so a window might define a property named view. A view has a frame, a particular spatial location, so a view might define a property whose name is frame, and that frame has a value associated with it and that value is a rectangle.
In the case in the first example, a window having a property view, that view is actually another object that itself has properties. So properties both are other objects and simple scaler values, like a frame.
We also have the concept of a specifier and that allows you to select a particular instance of a property. Some -- some properties like the view property of a window, a window might have several views, so you need a way to specify which view you're interested in, be it by name or by index or by some other needs. A property-like frame inside of a view, well, a view only has one frame so just the name itself is enough to identify what object or what value you're talking about.
An example here in the bottom is -- is get -- and I'm just using a pseudo-English style representation of this -- get Frame of View 1 of Window "xyz."
I'm going to use my little laser pointer here so my dad's happy. I've had this for years, it's the first time I actually remembered to bring it to one of these.
So Window "xyz" is a specifier for a window named xyz, inside of whatever application is dealing with this message. And View 1 is a specifier that applies to this window and here we're -- we're using a numeric value to specify a particular view via index, and then we just have frame at the end, which is talking about the specifier property inside of a view and since a view just has one frame, we don't need any further qualification.
So now I'm going to delve into what kinds of things you have to do in your own applications to work with the scripting that's built into this system and also implement new properties and extend existing properties.
First of all, what should you do with the built-in functionality. And first thing I want to do is mention that try to take advantage of what we do. We at Be have spent time making the objects in our system, the view widgets, controls, buttons, sliders, things like that. We've taken time to make them scriptable, so that's a good thing to use in your own applications, to take advantage of in your own applications. And the key there is to not defeat that built-in functionality when you're writing your own application.
If you want to make a fancier looking button object, that's fine. Override, RClass, make your own fancy object, but allow it to continue to be scriptable. And the key to that is when you're overriding various member functions to call inherited and that's how you enable the built-in functionality to operate.
A Speaker: So now we can't use inherit because --
Peter Potrebic: The question was about inherited. When I say "inherited" I just mean the term "inherited," not the key word inherited. It's so funny, you can't use the word in any case because now it confuses people. So I just mean just call your super class, your base class member function that you've overridden.
Now, if you have a specific reason for not doing that, that's fine, but in general that's what I recommend.
So I'm going to switch over to a demo and show how an application that had no concept of scripting when it was written, you know, how it can -- how it's still scriptable. I'm going to launch our mouse preference app here and as I had said, the author of this little preference application had no -- didn't consider scripting when they were implementing it, but yet, with our architecture it's automatically scriptable through the built-in functionality.
And I have a little simple tool here called a Doo tool, which is something that I wrote just to test out this scripting architecture as it's been developed. There is a real third-party tool called hey out there that is very similar to this, using a simple pseudolanguage.
And what this first command is doing here is some stuff that you had seen a couple slides ago, basically. I'm sending a message to the mouse application and I'm specifying I'm interested in Window 0, the first window in the app, the only window, and I'm interested in the view called FFM, which is going to be this CheckBox for the Focus Follows Mouse, and I'm going to ask that view for its value. I'm going to get the value of FFM and when it executes its command I'm going to get back to 0.
So this is an application that didn't have anything, any knowledge of scripting, but yet I'm still able to interrogate the application for useful information and, of course, I can set the value, here I'm going to set the value of that same view to 1, which is going to enable the CheckBox and now I have Focus Follows Mouse enabled. As I move between the mouse between windows, Focus Follows Mouse is enabled.
A Speaker: How do you save the value that you got?
Peter Potrebic: The question was how do I save the value I got.
This Doo tool is just a sample application showing you how -- there's a little parser that builds a scripting message. In a hey tool or a Doo tool you could, just like in any shell script, you can save the resulting output and then reuse it, but this isn't a real scripting environment.
And let me, before I mess myself up, since I don't use Focus Follows Mouse, let me return it to zero.
Okay. Next I'm going to go into how you define new functionality and there's two kinds of functionality I'm talking about here. One is kind of extending what's already built into the system, and that might be by defining some new ways to specify a property or, you know, or extending the functionality of a built-in property.
And then the other way is just defining entirely new concepts or entirely new properties in your own application and I'm going to -- this slide's a little misleading. I really consider this an example of extending property, extending a built-in property. And what I'm going to do to the mouse application that we just demonstrated is I'm going to define a new property called FFM and I'm going to define that property at the top level of the application.
If we switch back here, you can see here my initial example I was asking for the window and inside the window I was asking for the view, and then inside that view I was getting and setting the value, so I was kind of walking this path.
Now, what I'm going to do is I'm actually going to define the property FFM at the highest level of the application and what that -- that new property is going to be is a shortcut to get to that same CheckBox. So it's kind of like a pointer to the CheckBox, so I don't have to navigate, window, view and then actually get and set the value of the CheckBox. And I guess let me demonstrate that now.
So here is this -- so now directly in the mouse application I can skip all that extra stuff, all this and I can directly ask the mouse application to get me the value of FFM. That's the new property I defined. And you can see I get 0 again, just like I did in the initial example and if I execute the set command now, the CheckBox is checked and the Focus Follows Mouse worked and I can restore the value back to zero.
What I'm going to do now is show you the code that the author of the mouse application would have had to write in order to implement this extended FFM property.
The first thing they would have done is used something that's new in R3, which is BPropertyInfo class and an associated property_info Structure. And this is a structure where you describe the properties that this particular object understands and you describe the specifiers that can be used to identify a particular property.
And in this case the name of the property is FFM. This field here is empty, there's -- for more sophisticated properties there might be some information in here and FFM is a direct specifier, and that means I don't need to specify it by index or by name. There's only one FFM at the top level of the application, so all I need is the name. So that's the constant B_DIRECT_SPECIFIER.
So I would have created this little structure and then I would have written -- overrode an existing member function in my mouse application object, my subclass of -- for the entire mouse application. There's a member function called ResolveSpecifier and that's where you hook into the messaging architecture in order to get a message directed to the proper object in the system.
If you remember, we had that -- the initial command gets processed by the application, Window 0. The application gets the message and it sees Window 0, so the application decides, finds Window 0 and gives the message to Window 0. Window 0 then sees View FFM, finds that view, gives the message to the view, and then the view looks at value and says okay, my value is true or false or 1 or 0. All that happens inside the Be Operating System kind of automatically.
Now, when you add new properties that you want to direct to existing objects or new objects, you do that by overriding this ResolveSpecifier, hooking into that framework. And what you do is you -- you take that property_info Structure that you defined previously that just had that FFM property and you use this new class and you call a member function FindMatch, which the term is if the current message matches one of your defined properties.
And in this case we only had one property, the FFM property, so if we get back something greater than 0, we say aha, this is the FFM property and I know that this FFM property is really a pointer. Remember, we decided it's just going to be a pointer to that CheckBox.
So what I do is I just redirect the current message that I'm dealing with to that CheckBox. In my application I know, you know, this is my application code so I have a pointer to that CheckBox. So all I do is I send that message directly to the CheckBox, and otherwise I just call inherited. And here is one of the places where it's important to call inherited if it's not a message that you understand, that you're dealing with. So you call the base class BApplication::ResolveSpecifier.
And that's all the code that the mouse application would have to write in order to implement that new style, new FFM property.
Question?
A Speaker: I'm just wondering what are you returning in this case that this was the -- your returning target -- oh, no, it's just a NULL. Okay.
Peter Potrebic: Yeah, it's initialized to NULL.
If this application object determined that the message is for me, I understand the message, the application object itself, then you return -- someplace in the code here you'd set target to "this" and return. But in this case it's going to be NULL, which will say okay, it's not for me, but that's fine because you've redirected the message to the right place.
Okay. So now I'm going to get to defining a completely new property and again, I'm going to call that property FFM and I'm going to say that this is actually the more appropriate way to do it in most cases, and what I'm going to do is say that the mouse application has this concept of Focus Follows Mouse being on or off and -- and in terms of scriptibility I don't really want to simulate manipulating the user interface, I don't want to simulate clicking a CheckBox. I just want to turn on a state or turn off a state, true or false.
So what the mouse application is going to do is it's going to define such a property. It's just going to be a setting in this application that you can interrogate or that you can set. And I'll do that with a slightly modified version of the mouse app.
And now the command that you have to send to it is even shorter, not that necessarily shorter is better, but so now you can send the message to the mouse app just saying get me the FFM, because now it's just a state at the -- at the highest level of the application object and it's a boolean state, true or false.
So I send that message and I get back false, because it's telling me -- and I can see in the CheckBox here that indeed Focus Follows Mouse is off and, of course, now I can say set FFM to true and now Focus Follows Mouse is true and it's up to the application, the mouse application, to decide that oh, when the Focus Follows -- Focus Follows Mouse state is enabled, that I want to display that by having a CheckBox checked, but that's kind of divorced from the scripting interface and that's what many people would say is kind of the better interface.
A Speaker: Can we see the previous code again?
Peter Potrebic: Let me set it back to false. I don't use Focus Follows Mouse, so...
Okay. You have a question?
A Speaker: Can we see the previous case, the first one?
Peter Potrebic: The question was to see the previous case. It was here.
A Speaker: So you kept the value. Okay. Thank you. Very interesting.
Peter Potrebic: And now let's get into the code that you would write to implement that. There's a question?
A Speaker: Yeah, your ResolveSpecifier example. Since you weren't returning anything, how was it that your gets -- your get request is actually going back to the value?
Peter Potrebic: The question was how in the previous ResolveSpecifier -- now, this was an extended session, so it kind of assumes some knowledge of the BeOS, although that's not necessarily the case, so these questions are fine, but it might be kind of difficult to explain some of those concepts without having a lot more slides, but I'll try.
So the BApplication object got this message and it knew that the message, when it got into the FCase here, it knew that the message was destined for this other object that lived inside of another window, so all it did is it took that message, which was here, and it posted it. This is sending a message to this object.
A Speaker: Maybe I'm not being clear, maybe I'm missing something obvious. It's clear to me how that sets it, because it posts the message, but when you called it get and said your little scripting widget said return the value to me, it's not clear how this is going --
Peter Potrebic: So the question was how did a reply come back to my little Doo tool.
Any scripting message you get, part of the protocol is that it's a get, you're obviously going to put the data into a message and then reply.
A Speaker: So the thing that you posted to is handling a reply?
Peter Potrebic: Right. My next example shows you, there's code where you'll see the reply being built up.
So, okay. So here's the code you'd write for this. Let me go back to refresh people's memory now. So now we're dealing with a direct property FFM that's just a boolean property, true/false, it's not a pointer to a CheckBox and that you can get and set.
And the code that would be written for that is right here. And again, you use the BPropertyInfo structure and now it's a little bit different than the last time. It's still named FFM and this property information object would be associated with the application object. The application object is defining FFM.
And what it says is you can get and set that property, as we saw in the example. And again, it's a B_DIRECT_SPECIFIER, you don't need any further qualification.
And now we'll look at the ResolveSpecifier code, which is only slightly different than the first example and it actually kind of goes, something I mentioned earlier, the mouse application overrides ResolveSpecifier. It uses that structure that we just saw and initializes this class. It uses this kind of matching, parsing routine to see if the incoming message matches some of the properties that it defines and if it does it returns you an index into that potential array of properties.
This example there's only one property, so any positive value is good enough because it's always going to be 1. And if it does return 1 that means that this is a message that this object understands. The application object defined a property FFM, so it wants to deal with it. So what it does is it sets the target to be this. The else case is exactly the same and then it returns the target. So in this case it's going to return this, which is going to tell the system okay, it's this object that wants to handle this message, getting or setting FFM.
So that the current message will now get handled in a normal way of handling messages in the BeOS. So now the mouse app would also have to write some code to actually handle the message, to actually do something and that -- all of message handling for nonsystem messages gets handled in a function called MessageReceived.
So that same mouse app object overrides MessageReceived and to simplify things so it could fit on one slide I plan to remove stuff from the top here and there's logic -- if this code handles multiple messages, and a real application probably would, you'd need some logic to determine, yes, this message is the FFM message and there's helper routines in the BeOS in order to help you figure that out.
Okay. So we've determined we are dealing with FFM. Well, there's two cases that I, as the author, have defined. You can either get FFM, or set FFM. Well, if the current request is to get the property, all I do is I have a reply message and I add -- add to that reply message a boolean value in the field called result and this is a standard place where you put the results of messages, of scripting messages.
And again, I'm the application so I have some object that knows is FFM on or off, so there's some function I can call that tells me. So this is, you know, whatever that function is and in the case of the mouse app there's some global object that has a member function that returns a boolean and that's all I have to do.
And then what's not shown down here is you'd have a send reply command that would just send the reply to whoever in the system sent you that request. This else case is going to be if you want to set the property and it's similar. Let me move the --
Now we're trying to set the property so what we're doing is we're expecting a boolean value, and I've removed any error handling here in terms of if the data isn't there or in the wrong form or whatnot, just to simplify the code. Where I find the value, I find the boolean value, save it in its local variable and then I set the FFM state.
And another standard part of our scripting architecture is that you return an error code and potential -- if there's a real error you'd also want to return an error message, a descriptive hunk of text. In this call is where basically all the beef happens and it's where the mouse app decides when FFM is on or off, it enables or disables a CheckBox. So that's what you would write in the latter case.
So just to talk a little bit more about, you know, how to decide what you want to do and I mentioned most of this -- most of this before. It's important to kind of think about your application when you want to make it scriptable and define properties that make sense for your particular application. If it's a spreadsheet or if you're dealing with media, you want media-related terminology, things like that.
And that's kind of the process I went through with the mouse app, where manipulating the CheckBox might be nice because the app developer didn't have to think about it, but actually defining an FFM state, Focus Follows Mouse state seemed more appropriate. And in the same token, in looking at the mouse application, having a mouse setting speed and a double-click speed just adds numeric values that you can get and set to the mouse application, would be the right way to implement scripting in the mouse application, not by manipulating the particular sliders in the application.
A Speaker: I just want to ask you this: The -- right now when you launch Doo, which I realize we don't have a real framework at this point, we have hey and Doo, it launches the application, sends the -- and sends the script. So now if you wanted to set those properties via the mouse application, could we do it without actually bringing up the dialogue?
Peter Potrebic: So your question was how could you do some of these things without actually launching the application?
A Speaker: Because if you have a script, if you're writing a script, why would you want a dialogue or a window view to come up at all?
Peter Potrebic: The question was why would you want -- if you're dealing in this case with Focus Follows Mouse and mouse speed, why do you want a dialogue at all?
And in this case, yeah, you're probably right that you'd want -- a real scripting environment would probably want to define some system level stuff that you can just manipulate directly, such as mouse speed, because you don't want to have to have a window open up just to do something like that.
So yeah, I would agree with that, but for the demonstration purposes, you know, if you're scripting another application that you, you know, that you do want visible. I don't know if I'm being clear.
A Speaker: Well, what I was wondering if there'd be a way to actually define a faceless running of an app, running an app in a faceless mode, so that when it was run by a script it wouldn't put up any of its windows or dialogue, knowing that it was being manipulated from a script. So it'd be some sort of boolean faceless, don't even show anything, you're not being run by a human.
Peter Potrebic: The question was could there be a faceless way to run an application so that it would never appear, and there would be ways to kind of do that. You could have it open up in some other work space that wasn't currently on the screen and things like that, but there's nothing built into the OS support for that.
I'm going to talk a little bit about suites and the concept of suites. There was a simple concept in the last release, PR2 and that's gotten a lot richer, a lot more robust in R3 and it focuses on this property_info Structure that you've seen a couple times where -- and you've seen it in action for dealing with messages, and it allows -- but it also allows an application to advertise its functionality.
And basically, I've just repeated here that last property_info Structure that we've seen and so once you've defined such a structure describing the types of commands an object understands, you also want to work with another member function called GetSupport -- oops, should Be GetSupportedSuites and you would override that function and here's our mouse application again overriding GetSupportedSuites and in PR2 you might well have had this code, this first two lines, where you define a name for the suite of messages that you understand, and that suite could then have a published written description that people could access on the Internet or wherever, describing what that means.
But now in R3 you can also take that property_info object that you've built up and add that to the message as well. And then here again we're calling the inherited version, just to make sure that all the right things happen and any inherited functionality also gets described in these two ways and by overriding this function you automatically have a way for someone to interrogate the types of messages that you understand and that's with the built-in property called suites, and I could demonstrate that.
I can ask the mouse application, I can say get me your suites and my Doo tool just kind of dumps out that information in a somewhat pseudohuman, readable form. So I've executed that command and what I get, first I get a listing of the suites that that object understands and I'm going to read from the bottom.
At the bottommost is a suite called handler. In our system an application object is also a handler and a handler defines a certain core set of properties that every handler understands. We are also dealing with the mouse application object, so it's also just a generic BApplication object, which defines a certain set of built-in properties, and in addition, we've customized it to understand, in our case, just the FFM property and we've given a name to that as well, so that describes the final set of properties.
And then we also get this -- all this new stuff, which is a tape. I'm going to again read from the bottom up here.
These are the three properties that every BHandler understands. It understands the SuiteProperty, which is the one we just used, it understands messenger and it understands InternalName, and for each of those properties all you can do is get them. This is the get command, backwards on Intel.
At the next level there's the window property application, it understands windows and they also have a name. Here is a command that you can count the number of windows in an application and then finally up here we have that FFM property that we just defined and you can get and set that property.
So using this property_info object and the SuitesProperty gives you a way to add runtime, interrogate an application or a scripting environment could kind of build up tables and present them in a human, readable format for people to make use of.
A Speaker: Excuse me. I notice that we say that we can get and set the property of FFM, but we don't say that it's a boolean.
Peter Potrebic: Right.
A Speaker: Is that --
Peter Potrebic: The question was this describes that you can get it and set it, but not that its a boolean and that's correct. Currently the data type -- in a reply you can find out the data type, because of the messaging architecture, but to set you need to know apriori that the value is a boolean.
A Speaker: I was just wondering if that's going to be in a future version of the suites tables?
Peter Potrebic: Probably.
A Speaker: Because this is a very important, very rich functionality.
Peter Potrebic: Yeah, and as with everything in the BeOS, this is something that we've designed so that it can grow and get better over time.
Just a couple tips and tricks. I probably said this first one five times today. Call inherited, not the key word, but the parent class. The BPropertyInfo object is a new object and take advantage of that, you know, it does a nice job of allowing you to express the functionality in your system and its key for working with GetSupportedSuites.
And another trick is in this property_info objects having duplicate entries, and I'll explain what that means in a moment. It's helpful for parsing incoming messages, it makes that FindMatch function that we looked at earlier a lot more powerful.
Here's an example. Suppose you had a spreadsheet and you wanted to cell property, things you might want to do with that cell property. You might want to count the number of cells there are. You also might just want to get the number of cells by -- you might want to get a cell by index or by name.
And in the property_info object I would choose to describe that -- that functionality something like this: Where cell, I can count the number of specifiers and I can also access cells by index or cells by name and by breaking that up into three different entries in this array, it makes the FindMatch function which I described, it returns you the index into this array of the match.
It just makes FindMatch more powerful. It will tell you okay, someone asking by index or someone asking by name. So it's just a little trick that I didn't even realize I could use until I actually started using this structure in the existing view widgets in the interface kit.
So that's basically all I had to say about making your applications scriptable in the BeOS. There's lots of places where you can get information and help in making your app scriptable. It was really exciting to hear all the Adamation stuff that they're gung-ho about scripting and they're using that to integrate all their applications together. That's exactly the kind of things we hoped people would be doing, so if you have a questions or feedback, comments, things like that, let us know.
Questions? Right here.
A Speaker: Over on the Windows Mac side I work with a product called Director, which has things inside it called extras that are controlled somewhat like this, which has support for properties that you can get or set or/and for functions that you can call with arguments so you can specify like a set of different things.
I notice here you mention setting and getting properties. Is there any sort of support for functions now we're planning for the future?
Peter Potrebic: The question is we can get and set properties; can you actually initiate functions or call functions.
A Speaker: Yes.
Peter Potrebic: And the answer is yes, you can, if that's the -- the properties that you described can be actions. One of the default commands defined is execute, so you can do things like execute a menu item, which will, you know, initiate the action that selecting the menu item would do, so that's a way of calling a function. And your application could define properties that take arguments, take data in that message and along with an execute command, you know, do whatever it is that that property defines, so yeah, you can.
A Speaker: Because the messages can be quite images, they're not -- even though they come in pairs, you can build quite a images message and then it just gets parsed down.
Peter Potrebic: Yeah. Other questions?
(Inaudible.)
Peter Potrebic: The question is if you send a message to an application that isn't open, does it get opened automatically.
No, it doesn't. So if you were programmatically creating these messages, rather than with the Doo tool, there is simple API for determining is an application that I'm interested in open. If it is, you just send it the message. If it's not open there's a simple way to launch that application and send it a message as well.
A scripting environment might choose whatever interface it wants for doing that.
A Speaker: I missed some of the beginning of the talk. Was it your intention you could use the Doo tool in basically any scripting environment, such as, I don't know, you could do it in bash, you could do it in some kind of interpreted language in the shell?
Peter Potrebic: The idea of the scripting -- the question was was the Doo tool intended to be able to be able to be used with any scripting environment.
And my answer is that the scripting architecture was designed to work with any scripting environment. The Doo tool is just a little shell tool that's cheesy and if you mistype something it will crash.
A Speaker: Is it different than Natel?
Peter Potrebic: Well, Natel is a cheesy tool that if you do something it will crash. They're all just bin tools.
We do have plans on the drawing board, we don't have manpower assigned to looking at Python and adding some functionalities to Python to be able to build up arbitrary BMessages, to be able to define these specifiers, Windows 0 Frame, whatever, be able to send those messages to applications, to be able to launch applications from Python, to be able to get the replies back and interrogate the reply message to find out what happened. And that's the kinds of things that we'd see in Python or in any other scripting language or Perl or whatever.
A Speaker: Excuse me for asking why Python. I mean Python is a very structured language. I mean it's very rigid in its structure, as opposed to something like Perl, for example, but I mean that seems like an odd choice.
Peter Potrebic: The question is why Python.
The answer is because, you know, that's -- we are not trying to define a new language. We don't want to write -- we don't want to be Apple and come up with Apple script or we don't want to be somebody and come up with the next greatest language. We just want things that are out there. I don't think doing any of this to any particular language is very difficult.
You know, everything is reused so much, you just need the ability to manipulate a BMessage. Once you have that you can do -- that's like just about everything, so it's not a lot of work to do to any particular language. So we'd be happy for it to be in twenty different languages, and I've talked to some app developers who are thinking, well, in my app I'll probably have my own custom language, because it's so appropriate and it will be so much easier for my users to make use of it.
Well, I don't think adding scripting support adds that much complexity to defining a language.
Other questions?
All right. Thanks very much.
(Session concluded.)