Logo Search packages:      
Sourcecode: kdeutils version File versions

thinkpad.cpp

/*
   This file is part of the KDE project

   Copyright (c) 2004 Jonathan Riddell <jr@jriddell.org>

   Based on Demo kmilo service by George Staikos
   Copyright (c) 2003 George Staikos <staikos@kde.org>

   And tpb by Markus Braun
   Copyright (C) 2002,2003 Markus Braun <markus.braun@krawel.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

*/

#include <kgenericfactory.h>
#include <kconfig.h>
#include <krun.h>
#include <kurl.h>
#include "kmilointerface.h"

#include "thinkpad.h"

namespace KMilo {

ThinkPadMonitor::ThinkPadMonitor(QObject* parent, const char* name, const QStringList& args): Monitor(parent, name, args) {
      m_progress = 0;
      m_volume = 50;  //set in retrieveVolume()
}

ThinkPadMonitor::~ThinkPadMonitor() {
}

00047 bool ThinkPadMonitor::init() {

      KConfig config("kmilodrc");
      reconfigure(&config);

      if (m_run) {
            clearStruct(thinkpad_state);
            clearStruct(last_thinkpad_state);
            if ( getNvramState(&thinkpad_state) == false )  {
                  return false;
            }

            if (m_softwareVolume || m_volumeStep != defaultVolumeStep) {
                  kmixClient = new DCOPRef("kmix", "Mixer0");
                  kmixWindow = new DCOPRef("kmix", "kmix-mainwindow#1");
                  retrieveVolume();
                  setNvramVolume();
            }
      }

      return m_run;
}

00070 Monitor::DisplayType ThinkPadMonitor::poll() {

      // save last state and get new one
      memcpy(&last_thinkpad_state, &thinkpad_state, sizeof(thinkpad_state_struct));
      getNvramState(&thinkpad_state);

      Monitor::DisplayType pollResult = None;

      // determine the state of the mute button
      if (thinkpad_state.mute_toggle != last_thinkpad_state.mute_toggle ||
          (thinkpad_state.volume_toggle != last_thinkpad_state.volume_toggle
           && last_thinkpad_state.mute_toggle == 1)) {

            showToggleMessage(i18n("Mute %1"), thinkpad_state.mute_toggle == 1);
      }

      // determine the state of the Thinkpad button
      if (thinkpad_state.thinkpad_toggle != last_thinkpad_state.thinkpad_toggle &&
          thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
            _interface->displayText(i18n("Thinkpad Button Pressed"));

            KURL url(m_buttonThinkpad);
            (void) new KRun(url, 0, true, true);
      }

      // determine the state of ThinkLight
      if (thinkpad_state.thinklight_toggle != last_thinkpad_state.thinklight_toggle) {
            showToggleMessage(i18n("ThinkLight is %1"), thinkpad_state.thinklight_toggle == 1);
      }

      // determine the state of the volume buttons
      if (thinkpad_state.volume_level != last_thinkpad_state.volume_level) {

            pollResult = Volume;

            if (m_volumeStep == defaultVolumeStep && m_softwareVolume == false) {
                  //no need to write to nvram or set volume in software
                  m_progress = thinkpad_state.volume_level * 100 / defaultVolumeStep;
            } else {
                  if (thinkpad_state.volume_level > last_thinkpad_state.volume_level)  {
                        m_progress = m_volume + m_volumeStep;
                  } else {
                        m_progress = m_volume - m_volumeStep;
                  }
                  setVolume(m_progress);
            }

      }

      // determine the state of the brightness buttons
      if (thinkpad_state.brightness_level != last_thinkpad_state.brightness_level) {
            pollResult = Brightness;
            m_progress = thinkpad_state.brightness_level * 100 / 7;
      }

      // Buttons below here are untested because they do not exist on my R31

      // determine the state of zoom
      if (thinkpad_state.zoom_toggle != last_thinkpad_state.zoom_toggle) {
            showToggleMessage(i18n("Zoom is %1"), thinkpad_state.zoom_toggle == 1);
      }

      // determine the state of the home button
      if (thinkpad_state.home_toggle != last_thinkpad_state.home_toggle &&
          thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
            _interface->displayText(i18n("Home button pressed"));
            //FIXME go home
      }

      // determine the state of the search button
      if (thinkpad_state.search_toggle != last_thinkpad_state.search_toggle &&
          thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
            _interface->displayText(i18n("Search button pressed"));
            //FIXME goto search
      }

      // determine the state of the mail button
      if (thinkpad_state.mail_toggle != last_thinkpad_state.mail_toggle &&
          thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
            _interface->displayText(i18n("Mail button pressed"));
            //FIXME load e-mail
      }

      // determine the state of display
      if (thinkpad_state.display_toggle != last_thinkpad_state.display_toggle &&
         thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {

            // Some thinkpads have no hardware support to switch lcd/crt. They also
            // don't reflect the current state in thinkpad_state.display_state. So, if
            // thinkpad_state.display_toggle changes, but thinkpad_state.display_state does
            // not change, simulate the display state.

            unsigned int display_state = 1;
            if (thinkpad_state.display_state == last_thinkpad_state.display_state) {
                  display_state = display_state % 3 + 1;
            } else {
                  display_state = thinkpad_state.display_state;
            }

            switch (display_state & 0x03) {
            case 0x1:
                  _interface->displayText(i18n("Display changed: LCD on, CRT off"));
                  break;

            case 0x2:
                  _interface->displayText(i18n("Display changed: LCD off, CRT on"));
                  break;

            case 0x3:
                  _interface->displayText(i18n("Display changed: LCD on, CRT on"));
                  break;
            }
      }

      // determine the state of hv expansion
      if (thinkpad_state.expand_toggle != last_thinkpad_state.expand_toggle) {
            showToggleMessage(i18n("HV Expansion is %1"), (thinkpad_state.expand_toggle & 0x01) == 1);
      }

      // determine power management mode AC
      if (thinkpad_state.powermgt_ac != last_thinkpad_state.powermgt_ac) {
            switch(thinkpad_state.powermgt_ac) {
            case 0x4:
                  _interface->displayText(i18n("Power management mode AC changed: PM AC high"));
                  break;

            case 0x2:
                  _interface->displayText(i18n("Power management mode AC changed: PM AC auto"));
                  break;

            case 0x1:
                  _interface->displayText(i18n("Power management mode AC changed: PM AC manual"));
                  break;

            default:
                  _interface->displayText(i18n("Power management mode AC changed: PM AC unknown"));
                  break;
            }
      }

      // determine power management mode battery
      if (thinkpad_state.powermgt_battery != last_thinkpad_state.powermgt_battery) {
            switch(thinkpad_state.powermgt_battery) {
            case 0x4:
                  _interface->displayText(i18n("Power management mode battery changed: PM battery high"));
                  break;

            case 0x2:
                  _interface->displayText(i18n("Power management mode battery changed: PM battery auto"));
                  break;

            case 0x1:
                  _interface->displayText(i18n("Power management mode battery changed: PM battery manual"));
                  break;

            default:
                  _interface->displayText(i18n("Power management mode battery changed: PM battery unknown"));
                  break;
            }
      }

      return pollResult;
}


00235 int ThinkPadMonitor::progress() const {
      return m_progress;
}

00239 QString ThinkPadMonitor::message() const {
      //unused
      //return i18n("yer maw!");
      return "";
}

00245 bool ThinkPadMonitor::getNvramState(thinkpad_state_struct* thinkpad_state) {
      int file;
      unsigned char buffer[114];

      // open nvram for reading
      // must use open/close because seek is not supported by nvram
      if ((file=open(m_nvramFile.latin1(), O_RDONLY|O_NONBLOCK)) == -1) {
            kdError() << "Unable to open device: " << m_nvramFile << endl;
            return false;
      }

      // read nvram
      if (read(file, buffer, sizeof(buffer)) != sizeof(buffer)) {
            kdError() << "Unable to read from device: " << m_nvramFile << endl;
            return false;
      }

      // close nvram device
      if (close(file) == -1) {
            kdError() << "Unable to close device %s: " << m_nvramFile << endl;
            return false;
      }

      thinkpad_state->thinkpad_toggle
            = (thinkpad_state->thinkpad_toggle   & ~0x01) | (( buffer[0x57] & 0x08) >> 3);
      thinkpad_state->zoom_toggle
            = (thinkpad_state->zoom_toggle       & ~0x01) | ((~buffer[0x57] & 0x20) >> 5);
      thinkpad_state->display_toggle
            = (thinkpad_state->display_toggle    & ~0x01) | (( buffer[0x57] & 0x40) >> 6);
      thinkpad_state->home_toggle
            = (thinkpad_state->home_toggle       & ~0x01) | (( buffer[0x56] & 0x01)     );
      thinkpad_state->search_toggle
            = (thinkpad_state->search_toggle     & ~0x01) | (( buffer[0x56] & 0x02) >> 1);
      thinkpad_state->mail_toggle
            = (thinkpad_state->mail_toggle       & ~0x01) | (( buffer[0x56] & 0x04) >> 2);
      thinkpad_state->thinklight_toggle
            = (thinkpad_state->thinklight_toggle & ~0x01) | (( buffer[0x58] & 0x10) >> 4);
      thinkpad_state->hibernate_toggle
            = (thinkpad_state->hibernate_toggle  & ~0x01) | (( buffer[0x58] & 0x01)     );
      thinkpad_state->display_state
            =                                               (( buffer[0x59] & 0x03)     );
      thinkpad_state->expand_toggle
            = (thinkpad_state->expand_toggle     & ~0x01) | (( buffer[0x59] & 0x10) >> 4);
      thinkpad_state->brightness_level
            =                                               (( buffer[0x5E] & 0x07)     );
      thinkpad_state->brightness_toggle
            = (thinkpad_state->brightness_toggle & ~0x01) | (( buffer[0x5E] & 0x20) >> 5);
      thinkpad_state->volume_level
            =                                               (( buffer[0x60] & 0x0f)     );
      thinkpad_state->volume_toggle
            = (thinkpad_state->volume_toggle     & ~0x01) | (( buffer[0x60] & 0x80) >> 7);
      thinkpad_state->mute_toggle
            = (thinkpad_state->mute_toggle       & ~0x01) | (( buffer[0x60] & 0x40) >> 6);
      thinkpad_state->powermgt_ac
            =                                               (( buffer[0x39] & 0x07)     );
      thinkpad_state->powermgt_battery
            =                                               (( buffer[0x39] & 0x38) >> 3);

      return true;
}

00306 void ThinkPadMonitor::clearStruct(thinkpad_state_struct& thinkpad_state)  {
      thinkpad_state.thinkpad_toggle = 0;
      thinkpad_state.zoom_toggle = 0;
      thinkpad_state.display_toggle = 0;
      thinkpad_state.home_toggle = 0;
      thinkpad_state.search_toggle = 0;
      thinkpad_state.mail_toggle = 0;
      thinkpad_state.favorites_toggle = 0;
      thinkpad_state.reload_toggle = 0;
      thinkpad_state.abort_toggle = 0;
      thinkpad_state.backward_toggle = 0;
      thinkpad_state.forward_toggle = 0;
      thinkpad_state.fn_toggle = 0;
      thinkpad_state.thinklight_toggle = 0;
      thinkpad_state.hibernate_toggle = 0;
      thinkpad_state.display_state = 0;
      thinkpad_state.expand_toggle = 0;
      thinkpad_state.brightness_level = 0;
      thinkpad_state.brightness_toggle = 0;
      thinkpad_state.volume_level = 0;
      thinkpad_state.volume_toggle = 0;
      thinkpad_state.mute_toggle = 0;
      thinkpad_state.ac_state = 0;
      thinkpad_state.powermgt_ac = 0;
      thinkpad_state.powermgt_battery = 0;

}

00334 void ThinkPadMonitor::showToggleMessage(QString message, bool state) {
            QString stateString;
            if (state)  {
                  stateString = i18n("on");
            } else {
                  stateString = i18n("off");
            }
            message = message.arg(stateString);
            _interface->displayText(message);
}

00345 void ThinkPadMonitor::reconfigure(KConfig* config) {
      config->setGroup("thinkpad");

      m_nvramFile = config->readEntry("nvram", "/dev/nvram");
      m_softwareVolume = config->readBoolEntry("softwareVolume", true);
      m_run = config->readBoolEntry("run", false);
      m_volumeStep = config->readNumEntry("volumeStep", defaultVolumeStep);
      m_buttonThinkpad = config->readEntry("buttonThinkpad", "/usr/bin/konsole");
      m_buttonHome = config->readEntry("buttonHome", "/usr/bin/konqueror");
      m_buttonSearch = config->readEntry("buttonSearch", "/usr/bin/kfind");
      m_buttonMail = config->readEntry("buttonMail", "/usr/bin/kmail");
      m_buttonZoom = config->readEntry("buttonZoom", "/usr/bin/ksnapshot");
}

bool ThinkPadMonitor::retrieveVolume() {
      bool kmix_error = false;

      DCOPReply reply = kmixClient->call("volume", 0);
      if (reply.isValid())  {
            m_volume = reply;
      } else {
            kmix_error = true;
      }

      if (kmix_error) { // maybe the error occurred because kmix wasn't running
            if (kapp->startServiceByDesktopName("kmix")==0) { // trying to start kmix
                  // trying again
                  reply = kmixClient->call("volume", 0);
                  if (reply.isValid())  {
                        m_volume = reply;
                        kmix_error = false;
                        kmixWindow->send("minimize");
                  }
            }
      }

      if (kmix_error) {
            kdError() << "KMilo: ThinkPadMonitor could not access kmix/Mixer0 via dcop" << endl;
            return false;
      } else {
            return true;
      }
}

00389 void ThinkPadMonitor::setVolume(int volume) {
      if (!retrieveVolume())  {
            return;
      }

      if (volume > 100)  {
            m_volume = 100;
      } else if (volume < 0)  {
            m_volume = 0;
      } else {
            m_volume = volume;
      }
      kmixClient->send("setVolume", 0, m_volume);

      //write back to nvram but only if volume steps is not default
      if (m_volumeStep != defaultVolumeStep) {
            setNvramVolume();
      }
      m_progress = m_volume;
}

00410 void ThinkPadMonitor::setNvramVolume() {
      int file;
      char buffer;

      //open nvram
      if ((file = open(m_nvramFile.latin1(), O_RDWR|O_NONBLOCK)) == -1) {
            kdError() << "Unable to open device " << m_nvramFile << endl;
            return;
      }

      // jump to volume section
      if (lseek(file, 0x60, SEEK_SET) == -1 ) {
            kdError() << "Unable to seek device " << m_nvramFile << endl;
            return;
      }

      // read nvram
      if (read(file, &buffer, sizeof(buffer)) != sizeof(buffer)) {
            kdError() << "Unable to read from device " << m_nvramFile << endl;
            return;
      }

      // set volume_level to the value we write back to nvram
      // taken from tpb, unfortunatly I'm not sure why it works
      thinkpad_state.volume_level = 0x07;
      buffer &= 0xf0;
      buffer |= thinkpad_state.volume_level;

      // jump to volume section
      if (lseek(file, 0x60, SEEK_SET) == -1 ) {
            kdError() << "Unable to seek device " << m_nvramFile << endl;
            return;
      }

      // write std value for volume
      if (write(file, &buffer, sizeof(buffer)) != sizeof(buffer)) {
            kdError() << "Unable to write to device " << m_nvramFile << endl;
            return;
      }

      close(file);
}

}; //close namespace

K_EXPORT_COMPONENT_FACTORY(kmilo_thinkpad, KGenericFactory<ThinkPadMonitor>("kmilo_thinkpad"))

Generated by  Doxygen 1.6.0   Back to index