Be Newsletter

Volume II, Issue 43; October 27, 1998

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.