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.
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.
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.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf.
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.
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
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....
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 { uint8command
[16]; uint8command_length
; uint8flags
; uint8scsi_status
; /* SCSI Status Byte */ /* (0 = success, 2 = check cond, ... */ uint8cam_status
; /* CAM_* status conditions from CAM.h */ void *data
; size_tdata_length
; void *sense_data
; /* buffer to return mode sense data in */ size_tsense_data_length
; /* size of optional buffer for mode sense */ bigtime_ttimeout
; } 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.
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.
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). */ voidinquiry
(const char *dev
) { static intheader
= 1; intfd
,e
; intpath
,targ
,lun
,type
; raw_device_commandrdc
; uchardata
[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. */ voidwalkpath
(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 */ intmain
(intargc
, char *argv
[]) { if(argc
!= 2) {walkpath
("/dev/bus/scsi"); } else {inquiry
(argv
[1]); } return 0; }
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
,
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
BJoystick
->CountDevices()
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"); returnfalse
; } 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
); returnfalse
; }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
= newBStringView
(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
= newBStringView
(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.
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?