The Input Server: BInputServerDevice

The Input Server Table of Contents | The Input Server Index

# BInputServerDevice

Derived from: none

Declared in: be/add-ons/input_server/InputServerDevice.h

Library: The Input Server

Allocation: By the Input Server only

###### Summary

BInputServerDevice is a base class for input devices; these are instances of BInputServerDevice subclasses that generate input events. In most cases, an input device corresponds to a device driver that handles a specific brand or model of hardware (mouse, keyboard, tablet, etc.), but it doesn't have to: an input device can get events from the Net, or generate them algorithmically, for example. Also, a single BInputServerDevice can handle more than one device driver.

BInputServerDevice objects are created and deleted by the Input Server only—you never create or delete these objects themselves.

## Starting and Sending Messages

For each device that your object registers, it gets a `Start()` function call. This is the Input Server's way of telling your object that it can begin generating input events (for the designated device). So far, all of this—from the add-on load to the `Start()` call—happens within a single Input Server thread (for all input devices). When your `Start()` function is called, you should spawn a thread so your object can generate events without blocking the Server. Events are generated and sent through the `EnqueueMessage()` function.

## Device Types and Control Messages

The Input Server knows about two types of devices: keyboards, and pointing devices (mice, tablets, etc). When you register your object's devices (through `RegisterDevices()`) you have to indicate the device type. The Input Server uses the device type to predicate the input device control messages it sends to the devices. These messages, delivered in `Control()` calls, tell a device that there's been a change downstream that applies specifically to that type of device. For example, when the user changes the mouse speed, each pointing device receives a `B_MOUSE_SPEED_CHANGED` notification.

The Be-defined control messages are predicated on device type only.

If your BInputServerDevice object manages a device other than a pointer or a keyboard, you tell the Input Server that the device is undefined. In this case, the Input Server won't send your device any device-specific messages; to send your device a message you (or an application that knows about your device) have to use a BInputDevice object.

### Pointing Devices

Pointing devices such as mice, trackballs, drawing tablets, etc. generate `B_MOUSE_MOVED` messages (which trigger a BView>s `MouseMoved()` function) featuring a where field representing the cursor>s location in view co-ordinates. Unfortunately, your BInputServerDevice doesn>t know anything about views; that>s the App Server>s job. You'll still need to add this information to the `B_MOUSE_MOVED` messages generated by your BInputServerDevice, and the App Server will adjust it to view co-ordinates for you.

When generating a `B_MOUSE_MOVED` message, you add x and y fields in one of two ways:

- an offset relative to the cursor>s previous position (`B_INT32_TYPE` values)

- an absolute position expressed in the range 0.0 to 1.0 (`B_FLOAT_TYPE` values)

Mice always use relative locations; tablets can use either (though they usually provide absolute values).

#### Relative Locations

All mice (and some drawing tablets) express the pointer location relative to its previous position. If your pointing device is operating in relative co-ordinate mode, you add x and y entries as `B_INT32_TYPE` values in device-defined units. The App Server interprets these units as pixels, so you may need to scale your output:

       int32 xVal, yVal;
       ...
       event->AddInt32( "x", xVal );
       event->AddInt32( "y", yVal );

#### Absolute Locations

Drawing tablets or other pointing devices that provide absolute locations add the x and y entries as `B_FLOAT_TYPE`s:

       float xVal, yVal;
       ...
       event->AddFloat( "x", xVal );
       event->AddFloat( "y", yVal );

These values must be in the range [0.0 to 1.0]. The app_server scales them to the screen>s co-ordinate system so (0.0, 0.0) is the left-top, and (1.0, 1.0) is the right-bottom of the screen. This lets the pointing device work with any screen resolution, automatically.

#### Now where?

