* Copyright 2002-2008, Marcus Overhagen, Stefano Ceccherini, Fredrik Modéen.
* All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <Joystick.h>
#include <JoystickTweaker.h>
#include <new>
#include <stdio.h>
#include <sys/ioctl.h>
#include <Debug.h>
#include <Directory.h>
#include <List.h>
#include <Path.h>
#include <String.h>
#if DEBUG
static FILE *sLogFile = NULL;
inline void
LOG(const char *fmt, ...)
{
char buf[1024];
va_list ap;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
fputs(buf, sLogFile); fflush(sLogFile);
}
# define LOG_ERR(text...) LOG(text)
#else
# define LOG(text...)
# define LOG_ERR(text...) fprintf(stderr, text)
#endif
#define CALLED() LOG("%s\n", __PRETTY_FUNCTION__)
BJoystick::BJoystick()
:
timestamp(0),
horizontal(0),
vertical(0),
button1(true),
button2(true),
fBeBoxMode(false),
fFD(-1),
fDevices(new(std::nothrow) BList),
fJoystickInfo(new(std::nothrow) joystick_info),
fJoystickData(new(std::nothrow) BList)
{
#if DEBUG
sLogFile = fopen("/var/log/joystick.log", "a");
#endif
if (fJoystickInfo != NULL) {
memset(&fJoystickInfo->module_info, 0, sizeof(joystick_module_info));
fJoystickInfo->calibration_enable = false;
fJoystickInfo->max_latency = 0;
}
RescanDevices();
}
BJoystick::~BJoystick()
{
if (fFD >= 0)
close(fFD);
if (fDevices != NULL) {
for (int32 i = 0; i < fDevices->CountItems(); i++)
delete (BString *)fDevices->ItemAt(i);
delete fDevices;
}
delete fJoystickInfo;
if (fJoystickData != NULL) {
for (int32 i = 0; i < fJoystickData->CountItems(); i++) {
variable_joystick *variableJoystick
= (variable_joystick *)fJoystickData->ItemAt(i);
if (variableJoystick == NULL)
continue;
free(variableJoystick->data);
delete variableJoystick;
}
delete fJoystickData;
}
}
status_t
BJoystick::Open(const char *portName)
{
CALLED();
return Open(portName, true);
}
status_t
BJoystick::Open(const char *portName, bool enhanced)
{
CALLED();
if (portName == NULL)
return B_BAD_VALUE;
if (fJoystickInfo == NULL || fJoystickData == NULL)
return B_NO_INIT;
fBeBoxMode = !enhanced;
char nameBuffer[64];
if (portName[0] != '/') {
snprintf(nameBuffer, sizeof(nameBuffer), DEVICE_BASE_PATH"/%s",
portName);
} else
snprintf(nameBuffer, sizeof(nameBuffer), "%s", portName);
if (fFD >= 0)
close(fFD);
fFD = open(nameBuffer, O_RDWR | O_NONBLOCK | O_EXCL);
if (fFD < 0)
return B_ERROR;
_BJoystickTweaker joystickTweaker(*this);
joystickTweaker.GetInfo(fJoystickInfo, portName);
fJoystickInfo->module_info.flags |= js_flag_variable_size_reads;
LOG("ioctl - %d\n", fJoystickInfo->module_info.num_buttons);
ioctl(fFD, B_JOYSTICK_SET_DEVICE_MODULE, &fJoystickInfo->module_info,
sizeof(joystick_module_info));
ioctl(fFD, B_JOYSTICK_GET_DEVICE_MODULE, &fJoystickInfo->module_info,
sizeof(joystick_module_info));
LOG("ioctl - %d\n", fJoystickInfo->module_info.num_buttons);
bool supportsVariable
= (fJoystickInfo->module_info.flags & js_flag_variable_size_reads) != 0;
for (uint16 i = 0; i < fJoystickInfo->module_info.num_sticks; i++) {
variable_joystick *variableJoystick
= new(std::nothrow) variable_joystick;
if (variableJoystick == NULL)
return B_NO_MEMORY;
status_t result;
if (supportsVariable) {
result = variableJoystick->initialize(
fJoystickInfo->module_info.num_axes,
fJoystickInfo->module_info.num_hats,
fJoystickInfo->module_info.num_buttons);
} else {
result = variableJoystick->initialize_to_extended_joystick();
if (fJoystickInfo->module_info.num_axes > MAX_AXES)
fJoystickInfo->module_info.num_axes = MAX_AXES;
if (fJoystickInfo->module_info.num_hats > MAX_HATS)
fJoystickInfo->module_info.num_hats = MAX_HATS;
if (fJoystickInfo->module_info.num_buttons > MAX_BUTTONS)
fJoystickInfo->module_info.num_buttons = MAX_BUTTONS;
}
if (result != B_OK) {
delete variableJoystick;
return result;
}
if (!fJoystickData->AddItem(variableJoystick)) {
free(variableJoystick->data);
delete variableJoystick;
return B_NO_MEMORY;
}
}
return fFD;
}
void
BJoystick::Close(void)
{
CALLED();
if (fFD >= 0) {
close(fFD);
fFD = -1;
}
}
status_t
BJoystick::Update()
{
CALLED();
if (fJoystickInfo == NULL || fJoystickData == NULL || fFD < 0)
return B_NO_INIT;
for (uint16 i = 0; i < fJoystickInfo->module_info.num_sticks; i++) {
variable_joystick *values
= (variable_joystick *)fJoystickData->ItemAt(i);
if (values == NULL)
return B_NO_INIT;
ssize_t result = read_pos(fFD, i, values->data,
values->data_size);
if (result < 0)
return result;
if ((size_t)result != values->data_size)
return B_ERROR;
if (i > 0)
continue;
timestamp = *values->timestamp;
if (values->axis_count >= 1)
horizontal = values->axes[0];
else
horizontal = 0;
if (values->axis_count >= 2)
vertical = values->axes[1];
else
vertical = 0;
if (values->button_blocks > 0) {
button1 = (*values->buttons & 1) == 0;
button2 = (*values->buttons & 2) == 0;
} else {
button1 = true;
button2 = true;
}
}
return B_OK;
}
status_t
BJoystick::SetMaxLatency(bigtime_t maxLatency)
{
CALLED();
if (fJoystickInfo == NULL || fFD < 0)
return B_NO_INIT;
status_t result = ioctl(fFD, B_JOYSTICK_SET_MAX_LATENCY, &maxLatency,
sizeof(maxLatency));
if (result == B_OK)
fJoystickInfo->max_latency = maxLatency;
return result;
}
int32
BJoystick::CountDevices()
{
CALLED();
if (fDevices == NULL)
return 0;
int32 count = fDevices->CountItems();
LOG("Count = %d\n", count);
return count;
}
status_t
BJoystick::GetDeviceName(int32 index, char *name, size_t bufSize)
{
CALLED();
if (fDevices == NULL)
return B_NO_INIT;
if (index >= fDevices->CountItems())
return B_BAD_INDEX;
if (name == NULL)
return B_BAD_VALUE;
BString *deviceName = (BString *)fDevices->ItemAt(index);
if (deviceName->Length() > (int32)bufSize)
return B_NAME_TOO_LONG;
strlcpy(name, deviceName->String(), bufSize);
LOG("Device Name = %s\n", name);
return B_OK;
}
status_t
BJoystick::RescanDevices()
{
CALLED();
if (fDevices == NULL)
return B_NO_INIT;
ScanDevices(true);
return B_OK;
}
bool
BJoystick::EnterEnhancedMode(const entry_ref *ref)
{
CALLED();
fBeBoxMode = false;
return !fBeBoxMode;
}
int32
BJoystick::CountSticks()
{
CALLED();
if (fJoystickInfo == NULL)
return 0;
return fJoystickInfo->module_info.num_sticks;
}
int32
BJoystick::CountAxes()
{
CALLED();
if (fJoystickInfo == NULL)
return 0;
return fJoystickInfo->module_info.num_axes;
}
status_t
BJoystick::GetAxisValues(int16 *outValues, int32 forStick)
{
CALLED();
if (fJoystickInfo == NULL || fJoystickData == NULL)
return B_NO_INIT;
if (forStick < 0
|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
return B_BAD_INDEX;
variable_joystick *variableJoystick
= (variable_joystick *)fJoystickData->ItemAt(forStick);
if (variableJoystick == NULL)
return B_NO_INIT;
memcpy(outValues, variableJoystick->axes,
fJoystickInfo->module_info.num_axes * sizeof(uint16));
return B_OK;
}
status_t
BJoystick::GetAxisNameAt(int32 index, BString *outName)
{
CALLED();
if (index >= CountAxes())
return B_BAD_INDEX;
if (outName == NULL)
return B_BAD_VALUE;
*outName = "Axis ";
*outName << index;
return B_OK;
}
int32
BJoystick::CountHats()
{
CALLED();
if (fJoystickInfo == NULL)
return 0;
return fJoystickInfo->module_info.num_hats;
}
status_t
BJoystick::GetHatValues(uint8 *outHats, int32 forStick)
{
CALLED();
if (fJoystickInfo == NULL || fJoystickData == NULL)
return B_NO_INIT;
if (forStick < 0
|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
return B_BAD_INDEX;
variable_joystick *variableJoystick
= (variable_joystick *)fJoystickData->ItemAt(forStick);
if (variableJoystick == NULL)
return B_NO_INIT;
memcpy(outHats, variableJoystick->hats,
fJoystickInfo->module_info.num_hats);
return B_OK;
}
status_t
BJoystick::GetHatNameAt(int32 index, BString *outName)
{
CALLED();
if (index >= CountHats())
return B_BAD_INDEX;
if (outName == NULL)
return B_BAD_VALUE;
*outName = "Hat ";
*outName << index;
return B_OK;
}
int32
BJoystick::CountButtons()
{
CALLED();
if (fJoystickInfo == NULL)
return 0;
return fJoystickInfo->module_info.num_buttons;
}
uint32
BJoystick::ButtonValues(int32 forStick)
{
CALLED();
if (fJoystickInfo == NULL || fJoystickData == NULL)
return 0;
if (forStick < 0
|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
return 0;
variable_joystick *variableJoystick
= (variable_joystick *)fJoystickData->ItemAt(forStick);
if (variableJoystick == NULL || variableJoystick->button_blocks == 0)
return 0;
return *variableJoystick->buttons;
}
status_t
BJoystick::GetButtonValues(bool *outButtons, int32 forStick)
{
CALLED();
if (fJoystickInfo == NULL || fJoystickData == NULL)
return B_NO_INIT;
if (forStick < 0
|| forStick >= (int32)fJoystickInfo->module_info.num_sticks)
return B_BAD_INDEX;
variable_joystick *variableJoystick
= (variable_joystick *)fJoystickData->ItemAt(forStick);
if (variableJoystick == NULL)
return B_NO_INIT;
int16 buttonCount = fJoystickInfo->module_info.num_buttons;
for (int16 i = 0; i < buttonCount; i++) {
outButtons[i]
= (variableJoystick->buttons[i / 32] & (1 << (i % 32))) != 0;
}
return B_OK;
}
status_t
BJoystick::GetButtonNameAt(int32 index, BString *outName)
{
CALLED();
if (index >= CountButtons())
return B_BAD_INDEX;
if (outName == NULL)
return B_BAD_VALUE;
*outName = "Button ";
*outName << index;
return B_OK;
}
status_t
BJoystick::GetControllerModule(BString *outName)
{
CALLED();
if (fJoystickInfo == NULL || fFD < 0)
return B_NO_INIT;
if (outName == NULL)
return B_BAD_VALUE;
outName->SetTo(fJoystickInfo->module_info.module_name);
return B_OK;
}
status_t
BJoystick::GetControllerName(BString *outName)
{
CALLED();
if (fJoystickInfo == NULL || fFD < 0)
return B_NO_INIT;
if (outName == NULL)
return B_BAD_VALUE;
outName->SetTo(fJoystickInfo->module_info.device_name);
return B_OK;
}
bool
BJoystick::IsCalibrationEnabled()
{
CALLED();
if (fJoystickInfo == NULL)
return false;
return fJoystickInfo->calibration_enable;
}
status_t
BJoystick::EnableCalibration(bool calibrates)
{
CALLED();
if (fJoystickInfo == NULL || fFD < 0)
return B_NO_INIT;
status_t result = ioctl(fFD, B_JOYSTICK_SET_RAW_MODE, &calibrates,
sizeof(calibrates));
if (result == B_OK)
fJoystickInfo->calibration_enable = calibrates;
return result;
}
void
BJoystick::Calibrate(struct _extended_joystick *reading)
{
CALLED();
}
void
BJoystick::ScanDevices(bool useDisabled)
{
CALLED();
if (useDisabled) {
_BJoystickTweaker joystickTweaker(*this);
joystickTweaker.scan_including_disabled();
}
}
void BJoystick::_ReservedJoystick1() {}
void BJoystick::_ReservedJoystick2() {}
void BJoystick::_ReservedJoystick3() {}
status_t BJoystick::_Reserved_Joystick_4(void*, ...) { return B_ERROR; }
status_t BJoystick::_Reserved_Joystick_5(void*, ...) { return B_ERROR; }
status_t BJoystick::_Reserved_Joystick_6(void*, ...) { return B_ERROR; }