/* * logitechmouse.cpp * * Copyright (C) 2004 Brad Hards * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBUSB #include #include "logitechmouse.h" LogitechMouse::LogitechMouse( libusb_device *usbDev, int mouseCapabilityFlags, QWidget* parent, const char* name ) : LogitechMouseBase( parent ) , m_resolution( 0 ) { if ( !name ) setObjectName( "LogitechMouse" ); cordlessNameLabel->setText( i18n("Mouse type: %1", objectName() ) ); m_mouseCapabilityFlags = mouseCapabilityFlags; int result = libusb_open( usbDev, &m_usbDeviceHandle ); if ( !m_usbDeviceHandle || result < 0) { kWarning() << "Error opening usbfs file: " << libusb_strerror(static_cast(result)) ; return; } if ( mouseCapabilityFlags & USE_CH2 ) { m_useSecondChannel = 0x0100; } else { m_useSecondChannel = 0x0000; } permissionProblemText->hide(); if ( mouseCapabilityFlags & HAS_RES ) { updateResolution(); resolutionSelector->setEnabled( true ); connect( button400cpi, SIGNAL(clicked()), parent, SLOT(changed()) ); connect( button800cpi, SIGNAL(clicked()), parent, SLOT(changed()) ); if ( 4 == resolution() ) { button800cpi->setChecked( true ); } else if ( 3 == resolution() ) { button400cpi->setChecked( true ); } else { // it must have failed, try to help out resolutionSelector->setEnabled(false); permissionProblemText->show(); } } if ( mouseCapabilityFlags & HAS_CSR ) { initCordlessStatusReporting(); // Do a name cordlessNameLabel->setText( i18n("Mouse type: %1", cordlessName() ) ); cordlessNameLabel->setEnabled( true ); // Display the battery power level - the level gets updated in updateGUI() batteryBox->setEnabled( true ); // Channel channelSelector->setEnabled( true ); // if the channel is changed, we need to turn off the timer, otherwise it // just resets the button to reflect the current status. The timer is // started again when we applyChanges() connect( channel1, SIGNAL(clicked()), this, SLOT(stopTimerForNow()) ); connect( channel1, SIGNAL(clicked()), parent, SLOT(changed()) ); if ( isDualChannelCapable() ) { channel2->setEnabled( true ); connect( channel2, SIGNAL(clicked()), this, SLOT(stopTimerForNow()) ); connect( channel2, SIGNAL(clicked()), parent, SLOT(changed()) ); } updateGUI(); } } LogitechMouse::~LogitechMouse() { if ( m_usbDeviceHandle ) libusb_close( m_usbDeviceHandle ); } void LogitechMouse::initCordlessStatusReporting() { updateCordlessStatus(); doUpdate = new QTimer( this ); // will be automatically deleted connect( doUpdate, SIGNAL(timeout()), this, SLOT(updateGUI()) ); doUpdate->start( 20000 ); } void LogitechMouse::updateCordlessStatus() { QByteArray status(8, '\0'); int result = -1; if ( m_usbDeviceHandle ) result = libusb_control_transfer( m_usbDeviceHandle, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT,0x09, (0x0003 | m_useSecondChannel), (0x0000 | m_useSecondChannel), reinterpret_cast(status.data()), 0x0008, 1000); if (0 > result) { // We probably have a permission problem m_channel = 0; channelSelector->setEnabled( false ); batteryBox->setEnabled( false ); cordlessNameLabel->hide(); permissionProblemText->show(); } else { // kDebug() << "P6 (connect status): " << (status[0] & 0xFF); if ( status[0] & 0x20 ) { // mouse is talking m_connectStatus = ( status[0] & 0x80 ); m_mousePowerup = ( status[0] & 0x40 ); m_receiverUnlock = ( status[0] & 0x10 ); m_waitLock = ( status[0] & 0x08 ); } // kDebug() << "P0 (receiver type): " << (status[1] & 0xFF); /* 0x38 = pid C501 0x39 = pid C502 0x3B = pid C504 0x3C = pid C508 0x3D = pid C506 0x3E = pid C505 */ m_cordlessNameIndex = (status[2] & 0xFF); m_batteryLevel = (status[3] & 0x07 ); if ( status[3] & 0x08 ) { m_channel = 2; } else { m_channel = 1; } m_cordlessSecurity = ( ( status[4] ) & ( status[5] << 8 ) ); m_caseShape = ( status[6] & 0x7F ); // kDebug() << "PB1 (device Capabilities): " << (status[7] & 0xFF); m_numberOfButtons = 2 + ( status[7] & 0x07 ); // 9 means something more than 8 m_twoChannelCapable = ( status[7] & 0x40 ); m_verticalRoller = ( status[7] & 0x08 ); m_horizontalRoller = ( status[7] & 0x10 ); m_has800cpi = ( status[7] & 0x20 ); } } void LogitechMouse::updateGUI() { updateCordlessStatus(); batteryBar->setValue( batteryLevel() ); if ( isDualChannelCapable() ) { if ( 2 == channel() ) { channel2->setChecked( true ); } else if ( 1 == channel() ) { channel1->setChecked( true ); } // else it might have failed - we don't do anything } } void LogitechMouse::stopTimerForNow() { doUpdate->stop(); } void LogitechMouse::applyChanges() { if ( m_mouseCapabilityFlags & HAS_RES ) { if ( ( resolution() == 4 ) && ( button400cpi->isChecked() ) ) { // then we are in 800cpi mode, but want 400cpi setLogitechTo400(); } else if ( ( resolution() == 3 ) && (button800cpi->isChecked() ) ) { // then we are in 400 cpi mode, but want 800 cpi setLogitechTo800(); } } if ( isDualChannelCapable() ) { if ( ( channel() == 2 ) && ( channel1->isChecked() ) ) { // we are on channel 2, but want channel 1 setChannel1(); KMessageBox::information(this, i18n("RF channel 1 has been set. Please press Connect button on mouse to re-establish link"), i18n("Press Connect Button") ); } else if ( ( channel() == 1 ) && ( channel2->isChecked() ) ) { // we are on channel 1, but want channel 2 setChannel2(); KMessageBox::information(this, i18n("RF channel 2 has been set. Please press Connect button on mouse to re-establish link"), i18n("Press Connect Button") ); } initCordlessStatusReporting(); } } void LogitechMouse::save(KConfig * /*config*/) { kDebug() << "Logitech mouse settings not saved - not implemented yet"; } quint8 LogitechMouse::resolution() { // kDebug() << "resolution: " << m_resolution; if ( 0 == m_resolution ) { updateResolution(); } return m_resolution; } void LogitechMouse::updateResolution() { char resolution; int result = -1; // LIBUSB_ERROR_IO if ( m_usbDeviceHandle ) result = libusb_control_transfer( m_usbDeviceHandle, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_ENDPOINT, 0x01, 0x000E, 0x0000, reinterpret_cast(&resolution), 0x0001, 100); // kDebug() << "resolution is: " << resolution; if (0 > result) { kWarning() << "Error getting resolution from device : " << libusb_strerror(static_cast(result)) ; m_resolution = 0; } else { m_resolution = resolution; } } void LogitechMouse::setLogitechTo800() { int result = libusb_control_transfer( m_usbDeviceHandle, LIBUSB_REQUEST_TYPE_VENDOR, 0x02, 0x000E, 4, NULL, 0x0000, 100); if (0 > result) { kWarning() << "Error setting resolution on device: " << libusb_strerror(static_cast(result)) ; } } void LogitechMouse::setLogitechTo400() { int result = libusb_control_transfer( m_usbDeviceHandle, LIBUSB_REQUEST_TYPE_VENDOR, 0x02, 0x000E, 3, NULL, 0x0000, 100); if (0 > result) { kWarning() << "Error setting resolution on device: " << libusb_strerror(static_cast(result)) ; } } quint8 LogitechMouse::batteryLevel() const { return m_batteryLevel; } quint8 LogitechMouse::channel() const { return m_channel; } bool LogitechMouse::isDualChannelCapable() const { return m_twoChannelCapable; } void LogitechMouse::setChannel1() { int result = libusb_control_transfer( m_usbDeviceHandle, LIBUSB_REQUEST_TYPE_VENDOR, 0x02, (0x0008 | m_useSecondChannel), (0x0000 | m_useSecondChannel), NULL, 0x0000, 1000); if (0 > result) { kWarning() << "Error setting mouse to channel 1 : " << libusb_strerror(static_cast(result)) ; } } void LogitechMouse::setChannel2() { int result = libusb_control_transfer( m_usbDeviceHandle, LIBUSB_REQUEST_TYPE_VENDOR, 0x02, (0x0008 | m_useSecondChannel), (0x0001 | m_useSecondChannel), NULL, 0x0000, 1000); if (0 > result) { kWarning() << "Error setting mouse to channel 2 : " << libusb_strerror(static_cast(result)) ; } } QString LogitechMouse::cordlessName() const { switch ( m_cordlessNameIndex ) { case 0x00: return i18nc( "no cordless mouse", "none" ); break; case 0x04: return i18n( "Cordless Mouse" ); break; case 0x05: return i18n( "Cordless Wheel Mouse" ); break; case 0x06: return i18n( "Cordless MouseMan Wheel" ); break; case 0x07: return i18n( "Cordless Wheel Mouse" ); break; case 0x08: return i18n( "Cordless Wheel Mouse" ); break; case 0x09: return i18n( "Cordless TrackMan Wheel" ); break; case 0x0A: return i18n( "TrackMan Live" ); break; case 0x0C: return i18n( "Cordless TrackMan FX" ); break; case 0x0D: return i18n( "Cordless MouseMan Optical" ); break; case 0x0E: return i18n( "Cordless Optical Mouse" ); break; case 0x0F: return i18n( "Cordless Mouse" ); break; case 0x12: return i18n( "Cordless MouseMan Optical (2ch)" ); break; case 0x13: return i18n( "Cordless Optical Mouse (2ch)" ); break; case 0x14: return i18n( "Cordless Mouse (2ch)" ); break; case 0x82: return i18n( "Cordless Optical TrackMan" ); break; case 0x8A: return i18n( "MX700 Cordless Optical Mouse" ); break; case 0x8B: return i18n( "MX700 Cordless Optical Mouse (2ch)" ); break; default: return i18n( "Unknown mouse"); } } #include "moc_logitechmouse.cpp" #endif