When the Application Server receives one of these `B_MOUSE_MOVED` messages, it converts the x and y values into absolute values in the target view>s co-ordinate system, and then throws away the x and y entries in the message. Because of this, and the fact that some applications might want more accurate positional information from tablets, fill in the be:tablet_x and be:tablet_y fields as well:

       float xVal, yVal;
       ...
       event->AddFloat( "x", xVal );
       event->AddFloat( "y", yVal );
       event->AddFloat( "be:tablet_x", xVal );
       event->AddFloat( "be:tablet_y", yVal );

#### Other Useful Information

Pressure information is stored in the be:tablet_pressure field, as a float in the range [0.0 to 1.0] (minimum pressure to maximum pressure):

       float pressure;
       ...
       event->AddFloat( "be:tablet_pressure", pressure );

If the tablet supports tilt information, store it in be:tablet_tilt_x and be:tablet_tilt_y, scaling the information to the range [0.0 to 1.0]. A tilt of (-1.0, -1.0) tilts to the left-top, (1.0, 1.0) tilts to the right-bottom, and (0.0, 0.0) is no tilt.

       float tilt_x, tilt_y;
       ...
       event->AddFloat( "be:tablet_tilt_x", tilt_x );
       event->AddFloat( "be:tablet_tilt_y", tilt_y );

Tablets with pens that support an eraser store the eraser>s state in the be:tablet_eraser field. A value of 1 means the pen is reversed (i.e. the eraser is on), and 0 means it should behave normally.

       int32 erase_mode;
       ...
       event->AddInt32( "be:tablet_eraser", erase_mode );

## Device State

The `Control()` protocol is designed to accommodate queries (in addition to commands). Currently, however, the Input Server maintains the keyboard and pointing device state and answers these queries itself; it doesn't forward any of the Be-defined query messages. For example, when an application asks for the current mouse speed setting (through `get_mouse_speed()`), the query gets no further than the Input Server itself—it doesn't get passed as a control message to a pointing device.

If you're designing a BInputServerDevice that manages a keyboard or pointing device, you must keep in mind that your device is not responsible for its "Be-defined" state. The elements of the state—mouse speed, key map, etc.—correspond to the control messages listed in "Input Device Control Messages".

## Dynamic Devices

As hardware devices are attached and detached from the computer, you can add and remove items from your BInputServerDevice's list of registered devices (by calling `RegisterDevice()`/`UnregisterDevice()`). But your object has to first notice that a physical device has been added or removed. It does this by placing a node monitor on the device directory (/dev). As a convenience—and to help conserve resources—the BInputServerDevice class provides the `Start`/`StopMonitoringDevices()` functions which install and remove node monitors for you.

## Creating and Registering

To create a new input device, you must:

- create a subclass of BInputServerDevice

- implement the `instantiate_input_device()` C function to create an instance of your BInputServerDevice subclass

- compile the class and the function as an add-on

- install the add-on in one of the input device directories

At boot time, the Input Server loads the add-ons it finds in the input device directories. For each add-on it loads, the Server invokes `instantiate_input_device()` to get a pointer to the add-on's BInputServerDevice object. After constructing the object, the Server calls `InitCheck()` to give the add-on a chance to bail out if the constructor failed. If the add-on wants to continue, it calls `RegisterDevices()` (from within `InitCheck()`) to tell the Server which physical or virtual devices it handles.

## Installing an Input Device

The input server looks for input devices in the "input_server/devices" subdirectories of `B_BEOS_ADDONS_DIRECTORY`, `B_COMMON_ADDONS_DIRECTORY,` and `B_USER_ADDONS_DIRECTORY`.

- You can install your input devices in the latter two directories—i.e. those under `B_COMMON_ADDONS_DIRECTORY`, and `B_USER_ADDONS_DIRECTORY`.

- The `B_BEOS_ADDONS_DIRECTORY` is reserved for add-ons that are supplied by the BeOS.

## Hook Functions

- `Control()

  `
- `InitCheck()

  `
- `Start()

  `
- `Stop()

  `
- `SystemShuttingDown()

  `

## Constructor and Destructor

### BInputServerDevice()

`BInputServerDevice(`void`)`

