|
 |


Table of Contents
BE ENGINEERING INSIGHTS: Mastering Your Own BeOS Demo CD
By Ming Low ming@be.com
A few months ago, Be released the BeOS Demo CD as an
inexpensive and uncomplicated way for people to try the BeOS
without having to paritition/initialize their disk or
purchase the full version.
The BeOS Demo CD is a bootable CD. It boots into a version
of BeOS Release 3.2 which is fully functional except for the
ability to partition or initialize a BFS partition,
preventing the user from saving their work. Although some
features' performance is affected, everything works, and you
can even save preferences onto a floppy, preserving settings
across rebooting.
The BeOS Demo CD has since been used by people to try
systems at computer stores to see if they are BeOS
compatible, for Be enthusiasts to give friends a quick demo
of the BeOS on their system, and even to show off what the
BeOS can do at their local computer stores. Internally we
have a variation of the Demo CD that turns our Window 98
systems into a gaming station: booting from the CD directly
into a game such as Quake, Doom, or ATetris, just by turning
on the machine with the CD in the drive.
The usefulness of the BeOS Demo CD has stirred interest from
third-party developers who want to see demo versions of
their products on the Demo CD. We're working on making that
happen -- the next Demo CD (with R4) will include
third-party demonstration apps. Other developers have
wondered about the possibility of making their own demo CDs,
which boot the BeOS but only include demonstrations of their
own products. Marketing says, "Interesting idea!"
The purpose of this article is to explain how we made the
BeOS Demo CD, because it was an interesting technical
challenge. Before you use this as a recipe for your own demo
CDs, though, if you are planning to distribute a CD that
includes the BeOS, you need to obtain a license to
distribute it. Contact our guy in charge of licensing, Bart
Adao, at <bart@be.com> for details.
Making the CD Bootable into BeOS
To make the CD bootable, you need to create an ISO track on
it by following the PC El Torito Specification for creating
a bootable CD. This ISO track or partition must contain a
special zbeos boot image and a boot catalog file plus some
boot information for the CD's Boot Volume Descriptor. The
special zbeos image essentially mimics a BeOS Intel boot
floppy.
The BeOS needs to boot off the CD from a 2.88 MB boot image.
Some commercial software will create a bootable CD but only
from a 1.44 MB floppy. One commercial CD-burning package
that supports creating a 2.88 MB boot image is Ahead
Software's Nero <http://www.ahead.de/>.
If your toasting software can't create a bootable CD from a
2.88 MB image, you can still do it manually using your
existing software. Good step-by-step documentation that
walks you through creating the bootable ISO track manually
is available on the web, at
<http://www.nikko.simplenet.com/goldentime/bootcd1b.htm>.
I tested the manual process for creating a bootable BeOS CD
and it works, mostly. You need to change the setting from 02
(1.44 MB bootable floppy disk image) to 03 (2.88 MB bootable
floppy disk image) on the section that describes the
bootable media type. If you want additional information on
the El Torito Bootable CD Specification, a copy is available
for download from Phoenix Technology LTD at
<http://www.ptltd.com/products/specs.html>.
Creating the 2.88 MB BeOS Boot Image
Here's how to create the 2.88 MB BeOS boot image. You start
by creating a 2.88 MB file that contains all zeros. You can do
this with the dd command:
$ dd if=/dev/zero of=zerofile bs=1024 count=2880
Then merge the /boot/beos/system/zbeos file with the zerofile
using the cat command:
$ cat /boot/beos/system/zbeos zerofile > zbeos.tmp
Finally, chop off any excess bytes over 2.88 MB. You use the
dd command again to do this:
$ dd if=zbeos.tmp of=zbeos.boot count=2880 bs=1024
The zbeos.boot file is the BeOS boot image that makes your CD
bootable.
Creating the BFS Partition
To create the BeOS track for the bootable CD, first make a
partition on your hard disk that's big enough to contain the
"/boot/beos" files and the other files you want on your demo
CD. The partition should be smaller than 650 MB, minus the
size of your ISO track and the gap between the tracks. A
safe partition size would be 600 MB or smaller.
After you select your partition size, initialize the
partition with a block size of 2048 bytes or greater
(remember, the BeOS defaults to creating partitions with a
block size of 1024 bytes). You need to do this because the
BeOS won't recognize volumes with a block size smaller than
the device's minimum block size, which on a CD-ROM is 2048
bytes. If you forget to do this you end up with a nice
coaster or something to use for a microwave light show (yes,
we do that here at Be).
If you want to have your demo CD boot into the Tracker on
startup (like the BeOS Demo CD), you need to copy the
following files from your /boot/home/config/settings/
directory over to your BeOS CD partition:
Desktop_settings
Key_map
Keymap
Keymap_Data
MIMEPrefs_settings
Tracker
beos_mime
fonts_list
fonts_status
You may also want to copy over the Screen_data and
Screen_settings to retain your current screen resolution,
refresh rate, and color bit-depth. If you want the CD to use
your current network settings (say, configured to use DHCP)
then you need to copy the Network settings file. After you
copy all the settings files you're ready to tailor the
system boot process.
Below is a sample Bootscript you can use for your CD
partition. This script doesn't do everything the standard
Bootscript does; i.e., it does not load the variables in the
beos/system/boot/SetupEnvironment. You should tailor this
Bootscript to load the system you want and replace the one
that's in your CD partition.
# -------------------------------------------------------
# This is a special CD Bootscript for demo CDs.
# It manages starting up all the necessary servers
# and ensuring a sane system state.
#
# ++++++++++
# function: launch exectuable_path [ thread_to_wait_for ]
# ++++++++++
launch () {
if [ -f "/fd/$1" ]
then
echo Launching "/fd/$1"
"/fd/$1" &
[ "$2" != "" ] && waitfor "$2"
return 1
else
if [ -f "/boot/$1" ]
then
echo Launching "/boot/$1"
"/boot/$1" &
[ "$2" != "" ] && waitfor "$2"
return 1
else
echo There is no "$1"
fi
fi
return 0
}
# ++++++++++
# function: launchscript script_path
# ++++++++++
launchscript() {
if [ -f "/fd/$1" ]
then
echo Executing "/fd/$1"
. "/fd/$1"
else
if [ -f "/boot/$1" ]
then
echo Executing "/boot/$1"
. "/boot/$1"
fi
fi
}
#
# First we set up a bunch environment variables that
# we'll need later.
#
export PATH=:/bin:/boot/beos/apps:/boot/beos/preferences
export HOME=/boot/home
export SHELL=/bin/sh
export USER=demo
export GROUP=group
SCRIPTS=beos/system/boot
SERVERS=beos/system/servers
#
# Start the Boot of servers
#
launch $SERVERS/app_server picasso # launch app_server
launch $SERVERS/registrar _roster_thread_ # launch registrar
launch $SERVERS/debug_server # we hate crashing
launch beos/system/Tracker # start the Tracker
launch beos/system/Deskbar # launch DeskBar
launch $SERVERS/audio_server # we like it noisy
launch $SERVERS/print_server # we own paper stocks
sleep 2 # let things settle down a bit and then
# continue
launchscript $SCRIPTS/Netscript # start up networking
# -------------------------------------------------------
To create a BeOS CD that boots straight into an application
such as Quake for the BeOS, you can simplify the above
Bootscript by removing the following lines:
launch $SERVERS/debug_server # we hate crashing
launch beos/system/Tracker # start the Tracker
launch beos/system/Deskbar # launch DeskBar
launch $SERVERS/print_server # we own paper stocks
## Remove these two lines if you don't need networking
sleep 2 # let things settle down a bit and then continue
launchscript $SCRIPTS/Netscript # start up networking
If you have a Matrox Video card, you may want to increase
the performance by installing the fast_video driver from the
BeOS R3.1 CD,
/optional/experimental/drivers/kernel/fast_video. Once
you've installed the drivers you can add the following lines
to the Bootscript to make this into a bootable BeOS Game CD:
## This assuming the fastvid was installed
## into /boot/home/config/bin.
/boot/home/config/bin/fastvid set # Speed up for
# Matrox video card
/boot/quake/Quake # Launch Quake
shutdown -r -d 4 & # Reboot the machine when
# the user quits the game
Creating a Floppy for Saving Settings
The BeOS Demo CD lets you save preference settings files to
a floppy. You do this is by renaming the actual config
directory on the CD from /boot/home/config to
/boot/home/config2. It then creates a symlink for
/boot/home/config to point to /config. Using this round
about way, you can change the symlink at /config to point to
either the config directory on the floppy or the /config2 on
the CD, depending on whether the floppy exists.
## Add the following lines before the app_server is launched
## to determine if it should use the cd or the fd config
## directory
##
mkdir /fd # create the fd mount point
mount /dev/disk/floppy/raw /fd # try and mount floppy
if [ $? -eq 0 ] # check to see if the floppy
# was mounted successfully
then
ln -s /fd/home/config /config # point the link from the
# floppy to the cd
launchscript Bootscript.fd # if you need it,
# launch Bootsript.fd
else
ln -s /boot/home/config2 /config # create the cd link
fi
By using this technique, you can save high scores from your
Bootable Game CD or give all your kids a personal settings
floppy so they won't change yours....
BE ENGINEERING INSIGHTS: SCSI From UserLand
By Brian Swetlandswetland@be.com
BeOS R4 adds a new driver (scsi_raw) which allows programs
to send arbitrary commands over the SCSI bus from userspace,
without having to write a device driver for every little
thing. What's this good for, you might ask? Not every SCSI
device has a simple interface easily wrapped with a device
using the POSIX-style open/close/read/write model of IO.
This works great for disks, CD-ROMs, and other "simple"
devices, but sometimes it's nice to be able to send the SCSI
commands you need without having a special driver for that
device -- maybe for a utility program or a CD-R authoring
tool. Or maybe just to muck about with the raw bus to find
out what makes it go.
That's where the scsi_raw device fits in. scsi_raw publishes
device entries that look like /dev/bus/scsi/1/4/0/raw --
where the first number is the controller (in this case 1,
the second controller), the second number is the target or
SCSI ID (device 4 in this case), and the third number is the
logical unit number (0 in this case). Many systems only have
one SCSI bus (so the first number is often zero) and most
SCSI devices have no logical units, CD changers and such
being the exception (so the last number is often zero as
well).
This naming scheme is used for all SCSI devices in R4 --
what was /dev/disk/scsi/000/raw is now
/dev/disk/scsi/0/0/0/raw, and so on. We suggest that tape
devices be named in a similar fashion as well. The major
reason for this renaming is to allow wide SCSI to work
without confusion -- wide device IDs can be greater than 9,
which would cause problems in the old scheme.
/dev/disk/scsi/1120/raw is more likely cause confusion than
/dev/disk/scsi/1/12/0/raw where you can clearly see the
division between path, target, and lun.
Raw SCSI devices can be opened or closed, but not read from
or written to. The only operation that can be done on a raw
SCSI device is an ioctl call of the form
ioctl(fd, B_RAW_DEVICE_COMMAND, &rawdevcmd,
sizeof(raw_device_command))
This ioctl is also supported by ATAPI drives on the IDE
chain (since ATAPI devices speak SCSI commands over the IDE
bus). The raw device command data structure appears here and
is explained below:
/* raw device commands - from be/devices/scsi.h */
typedef struct {
uint8 command[16];
uint8 command_length;
uint8 flags;
uint8 scsi_status;
/* SCSI Status Byte */
/* (0 = success, 2 = check cond, ... */
uint8 cam_status;
/* CAM_* status conditions from CAM.h */
void *data;
size_t data_length;
void *sense_data;
/* buffer to return mode sense data in */
size_t sense_data_length;
/* size of optional buffer for mode sense */
bigtime_t timeout;
} raw_device_command;
enum {
B_RAW_DEVICE_DATA_IN = 0x01,
B_RAW_DEVICE_REPORT_RESIDUAL = 0x02,
B_RAW_DEVICE_SHORT_READ_VALID = 0x04
};
* command -- this field contains the SCSI command (aka
CDB) that will be issued.
* command_length -- the length of the SCSI command in
bytes (6, 10, and 12 are valid).
* flags -- one of the three flags described below,
logically or'd together:
B_RAW_DEVICE_DATA_IN -- after the command is issued, data
is read from the bus to the buffer provided. If this flag
is not set, data is written to the bus instead.
B_RAW_DEVICE_REPORT_RESIDUAL -- this flag requests that
the residual (amount of data NOT transferred) be reported
in the same fields that were used to issue the request
(e.g., data_length will contain the residual data_length
upon return).
B_RAW_DEVICE_SHORT_READ_VALID -- this flag indicates that
a short read (the entire provided buffer is not filled)
will not be considered an error condition.
* scsi_status -- the SCSI bus status is reported here upon
return. 0 = success, 2 = check condition, etc. Consult
your favorite SCSI reference for more detail
* cam_status -- this status field reports the success or
failure of the transaction. CAM_REQ_CMP indicates
successful completion (scsi_status = 0 is implied),
CAM_REQ_CMP_ERR indicates an error occurred, etc. CAM.h
provides definitions of these and several other result
codes, though the two listed here are most common. If an
error occurred and sense data was read into the sense
buffer, CAM_AUTOSNS_VALID (0x80) will be or'd with the
cam_status byte.
* data -- a pointer to a block of data to send or a data
buffer to receive into. If the B_RAW_DEVICE_DATA_IN flag
is set, this buffer is used to read from the bus.
Otherwise, this buffer is sent over the bus after the
command. If data_length is 0, no data transaction occurs.
* data_length -- the amount of data to send or size of the
receive buffer. If the B_RAW_DEVICE_REPORT_RESIDUAL flag
is set, this field will contain the number of bytes NOT
read or written upon return.
* sense_data -- a buffer to receive sense data (SCSI error
information) if an error occurs.
* sense_data_length -- the size of the sense buffer. If
the B_RAW_DEVICE_REPORT_RESIDUAL flag is set, this field
will contain the number of bytes NOT read upon return (but
only if the sense buffer was read into due to a check
condition).
* timeout -- this field specifies a timeout in
microseconds. It only has meaning when used with an ATAPI
device on an IDE bus.
A Word Of Caution
Be aware that the /dev/bus/scsi/... device entries allow you
to send arbitrary commands to any SCSI device in your
system. This is very powerful and also can be quite
dangerous if you're not careful. Mucking around with things
like disks that have mounted file systems on them can be
unwise if you don't know exactly what you're doing.
Example: SCSIProbe for the command line
The following simple program walks the /dev/bus/scsi/...
hierarchy and issues an inquiry command to every listed
device, spitting the information out in a simple table. It's
a C++ app because it uses some Storage Kit classes to do the
directory walking, but everything else is vanilla C code.
/* inquiry.cpp - SCSIProbe for the command line, more or less
**
** PPC: cc -o inquiry inquiry.cpp
** Intel: gcc -o inquiry inquiry.cpp -lbe
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <Directory.h>
#include <Entry.h>
#include <Path.h>
#include <scsi.h>
#include <CAM.h>
/* table with textual descriptions of the inquiry data's
** device type
*/
char *devtype[] = {
"Disk ", "Tape ", "Printer", "CPU ", "WORM ",
"CD-ROM ", "Scanner", "Optical", "Changer", "Comm ",
"Unknown"
};
/* open a raw device, issue an inquiry command, print the
** results (and a header if it's the first time).
*/
void inquiry(const char *dev)
{
static int header = 1;
int fd,e;
int path,targ,lun,type;
raw_device_command rdc;
uchar data[36], sense[16];
if(!strncmp("/dev/bus/scsi/",dev,14)){
sscanf(dev,"/dev/bus/scsi/%d/%d/%d/raw",
&path,&targ,&lun);
} else {
path = targ = lun = 0;
}
/* fill out our raw device command data */
rdc.data = data;
rdc.data_length = 36;
rdc.sense_data = sense;
rdc.sense_data_length = 0;
rdc.timeout = 1000000;
rdc.flags = B_RAW_DEVICE_DATA_IN;
rdc.command_length = 6;
rdc.command[0] = 0x12;
rdc.command[1] = 0x00;
rdc.command[2] = 0x00;
rdc.command[3] = 0x00;
rdc.command[4] = 36;
rdc.command[5] = 0x00;
if((fd = open(dev,0)) < 0) return;
e = ioctl(fd, B_RAW_DEVICE_COMMAND, &rdc, sizeof(rdc));
close(fd);
if((e != 0) || (rdc.cam_status != CAM_REQ_CMP)) return;
if(header){
printf("Bus ID LUN Type Vendor Device Rev \n"
"--- --- --- ------- -------- ---------------- ----\n");
header = 0;
}
type = data[0] & 0x1F;
if(type > 9) type = 10;
printf("%3d %3d %3d %7s %8.8s %16.16s %4.4s\n",
path, targ, lun, devtype[type], data + 8, data + 16,
data + 32);
}
/* recursively wander down from a path, looking for "raw"
** devices to call inquiry on.
*/
void walkpath(const char *path)
{
BDirectory dir(path);
if(dir.InitCheck() == B_OK){
BEntry entry;
while(dir.GetNextEntry(&entry) >= 0) {
BPath name;
entry.GetPath(&name);
if(entry.IsDirectory()) {
walkpath(name.Path());
} else if(!strcmp(name.Leaf(),"raw")){
inquiry(name.Path());
}
}
}
}
/* handle a command line arg or just walk the directory
** tree looking for devices
*/
int main(int argc, char *argv[])
{
if(argc != 2) {
walkpath("/dev/bus/scsi");
} else {
inquiry(argv[1]);
}
return 0;
}
DEVELOPERS' WORKSHOP: Getting a Grip on BJoystick
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.
The BJoystick class is getting a major overhaul for BeOS
Release 4. In earlier versions of BeOS, BJoystick was
limited to supporting a simple two-axis, two-button analog
stick. But in this age of games with complicated moves,
high-realism flight simulations, and the like, this just
isn't enough. The new, improved BJoystick class gives you
access to modern digital game controllers (such as the
Logitech Wingman Extreme Digital, the Microsoft Sidewinder,
and others). As always, the warning goes (come on, you can
say it along with me): "BeOS R4 isn't final, and this is all
subject to change."
These sticks often have a half-dozen buttons or more, thumb
hats (those little direction knobs on top of the main
stick), and additional axes, such as rotational controls or
throttles. Game players want to use these controls to their
maximum potential, and the new BJoystick class lets you do
just that.
The old BJoystick mechanism still works, but I'm not going
to talk about it, because the old API doesn't support these
advanced features. Note, however, that enhanced joysticks
don't work on the BeBox's built-in game port hardware -- all
you can get are two-axis, two-button joysticks.
This article is based on the sample project StickIt. StickIt
lets you pick a joystick (previously configured using the
new Joysticks preference application), then presents a
window showing all the buttons, hats, and axes provided by
that joystick, providing instant feedback as the joystick is
manipulated. Only key portions of the code will be shown
here; you can download the complete source code at
<ftp://ftp.be.com/pub/samples/device_kit/stickit.tgz>.
On R4 Intel, you can use the following command in a Terminal
window to compile and link the program:
cc -o StickIt main.cpp japplication.cpp jwindow.cpp -lbe -ldevice
On R4 PowerPC, this command compiles and links the program:
cc -o StickIt main.cpp japplication.cpp jwindow.cpp
Note that I use the term "joystick," but I mean "any
BeOS-compatible game controller." There are supported game
pads as well.
Let's begin by looking at how to figure out what joysticks
are available, and how to open them. Take a look at the
PickJoystick() function in StickIt's main.cpp file. This
function presents, in the Terminal from which StickIt was
launched, a list of joysticks, and lets the user pick the
one they want to play with.
It begins by calling BJoystick->CountDevices(), which
returns the number of devices connected (this isn't
technically the same thing as the number of joysticks
connected, since "devices" really refers to game ports, and
it's possible for multiple devices to be chained to one game
port, although none of the drivers provided in R4 support
this). If there aren't any devices available, an error
message is printed, and PickJoystick() returns false.
Otherwise, a loop prints out the names of the joysticks the
user has configured for each game port:
for (i=0; i<numDevices; i++) {
if (stick->GetDeviceName(i, devName) != B_OK) {
printf("*** Error while reading controller list.\n");
return false;
}
if (stick->Open(devName, true) < B_OK) {
printf("%4d. No controller on %s.\n", i+1, devPath);
}
else {
if (stick->GetControllerName(&name) != B_OK) {
printf("*** Can't get name of controller %s.\n",
devPath);
return false;
}
printf("%4d. %s\n", i+1, name.String());
stick->Close();
}
}
This begins by obtaining the device name of the joystick
that's configured for the port (calling GetDeviceName() with
an index number indicating which port to check).
BJoystick::Open() is then called to open the joystick
device. If this fails, an error message is displayed and the
loop continues (in case there are empty or unconfigured game
ports, but others may be valid).
Once the device is open, GetControllerName() is called to
get the name of the joystick. The returned name is the same
as the name indicated by the Joysticks preference
application. This name is then displayed as the option for
the user to select, and the joystick is closed. This loop
continues until the entire menu is displayed on the
terminal.
PickJoystick() then lets the user select the joystick they
want to use, and then the device is opened, using code very
similar to the code above: first the device name is obtained
by calling GetDeviceName(), then the Open() function is used
to actually open the device. Open() returns the file
descriptor of the joystick's device driver (which you don't
really need to know), or a negative number if an error
occurred while opening the device).
PickJoystick() returns with the BJoystick object open and
ready to use.
StickIt's main() function is fairly simple. It calls
PickJoystick() to get a joystick to use, then instantiates a
JWindow , in which the instant joystick feedback is
presented.
JWindow is a very simple class and we won't dwell on it --
it just sets up the JView, which does all the real work, and
sets the pulse rate to 100,000 microseconds.
Let's just skim on to the JView class, where all the cool
stuff is done. The constructor creates labels for the
various displays in the view, and resizes the view and the
window, vertically, so it's the right size for the controls
provided by the joystick it's displaying).
At the top of the window, in a nice large font, the
joystick's name is displayed:
stick->GetControllerName(&name);
stickName = new BStringView(BRect(5,5,350,25),
"stickname", name.String());
stickName->SetFontSize(18);
AddChild(stickName);
We've seen how GetControllerName() works already, in
PickJoystick(), so we skip on to the labels for the buttons:
numButtons = stick->CountButtons();
r.Set(5,50,100, 64);
for (i=0; i<numButtons; i++) {
stick->GetButtonNameAt(i, &name);
name.Append(":");
sview = new BStringView(r, "buttonlabel", name.String());
sview->SetAlignment(B_ALIGN_RIGHT);
sview->SetFont(be_bold_font);
AddChild(sview);
r.top += 18;
r.bottom += 18;
}
CountButtons() returns the number of buttons the joystick
provides. The buttons are numbered from 0 to numButtons-1.
The BRect, r, is initialized to the rectangle of the first
button's label, and then we enter the for loop.
In the loop, each button's name is retrieved by calling
GetButtonNameAt(), which returns the name (as specified by
the joystick's driver) for the specified button number. The
name is returned in a BString object. We append a colon to
the name (which makes it look like a label, instead of just
random text displayed in a window), then we create a
BStringView using the name as the label. Alignment and font
settings are tweaked as appropriate, and the rectangle is
adjusted so that the next button will be created 18 pixels
further down in the window.
A similar procedure is done to create the labels for the
hats, which are displayed in the same column as the buttons.
CountHats() is called to get the number of hats, and the
labels are generated in the same way (except that the hat
displays are larger, so each hat is displayed 40 pixels
below the previous one, instead of just 18 pixels).
The right-hand column is dedicated to displays for the axes.
The topmost display is a two-dimensional display for the X
and Y axes, and a "Stick:" label is displayed there, under
the assumption that all joysticks have an X/Y axis pair.
Below this are created labels for any other axes, such as
throttles, twist controls, and the like. This is done just
like the button labels (except that CountAxes() is called to
get the number of axes available). Note that axes 0 and 1
are the X and Y axes (this is standard), and all axes above
that are treated as one-dimensional axes.
Finally, the view and window are resized so the height of
the view and window is just a bit higher than needed to
display the taller of the two columns. This makes the window
look nice, without a lot of wasted space on the screen.
The Pulse() function just locks the window and calls Draw()
to refresh the display.
The Draw() function actually handles drawing the joystick's
movements interactively. It begins by getting the numbers of
buttons, hats, and axes, and by allocating buffers for the
axis and hat values:
numButtons = stick->CountButtons();
numHats = stick->CountHats();
numAxes = stick->CountAxes();
axes = (int16 *) malloc(sizeof(int16) * numAxes);
hats = (uint8 *) malloc(numHats);
The axes and hats arrays will be used when we call
GetAxisValues() and GetHatValues(); these functions fill
these arrays with the values of each of the axes and hats on
the joystick.
Then, BJoystick::Update() is called. This tells the joystick
driver to look at the state of the joystick and record the
current values. Now we can actually retrieve the values and
do something with them.
We begin by drawing the state of the buttons. Each button is
represented by a box next to the corresponding label. If the
button is pressed, a solid black box is drawn. If the button
isn't pressed, a hollow box is drawn. This is done in a
loop, as follows:
r.Set(105,50,115,60);
buttons = stick->ButtonValues();
for (i=0; i<numButtons; i++) {
if (buttons & (1 << i)) {
FillRect(r, B_SOLID_HIGH);
}
else {
r.InsetBy(1,1);
FillRect(r, B_SOLID_LOW);
r.InsetBy(-1,-1);
StrokeRect(r, B_SOLID_HIGH);
}
r.top += 18;
r.bottom += 18;
}
The ButtonValues() function returns a uint32 that contains
bitmapped flags, one for each button (so BJoystick supports
up to 32 buttons per joystick). The low-order bit represents
button 0, the next bit is button 1, and so forth. If the bit
is set to 1, the button is pressed, otherwise it's not.
This code loops through, once for each button, and looks to
see whether that button is pressed or not. If it's pressed,
the rectangle is filled solid with the high color;
otherwise, the interior of the rectangle is cleared to the
low color and the frame is refreshed in the high color.
The states of the hats are obtained by calling
GetHatValues(), passing in a pointer to the hats array we
allocated at the beginning of the function. On return, each
byte in the array is filled in with the state of the
corresponding hat. Each direction the hat might be pointing
is represented by a different value:
- 0 Centered
- 1 Up
- 2 Up and Right
- 3 Right
- 4 Down and Right
- 5 Down
- 6 Down and Left
- 7 Left
- 8 Up and Left
These nine values represent nine different positions, which
can be nicely displayed in a three-by-three grid of squares.
I won't go into the specifics of how the code that draws
this grid works (this article is getting long already), but
we fill in the appropriate square given the value of each
hat, and make sure the others are all cleared.
We read the axes by calling GetAxisValues(). This works just
like GetHatValues() -- we pass in a pointer to an array of
int16s that will contain the values of each axis on return.
Each of these values may range from -32,768 to 32,767.
Next, the X and Y axes (which we're treating as a
two-dimensional field) are drawn. This code isn't very
smart, but it does the job. It simply scales the X and Y
values into the range needed to draw the dot, and draws a
red, filled circle to represent the stick's position.
The rest of the axes are drawn as a horizontal slider-type
display, with a black box and a red oblong indicating the
value of the axis.
Download StickIt and have a look at the code. When you get
R4, give it a try. The new BJoystick class should make it
much easier to provide powerful control in games.
Notes From the Road
By Jean-Louis Gassée
I'm in Europe -- actually, in Paris, France. In the old
days, the idea of Europe was just that, an idea cherished by
citizens with memories of the bad old days and hopes for a
better future. In those times, one could not freely move
one's person or belongings or profession from one country to
another.
Half a century later, preparing for landing at the Charles
de Gaulle airport, the airline attendant mentions the
"Schengen Space," the set of countries we can all move
freely between, citizens of the European Union or not. And
talk freely between -- your GSM cell phone works everywhere.
Not the same frequency of GSM as the one timidly offered in
the US, but multistandard phones are becoming available and,
voilà , a world-wide cell phone (almost, no GSM in Japan, if
memory serves).
So, slowly, with all the difficulties involved in mediating
cultural differences, with the danger lurking in an
additional layer of civil servants, the occasionally reviled
eurocrats, the shining idea of a united Europe is becoming a
dented reality.
This seems to generate many opportunities for the PC
industry. PC sales are growing everywhere, even in the midst
of economic uncertainty. On a country by country basis,
growth rates range from 18% in the UK to more than 21% in
France. Compaq still leads and Dell registers the strongest
growth, almost doubling unit sales last quarter (possibly a
response to its sending a seed team of elite troops to one
country after another to firmly implant its methods, in
reaction to its problematic first efforts). On the surface,
with the possible exception of Siemens, major European
brands don't have much market presence any more.
In a world of standard products based on Windows and Intel
Architecture processors, in theory, there is little
technological advantage available to any one player. What
follows, still in theory, is that European companies have a
more level playing field than with IBM proprietary mainframe
architectures. Instead, they are leveled not by technology
but by strong corporate cultures, although this is only
partly true. In the US, a good 25% of all PCs are no-name
systems assembled to order by aggressive local retailers who
manage to "out price" and "out service" the big guys. In
Europe, each sizeable city enjoys such an active core of
small companies who, according to a local study, serve an
even higher percentage of the market than in the US.
Conversations here put less emphasis on the Y2K problem,
rightly or wrongly, than on opening markets to competition
in fields such as telecommunications. Local monopolies no
longer control the field and, what do you know, the just
opened market consumes so much more of the newly competitive
services that the old monopolies prosper more than ever,
along with their new competitors. This, in turn, has removed
obstacles that used to stand in the way of Internet
development. What was seen as yet another invasion of
American culture is now embraced by both the new blood who
see the future on the Net, and the old guard who know they
must follow or die.
For the software industry, the emerging new Europe offers an
interesting mix of opposing trends. On one hand, we have the
obvious: the easier circulation of goods, services, people,
and even professions (your professional degree is now
recognized in more places) all make it easier to sell more
product in more places. On the other, but not opposed, hand,
Europeans are used to different, fragmented cultures. This
makes it easy for us to promote what in other places is
sometimes perceived as a "fragmenting" product.
In other less politically correct words, Microsoft is a
lesser god in Europe than in the US. Where the common US
view is "there is no other Microsoft but Microsoft," Europe
is not so monotheistic. As a result, BeOS enjoys
proportionally higher acceptance with software developers
and distribution channels, as well as with the general and
professional media. This is in line with what happened with
the VCR and audio CDs, or even the Macintosh -- they all
first gained traction in Europe.
In conclusion, and in contradiction with some of the above,
if the story is that the US favors standardization and
Europe accepts more diversity, how do I explain five or six
incompatible cellular telephone systems in the US vs.
Europe-wide (and beyond) GSM?
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.
|