void vrpn_DreamCheeky_Drum_Kit::decodePacket(size_t bytes, vrpn_uint8 *buffer)
{
  // The reports are each 8 bytes long.  Since there is only one type of
  // report for this device, the report type is not included (stripped by
  // the HIDAPI driver).  The bytes are 8 identical reports.
  // There is one byte per report, and it holds a binary encoding of
  // the buttons.  Button 0 is in the LSB, and the others proceed up
  // the bit chain.  Parse each report and then send any changes on.
  // need to send between each report so we don't miss a button press/release
  // all in one packet.

  size_t i, r;
  // Truncate the count to an even number of 8 bytes.  This will
  // throw out any partial reports (which is not necessarily what
  // we want, because this will start us off parsing at the wrong
  // place if the rest of the report comes next, but it is not
  // clear how to handle that cleanly).
  bytes -= (bytes % 8);
  
  // Decode all full reports, each of which is 8 bytes long.
  for (i = 0; i < (bytes / 8); i++) {

    // If we're debouncing the buttons, then we set the button
    // to "pressed" if it has 4 or more pressed events in the
    // set of 8 and to "released" if it has less than 4.
    if (d_debounce) {
      int btn;
      for (btn = 0; btn < vrpn_Button::num_buttons; btn++) {
        unsigned count = 0;
        vrpn_uint8 mask = 1 << btn;
        for (r = 0; r < 8; r++) { // Skip the all-zeroes byte
          vrpn_uint8 *report = buffer + 9*i + r;
          count += ((*report & mask) != 0);
        }
        buttons[btn] = (count >= 4);
        vrpn_gettimeofday(&_timestamp, NULL);
        report_changes();
      }

    // If we're not debouncing, then we report each button event
    // independently.
    }else {
      for (r = 0; r < 8; r++) { // Skip the all-zeroes byte
        vrpn_uint8 *report = buffer + 9*i + r;
        int btn;
        for (btn = 0; btn < vrpn_Button::num_buttons; btn++) {
          vrpn_uint8 mask = 1 << btn;
          buttons[btn] = ((*report & mask) != 0);
        }
        vrpn_gettimeofday(&_timestamp, NULL);
        report_changes();
      }
    }
  }
}
void vrpn_DevInput::mainloop()
{
  get_report();

  server_mainloop();

  report_changes();
}
void vrpn_Xkeys_Desktop::mainloop()
{
	update();
	server_mainloop();
	vrpn_gettimeofday(&_timestamp, NULL);
	report_changes();

	vrpn_Button::server_mainloop();
}
void vrpn_DreamCheeky_Drum_Kit::mainloop()
{
	update();
	server_mainloop();
	vrpn_gettimeofday(&_timestamp, NULL);
	report_changes();

	vrpn_Button::server_mainloop();
}
Exemple #5
0
void vrpn_Xkeys_Pro::mainloop()
{
	update();
	server_mainloop();
	vrpn_gettimeofday(&_timestamp, NULL);
	report_changes();

        // Call the server_mainloop on our unique base class.
        server_mainloop();
}
void vrpn_Xkeys_Joystick::mainloop()
{
	update();
	server_mainloop();
	vrpn_gettimeofday(&_timestamp, NULL);
	report_changes();

	vrpn_Analog::server_mainloop();
	vrpn_Button::server_mainloop();
}
Exemple #7
0
void vrpn_3DConnexion::mainloop()
{
#if defined(VRPN_USE_HID)
	// Full reports are 7 bytes long.
	// XXX If we get a 2-byte report mixed in, then something is going to get
	// truncated.
	update();
#elif defined(linux) && !defined(VRPN_USE_HID)
    struct timeval zerotime;
    fd_set fdset;
    struct input_event ev;
    int i;

    zerotime.tv_sec = 0;
    zerotime.tv_usec = 0;

    FD_ZERO(&fdset);                      /* clear fdset              */
    FD_SET(fd, &fdset);                   /* include fd in fdset      */
    int moreData = 0;
    do {
        vrpn_noint_select(fd + 1, &fdset, NULL, NULL, &zerotime);
        moreData = 0;
        if (FD_ISSET(fd, &fdset)) {
            moreData = 1;
            if (vrpn_noint_block_read(fd, reinterpret_cast<char*>(&ev), sizeof(struct input_event)) != sizeof(struct input_event)) {
                send_text_message("Error reading from vrpn_3DConnexion", vrpn_Analog::timestamp, vrpn_TEXT_ERROR);
                if (d_connection) { d_connection->send_pending_reports(); }
                return;
            }
            switch (ev.type) {
                case EV_KEY:    // button movement
                    vrpn_gettimeofday((timeval *)&this->vrpn_Button::timestamp, NULL);
                    buttons[ev.code & 0x0ff] = ev.value;
                    break;
 
                case EV_REL:    // axis movement
                case EV_ABS:    // new kernels send more logical _ABS instead of _REL
                    vrpn_gettimeofday((timeval *)&this->vrpn_Analog::timestamp, NULL);
                    // Convert from short to int to avoid a short/double conversion
                    // bug in GCC 3.2.
                    i = ev.value;
                    channel[ev.code] = static_cast<double>(i)/400.0;           
                    break;
 
                default:
                    break;
            }
        }
        report_changes();
    } while (moreData == 1);
#endif

    server_mainloop();
    vrpn_gettimeofday(&_timestamp, NULL);
}
Exemple #8
0
void vrpn_Griffin_PowerMate::mainloop(void)
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL ) {
		_timestamp = current_time;
		report_changes();

                // Call the server_mainloop on our unique base class.
                server_mainloop();
	}
}
Exemple #9
0
void vrpn_Analog_5dtUSB::on_data_received(size_t bytes, vrpn_uint8 *buffer) {
	if (bytes != 64) {
		std::ostringstream ss;
		ss << "Received a too-short report: " << bytes;
		struct timeval ts;
		vrpn_gettimeofday(&ts, NULL);
		send_text_message(ss.str().c_str(), ts, vrpn_TEXT_WARNING);
		return;
	}

	// Decode all full reports.
	const float scale = 1.0f / 4096.0f;
	vrpn_uint8 * bufptr = buffer;
	for (size_t i = 0; i < 16; i++) {
		_rawVals[i] = vrpn_unbuffer<vrpn_int16>(bufptr) * scale;
	}

	switch (vrpn_Analog::num_channel) {
		case 5:
			for (size_t i = 0; i < 5; ++i) {
				channel[i] = _rawVals[i * 3];
				// Report this event before parsing the next.
				report_changes();
			}
			break;

		case 14:
			for (size_t i = 0; i < 14; ++i) {
				channel[i] = _rawVals[i];
				// Report this event before parsing the next.
				report_changes();
			}
			break;
		default:
			std::cerr << "Internal error - should not happen: Unrecognized number of channels!" << std::endl;
	}
}
void vrpn_Microsoft_SideWinder_Precision_2::mainloop(void)
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL)
	{
		_timestamp = current_time;
		report_changes();

                // Call the server_mainloop on our unique base class.
                server_mainloop();
	}
}
Exemple #11
0
void vrpn_LUDL_USBMAC6000::mainloop()
{
  if (_device_handle == NULL) {
    return;
  }

  // Let libusb handle any outstanding events
  struct timeval	zerotime;
  zerotime.tv_sec = 0;
  zerotime.tv_usec = 0;
  libusb_handle_events_timeout(_context, &zerotime);

  // If one of the axes is moving, check to see whether it has stopped.
  // If so, report its new position.
  // XXX Would like to change this to poll (or have the device send
  // continuously) the actual position, rather than relying on it having
  // gotten where we asked it to go).
  if (!_axis_moving || !_axis_destination) { return; }
  int i;
  for (i = 0; i < o_num_channel; i++) {
    if (_axis_moving[i]) {
      if (!ludl_axis_moving(i+1)) {
        vrpn_Analog::channel[i] = _axis_destination[i];
        _axis_moving[i] = false;
      }
    }
  }

  // Ask for and record the positions of the two axes.
  // Remember that the axes are numbered starting from 1 on the
  // LUDL controller but they go in Analog channels 2 and 3.
  vrpn_int32  position;
  if (ludl_axis_position(1, &position)) {
    channel[2] = position;
  }
  if (ludl_axis_position(2, &position)) {
    channel[3] = position;
  }

  // Let all of the servers do their thing.
  server_mainloop();
  vrpn_gettimeofday(&_timestamp, NULL);
  report_changes();

  // Call the server_mainloop on our unique base class.
  server_mainloop();
}
Exemple #12
0
void vrpn_XInputGamepad::mainloop() {
	XINPUT_STATE state;
	DWORD rv;

	server_mainloop();
	if ((rv = XInputGetState(_controllerIndex, &state)) != ERROR_SUCCESS) {
		char errMsg[256];
		struct timeval now;

		if (rv == ERROR_DEVICE_NOT_CONNECTED)
			sprintf(errMsg, "XInput device %u not connected", _controllerIndex);
		else
			sprintf(errMsg, "XInput device %u returned Windows error code %u",
				_controllerIndex, rv);

		vrpn_gettimeofday(&now, NULL);
		send_text_message(errMsg, now, vrpn_TEXT_ERROR);
		return;
	}

	// Set device state in VRPN_Analog
	channel[0] = normalize_axis(state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
	channel[1] = normalize_axis(state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
	channel[2] = normalize_axis(state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
	channel[3] = normalize_axis(state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
	channel[4] = normalize_dpad(state.Gamepad.wButtons);
	channel[5] = normalize_trigger(state.Gamepad.bLeftTrigger);
	channel[6] = normalize_trigger(state.Gamepad.bRightTrigger);

	// Set device state in VRPN_Button
	// Buttons are listed in DirectInput ordering
	buttons[0] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) != 0;
	buttons[1] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) != 0;
	buttons[2] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) != 0;
	buttons[3] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) != 0;
	buttons[4] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) != 0;
	buttons[5] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) != 0;
	buttons[6] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) != 0;
	buttons[7] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) != 0;
	buttons[8] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) != 0;
	buttons[9] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) != 0;

	vrpn_gettimeofday(&_timestamp, NULL);
	report_changes();
}
void vrpn_Logitech_Extreme_3D_Pro::mainloop(void)
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL)
	{
		_timestamp = current_time;
		report_changes();

		vrpn_Analog::server_mainloop();
		vrpn_Button::server_mainloop();
		if (vrpn_Dial::num_dials > 0)
		{
			vrpn_Dial::server_mainloop();
		}
	}
}
Exemple #14
0
void vrpn_Button_SerialMouse::mainloop()
{
	// Call the generic server mainloop, since we are a server
	server_mainloop();
 
    switch (status) {
	case BUTTON_READY:
	    read();
	    report_changes();
	    break;
	case BUTTON_FAIL:
      	{	
	    if (printed_error)	break;
	    printed_error = true;
	    send_text_message("vrpn_Button_SerialMouse failure!", timestamp, vrpn_TEXT_ERROR);
        }
      	break;
    }
}
void vrpn_Microsoft_SideWinder_Precision_2::mainloop(void)
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL)
	{
		_timestamp = current_time;
		report_changes();

		vrpn_Analog::server_mainloop();
		vrpn_Button::server_mainloop();
		if (vrpn_Dial::num_dials > 0)
		{
			vrpn_Dial::server_mainloop();
		}
	}
}
void vrpn_nVidia_shield_USB::mainloop(void)
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, d_timestamp) > POLL_INTERVAL ) {
		d_timestamp = current_time;
		report_changes();

		if (vrpn_Analog::num_channel > 0)
		{
			vrpn_Analog::server_mainloop();
		}
		if (vrpn_Button::num_buttons > 0)
		{
			vrpn_Button::server_mainloop();
		}
	}
}
void    vrpn_Analog_USDigital_A2::mainloop()
{
    server_mainloop();    //  let the server do its stuff
#ifdef VRPN_USE_USDIGITAL
    long readErr, readVal ;

    //  Read the data from the available channels
    for (vrpn_uint32 c=0 ; c<(vrpn_uint32) num_channel ; c++)
    {
        //  see if there's really a readable device there.
        if (c<_numDevices && _devAddr[c]>=0)
        {
            readErr = A2GetPosition(_devAddr[c], &readVal) ;
            if (readErr)
            {
                fprintf(stderr, 
                "vrpn_Analog_USDigital_A2: Error code %d received while reading channel %d.\n",
                    readErr, c) ;
                fprintf(stderr, "vrpn_Analog_USDigital_A2: Attempting to reinitialize SEI bus...") ;
                readErr = ResetSEI() ;
                if (readErr)
                    fprintf(stderr, "failed.") ;
                fprintf(stderr, "failed.") ;
                //  don't flood the log, and give the reset time to work
                vrpn_SleepMsecs(1000) ;
            }
            else
                channel[c] = (vrpn_float64) readVal ;
        }
        else
            channel[c] = 0 ;    //  default to 0 for unreadable/unavailable.
    }    //  for
#endif
  
    //  Finally, the point of all this, deliver the data
    if (_reportChange)
        report_changes() ;
    else
        report() ;

}    // mainloop
Exemple #18
0
int vrpn_Keyboard::get_report(void)
{
    struct timeval time;
    vrpn_gettimeofday(&time, NULL); // set timestamp of this event
    timestamp = time;
    int i;
#ifdef	_WIN32
    // Read one key state, which will read all of the events
    // and make it possible to read the state of all the keys;
    // We're ignoring the return for this particular key; it
    // will be read again as part of the 256-key read below.
    GetKeyState(1);

    // Read all 256 keys from the keyboard, then translate the
    // "virtual key" value from each into a scanline code and fill
    // in the appropriate entry with each value.
    BYTE  virtual_keys[256];
    if (GetKeyboardState(virtual_keys) == 0) {
      fprintf(stderr,"vrpn_Keyboard::get_report(): Could not read keyboard state\n");
      return 0;
    }

    // Clear all 256 key values, then fill in the ones that are
    // nonzero.  This is done because some of the keys from the
    // keyboard (control and shift) map into the same scan code;
    // if we just set all of the codes, then the right ones
    // overwrite the left ones.
    for (i = 0; i < 256; i++) {
      buttons[i] = 0;
    }
    for (i = 0; i < 256; i++) {
      unsigned scancode = MapVirtualKey(i, 0);
      if ( (scancode != 0) && ((0x80 & virtual_keys[i]) != 0) ) {
        buttons[scancode] = 1;
      }
    }
#endif
    report_changes();        // Report updates to VRPN
    return 0;
}
Exemple #19
0
int vrpn_3DMicroscribe::get_report(void)
{
#ifdef VRPN_USE_MICROSCRIBE
	length_3D tipPosition;
	angle_3D tipOri;
	DWORD buts;
	int iResult = ArmGetTipPosition(&tipPosition); //retrieves the current stylus tip position in Cartesian coordinates
	iResult = ArmGetTipOrientation(&tipOri); //retrieves the current stylus tip's unit vector orientation
	iResult = ArmGetButtonsState(&buts);
	if(iResult == ARM_NOT_CONNECTED)
	{ 
		//error connecting
		VRPN_MSG_ERROR( "MicroScribe connection lost!" );
		return 0;
	}

	//set the position, considering the scale, offset and origin matrix
	pos[0] = (tipPosition.y * m_Scale + m_OffSet[0])* MM_TO_METERS ;
	pos[1] = (tipPosition.z * m_Scale + m_OffSet[1])* MM_TO_METERS;
	pos[2] = (tipPosition.x * m_Scale + m_OffSet[2])* MM_TO_METERS;
	//vPosition = m_Matrix * vPosition + m_vPlaneOffset; extending the microscribe onto a plane 

	//set the orientation, considering the origin matrix
	float ori[3]={tipOri.y, tipOri.z, tipOri.x};
	ConvertOriToQuat(ori);

	
    status = STATUS_READING;        // ready to process event packet
    vrpn_gettimeofday(&timestamp, NULL); // set timestamp of this event
  
    buttons[0]  = ((buts & 0x02) != 0); // button 1
    buttons[1]  = ((buts & 0x01) != 0); // button 2

#endif

  report_changes();        // Report updates to VRPN
  return 0;
}
Exemple #20
0
void vrpn_Dial_Example_Server::mainloop()
{
    struct timeval current_time;
    int i;

    // Call the generic server mainloop, since we are a server
    server_mainloop();

    // See if its time to generate a new report
    // IN A REAL SERVER, this check would not be done; although the
    // time of the report would be updated to the current time so
    // that the correct timestamp would be issued on the report.
    vrpn_gettimeofday(&current_time, NULL);
    if (vrpn_TimevalDuration(current_time, timestamp) >=
        1000000.0 / _update_rate) {

        // Update the time
        timestamp.tv_sec = current_time.tv_sec;
        timestamp.tv_usec = current_time.tv_usec;

        // Update the values for the dials, to say that each one has
        // moved the appropriate rotation (spin rate is revolutions per
        // second, update rate is report/second, the quotient is the number
        // of revolutions since the last report). When the changes are
        // reported, these values are set back to zero.

        // THIS CODE WILL BE REPLACED by the user code that tells how
        // many revolutions each dial has changed since the last report.
        for (i = 0; i < num_dials; i++) {
            dials[i] = _spin_rate / _update_rate;
        }

        // Send reports. Stays the same in a real server.
        report_changes();
    }
}
void vrpn_Griffin_PowerMate::mainloop(void)
{
	update();
	server_mainloop();
	struct timeval current_time;
	vrpn_gettimeofday(&current_time, NULL);
	if (vrpn_TimevalDuration(current_time, _timestamp) > POLL_INTERVAL ) {
		_timestamp = current_time;
		report_changes();

		if (vrpn_Analog::num_channel > 0)
		{
			vrpn_Analog::server_mainloop();
		}
		if (vrpn_Button::num_buttons > 0)
		{
			vrpn_Button::server_mainloop();
		}
		if (vrpn_Dial::num_dials > 0)
		{
			vrpn_Dial::server_mainloop();
		}
	}
}
Exemple #22
0
int vrpn_Mouse::get_report()
{
#if defined(linux) && defined(VRPN_USE_GPM_MOUSE)
    fd_set readset;

    FD_ZERO( &readset );
    FD_SET( gpm_fd, &readset );
    struct timeval timeout = { 0, 0 };
    select( gpm_fd+1, &readset, NULL, NULL, &timeout );
    if( ! FD_ISSET( gpm_fd, &readset ) )
	return 0;

    Gpm_Event evt;
    if( Gpm_GetEvent( &evt ) <= 0 )
	return 0;

    if( evt.type & GPM_UP )
    {
	if( evt.buttons & GPM_B_LEFT )   buttons[0] = 0;
	if( evt.buttons & GPM_B_MIDDLE ) buttons[1] = 0;
	if( evt.buttons & GPM_B_RIGHT )  buttons[2] = 0;
    }
    else
    {
	buttons[0] = (evt.buttons & GPM_B_LEFT)   ? 1 : 0;
	buttons[1] = (evt.buttons & GPM_B_MIDDLE) ? 1 : 0;
	buttons[2] = (evt.buttons & GPM_B_RIGHT)  ? 1 : 0;
    }

    channel[0] = (vrpn_float64) evt.dx / gpm_mx;
    channel[1] = (vrpn_float64) evt.dy / gpm_my;
    return 1;
#elif defined(_WIN32)
    const unsigned LEFT_MOUSE_BUTTON = 0x01;
    const unsigned RIGHT_MOUSE_BUTTON = 0x02;
    const unsigned MIDDLE_MOUSE_BUTTON = 0x04;

    // Find out if the mouse buttons are pressed.
    if (0x80000 & GetKeyState(LEFT_MOUSE_BUTTON)) {
      vrpn_Button::buttons[0] = 1;
    } else {
      vrpn_Button::buttons[0] = 0;
    }
    if (0x80000 & GetKeyState(MIDDLE_MOUSE_BUTTON)) {
      vrpn_Button::buttons[1] = 1;
    } else {
      vrpn_Button::buttons[1] = 0;
    }
    if (0x80000 & GetKeyState(RIGHT_MOUSE_BUTTON)) {
      vrpn_Button::buttons[2] = 1;
    } else {
      vrpn_Button::buttons[2] = 0;
    }

    // Find the position of the cursor in X,Y with range 0..1 across the screen
    POINT curPos;
    GetCursorPos(&curPos);
    vrpn_Analog::channel[0] = (vrpn_float64)(curPos.x - GetSystemMetrics(SM_XVIRTUALSCREEN)) / GetSystemMetrics(SM_CXVIRTUALSCREEN);
    vrpn_Analog::channel[1] = (vrpn_float64)(curPos.y - GetSystemMetrics(SM_YVIRTUALSCREEN)) / GetSystemMetrics(SM_CYVIRTUALSCREEN);

    vrpn_gettimeofday( &timestamp, NULL );
    report_changes();
    return 1;
#else
    return 0;
#endif
}
Exemple #23
0
/******************************************************************************
 * NAME      : vrpn_5dt::get_report
 * ROLE      : This function will read characters until it has a full report, then
 *             put that report into analog fields and call the report methods on these.   
 * ARGUMENTS : void
 * RETURN    : void
 ******************************************************************************/