Creates a new BInputServerDevice object. You can initialize your object—set initial values, spawn (but not necessarily resume; do that in `Start()`) threads, open drivers, etc.—either here or in the `InitCheck()` function, which is called immediately after the constructor.

### ~BInputServerDevice()

`~`BInputServerDevice`() `

Deletes the BInputServerDevice object. The destructor is invoked by the Input Server only—you never delete a BInputServerDevice object from your own code. When the destructor is called, the object's devices will already be unregistered and `Stop()` will already have been called. If this object spawned its own threads or allocated memory on the heap, it must clean up after itself here.

## Member Functions

### Control()

virtual status_t `Control(`const char *name, void *cookie, uint32 command, BMessage *message`) `

The `Control()` hook function is invoked by the Input Server to send an input device control message or a Node Monitor message to this object. name and cookie are the readable name and pointer-to-whatever-you-want that you used when registering the device (with the `RegisterDevices()` function).

| The function's return value is ignored.

##### Input Device Control Messages

An input device control message is sent when a downstream change needs to be propagated to an input device. For example, when the user resets the mouse speed (through the Mouse preference), a `B_MOUSE_SPEED_CHANGED` control message is sent to all objects that have registered a `B_POINTING_DEVICE` device (see `RegisterDevices()`). name and cookie identify the device that this message applies to. The control message itself is represented by the command constant, optionally supplemented by message.

See "Input Device Control Messages" for a list of the control messages that the BeOS defines, and instructions for how to respond to them. An application can send a custom control message through a BInputDevice object; see `BInputDevice::Control()` for details.

##### Node Monitor Messages

A Node Monitor message is sent if an entry is added to or removed from one of the device directories that the object is monitoring, as set through `StartMonitoringDevice()`. In this case, name and cookie are `NULL`, command is `B_NODE_MONITOR`, and message describes the file that was added or deleted. The message's `opcode` field will be `B_ENTRY_CREATED` or `B_ENTRY_REMOVED` (or, potentially but nonsensically, `B_ENTRY_MOVED`). For instructions on how to read these messages, see "The Node Monitor" in the Storage Kit (or click on the opcode constants).

### EnqueueMessage()

status_t `EnqueueMessage(` BMessage *message`) `

Sends an event message to the Input Server, which passes it through the input methods and input filters before sending it to the App Server. The message you create should be appropriate for the action you're trying to depict. For example, if the user presses a key, you should create and send a `B_KEY_DOWN` message. A list of the system-defined event messages that an input device is expected to create and send is given in "Input Device Event Messages".

RETURN CODES

`B_OK`. The message was sent.

- Anything else. The connection to the App Server has been broken—this isn't good, and you may want to check the `is_computer_on_fire()` function found in the Kernel Kit.

### InitCheck()

virtual status_t `InitCheck(`void`)`

Invoked by the Input Server immediately after the object is constructed to test the validity of the initialization. If the object is properly initialized (i.e. all required resources are located or allocated), this function should return `B_OK`. `Start()` will be invoked soon if you need to do any extra initialization. If the object returns non-`B_OK`, the object is deleted and the add-on is unloaded.

The default implementation returns `B_OK`.

### RegisterDevices() , UnregisterDevices()

status_t `RegisterDevices(`input_device_ref **devices`) `
status_t `UnregisterDevices(`input_device_ref **devices`) `

`RegisterDevices()` tells the Input Server that this object is responsible for the listed devices. This means that when a control message is sent back upstream, the message—which is tagged as being relevant for a specific device, or type of device—will be forwarded (through the `Control()` hook) to the responsible BInputServerDevice object(s). Typically, you initially register your devices as part of the constructor or `InitCheck()`. Registration is cumulative—each `RegisterDevices()` call adds to the object's current list of devices.

`UnregisterDevices()` tells the Input Server that this object is no longer responsible for the listed devices. The devices are automatically unregistered when your object is deleted.

`RegisterDevices()` invokes `Start()` for each device in the devices list; `UnregisterDevices()` invokes `Stop()`.

