|
 |


Table of Contents
Upcoming Events:
BE EUROPE: CeBIT '99
Be Europe will be exhibiting at the CeBIT '99 show in
Hannover, Germany from March 18 through March 24 1999
<http://www.messe.de/cb99/index_e.html>.
To give you an idea of the size of this event, there were
7,508 exhibitors and 678,560 visitors in 1998 from all over
the world.
At the 40-50 sqm (15x30 ft.) booth we will obviously
demonstrate the latest release of the BeOS, but this is also
a great opportunity to show off your applications. This can
be done in several ways:
- Come to Hannover to demonstrate your application. (We may be able to help with accommodation).
- Send us a version of your application(s) and have someone from Be demonstrate it.
- Forget the whole thing (including those 700,000 visitors).
Whatever your choice is, please contact Christophe Droulers
<droulers@beeurope.com> for further details about this
event.
BeDC: Programming the Media Kit
This spring we will be hosting a Be Developer Conference
devoted to the new Media Kit. Developers wishing to attend
should be versed in the basics of programming the BeOS;
there will be no introductory sessions. The conference will
be held April 9th and 10th at the Crowne Plaza Cabana Hotel
in Palo Alto, California. Look for announcements soon about
session topics and registration procedures.
BE ENGINEERING INSIGHTS: An Amusing Diversion, or How to Make Magnify Suck Up even More CPU Cycles
By Robert Chin - rudeboy@be.com
A while ago we received a bug report that included
scanned-in polaroids of an engineer's BIOS screen and serial
card. Pretty twisted -- but we had fun using Magnify to
examine the pictures. The only problem was that the image
that Magnify showed was pixelated. The solution: Magnify
filter add-ons.
This article shows you how to add add-on functionality to
Magnify. It should also work as a tutorial for adding
similar functionality to your own app. Add-on recognition is
simple to incorporate, and it's a great way to extend, or
have others extend, the functionality of an application.
To follow this article, open the sample source for Magnify
and copy and paste. Note that all of the image-manipulation
work is done in the add-ons themselves; Magnify just calls
the add-on's functions to process the image.
We start by adding a couple of global variables to the app.
The variables point to the filter's entry point function
(gFilterFunc) and image id (gAddOnImageID). The function
takes a BBitmap object; this is the image that the filter
will work on:
void (*gFilterFunc)(BBitmap*);
image_id gAddOnImageID;
We initialize the variables in the TWindow constructor:
TWindow::TWindow(int32 pixelCount)
: BWindow( BRect(0,0,0,0), "Magnify", B_TITLED_WINDOW,
B_OUTLINE_RESIZE)
{
gFilterFunc = 0;
gAddOnImageID = 0;
...
}
We also create a global function that examines Magnify's
add-on directory (a subdirectory of the default add-on
directory). We create a menu item for each file we find
(each file is assumed to be a Magnify filter):
static void
AddFilters(BMenu *menu)
{
BPath path;
find_directory (B_USER_ADDONS_DIRECTORY, &path);
path.Append ("Magnify");
BDirectory dir(path.Path());
// only add the rest of the menu if filters exist
if (dir.CountEntries() <= 0)
return;
menu->AddSeparatorItem();
BMessage* msg = new BMessage('filt');
msg->AddString("filter", "disable");
BMenuItem* menuItem =
new BMenuItem("Disable Current Filter", msg);
menu->AddItem(menuItem);
int32 index=1;
entry_ref ref;
while (true) {
status_t err = dir.GetNextRef(&ref);
if (err == B_ENTRY_NOT_FOUND)
break;
else {
msg = new BMessage('filt');
msg->AddString("filter", ref.name);
menuItem = new BMenuItem(ref.name, msg);
menu->AddItem(menuItem);
index++;
}
}
}
AddFilters() is called alongside the app's other
menu-building functions, in TInfoView's AddMenu() function:
void
TInfoView::AddMenu()
{
fMenu = new TMenu(dynamic_cast(Window()), "");
BuildInfoMenu(fMenu);
AddFilters(fMenu);
...
}
When the user selects a filter from the menu,
TWindow::MessageReceived() catches the in-coming "load
filter" message (which contains the name of the filter) and
passes it to TWindow::LoadFilter():
void
TWindow::MessageReceived(BMessage* m)
{
...
case 'filt':
LoadFilter(m);
break;
...
}
In TWindow::LoadFilter(), we reconstruct the pathname to the
filter, and load it by passing the pathname to load_add_on()
(first unloading the previous filter). We then call
get_image_symbol() to find the add-on's filter() function --
this is our interface to the add-on's functionality:
void
TWindow::LoadFilter(BMessage* m)
{
char* fname;
m->FindString("filter", (const char**)&fname);
if (fname) {
if (strcmp(fname, "disable") == 0) {
gFilterFunc = 0;
gAddOnImageID = 0;
return;
}
// unload the previous add-on, free its memory, etc.
if (gFilterFunc)
unload_add_on(gAddOnImageID);
BPath path;
find_directory (B_USER_ADDONS_DIRECTORY, &path, true);
path.Append ("Magnify");
path.Append(fname);
// load the add-on
gAddOnImageID = load_add_on(path.Path());
if (gAddOnImageID < 0) {
printf("ERROR: can't load addon\n");
return;
}
// get the filter function symbol
int result = get_image_symbol(gAddOnImageID, "filter",
B_SYMBOL_TYPE_TEXT,(void**)&gFilterFunc);
if (result < 0) {
printf("ERROR: can't get image symbol\n");
return;
}
}
}
The filter code is now loaded, and gFilterFunc points to its
filter() function. To call the function, all we have to do
is pass it the bitmap that's displayed on screen and render
the result. We do this from the TOSMagnify::CreateImage()
function, just before the DrawBitmap() call:
bool
TOSMagnify::CreateImage(BPoint mouseLoc, bool force)
{
...
if (gFilterFunc)
(*gFilterFunc)(fBitmap);
DrawBitmap(fBitmap, srcRect, destRect);
...
}
What would add-on support be without some sample add-ons.
Here is some sample source for a couple filters (thanks to
Victor Tsou for coming up with this idea and creating both
of these filters).
This first filter (continuously) rotates the Magnify image.
#include <math.h>
#include <stdio.h>
#include <Bitmap.h>
extern "C" _EXPORT void filter(BBitmap* image);
BRect bounds;
uint32 *bits = NULL;
int *xcosT, *xsinT;
int frame = 30;
#define max(a,b) (((a)>(b))?(a):(b))
#define PI 3.14159265
#define SCALE 1
void
filter(BBitmap* image)
{
frame = (system_time() / 200000) % 360;
if (bits && (bounds != image->Bounds())) {
delete bits; bits = NULL;
delete xcosT; xcosT = NULL;
delete xsinT; xsinT = NULL;
}
if (!bits) {
int32 m;
bounds = image->Bounds();
bits = new uint32[(image->BytesPerRow() / 4) *
(bounds.Height() + 1)];
m = max((int32)bounds.Width(), (int32)bounds.Height());
xcosT = new int[m];
xsinT = new int[m];
}
// prepare for 27.5 fixed point
float sinT = 32*sin(frame*PI/180*SCALE),
cosT = 32*cos(frame*PI/180*SCALE);
int center_x = image->BytesPerRow()/8,
center_y = (int32)bounds.Height()/2;
int shiftx = (int)(-center_x*cosT - center_y*sinT +
center_x*32),
shifty = (int)(center_x*sinT - center_y*cosT +
center_y*32);
int newx, newy;
int32 *dest_int32 = (int32 *)bits,
*bitmap_bits_int32 = (int32*)image->Bits();
int width = image->BytesPerRow() / 4,
height = (int)image->Bounds().Height() + 1;
for (int z=((width > height)?width:height)-1;z>-1;z--) {
xcosT[z] = (int)(z*cosT); xsinT[z] = (int)(z*sinT);
}
for (int y=0;y<height;y++) {
int ysinT = (int)(y*sinT), ycosT = (int)(y*cosT);
int basex = ysinT + shiftx + (3*width << 5),
basey = ycosT + shifty + (3*height << 5);
for (int x=0;x
The second filter makes the image wave. Just build and run
it, the visual is better than a description.
#include <math.h>
#include <stdio.h>
#include <Bitmap.h>
extern "C" _EXPORT void filter(BBitmap* image);
BRect bounds;
uchar *tbits;
int32 bpr;
#define PI 3.14159265
void
filter(BBitmap* image)
{
int32 w, h, x, y;
uchar *bits;
int32 bpp;
int frame = (system_time() / 20000) % 360;
if (tbits && ((bounds != image->Bounds()) ||
(bpr != image->BytesPerRow()))) {
delete tbits; tbits = NULL;
}
if (!tbits) {
bounds = image->Bounds();
tbits = new uchar[image->BytesPerRow()];
bpr = image->BytesPerRow();
}
w = (int32)bounds.Width() + 1;
h = (int32)bounds.Height() + 1;
bits = (uchar *)image->Bits();
switch (image->ColorSpace()) {
case B_RGB32 :
case B_RGB32_BIG :
bpp = 4; break;
case B_RGB16 :
case B_RGB16_BIG :
case B_RGB15 :
case B_RGB15_BIG :
bpp = 2; break;
case B_CMAP8 :
bpp = 1; break;
default :
printf("Unknown color space (%x)\n",
image->ColorSpace());
return;
}
for (y=0;y<h;y++) {
int32 delta = (int32)
(30 * sin((10 * frame + y) * PI/180) +
15 * sin(( 7 * frame + 3 * y) * PI/180));
uchar *s;
s = bits + y * image->BytesPerRow();
if (delta < 0) {
delta = -delta;
memcpy(tbits, s, bpp * delta);
memcpy(s, s + bpp * delta, bpp * (w - delta));
memcpy(s + bpp * (w - delta), tbits, bpp * delta);
} else if (delta > 0) {
memcpy(tbits, s, bpp * (w - delta));
memcpy(s, s + bpp * (w - delta), bpp * delta);
memcpy(s + bpp * delta, tbits, bpp * (w - delta));
}
}
}
To build these filters, create a BeIDE "SharedLibrary"
project (one project for each filter), and move the results
to /boot/home/config/add-ons/Magnify. Name the first filter
"Rotate" and the second "Wave". Next time you launch
Magnify, you'll find the new menu items at the bottom of the
pop-up list. Select a filter and watch the image.
What's next? How about combining filters and adding a game
to Magnify.
BE ENGINEERING INSIGHTS: Useful Applications of BeOS Scripting
By Jeff Bush - jeff@be.com
Contrary to its name, BeOS message scripting doesn't refer
to a scripting language such a Perl or Awk. BeOS scripting
is a format for passing messages between applications. Your
programs already support scripting as many classes in the
Interface Kit have it built in. The really interesting
aspect of scripting is that applications can inter-operate
without necessarily having to be specially designed to work
together. The sample app, Thesaurus demonstrates this
principle. You can download it from here:
<ftp://ftp.be.com/pub/samples/application_kit/Thesaurus.zip>
Thesaurus lets you scan through text that's displayed by
some other application, and replace words with their
synonyms. Ok, it's not totally useful (unless you're writing
a college paper maybe!), but it is mildly entertaining, and
the code could easily be adapted to work as a spelling
checker or similar application. The synonyms are taken from
a file that's included in the optional/goodies folder on the
R4 CD.
The basic operation of this application is relatively
simple. BTextView has a property named "Text" which gives
you access to the contents of the text view. This
application reads some text, scans for a word that it has
synonyms for, presents the list of choices to the user, and
then sets the text depending on what the user chooses.
Unfortunately, the text view scripting messages used by
Thesaurus don't yet belong to a suite, so if you try this
with a program such as Eddie or BeIDE, you may find that it
doesn't work.
One problem I ran into was how to find out which application
Thesaurus should talk to. An easy way to do it was to allow
the user to drag an icon onto the app. The message that is
dragged is just bogus. When the view in question receives
it, it won't know how to handle it and will reply with
B_MESSAGE_NOT_UNDERSTOOD. The returned message, however,
will have a messenger to the view in question which we can
use to talk to the app. This worked fine for BeMail, but
unfortunately StyledEdit doesn't accept dropped messages. To
solve this problem, I've added optional command line apps
that can be used to manually select a target. In the case of
StyledEdit, you can use the optional command line arguments
as follows:
Thesaurus --app StyledEdit --window 0 --view text
The meat and potatoes of Thesaurus is in the
RemoteTextScanner class. It handles communication with the
targeted text view. The actual work for getting the text is
done in FetchNextTextChunk(), which fetches 1k of text from
the targeted text view. BTextView has a property called
"Text" which represents text that it contains. You can use
an index specifier to choose which text you get back. The
following code retrieves a chunk of text that starts at
fTextBufferOffset and is TEXT_BUFFER_SIZE long:
BMessage textRequestMessage(B_GET_PROPERTY);
textRequestMessage.AddSpecifier("Text", fTextBufferOffset,
TEXT_BUFFER_SIZE);
fTextViewMessenger.SendMessage(&textRequestMessage, &reply);
if (reply.FindString("result", &text) == B_OK)
strncpy(fTextBuffer, text, TEXT_BUFFER_SIZE);
That's it. Replacing the targeted word is almost as easy. In
this case, we change the verb to B_SET_PROPERTY. It is then
a two step process of removing the old text and inserting
the new:
// Erase the word that is going to be replaced
BMessage reply;
BMessage textDelMessage(B_SET_PROPERTY);
textDelMessage.AddSpecifier("Text", fCurrentWordOffset,
fCurrentWord.Length());
fTextViewMessenger.SendMessage(&textDelMessage, &reply);
// Now insert the new word beginning at the same location
BMessage textSetMessage(B_SET_PROPERTY);
textSetMessage.AddString("data", newWord);
textSetMessage.AddSpecifier("Text", fCurrentWordOffset,
strlen(newWord));
fTextViewMessenger.SendMessage(&textSetMessage, &reply);
The TextView also has a "selection" property, which, as you
might guess, allows us to change what text is selected. My
plan was to set the selection to the currently selected
word, so the user could see it in its context. This is where
I ran into a little snag. It turns out that when the window
containing a text view doesn't have the focus, the selection
isn't highlighted. Since the thesaurus window has the focus
when you're changing words, this won't work.
Luckily, the text view also has a property called
'text_run_array'. This property contains raw information
about the font and color of text in the view, so it was a
simple matter of setting the color of the word to red to
indicate which word is currently being scanned. Note that
this only works when multiple colors/fonts are enabled for
the view, however, in most cases where you would want to use
this app, they are.
There's quite a bit of room for improvement in this
application: You could modify it to work within a selection
by getting the selection property from the text view before
starting, or improve the UTF8 support (BTextView does
utilize UTF8, so fixing this should be simple).
I hope this gets your creative juices flowing in thinking of
ways to make applications more scriptable. The scripting
mechanism is still evolving, so if you have ideas for new
properties or suites, you can submit requests using the bug
database.
DEVELOPERS' WORKSHOP: A Stitch in MIME
By Eric Shepherd - sheppy@be.com
"Developers' Workshop" is a weekly feature that provides
answers to our developers' questions, or topic requests.
To submit a question, visit
<http://www.be.com/developers/suggestion_box.html>.
Sometimes when you're writing an application that reads data
files, it makes a difference what type of data the file
contains. The BeOS uses MIME types to describe the type of
data contained in files. For example, ASCII text files are
type text/plain, and HTML files are text/html.
Sometimes applications try to read these files but the type
information isn't correct. The most common case of this
occurs when a file has been archived using the "tar"
utility, then unarchived. This utility doesn't preserve
attributes such as the file's type, so the file is untyped.
If an application is trying to read the file and interpret
its contents, this can complicate matters. For example, a
web server application (such as, say, the PoorMan web server
that comes with the BeOS) needs to be able to tell the web
browser receiving a file what type of file it is.
Here's some code that looks up the MIME type of a file whose
name is given by filename:
BFile file;
char type[256];
status_t status;
status = file.SetTo(filename, B_READ_ONLY);
if (status == B_OK) {
BNodeInfo ninfo(&file);
if (ninfo.GetType(type) < B_OK) {
printf("Oh no! Can't get the type!\n");
}
else {
printf("Type is %s\n", type);
}
}
This code just uses the BNodeInfo::GetType() function to get
the MIME type of the file, and fails if the function returns
an error (which typically indicates that the file doesn't
have type information established).
You could write code to look at the file's extension and/or
contents and figure out what type of data it contains, but
this isn't an optimal solution because your application
shouldn't have to be aware of every possible type of data
the file might contain.
The BeOS provides a function, update_mime_info(), that lets
you ask the BeOS to attempt to identify the type of data in
a file and update the file's type information.
Here's an altered version of the example code that uses
update_mime_info() to try to have BeOS identify the file's
type:
BFile file;
char type[256];
status_t status;
type[0] = '\0';
status = file.SetTo(filename, B_READ_ONLY);
if (status == B_OK) {
BNodeInfo ninfo(&file);
if (ninfo.GetType(type) < B_OK) {
update_mime_info(filename, 0, 1, 0);
if (ninfo.GetType(type) < B_OK) {
printf("Can't get the type!\n");
}
}
if (strlen(type)) {
printf("Type is %s\n", type);
}
}
This version, if GetType() fails, calls update_mime_info()
to ask the BeOS to attempt to identify the file. Once that's
been done, GetType() is called again to see if the type has
been fixed. If not, an error message is displayed .
Let's look at update_mime_info() in more depth. Here's the
prototype:
int update_mime_info(const char *path,
int recursive,
int synchronous,
int force);
The path argument indicates the pathname of the file to
update.
The recursive argument indicates that if pathname indicates
a directory, whether or not the entire directory tree at
that point should be traversed, updating every file in the
tree. If this value is 1, the tree is traversed recursively;
if it's 0, only the indicated file is updated.
To ask update_mime_info() to operate synchronously (so it
will only return when it's finished updating), specify 1 for
the synchronous parameter. Specify 0 if you want
update_mime_info() to perform in the background; in this
case, it returns immediately.
If the force argument is 1, files are updated even if they
already have type information specified. If it's 0, files
with types assigned already are skipped.
update_mime_info() returns a B_OK if all is well, otherwise
it returns a negative value indicating the error that
occurred.
If you haven't guessed already, this one function does at
least 50% of the work of the mimeset utility. Most of the
other 50% of the work is done by create_app_meta_mime(),
which updates application meta-MIME information (it's called
if the -apps or -all flag is specified to mimeset).
This may be helpful if you run into situations in which your
application is trying to read files and is being confused by
nonexistent type information.
The Next BeDC
By Jean-Louis Gassée
On April 9 and 10, we'll hold our next Be Developer
Conference in Palo Alto. Last year, the event spotlighted
the first release of the BeOS for Intel-based PCs. This year
we're on to our third Intel-based release (or fourth or
fifth, depending on how you count 3.1, 3.2, 4.0 and the
upcoming 4.1) and, indeed, we'll be happy to chat about the
progress we've made in our UI, the kernel, the file system,
etc. But at the April conference we're going to focus on the
Media Kit, one of Release 4's most notable additions to the
BeOS.
Given our Media OS banner, and our plans for a media-rich
future for the Be commonwealth, this topic is clearly rich
enough for several Developer Conferences. Using the Media
Kit, and using it well, is essential to apps that want to be
fast and flexible.
So, does this mean Be is only interested in Media Kit-based
applications? I'll just summarize earlier statements made in
this space: Media applications are the ones that are most
likely to help distinguish the BeOS from its venerable
elders. But drawn by media, BeOS users need to be delighted
enough with our "everyday" apps -- e-mail, Web browsing,
word processing, spreadsheet -- that they'll find no reason
to leave.
Prior to the April BeDC we'll promote the BeOS Media Kit at
events such as the NAMM Music Market in Los Angeles, January
28-31, the Frankfurt Musikmesse, March 3-7, and the CeBIT
show in Hannover, March 18-24. Tim Self and his team expect
a number of announcements for these events; these
announcements will provide good technical (and business)
cases, which we'll use to encourage more developers to
embrace our technology.
In June we're off to PC Expo. Last year was the first time
Be took part in a PC-centric trade show. This year we'll
return with a proof of the concept that we demonstrated in
1998. This represents a lot of work -- but this is fun work,
and it's paying off. The Release 4 sales results so far are
a gentle but firm tug that indicates traction: Distributors
are paying their bills and ordering more. This motivates all
of us for the next milestones and we all look forward to a
fun and productive BeDC.
Recent Be Newsletters |
1998 Be Newsletters
1997 Be Newsletters |
1995 & 1996 Be Newsletters
Copyright ©1998 Be, Inc.
Be is a registered trademark, and BeOS, BeBox, BeWare, GeekPort, the Be logo and the BeOS logo
are trademarks of Be, Inc. All other trademarks mentioned are the property of their respective owners.
Comments about this site? Please write us at webmaster@be.com.
|