void vrpn_5dt::get_report (void)
{
  int  l_ret;		// Return value from function call to be checked

  // XXX This should be called when the first character of a report is read.
  vrpn_gettimeofday(&timestamp, NULL);

  //--------------------------------------------------------------------
  // Read as many bytes of this report as we can, storing them
  // in the buffer.  We keep track of how many have been read so far
  // and only try to read the rest.
  //--------------------------------------------------------------------

  l_ret = vrpn_read_available_characters (serial_fd, &_buffer [_bufcount],
                                          _expected_chars - _bufcount);
  if (l_ret == -1) {
      _5DT_ERROR ("Error reading the glove");
      _status = STATUS_RESETTING;
      return;
  }
#ifdef  VERBOSE
  if (l_ret != 0) printf("... got %d characters (%d total)\n",l_ret, _bufcount);
#endif

  //--------------------------------------------------------------------
  // The time of the report is the time at which the first character for
  // the report is read.
  //--------------------------------------------------------------------

  if ( (l_ret > 0) && (_bufcount == 0) ) {
	vrpn_gettimeofday(&timestamp, NULL);
  }

  //--------------------------------------------------------------------
  // We keep track of how many characters we have received and keep
  // going back until we get as many as we expect.
  //--------------------------------------------------------------------

  _bufcount += l_ret;
  if (_bufcount < _expected_chars) {    // Not done -- go back for more
      return;
  }

  //--------------------------------------------------------------------
  // We now have enough characters to make a full report.  First check to
  // make sure that the first one is what we expect.

  if (_buffer[0] != 128) {
    _5DT_WARNING ("Unexpected first character in report, resetting");
    _status = STATUS_RESETTING;
    _bufcount = 0;
    return;
  }

  if (_wireless) {
    if (_buffer[_bufcount - 1] != 0x40 && _buffer[_bufcount - 1] != 0x01) {
      // The last byte wasn't a capability byte, so this report is invalid.
      // Reset!
      _5DT_WARNING ("Unexpected last character in report, resetting");
      _status = STATUS_RESETTING;
      _bufcount = 0;
      return;
    }
  }

#ifdef	VERBOSE
  printf ("Got a complete report (%d of %d)!\n", _bufcount, _expected_chars);
#endif

  //--------------------------------------------------------------------
  // Decode the report and store the values in it into the analog values
  // if appropriate.
  //--------------------------------------------------------------------

  channel[1] = _buffer[1] / 255.0; //Thumb
  channel[2] = _buffer[2] / 255.0;
  channel[3] = _buffer[3] / 255.0;
  channel[4] = _buffer[4] / 255.0;
  channel[5] = _buffer[5] / 255.0; // Pinkie
  channel[6] = 180 * _buffer[6] / 255.0; // Pitch
  channel[7] = 180 * _buffer[7] / 255.0; // Roll

  if (_wireless && !_gotInfo) {
    _gotInfo = true;
    // Bit 0 set in the capability byte implies a right-hand glove.
    if (_buffer[9] == 0x01) {
      _5DT_INFO ("A 'wireless-type' right glove is ready and reporting");
    } else {
      _5DT_INFO ("A 'wireless-type' left glove is ready and reporting");
    }
  }

  //--------------------------------------------------------------------
  // Done with the decoding, send the reports and go back to syncing
  //--------------------------------------------------------------------

  report_changes();
  switch (_mode) {
    case 1:
      _status = STATUS_SYNCING;
      break;
    case 2:           // Streaming Mode, just go back for the next report.
      _bufcount = 0;
      break;
    default :
      _5DT_ERROR ("vrpn_5dt::get_report : internal error : unknown state");
      break;
  }
}
Exemple #24
0
void vrpn_Nidaq::mainloop(void) {
  server_mainloop();
  report_changes();
}
Exemple #25
0
int vrpn_ImmersionBox::get_report(void)
{
    unsigned char responseString[MAX_IBOX_STRING];
    int i;
    unsigned int buttonBits = 0;

    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 100000;

    status = STATUS_SYNCING;

    // process as long as we can get characters
    while (1 == vrpn_read_available_characters(serial_fd, buffer, 1)) {
	// if not a record start, skip it
	if (buffer[0] != dataPacketHeader) {
	    continue;
	}
	// we got a good one... we're reading now
	status = STATUS_READING;
	break;
    }

    // we broke out.. if we're not reading, then we have nothing to do
    if (STATUS_READING != status) {
	return 0;
    }    


    // we're reading now, get the report   

    // get the expected number of data record bytes
    int result = vrpn_read_available_characters(serial_fd, 
						(unsigned char *) responseString, 
						dataRecordLength, 
						&timeout );    
    
    if (result < dataRecordLength) {
	status = STATUS_SYNCING;
	return 0;
    }
    
    // parse the report here
    buttonBits = responseString[0];

    for (i = 0; i < _numbuttons; i++) {
	vrpn_Button::lastbuttons[i] = vrpn_Button::buttons[i];
	vrpn_Button::buttons[i]	= static_cast<unsigned char>(buttonBits & (1 << i));
    }

#if VERBOSE
    if (vrpn_Button::buttons[3])
	cerr << "left button pressed " << endl;
#endif

    // if we processed timer bits we would do so here

    // here is where we decode the analog stuff
    for (i = 0; i < _numchannels; i++) {
	vrpn_Analog::last[i] = vrpn_Analog::channel[i];
    }

    // here is where we convert the angle encoders
    for (i = 0; i < _numencoders; i++) {
	vrpn_Dial::dials[i] = 0.0;
    }

    report_changes();
    vrpn_gettimeofday(&timestamp, NULL);	// Set watchdog now

    return 1;
}
Exemple #26
0
int vrpn_Magellan::get_report(void)
{
   int ret;		// Return value from function call to be checked
   int i;		// Loop counter
   int nextchar = 1;	// Index of the next character to read

   //--------------------------------------------------------------------
   // If we're SYNCing, then the next character we get should be the start
   // of a report.  If we recognize it, go into READing mode and tell how
   // many characters we expect total. If we don't recognize it, then we
   // must have misinterpreted a command or something; reset the Magellan
   // and start over
   //--------------------------------------------------------------------

   if (status == STATUS_SYNCING) {
      // Try to get a character.  If none, just return.
      if (vrpn_read_available_characters(serial_fd, _buffer, 1) != 1) {
      	return 0;
      }

      switch (_buffer[0]) {
	  case 'k':
	      _expected_chars = 5; status = STATUS_READING; break;
	  case 'b':
	      _expected_chars = 2; status = STATUS_READING; break;
	  case 'm':
	      _expected_chars = 3; status = STATUS_READING; break;
	  case 'd':
	      _expected_chars = 26; status = STATUS_READING; break;
	  case 'n':
	      _expected_chars = 3; status = STATUS_READING; break;
	  case 'q':
	      _expected_chars = 4; status = STATUS_READING; break;
	  case 'z':
	      _expected_chars = 2; status = STATUS_READING; break;
	  case 'p':
	      _expected_chars = 4; status = STATUS_READING; break;
	  case 'c':
	      _expected_chars = 4; status = STATUS_READING; break;

	  default:
	      fprintf(stderr,"vrpn_Magellan: Unknown command (%c), resetting\n", _buffer[0]);
	      status = STATUS_RESETTING;
	      return 0;
      }


      // Got the first character of a report -- go into READING mode
      // and record that we got one character at this time. The next
      // bit of code will attempt to read the rest of the report.
      // The time stored here is as close as possible to when the
      // report was generated.
      _bufcount = 1;
      vrpn_gettimeofday(&timestamp, NULL);
      status = STATUS_READING;
#ifdef	VERBOSE
      printf("... Got the 1st char\n");
#endif
   }

   //--------------------------------------------------------------------
   // Read as many bytes of this report as we can, storing them
   // in the buffer.  We keep track of how many have been read so far
   // and only try to read the rest.
   //--------------------------------------------------------------------

   ret = vrpn_read_available_characters(serial_fd, &_buffer[_bufcount],
		_expected_chars-_bufcount);
   if (ret == -1) {
	send_text_message("vrpn_Magellan: Error reading", timestamp, vrpn_TEXT_ERROR);
	status = STATUS_RESETTING;
	return 0;
   }
   _bufcount += ret;
#ifdef	VERBOSE
   if (ret != 0) printf("... got %d characters (%d total)\n",ret, _bufcount);
#endif
   if (_bufcount < _expected_chars) {	// Not done -- go back for more
	return 0;
   }

   //--------------------------------------------------------------------
   // We now have enough characters to make a full report. Check to make
   // sure that its format matches what we expect. If it does, the next
   // section will parse it. If it does not, we need to go back into
   // synch mode and ignore this report. A well-formed report has the
   // last character '\r'
   //--------------------------------------------------------------------

   if (_buffer[_expected_chars-1] != '\r') {
	   status = STATUS_SYNCING;
      	   send_text_message("vrpn_Magellan: No carriage return in record", timestamp, vrpn_TEXT_ERROR);
	   return 0;
   }

#ifdef	VERBOSE
   printf("got a complete report (%d of %d)!\n", _bufcount, _expected_chars);
#endif

   //--------------------------------------------------------------------
   // Decode the report and store the values in it into the parent classes
   // (analog or button) if appropriate.
   //--------------------------------------------------------------------

   switch ( _buffer[0] ) {
     case 'k':
	 // This is a button command from the device. It gives us the state
	 // of each of the buttons on the device.  Buttons 1-4 are encoded
	 // in the 4 LSBs of the first byte (key 1 in the LSB); buttons 5-8
	 // are in the 4 LSBs of the second byte; the * button (which we'll
	 // call button 0) is in the LSB of the third byte (the other 3 bits
	 // don't seem to be used).
	 buttons[0] = ( (_buffer[3] & 0x01) != 0);
	 buttons[1] = ( (_buffer[1] & 0x01) != 0);
	 buttons[2] = ( (_buffer[1] & 0x02) != 0);
	 buttons[3] = ( (_buffer[1] & 0x04) != 0);
	 buttons[4] = ( (_buffer[1] & 0x08) != 0);
	 buttons[5] = ( (_buffer[2] & 0x01) != 0);
	 buttons[6] = ( (_buffer[2] & 0x02) != 0);
	 buttons[7] = ( (_buffer[2] & 0x04) != 0);
	 buttons[8] = ( (_buffer[2] & 0x08) != 0);
	 break;

     case 'b':
	 // Beep command received. We don't care.
	 break;

     case 'm':
	 // Mode set command. We really only care that it is still in
	 // 3D mode (as opposed to Mouse mode); the other fields tell
	 // whether it is in dominant axis mode, and whether translations
	 // and rotations are being sent. We can handle any of these without
	 // incident.
	 if ( (_buffer[1] & 0x08) != 0) {
	     send_text_message("vrpn_Magellan: Was put into mouse mode, resetting", timestamp, vrpn_TEXT_ERROR);
	     status = STATUS_RESETTING;
	     return 1;
	 }
	 break;

     case 'd':
	 // Axis data is being returned (telling what the X,Y,Z and A,B,C axes
	 // are currently set to). This data is put into the range [-1,1] and
	 // put into the analog channels (0=X, 1=Y, 2=Z, 3=A, 4=B, 5=C).  It comes
	 // from the device with each axis packed into the lower nybble of 4
	 // consecutive bytes; the translation back to a signed 16-bit integer
	 // is done with (N0 * 4096) + (N1 * 256) + (N2 * 16) + (N3) - 32768
	 // for each value; this is then scaled to [-1,1].
	 nextchar = 1;	// Skip the zeroeth character (the command)
	 for (i = 0; i < _numchannels; i++) {
	    long intval;
	    intval = (0x0f & _buffer[nextchar++]) << 12;
	    intval += (0x0f & _buffer[nextchar++]) << 8;
	    intval += (0x0f & _buffer[nextchar++]) << 4;
    	    intval += (0x0f & _buffer[nextchar++]);
	    intval -= 32768;

	    // If the absolute value of the integer is <= the NULL radius, it should
	    // be set to zero.
	    if ( (intval <= _null_radius) && (intval >= - _null_radius) ) {
		intval = 0;
	    }

	    // The largest values that seem to come out of the Magellan I've got
	    // even under the maximum acceleration are absolute value 7200 or so.
	    // We'll divide by 7500 to keep it safe.
	    double realval = intval / 7500.0;
	    channel[i] = realval;
	 }
	 break;
	 
     case 'n':
	 // NULL radius set. This is the number of ticks around zero that should
	 // count as zero, to allow a "dead zone" for the user near the center.
	 // We store this for the analog parsing code.  The low nybble in the data
	 // word holds the new value
	 _null_radius = 0x0f & _buffer[1];
	 break;

     case 'q':
	 // Sensitivity set. We don't care.
	 break;

     case 'z':
	 // The device was zeroed. We don't care.
	 break;

     case 'p':
	 // The min/max periods were set.  We don't care.
	 break;

     case 'c':
	 // Some extended command was sent.  I hope we don't care.
	 // XXX Should check to make sure compression is not on.
	 break;

     default:
	fprintf(stderr,"vrpn_Magellan: Unknown [internal] command (%c), resetting\n", _buffer[0]);
	status = STATUS_RESETTING;
	return 1;
   }

   //--------------------------------------------------------------------
   // Done with the decoding, send the reports and go back to syncing
   //--------------------------------------------------------------------

   report_changes();
   status = STATUS_SYNCING;
   _bufcount = 0;

   return 1;	// We got a full report.
}
Exemple #27
0
int vrpn_BiosciencesTools::get_report(void)
{
   int ret;		// Return value from function call to be checked

   //--------------------------------------------------------------------
   // If we're SYNCing, then the next character we get should be the start
   // of a report.  If we recognize it, go into READing mode and tell how
   // many characters we expect total. If we don't recognize it, then we
   // must have misinterpreted a command or something; reset
   // and start over
   //--------------------------------------------------------------------

   if (status == STATUS_SYNCING) {
      // Try to get a character.  If none, just return.
      if (vrpn_read_available_characters(serial_fd, (unsigned char *)(d_buffer), 1) != 1) {
      	return 0;
      }

      d_expected_chars = 8;

      // Got the first character of a report -- go into READING mode
      // and record that we got one character at this time. The next
      // bit of code will attempt to read the rest of the report.
      // The time stored here is as close as possible to when the
      // report was generated.
      d_bufcount = 1;
      vrpn_gettimeofday(&timestamp, NULL);
      status = STATUS_READING;
#ifdef	VERBOSE
      printf("... Got the 1st char\n");
#endif
   }

   //--------------------------------------------------------------------
   // Read as many bytes of this report as we can, storing them
   // in the buffer.  We keep track of how many have been read so far
   // and only try to read the rest.
   //--------------------------------------------------------------------

   ret = vrpn_read_available_characters(serial_fd, (unsigned char *)(&d_buffer[d_bufcount]),
		d_expected_chars-d_bufcount);
   if (ret == -1) {
	DO_ERROR("Error reading");
	status = STATUS_RESETTING;
	return 0;
   }
   d_bufcount += ret;
#ifdef	VERBOSE
   if (ret != 0) printf("... got %d characters (%d total)\n",ret, d_bufcount);
#endif
   if (d_bufcount < d_expected_chars) {	// Not done -- go back for more
	return 0;
   }
   d_buffer[d_expected_chars] = '\0'; // NULL terminate.

   //--------------------------------------------------------------------
   // We now have enough characters to make a full report. Check to make
   // sure that its format matches what we expect. If it does, the next
   // section will parse it.
   // Store the report into the appropriate analog channel.
   //--------------------------------------------------------------------

   float value = convert_bytes_to_reading(d_buffer);
   if (value == -1000) {
     char msg[256];
     sprintf(msg,"Invalid report, channel %d, resetting", d_next_channel_to_read);
     DO_ERROR(msg);
     status = STATUS_RESETTING;
   }
   channel[d_next_channel_to_read] = value;

#ifdef	VERBOSE
   printf("got a complete report (%d of %d)!\n", d_bufcount, d_expected_chars);
#endif

   //--------------------------------------------------------------------
   // Request a reading from the next channe.
   //--------------------------------------------------------------------

   d_next_channel_to_read = (d_next_channel_to_read + 1) % 6;
   if (!request_temperature(d_next_channel_to_read)) {
     char msg[256];
     sprintf(msg,"Can't request reading, channel %d, resetting", d_next_channel_to_read);
     DO_ERROR(msg);
     status = STATUS_RESETTING;
   }

   //--------------------------------------------------------------------
   // Done with the decoding, send the reports and go back to syncing
   //--------------------------------------------------------------------

   report_changes();
   status = STATUS_SYNCING;
   d_bufcount = 0;

   return 1;
}
Exemple #28
0
// VRPN main loop
// Poll the device and let the VRPN change notifications fire
void vrpn_WiiMote::mainloop() {
    static time_t last_error = time(NULL);

    vrpn_gettimeofday(&_timestamp, NULL);

    //XXX Try to connect once a second if we have found but not connected device.

    server_mainloop();

    // Poll to get the status of the device.  When an event happens, call
    // the appropriate handler to fill in our data structures.  To do this,
    // we need a list of pointers to WiiMotes, so we create one with a single
    // entry.
    wiimote_t *selected_one[1];
    selected_one[0] = wiimote->device;
    if (wiimote->connected && wiiuse_poll(selected_one, 1) ) {
        switch (wiimote->device->event) {
        case WIIUSE_EVENT:  // A generic event
            handle_event();
            break;

        case WIIUSE_STATUS: // A status event
            // Nothing to do here, we're polling what we need to know in mainloop.
            break;

        case WIIUSE_DISCONNECT:
        case WIIUSE_UNEXPECTED_DISCONNECT:
            wiimote->connected = false;
            send_text_message("Disconnected", _timestamp, vrpn_TEXT_ERROR);
            break;

        case WIIUSE_READ_DATA:
            // Data we requested was returned.  Take a look at wiimote->device->read_req
            // for the info.
            break;

        case WIIUSE_NUNCHUK_INSERTED:
            send_text_message("Nunchuck inserted", _timestamp);
            break;

        case WIIUSE_CLASSIC_CTRL_INSERTED:
            send_text_message("Classic controller inserted", _timestamp);
            break;

        case WIIUSE_GUITAR_HERO_3_CTRL_INSERTED:
            send_text_message("Guitar Hero 3 controller inserted", _timestamp);
            break;

        case WIIUSE_NUNCHUK_REMOVED:
        case WIIUSE_CLASSIC_CTRL_REMOVED:
        case WIIUSE_GUITAR_HERO_3_CTRL_REMOVED:
            send_text_message("An expansion controller was removed", _timestamp,
                              vrpn_TEXT_WARNING);
            break;

        default:
            send_text_message("unknown event", _timestamp);
            break;
        }
    }

    // Send any changes out over the connection.
    vrpn_gettimeofday(&_timestamp, NULL);
    report_changes();
}
Exemple #29
0
int vrpn_inertiamouse::get_report(void)
{
    int ret; // Return value from function call to be checked

    //
    // If we're SYNCing, then the next character we get should be the
    // start of a report.  If we recognize it, go into READing mode
    // and tell how many characters we expect total. If we don't
    // recognize it, then we must have misinterpreted a command or
    // something; reset the intertiamouse and start over
    //
    if (status_ == STATUS_SYNCING) {
        // Try to get a character.  If none, just return.
        ret = vrpn_read_available_characters (serial_fd, &buffer_[0], 1);
        if (ret != 1) { return 0; }

        switch (buffer_[0]) {
        case 'D':
            expected_chars_ = 25;
            break;
        case 'B':
            expected_chars_ = 4;
            break;
                
        default:
            fprintf(stderr, "vrpn_inertiamouse: Unknown command (%c), resetting\n", buffer_[0]);
            status_ = STATUS_RESETTING;
            return 0;
        }

        bufcount_ = 1;
        vrpn_gettimeofday (&timestamp, NULL);

        status_ = STATUS_READING;
    }

    ret = vrpn_read_available_characters(serial_fd, 
                                         &buffer_[bufcount_],
                                         expected_chars_ - bufcount_);
    if (ret == -1) {
	send_text_message("vrpn_inertiamouse: Error reading", 
                          timestamp, 
                          vrpn_TEXT_ERROR);
	status_ = STATUS_RESETTING;
	return 0;
    }
    bufcount_ += ret;

    if (bufcount_ < expected_chars_) {	// Not done -- go back for more
	return 0;
    }

    if (buffer_[expected_chars_ - 1] != '\n') {
        status_ = STATUS_SYNCING;
        send_text_message("vrpn_inertiamouse: No newline in record", 
                          timestamp, 
                          vrpn_TEXT_ERROR);
        return 0;
    }

    switch ( buffer_[0] ) {
    case 'D':
      {
        int i;
        int nextchar;
        for (i = 0, nextchar = 0; i < numchannels_; ++i) {
            int packet;
            packet =  (buffer_[++nextchar] & 0xf0) << 8;  
            packet |= (buffer_[++nextchar] & 0xf0) << 4;
            packet |= (buffer_[++nextchar] & 0xf0);
            packet |= (buffer_[++nextchar] & 0xf0) >> 4;

            int chnl = (packet >> 10) & 7;
            if (chnl >= Channels) {
               status_ = STATUS_SYNCING;
               send_text_message("vrpn_inertiamouse: Too-large channel value", 
		  timestamp, 
		  vrpn_TEXT_ERROR);
               return 0;
            }
            int acc = packet & 0x3ff; // 10 bits
            
            // normalize to interval [-1,1]
            // just a guess, block dc later
            double normval = ((double)(acc - 256) / 
                              (double)256);

//            normval *= 1.5;

            // update rotation data
            if ( (chnl == 4) || (chnl == 5) ) {
                channel[chnl] = normval;
                break;
            }
            normval = dcb_[chnl].filter (normval);
            normval = lp_[chnl].filter (normval);
           
            double dt = 0.25;

            // update velocity and position only when button[0] pressed
            if (buttons[0]) {
                
                double pos = vel_[chnl] * dt + normval * dt * dt / 2;

                vel_[chnl] += normval*dt;
                if(fabs (vel_[chnl]) < dt/2.0)
                    vel_[chnl] *= 0.90;
//                else
//                 if (fabs (vel_[chnl]) > 1.0)
//                     vel_[chnl] *= 1.0 / fabs (vel_[chnl]);

                channel[chnl] = pos;
//                channel[chnl] *= 0.95;
            } else {
                vel_[chnl] = 0.0;
                channel[chnl] = 0.0;
            }
        }
      }
        break;
    case 'B':
        buttons[0] = ((buffer_[1] & 1) != 0);
        buttons[1] = ((buffer_[1] & 2) != 0);
        break;
    default:
	fprintf(stderr, "vrpn_inertiamouse: Unknown [internal] command (%c), resetting\n", buffer_[0]);
	status_ = STATUS_RESETTING;
	return 0;
   }

    //
    // Done with the decoding, send the reports and go back to syncing
    //

    report_changes();
    status_ = STATUS_SYNCING;
    bufcount_ = 0;

    return 1;	// We got a full report.
}
Exemple #30
0
int vrpn_Spaceball::get_report(void)
{
    unsigned char rawbuf[1024]; // raw unprocessed incoming characters
    int i, num, packs;
#if defined(DEBUG)
    int j;
#endif
    packs = 0; /* no packs received yet */

    // read up to 1023 unprocessed characters from the serial device at once
    num = vrpn_read_available_characters(serial_fd, rawbuf, 1023);

    // if we receive 1 or more chars, we will see if this completes any
    // pending packets we're trying to process.
    if (num > 0) {
        for (i=0; i<num; i++) {
            /* process potentially occurring escaped character sequences */
            if (rawbuf[i] == '^') {
                if (!escapedchar) {
                    escapedchar = 1;
                    continue; /* eat the escape character from buffer */
                }
            }

            /* if in escaped mode, we convert the escaped char to final form */
            if (escapedchar) {
                escapedchar = 0; /* we're back out of escape mode after this */

                switch (rawbuf[i]) {
                case '^': /* leave char in buffer unchanged */
                    break;

                case 'Q':
                case 'S':
                case 'M':
                    rawbuf[i] &= 0x1F; /* convert character to unescaped form */
                    break;

                default:
#if defined(DEBUG)
                    printf("\nGot a bad escape sequence! 0x%02x", rawbuf[i]);
                    if (isprint(rawbuf[i]))
                        printf(" (%c)", rawbuf[i]);
                    else
                        printf(" (unprintable)");
                    printf("\n");
#endif
                    break;
                }
            }


            /* figure out what kind of packet we received */
            if (bufpos == 0) {
                status = STATUS_SYNCING; /* update our status */

                switch(rawbuf[i]) {
                case 'D':  /* Displacement packet */
                    packtype = 'D';
                    packlen = 16;    /* D packets are 15 bytes long */
                    break;

                case 'K':  /* Button/Key packet */
                    packtype = 'K';
                    packlen = 4;     /* K packets are 3 bytes long */
                    break;

                case '.': /* Spaceball 4000 FLX "advanced" button press event */
                    packtype = '.';
                    packlen = 4;     /* . packets are 3 bytes long */
                    break;

                case 'C': /* Communications mode packet */
                    packtype = 'C';
                    packlen = 4;
                    break;

                case 'F': /* Spaceball sensitization mode packet */
                    packtype = 'F';
                    packlen = 4;
                    break;

                case 'M': /* Movement mode packet */
                    packtype = 'M';
                    packlen = 5;
                    break;

                case 'N': /* Null region packet */
                    packtype = 'N';
                    packlen = 3;
                    break;

                case 'P': /* Update rate packet */
                    packtype = 'P';
                    packlen = 6;
                    break;

                case '\v': /* XON at poweron */
                    packtype = '\v';
                    packlen = 1;
                    break;

                case '\n': /* carriage return at poweron */
                case '\r': /* carriage return at poweron */
                    packtype = '\r';
                    packlen = 1;
                    break;

                case '@': /* Spaceball Hard/Soft Reset packet */
                    resetoccured=1;
                    packtype = '@';
                    packlen = 62;    /* Resets aren't longer than 62 chars */
                    break;

                case 'E': /* Error packet */
                    packtype = 'E';
                    packlen = 8;     /* E packets are up to 7 bytes long */
                    break;

                case 'Z': /* Zero packet (Spaceball 2003/3003/4000 FLX) */
                    packtype = 'Z';
                    packlen = 14;    /* Z packets are hardware dependent */
                    break;

                default:  /* Unknown packet! */
#if defined(DEBUG)
                    printf("\nUnknown packet (1) [%d]: 0x%02x \n ", i, rawbuf[i]);
                    printf("                char:  ");
                    if (isprint(rawbuf[i]))
                        printf("%c", rawbuf[i]);
                    else
                        printf(" (unprintable)");
                    printf("\n");
#endif
                    continue;
                }
            }


            buf[bufpos] = rawbuf[i]; /* copy processed chars into long-term buffer */
            bufpos++;                /* go to next buffer slot */

            /* Reset packet processing */
            if (packtype == '@') {
                if (rawbuf[i] != '\r')
                    continue;
                else
                    packlen = bufpos;
            }

            /* Error packet processing */
            if (packtype == 'E') {
                if (rawbuf[i] != '\r')
                    continue;
                else
                    packlen = bufpos;
            } else if (bufpos != packlen)
                continue;

            status = STATUS_READING;        // ready to process event packet
            vrpn_gettimeofday(&timestamp, NULL); // set timestamp of this event

            switch (packtype) {
            case 'D':  /* ball displacement event */
            {
                int nextchar, chan;

                /* number of 1/16ths of milliseconds since last */
                /* ball displacement packet */

                nextchar = 1;  // this is where the timer data is, if we want it.
                nextchar = 3;  // Skip the zeroeth character (the command)
                for (chan = 0; chan < _numchannels; chan++) {
                    vrpn_int16 intval;
                    intval  = (buf[nextchar++]) << 8;
                    intval |= (buf[nextchar++]);

                    // If the absolute value of the integer is <= the NULL
                    // radius, it should be set to zero.
                    if ( (intval <= null_radius) && (intval >= - null_radius) ) {
                        intval = 0;
                    }

                    // maximum possible values per axis are +/- 32768, although the
                    // largest value I've ever observed among several devices
                    // is only 20,000.  For now, we'll divide by 32768, and if this is
                    // too insensitive, we can do something better later.
                    double realval = intval / 32768.0;
                    channel[chan] = realval;
                    // printf("XXX Channel[%d] = %f   %d \n", z, realval, intval);
                }
                channel[2] = -channel[2]; // Negate Z translation
                channel[5] = -channel[5]; // Negate Z rotation
            }
            break;

            case 'K': /* button press event */
                /* Spaceball 2003A, 2003B, 2003 FLX, 3003 FLX, 4000 FLX       */
                /* button packet. (4000 only for backwards compatibility)     */
                /* The lowest 5 bits of the first byte are buttons 5-9        */
                /* Button '8' on a Spaceball 2003 is the rezero button        */
                /* The lowest 4 bits of the second byte are buttons 1-4       */
                /* For Spaceball 2003, we'll map the buttons 1-7 normally     */
                /* skip 8, as its a hardware "rezero button" on that device   */
                /* and call the "pick" button "8".                            */
                /* On the Spaceball 3003, the "right" button also triggers    */
                /* the "pick" bit.  We OR the 2003/3003 rezero bits together  */

                /* if we have found a Spaceball 4000, then we ignore the 'K'  */
                /* packets entirely, and only use the '.' packets.            */
                if (spaceball4000)
                    break;

// XXX    my original libsball button decoding code, for reference
//           buttons =
//             ((buf[1] & 0x10) <<  3) | /* 2003 pick button is "8" */
//             ((buf[1] & 0x20) <<  9) | /* 3003 rezero button      */
//             ((buf[1] & 0x08) << 11) | /* 2003 rezero button      */
//             ((buf[1] & 0x07) <<  4) | /* 5,6,7    (2003/4000)    */
//             ((buf[2] & 0x30) <<  8) | /* 3003 Left/Right buttons */
//             ((buf[2] & 0x0F));        /* 1,2,3,4  (2003/4000)    */

                // Mapping the buttons is tricky since different models
                // of the Spaceball have different button sets.
                // 2003 has 9 buttons (rezero included)
                // 3003 has 3 buttons (rezero included)
                // 4000 has 12 buttons, and can be in "lefty" or "righty" mode.
                // We'll skip reporting lefty/righty mode for now though.

                // Spaceball 2003/4000 buttons 1-4 mapped to slots 0-3
                // Spaceball 3003 L/R buttons are mapped as slots 0/1
                buttons[0] = static_cast<unsigned char>(((buf[2] & 0x01) != 0) | ((buf[2] & 0x10) != 0));
                buttons[1] = static_cast<unsigned char>(((buf[2] & 0x02) != 0) | ((buf[2] & 0x20) != 0));
                buttons[2] = static_cast<unsigned char>(((buf[2] & 0x04) != 0));
                buttons[3] = static_cast<unsigned char>(((buf[2] & 0x08) != 0));

                // Spaceball 2003/4000 buttons 5,6,7 mapped to slots 4-6
                buttons[4] = static_cast<unsigned char>(((buf[1] & 0x01) != 0));
                buttons[5] = static_cast<unsigned char>(((buf[1] & 0x02) != 0));
                buttons[6] = static_cast<unsigned char>(((buf[1] & 0x04) != 0));

                // Spaceball 2003/3003 rezero buttons are mapped to slot 7
                // The rezero button's function is sometimes hard-wired,
                // and may or may not be used by applications, this is up
                // in the air still.  For now, I'll map it to slot 7 which
                // keeps the numbering in a sane order on the 2003 device anyway.
                buttons[7] = static_cast<unsigned char>(((buf[1] & 0x20) != 0) | ((buf[1] & 0x08) != 0));

                // Spaceball 2003 pick button mapped to slot 8
                // Note: the pick button is the button embedded into the front
                //           surface of the control sphere.
                buttons[8] = static_cast<unsigned char>(((buf[1] & 0x10) != 0));
                break;


            case '.': /* button press event (4000) */
                /* Spaceball 4000 FLX "expanded" button packet, with 12 buttons */

                /* extra packet validity check, since we use this packet type */
                /* to override the 'K' button packets, and determine if its a */
                /* Spaceball 4000 or not...                                   */
                if (buf[3] != '\r') {
                    break; /* if not terminated with a '\r', probably garbage */
                }

                /* if we got a valid '.' packet, this must be a Spaceball 4000 */
#if defined(DEBUG)
                if (!spaceball4000)
                    printf("\nDetected a Spaceball 4000 FLX\n");
#endif
                spaceball4000 = 1; /* Must be talking to a Spaceball 4000 */

// XXX    my original libsball button decoding code, for reference
//          buttons =
//            (((~buf[1]) & 0x20) << 10) |  /* "left handed" mode  */
//            ((buf[1] & 0x1F) <<  7)    |  /* 8,9,10,11,12        */
//            ((buf[2] & 0x3F)      )    |  /* 1,2,3,4,5,6         */
//            ((buf[2] & 0x80) >>  1);      /* 7                   */

                /* Spaceball 4000 series "expanded" button press event      */
                /* includes data for 12 buttons, and left/right orientation */
                buttons[0]  = ((buf[2] & 0x01) != 0); // SB 4000 button 1
                buttons[1]  = ((buf[2] & 0x02) != 0); // SB 4000 button 2
                buttons[2]  = ((buf[2] & 0x04) != 0); // SB 4000 button 3
                buttons[3]  = ((buf[2] & 0x08) != 0); // SB 4000 button 4
                buttons[4]  = ((buf[2] & 0x10) != 0); // SB 4000 button 5
                buttons[5]  = ((buf[2] & 0x20) != 0); // SB 4000 button 6
                buttons[6]  = ((buf[2] & 0x80) != 0); // SB 4000 button 7

                buttons[7]  = ((buf[1] & 0x01) != 0); // SB 4000 button 8
                buttons[8]  = ((buf[1] & 0x02) != 0); // SB 4000 button 9
                buttons[9]  = ((buf[1] & 0x04) != 0); // SB 4000 button 10
                buttons[10] = ((buf[1] & 0x08) != 0); // SB 4000 button 11
                buttons[11] = ((buf[1] & 0x10) != 0); // SB 4000 button 12

                // XXX lefty/righty mode handling goes here if we wish to
                //     represent it as a "button" etc..
                //buttons[??] = ((~buf[1]) & 0x20) != 0) // SB 4000 "lefty" mode bit

#if defined(DEBUG)
                if (leftymode4000 != ((buf[1] & 0x20) == 0))
                    printf("\nSpaceball 4000 mode changed to: %s\n",
                           (((buf[1] & 0x20) == 0) ? "left handed" : "right handed"));
#endif
                /* set "lefty" orientation mode if "lefty bit" is _clear_ */
                if ((buf[1] & 0x20) == 0)
                    leftymode4000 = 1; /* left handed mode */
                else
                    leftymode4000 = 0; /* right handed mode */
                break;

            case 'C': /* Communications mode packet */
            case 'F': /* Spaceball sensitization packet */
            case 'P': /* Spaceball update rate packet */
            case 'M': /* Spaceball movement mode packet */
            case 'N': /* Null region packet */
            case '\r': /* carriage return at poweron */
            case '\v': /* XON at poweron */
                /* eat and ignore these packets */
                break;

            case '@': /* Reset packet */
#if defined(DEBUG)
                printf("Spaceball reset: ");
                for (j=0; j<packlen; j++) {
                    if (isprint(buf[j]))
                        printf("%c", buf[j]);
                }
                printf("\n");
#endif
                /* if we get a reset packet, we have to re-initialize       */
                /* the device, and assume that its completely schizophrenic */
                /* at this moment, we must reset it again at this point     */
                resetoccured=1;
                reset(); // was sball_hwreset()
                break;


            case 'E': /* Error packet, hardware/software problem */
                erroroccured++;
#if defined(DEBUG)
                printf("\nSpaceball Error!!    ");
                printf("Error code: ");
                for (j=0; j<packlen; j++) {
                    printf(" 0x%02x ", buf[j]);
                }
                printf("\n");
#endif
                break;

            case 'Z': /* Zero packet (Spaceball 2003/3003/4000 FLX) */
                /* We just ignore these... */
                break;

            default:
#if defined(DEBUG)
                printf("Unknown packet (2): 0x%02x\n", packtype);
                printf("              char:  ");
                if (isprint(packtype))
                    printf("%c", packtype);
                else
                    printf(" (unprintable)");
                printf("\n");
#endif
                break;
            }

            /* reset */
            bufpos = 0;
            packtype = 0;
            packlen = 1;
            packs++;
        }
    }

    report_changes();        // Report updates to VRPN

    if (packs > 0)
        return 1; // got at least 1 full report
    else
        return 0; // didn't get any full reports
}