For both functions, the devices list must be `NULL`-terminated, and the caller retains ownership of the list and its contents.

Note that the BeOS currently only targets the device types when sending a `Control()` message. For example, let's say you've registered two pointing devices and a keyboard:

       status_t MyISDevice::InitCheck()
       {
       ...
          input_device_ref **devices =
             (input_device_ref **)malloc(sizeof(*input_device_ref * 4));
          input_device_ref mouse1 = {"Mouse 1", B_POINTING_DEVICE,
                                     (void *)this)};
          input_device_ref mouse2 = {"Mouse 2", B_POINTING_DEVICE,
                                     (void *)this)};
          input_device_ref keyboard = {"Keyboard", B_KEYBOARD_DEVICE,
                                       (void *)this)};
          devices[0] = &mouse1;
          devices[1] = &mouse2;
          devices[2] = &keyboard;
          devices[3] = NULL;
          RegisterDevices(devices);
          ...
       }

When the user fiddles with the Mouse preference (more specifically, if an application calls `set_mouse_speed()` et. al.), this object will receive two `Control()` messages: one targets "Mouse 1", and the other targets "Mouse 2". That's because the mouse and keyboard functions (as defined by the BeOS and as used by the system preferences) know which type of device to control, but they don't provide a means for more granular identification. If you need a UI that identifies specific devices, you have to create the UI yourself, and use a BInputDevice object to tune the control messages that are sent back upstream.

RETURN CODES

`B_OK`. At least one of the devices was registered.

- `B_ERROR`. None of the devices were registered.

The functions don't let you un/register the same device definition twice, and `RegisterDevices()` won't register a device that doesn't have a name (although the name can be `""`). However, the functions don't complain about violations of these conditions as long as at least one definition is properly formed.

### Start() , Stop()

virtual status_t `Start(`const char *name, void *cookie`) `
virtual status_t `Stop(`const char *name, void *cookie`) `

`Start()` is invoked by the Input Server to tell the object that it can begin sending events for the registered device identified by the arguments. The values of the arguments are taken from the `input_device_ref` structure you used to register the device (see `RegisterDevices()`). If your object needs to resume a thread (spawned in the constructor, in `InitCheck()`, or here), this is the place to do it.

`Stop()` is invoked to tell the object to stop sending events for the registered device. The device is not unregistered—you can still receive `Control()` messages for the device while it's stopped. You should pause or kill any threads associated with the device (that were spawned by this object) from here.

| The return value (for both of these functions) is ignored.

### StartMonitoringDevice() , StopMonitoringDevice()

status_t `StartMonitoringDevice(`const char *deviceDir`) `
status_t `StopMonitoringDevice(`const char *deviceDir`) `

These are convenient covers for the Node Monitor's `watch_node()` and `stop_watching()` functions. You use them to watch for physical devices that are attached and detached, as indicated by changes to subdirectories of the system device directory (/dev).

deviceDir is the name of the device subdirectory that you want to watch. The "/dev/" root is automatically prepended; for example, if you want to watch for new ps2 mice, you would pass "input/mouse/ps2" as the deviceDir name. The Node Monitor is told to look for changes to the directory (`B_WATCH_DIRECTORY` opcode). When an entry is added or removed, this object receives a `B_NODE_MONITOR` message delivered to its `Control()` function.

RETURN CODES

`B_OK`. Success.

- `B_ERROR`. Unspecified failure.

- `B_NOT_A_DIRECTORY`. You're trying to monitor a node that isn't a directory.

- `B_BAD_VALUE`. deviceDir not found.

### SystemShuttingDown()

virtual status_t `SystemShuttingDown``(`void`)` const

Tells the object that the Input Server is in the process of shutting down. Unless something interrupts the shutdown, this notification will be followed by a`Stop()` and `delete`, thus you don't have to do much from this function (other than note that the end is near).

| The return value is ignored.

### UnregisterDevices see RegisterDevices()

The Input Server Table of Contents | The Input Server Index

The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..