Approaching Applications |
WILLIAM ADAMS: Hello. Okay. Question already?
A SPEAKER: The Java on the advanced access CD?
A SPEAKER: Repeat the question.
WILLIAM ADAMS: The Java on the advanced access CD, is that the question? The Java, I'm afraid to ask Metrowerks. It will show up soon. I don't know an exact date.
A SPEAKER: Okay.
WILLIAM ADAMS: No more questions.
A SPEAKER: Does the BeOS file system actually support indexed files where you can look up things by an index?
WILLIAM ADAMS: Are you talking database like?
A SPEAKER: Yeah, database like.
WILLIAM ADAMS: We're going to get into some of that in this session so let's hold it for later. If I haven't answered it by then, ask it again and we'll get into the details. So this session is Approaching Be, even closer. In time I'm actually going to give you information that's useful for programing.
And the general idea is I'm going to go through 2 or 3 apps, depending on the amount of time that we have, and try to give you a feel for what the general architecture is, what you have to do, what you don't have to do, and give you some visuals, run a couple apps, and then we'll deconstruct them. And in this session I think it will work out a little better if, while we're going through it, you ask questions because I think we'll get closer to the information you desire more quickly. So as it was pointed out to me, too much hype. So okay.
Okay. A typical application in the BeOS, and I'm going to talk primarily about GUI type things because I think that's the most broad and easy thing to talk about, and the first application I'm going to talk about is the Calendar app.
First, let me show you this thing so we get a feel for what it is we're going to be going through. This is one of our sample apps that's just been laying around for years and years, and it's a calendar. And some of it is interesting features.
As you'll notice, when it gets really small, the letters get smaller and the numbers get smaller. And when it gets really big, things get bigger. And it resizes to fit the size of the window that it's being dragged around in. So, a very straightforward application. There's no date navigation. You can't do really anything interesting here. But I think it's a good jumping off point for showing what's going on.
As I pointed out in the last session, every window is in its own thread. And the general architecture of the system is that you have an application object. It's a C++ class called Be Application. And that's usually the first thing that you need in order to get started. The way it's architected is there's server called the app server and it kind of lives, say, on this side of the wall, and the app server is responsible for getting keyboard and mouse events and getting them up to your app, targeting what view the mouse is clicked in, and where key presses should go and things like that.
The application object is on this side of that wall and basically is your connection to the app server. So everything, drawing, keyboard, anything, it ends up going through this connection that's been established, either through the application object or through a window object. But basically this is a jumping off point. Window is a window. That's where eventually drawing takes place.
Windows, depending on the type of application you have, you'll typically have a menu bar. We don't have, say, on the Mac, there's a single menu at the top of the window and applications swap between that menu. In windows, the menu bar is typically in the window. That's how we do it. The menu bar is in the window. So there's a menu bar.
Views are things that actually do drawing and can handle things like mouse downs and key downs if you so choose to implement them. And you typically do drawing and that happens through the view. The view object is basically your graph port. It's the thing that knows how to actually draw lines and ellipses and circles and what not. In 90 percent of the cases, you're going to draw through a view. Sometimes you'll draw directly into a bitmap, but most of the time you're going to draw through a view.
Loopers and messaging. A looper, previously we talked about threads, threads, threads, threads, threads, thread of the world. Threads are the savior. Loopers are kind of an extraction of threads. If you will, it's a guy who kind of sits around and says: If you're familiar with the Mac, I'm the event loop and you can have one of these event loops per thread.
And it's basically just sitting around waiting for someone to give it a message. Those messages can be things like why don't you close the window, or why don't you turn up the volume on that sound device, or whatever.
A very important concept in the BeOS is this whole idea of messaging. And it helps out in the multi-thread environment, because to eliminate some deadlock situations and to make for much cleaner code, it's much easier to send a message to someone to do something at their leisure, as opposed to trying to tell them to do something and waiting for them to complete it. So if you will, it's an asynchronous way of doing programing.
So on one hand, you have a looper, which, in turn, on the other hand, you have messages. And there are different ways of sending messages to loopers, but essentially you send messages and they eventually get handled and then you go on with your world on this side.
So in the context of this -- oops. In the context of this calendar application, how does this -- what does this mean?
Okay. First of all, can you guys see that or should I make it bigger?
A SPEAKER: Bigger.
WILLIAM ADAMS: Bigger. Okay.
A SPEAKER: Most of us wear glasses.
WILLIAM ADAMS: Me, too. Okay. Let's see that. That will probably be big enough. Sorry. I should have done this already. Oops. Sorry. Well -- oh, sorry. Let's try it again. This --
A SPEAKER: Go back to fonts.
WILLIAM ADAMS: Sorry?
A SPEAKER: Go back to fonts.
WILLIAM ADAMS: Okay. You can tell I'm a real -- I don't usually change my fonts size so that's why I'm kind of lazy.
A SPEAKER: That's fine.
A SPEAKER: That's it.
A SPEAKER: You're done.
WILLIAM ADAMS: Thank you. Your check will be in the mail. Yeah, I didn't think so. Here, let's just go forward. I'll point out what's going on. It's not that difficult to figure out. In this case I have an application class and an application window and a main. Here in the main what's going on is I create an instance of my application. Lights?
A SPEAKER: Can we dim the room lights?
A SPEAKER: Yeah, please.
WILLIAM ADAMS: Can we dim the room lights?
A SPEAKER: If you highlight it with the mouse, the reverse video may help.
WILLIAM ADAMS: Okay. Does that help?
A SPEAKER: Yeah.
WILLIAM ADAMS: I'm going to create an instance of my application object, which is what I do right here. This is just a signature so that it's uniquely identified in the system. I'm going to create a window, which is of my particular window class. I'm going to give it a rectangle which is going to be where it's going to be located on the screen when it comes up. I create a view to put inside that window. I add the view to the window. I show the window, then I run the application. This application run loop is not going to return until the application actually quits and at the time that it quits, I can delete the application object and everyone gets cleaned up nicely. If we want to look at -- so that's your basic main.
Now, you can segment things differently. You can have your application object actually create the window and the window can actually create the view and you can put it together, however you want. But essentially this is what you're going to do.
If we look in this specific case at the application object, or the application window, one thing that you're responsible for, windows on their own, when you close them, will not quit the application, so that's behavior that you have decided when this particular window is closed, I want it to shut down the application. And the way you do that is you subclass and implement this Quit Requested method. Quit Requested gets called when you close the window by clicking on the close box.
And what you do, in turn, is send a message to the application that says: Hey, the guy wants to quit. You're just sending it off, a message, again. If I said that, here's the message, here's the looper, basically, waiting for a message. Send the message over to him, eventually he says: Oh, quit. Okay. And he quits and closes down the application. The window is automatically going to be closed. So that's really all we had to implement the window class forward is for that quit.
Again, I could have put the view code inside of there, but it's not how this one went. The view itself, there's a few things that you do when you create a view, and I'll highlight this part right here because that's what is interesting. When you create a view, you give it a name, a name that gets used in the out server, and you can actually go and look for views based on their name. Follow all is a thing that says, if I resize something, follow all sides. You can do things like follow the upper left corner, follow the lower right corner, follow the right side, left side, whatever.
Full update on resize, that's so that the view drawing is optimized, so that -- I'll give you an example, if that could easily. . .
When you look at the Calendar application when it's normally running, you notice when I resize, it redraws the whole view. That's why it's flickering a little bit. What's going on there is since I have this flag in here that says redraw, do a full update on resize, if I didn't do that, the view system would say, well, you resized the window. There's a full exposed rectangle on the right and on the bottom. The rest of it doesn't need to be redrawn so I won't redraw it. So it doesn't.
If you say full update on resize, when you resize, it won't just give you this clicked area to draw in. It will give you the whole area. In this particular application, that's what I want. I want to redraw the full thing because I relay out the size based on the size of the view. If you had an application that wasn't like that, let's say it's a -- an image viewing problem where you just click the edge of the image, you wouldn't do full update on resize. You would say okay, resize. I'll just draw that little strip that needs to be shown.
Will Draw means that you will draw. It's pretty straightforward. And Frame Events means that you will get the resized events. If you don't put that in there, that particular flag, then you won't be sent the events that say, oh, the frame resize, you need to do something about that.
All these, having the different flags in there, allows you to get efficiencies, because if you don't want the frame resize events, and you don't want to do the -- if you don't want to do the following all sides, these are all events the system doesn't have to pass down to you. It makes it more efficient. There's less to do. So we give you the ability to control how much does and doesn't get done in your application by giving you the different flags.
A SPEAKER: How do you redraw on a mouse up?
WILLIAM ADAMS: How do you redraw on a mouse up?
A SPEAKER: Instead of having to click there, redraw the whole rectangle?
WILLIAM ADAMS: Well, the right way of doing this without flicker would be to draw in a mouse with bitmap outside of the view of the user and just blast the bits to the screen. That would do it.
In this particular example, it doesn't do it like that. The code was actually implemented with an off-screen buffer as well. But I found that the speed wasn't really that much dramatically different and it's a little more complex to deal with an off-screen bitmap. So I just decided to simplify it and not use it. But if you want no flicker, you can use off-screen images.
Here you see the frame resize call, and, in particular, you're past the width, and the new width and the height that the frame is going to be. And in my particular case, I have a graphic object that I want to tell it, your new size is this size.
The way I constructed this particular application is I abstracted the drawing code into a single object. And we'll look at that next. It's a GMC graphics object. And it knows how to draw a calendar at any size, so you set the size. And then when it comes time to draw, it will actually do the drawing. So when you look at the view itself, the draw method is very simple. It just tells that graphic, draw yourself into me.
Because the the view, again, is the graph port, it's the thing that knows how to actually do drawing. It's the place where you go to draw a line and draw a circle and art and what not. So that's the extent of the draw.
Now, what you'll notice here is I have an update rectangle. That's telling you exactly what portion of the view needs to be updated. In this particular case, like I said, the background doesn't really care. It says, ah, I draw pretty fast, I'll just draw the whole thing and don't worry about it. I'll take the update rectangle. In the case of app image viewing application, you probably would care about the update rectangle and you would only blast the bits that are newly changed, and not the whole thing.
So the last part of this is the graphic object itself. Now, this is the meat of this whole thing, and I'm not really going to go through a heck of a lot of it, but this is just all the code that deals with, okay. If it's this size, then make the screens this width, and all those sorts of things. It's just a bunch of calculations that go on, depending on what size the view is. But the more interesting part is when you get to the draw method here, and you're handed the view that you're supposed to draw into. I called it a port, just for ease.
Now, what I do is I do some calculations, figure out where my labels are supposed to be based on the size. I set a particular drawing mode. There's over, invert, copy, things like that. These are probably familiar to you. I fill the background with a particular color. This is is an RGB combination, basically a gray, this gray that you see in the background here. And then I set a high color to white, and I fill this grid in here, which is this white area where the numbers are going to be laid on top of. And then I draw. And then I draw a rectangle around the area. That's what this StrokeRect is. So basically we have 2 types of primitives, filling primitives and stroking primitives.
Filling primitives fill in and stroking primitives do outlines. So FillRect, FillElipse, FillCircle, FillArt and StrokeRect. You know, I think you get the idea.
A SPEAKER: What do you mean by high color?
WILLIAM ADAMS: High color and low color have to do if you use patterned drawing. All these draw calls have the ability to give it pattern so you can do, you know, checkered, or something like that.
Well, when you do a checkered pattern, you need a high color and a low color, just 2 colors basically. Otherwise, the high color is typically the pattern that is used by default if you don't give it a pattern, as an argument is the high color. High color, solid pattern. So that's why you set the high color. So if you just, you know, remove the high part and just think set color, that is now the color you're going to be drawing in, both text and graphics primitives.
So this date label is another object which is a string label that knows how to do left, centered, right. It's not particularly important. So I just draw the column labels, these little labels showing the days at the top. And then I do some funky calculations and draw the grid lines.
Now, this is an interesting feature of the system. Every time you do -- every time you draw something, you make a trip potentially to the app circle, which, again, was this side of the wall. It's a server. You send a message to that server and it performs operations for you. This can be expensive because, like any server, there's overhead associated with packaging up the parameters and sending them across.
One way to make that a little simpler, at least for the case of lines, is to create a -- begin a line array. This is a way of packaging up several draw calls and saying, here, do it all at once.
So what you'll see at the top here is that I'm saying, begin line array -- actually, this should be 6, but that's okay. And then I draw -- I draw these lines, if I stop moving the code now. So here I'm packing up several lines to be drawn and then at the end of that I'm saying, okay, end that line array and --
A SPEAKER: Do the undos.
WILLIAM ADAMS: Undos is for wimps. So I undo the line array and it will blast it all to the server and we get faster line drawing. So here is for the horizontal lines. The same thing, I've got a line array, I blast to the server and move on. And then I go and draw the individual days into -- the date numbers into the calendar, and that's just simply calculating, okay, what's the starting day, and then just creating a string that has the day number in it, and, in particular, calling the draw string -- actually, I'm using my string labels again, so I can use them left centered or right just identified to get those things onto the screen. This kind of -- yeah, I'm going to show this string label class because I think it's interesting.
So are there any questions about that? I think you get the general idea; right? You have windows, you have views, you can draw, do views. There's -- if you saw in a couple places that are drawing with string labels, you can create views to draw those labels.
You know, you don't have to do them separately. But why would you or why wouldn't you? Well, views are resources that there are not an infinite number of them. Views are created on the app server and the app server deals with them. And in my opinion, it's not a good idea to have infinite numbers of resources out of your control, you know. They are over in the app server. You don't know quite how many you're going to put in there or not. And it just seems like it's not always the right idea. And on top of that, views have a little more heavyweight than if you don't create views.
In this particular case, I created a string label and it's particular purpose for me was to be able to draw a string on the screen without having to create a view. Because I wanted something that was very lightweight that I could just say, you know, I want you to be left center, right just identified with this slant or rotation and what not, and I just want to draw you where I tell you to be drawn, when I tell you to be drawn. I don't need any automatic handling. I don't need mouse down or key down or auto growing or any of that other stuff.
So here I have several interfaces that allow me to set things on the view like -- not the view, on the label which are like the text, the alignment, where it should be, the font, all these pieces of information. And then I implement a draw call where it can just draw into the view. And if you're familiar with the -- I don't know, the Apple system where you've got geometries and graphics, this is basically a graphic, and you draw graphics into ports, the port being the view. It's a quick and handy way of doing things and wrapping things up.
And you can also package up the whole calendar graphic and put it anywhere. So any time you need a calendar of a particular size, you just draw it. You don't have to subclass off of a view class and do all this drawing. You can just package it all up in a handy little thing and away you go. So okay.
I showed you this application as the Hello World, but, really, we do have a Hello World that's even simpler than that. There's not much to it. Hello World. And again, it's the same thing. This application has, if you look here at the project, that's the end result and the number of files that are in the project are about the same as the other one. I've got a main, I've got a window, I've got a view. In this particular case, I don't have any graphic object to do any drawing because it's so simple, I do it just right here. Move to where you want to draw, draw the string. This is fairly typical. Okay.
And my second one is -- we can deconstruct Mandelbrot, but I think it's a bit too complicated. One of the applications that comes with this system is called Rraster. And the idea behind Rraster is to be a quick and easy image viewer. And if you're already a Be programer, you've probably heard of Data Types. This is not Data Types. This is a teaching tool, so it's kind of put Data Types to the side for now.
Okay. So you've got the general idea is I can drag and drop a file onto the icon. And here is that graphic and I can move pieces of that graphic around and even -- and the general idea is this is a GIF file and I can also read Targa files, and various other formats. So as an application, this one demonstrates the basic things that we saw before, which are, okay, I've got a main, I've got an app, I've got a view, I've got a window. But what are these things?
First, we'll go into the application and the window. They're a little bit different than the previous applications. Both the Calendar and the Hello World, you couldn't drop anything onto them and open them up. But that's a common sort of thing you want to do, drag and draw. So the way you do that is your application, if you want something to open up by dropping it on top of your icon, you implement this method called Refs Received and basically what you're going to do is -- I'm not going to go too much into what a ref is in the first place, but basically it is a thing that represents a file. It used to represent a whole lot more in DR8, but it basically represents a file. And once they're dropped on you, in this particular application, I turn it into the name of something, which is what all this is about, and I ultimately open that file.
Now, Refs Received can get called also if you have an open dialogue panel. As soon as you click on a file and say, okay, open that one, the dialogue -- the open file panel will call your Refs Received and say this is the file that he chose to open. So that gives you the same mechanism for opening files, whether it was dragged and dropped on top of you or opened from within the application. You don't have to implement, oh, well, dialogue open is this way and Refs Received is -- drag and drop is that way. It's all combined into one place.
And here we also see the concept of the BMessage, which, again, in that first slide, there's the message and there's the looper. The message is basically a bag that you can stuff data into, that data can be anything. It's named data, which means that -- well, in this case, you see there's a piece of data called refs. And I'm going to get out an item which is a ref because I had to find it as such up here. So I can get out -- I can put in and get out things like integers, longs, floats, rectangles, points and just random bits of data. So you can stick all of the stuff into the bag and then you can send it to somewhere. And that somewhere can pull out what they need and do whatever they're going to do with it.
In this case, I pull out the thing that says, this is a representation of a file, now, go open this file. So that's what that's all about. Is that -- does that BMessage concept -- is that clear to everyone or should I go about it? It's kind of important.
A SPEAKER: Now, which part of the windows is sending the message? The view, the icon?
WILLIAM ADAMS: In this case, what's happening, when you're out here in the wide open desktop, there is an object or a server, basically, called the Rraster. And what the Rraster does is when you're in the -- when you're in here, it knows about what applications are around and what windows they have and things like that. So when I go and click on something and drag and drop it over here, the desktop application, tracker, is basically saying, well, what application is that? And launch that application.
And once you launch that application, I'm going to send a message to it that includes the reference to this file that they dragged and say Refs Received and then it's going to open it.
So eventually, it happens through the tracker that your application gets launched and then the message that has the file pointer in it gets sent to it. And that's why it, you know, opens up the graphic.
A SPEAKER: If Rraster was open without a file and then you go click on the icon and drag it in there, what part of that window is responsible for sending the message to be received?
WILLIAM ADAMS: Okay. I got it. If I do something like this, who's responsible for doing the right thing?
A SPEAKER: Right.
WILLIAM ADAMS: It's in message received or the window, which can do the same thing as dragging it onto the app, if you want. In this particular case, I don't have that implemented. What I have is saving, saving is this bit, and this particular message which is opened. You would have another message here, where the what field, and this is just on what kind of message it is, says was dropped, or actually, you ask the message, were you dropped? And then you can find out whether it has Refs Received or not.
So the window would be responsible or can be responsible for intercepting that message and opening a file based on that. And you can basically do it by calling Refs Received on the application if you want or you could just open it directly since ultimately the window knows how to open a file anyway.
A SPEAKER: Can you say again what structures are in a message?
WILLIAM ADAMS: Oh, sure. Let's go look at the real thing. We'll look at the real deal.
One thing you'll notice when you're playing with the system is in our headers we have structured into what Be has, what's in the C++ library, what's in the new library and what's in the POSIX library. That's just a way of organizing things and message is a part of the application kit, if you will. And the message object has -- you can ignore all that stuff. This is the crux of it, basically. So to enumerate, add Rect, Point, String, 8 -- 1632, 1634, Dual Float, Double Pointer Messenger, Ref Message and Data. That pretty much covers all the basic data types you can possibly have in C, at least.
A SPEAKER: Other messages there?
WILLIAM ADAMS: Yep.
PETER POTREBIC: William, can I add something else to that?
WILLIAM ADAMS: Sure.
PETER POTREBIC: I think it's in another part of the header file, but there's a new -- if you look for the -- for an add --
WILLIAM ADAMS: Add?
PETER POTREBIC: Another add. I don't remember where I added it. But there's a new protocol. If you have some other kind of struct that you want to add to a message, you can basically subclass this one class called flattenable.
WILLIAM ADAMS: Add flat.
PETER POTREBIC: And you just implement protocol to turn your object into a string of bits and that will allow you to add and find --
WILLIAM ADAMS: Oh, geez. Sorry.
PETER POTREBIC: That allows you to add and find in a typesafe way just like you can add and find in 832. You can add and find your own type of objects. This makes message more accessible than it was in DR8. That's all it was. Thanks.
WILLIAM ADAMS: Thanks. Peter is the god. Corner him.
So those are all the things in the message and similarly you can get all those things back out of a message.
Now, the only other interesting thing about Rraster is I mentioned that it will load -- it will allow you to read GIFs and Targa images. Actually, it will allow you to read any type of image that someone has written an add-on for or a decoder, a code add, and it will display it.
One of the very strong points of our operating system is you can have dynamic link libraries, basically. You can load in an image and you can execute code from a shared library is what it amounts to. And all -- and refs was designed with the ability for you to just add as many decoders as you want. They go in a particular folder in the system directory and away you go.
So this is what allows you to extend your applications without having to fill them up with all the code yourself. You write a basic framework, come up with an API that says if you want to add a decoder, do this. Then other people write those decoders, put them in the right place, and now your application is extended, do the work that other people do. So let's -- that's a fine feature.
We're pretty much out of time so we can answer some questions. Maybe a couple.
Lights?
A SPEAKER: So now messages, messages are being passed directly from office to office?
WILLIAM ADAMS: Messages are typically passed eventually to a looper, which is a thread. And the looper will say, well, who wants to handle this message? And someone eventually will handle this message. But you're basically passing a message to someone who's just kind of sitting around waiting to receive messages and then they go and process it. Okay. It's not necessarily doing anything with the app server. You could have multiple loopers in your own app passing messages back and forth to each other, so --
A SPEAKER: Is there any mechanism to having messages passed between 2 machines on a network?
WILLIAM ADAMS: Well, there is a messenger object which you generally use to pass messages between processes on the same machine. One of our sample applications extends that and creates a met messenger object which does allow you to pass messages between machines. But by default there isn't a class in the kit that does it automatically.
But if you look at that sample app, it's pretty straightforward to do. It just amounts to creating it with a name of a machine and a port and now you can send messages to that as easily as you could send to any thread within your application.
So one more.
A SPEAKER: The attributed file system, a database?
WILLIAM ADAMS: Okay. So database, the attributed file system, what is that? It's basically every file can have a number of attributes attached to it and you can kind of think of this from the Mac World like resource, much more than that, say, multiple resources you can have things like a picture attribute, name attribute, extra float attached to, say, my resume. My resume could have my picture, my salary history, maybe some other things kind of floating around in it. I could index these attributes about, I can search for files that have particular values for these attributes. You're probably not going to use that as your general database because it's not really designed for that. It's designed to be attributes on a file.
If you're going to -- if you're really into heavy-duty things like you would do with, say, Oracle or Sybase or something like that, you really need to do another mechanism, but this is the easy mechanism to do things like printing.
Okay. How would you create a principle? You would create a bunch of documents, give them a bunch of attributes, say you're printing, query the system that has the files, finds the attribute that says you're printing, and now you have a principle and it extends across many different areas. It seems like you want to have an attribute on file that says this is a person and now you have a list of people so that you can go and look and find their E-mail address and their phone number, and blah, blah, blah, blah, blah.
So to answer the original question, which is, can you do index searches, you can index attributes that are on files. It's probably not what you're used to in terms of a traditional relational database or something like that. But it has the pieces, so -- and I think that's it.
So thank you very much.
Home | About Be | Be Products | BeWare | Purchase | Events | Developers | Be User Groups | Support