00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <QDBusReply>
00033 #include <kpluginfactory.h>
00034 #include <kpluginloader.h>
00035 #include <kconfig.h>
00036 #include <kconfiggroup.h>
00037 #include <krun.h>
00038 #include <kdebug.h>
00039 #include <kurl.h>
00040 #include <klocale.h>
00041 #include "kmilointerface.h"
00042 #include <QDBusInterface>
00043 #ifdef Q_OS_FREEBSD
00044 #include <sys/sysctl.h>
00045 #endif
00046 #include <ktoolinvocation.h>
00047
00048 #include "thinkpad.h"
00049
00050 namespace KMilo {
00051
00052 ThinkPadMonitor::ThinkPadMonitor(QObject* parent, const QVariantList& args): Monitor(parent, args) {
00053 m_progress = 0;
00054 m_volume = 50;
00055 }
00056
00057 ThinkPadMonitor::~ThinkPadMonitor() {
00058 }
00059
00060 bool ThinkPadMonitor::init() {
00061
00062 KConfig config("kmilodrc");
00063 reconfigure(&config);
00064
00065 if (m_run) {
00066 clearStruct(thinkpad_state);
00067 clearStruct(last_thinkpad_state);
00068 if ( getNvramState(&thinkpad_state) == false ) {
00069 return false;
00070 }
00071
00072 if (m_softwareVolume || m_volumeStep != defaultVolumeStep) {
00073 kmixAdaptor = new QDBusInterface("org.kde.kmix", "/Mixer0", "org.kde.KMix");
00074 kmixWindowAdaptor = new QDBusInterface("org.kde.kmix","/kmix/KMixWindow", "org.kde.kmix.KMixWindow");
00075 retrieveVolume();
00076 setNvramVolume();
00077 }
00078 }
00079
00080 return m_run;
00081 }
00082
00083 Monitor::DisplayType ThinkPadMonitor::poll() {
00084
00085
00086 memcpy(&last_thinkpad_state, &thinkpad_state, sizeof(thinkpad_state_struct));
00087 getNvramState(&thinkpad_state);
00088
00089 Monitor::DisplayType pollResult = None;
00090
00091
00092 if (thinkpad_state.mute_toggle != last_thinkpad_state.mute_toggle ||
00093 (thinkpad_state.volume_toggle != last_thinkpad_state.volume_toggle
00094 && last_thinkpad_state.mute_toggle == 1)) {
00095
00096 showToggleMessage(i18n("Mute on"), i18n("Mute off"), thinkpad_state.mute_toggle == 1);
00097 if (m_softwareVolume || m_volumeStep != defaultVolumeStep) {
00098 kmixAdaptor->call("setMute",QString(),(thinkpad_state.mute_toggle == 1));
00099 }
00100 }
00101
00102
00103 if (thinkpad_state.thinkpad_toggle != last_thinkpad_state.thinkpad_toggle &&
00104 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
00105 _interface->displayText(i18n("Thinkpad Button Pressed"));
00106
00107 KUrl url(m_buttonThinkpad);
00108 (void) new KRun(url, 0, true, true);
00109 }
00110
00111
00112 if (thinkpad_state.thinklight_toggle != last_thinkpad_state.thinklight_toggle) {
00113 showToggleMessage(i18n("ThinkLight is on"), i18n("ThinkLight is off"), thinkpad_state.thinklight_toggle == 1);
00114 }
00115
00116
00117 if (thinkpad_state.volume_level != last_thinkpad_state.volume_level) {
00118
00119 pollResult = Volume;
00120
00121 if (m_volumeStep == defaultVolumeStep && m_softwareVolume == false) {
00122
00123 m_progress = thinkpad_state.volume_level * 100 / defaultVolumeStep;
00124 } else {
00125 if (thinkpad_state.volume_level > last_thinkpad_state.volume_level) {
00126 m_progress = m_volume + m_volumeStep;
00127 } else {
00128 m_progress = m_volume - m_volumeStep;
00129 }
00130 setVolume(m_progress);
00131 }
00132
00133 }
00134
00135
00136 if (thinkpad_state.brightness_level != last_thinkpad_state.brightness_level) {
00137 pollResult = Brightness;
00138 m_progress = thinkpad_state.brightness_level * 100 / 7;
00139 }
00140
00141
00142
00143
00144 if (thinkpad_state.zoom_toggle != last_thinkpad_state.zoom_toggle) {
00145
00146
00147
00148 _interface->displayText(i18n("Zoom button pressed"));
00149
00150 KUrl url(m_buttonZoom);
00151 (void) new KRun(url, 0, true, true);
00152 }
00153
00154
00155 if (thinkpad_state.home_toggle != last_thinkpad_state.home_toggle &&
00156 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
00157 _interface->displayText(i18n("Home button pressed"));
00158 KUrl url(m_buttonHome);
00159 (void) new KRun(url, 0, true, true);
00160 }
00161
00162
00163 if (thinkpad_state.search_toggle != last_thinkpad_state.search_toggle &&
00164 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
00165 _interface->displayText(i18n("Search button pressed"));
00166 KUrl url(m_buttonSearch);
00167 (void) new KRun(url, 0, true, true);
00168 }
00169
00170
00171 if (thinkpad_state.mail_toggle != last_thinkpad_state.mail_toggle &&
00172 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
00173 _interface->displayText(i18n("Mail button pressed"));
00174 KUrl url(m_buttonMail);
00175 (void) new KRun(url, 0, true, true);
00176 }
00177
00178
00179 if (thinkpad_state.display_toggle != last_thinkpad_state.display_toggle &&
00180 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
00181
00182
00183
00184
00185
00186
00187 unsigned int display_state = 1;
00188 if (thinkpad_state.display_state == last_thinkpad_state.display_state) {
00189 display_state = display_state % 3 + 1;
00190 } else {
00191 display_state = thinkpad_state.display_state;
00192 }
00193
00194 switch (display_state & 0x03) {
00195 case 0x1:
00196 _interface->displayText(i18n("Display changed: LCD on, CRT off"));
00197 break;
00198
00199 case 0x2:
00200 _interface->displayText(i18n("Display changed: LCD off, CRT on"));
00201 break;
00202
00203 case 0x3:
00204 _interface->displayText(i18n("Display changed: LCD on, CRT on"));
00205 break;
00206 }
00207 }
00208
00209
00210 if (thinkpad_state.expand_toggle != last_thinkpad_state.expand_toggle) {
00211 showToggleMessage(i18n("HV Expansion is on"), i18n("HV Expansion is off"), (thinkpad_state.expand_toggle & 0x01) == 1);
00212 }
00213
00214
00215 if (thinkpad_state.powermgt_ac != last_thinkpad_state.powermgt_ac) {
00216 switch(thinkpad_state.powermgt_ac) {
00217 case 0x4:
00218 _interface->displayText(i18n("Power management mode AC changed: PM AC high"));
00219 break;
00220
00221 case 0x2:
00222 _interface->displayText(i18n("Power management mode AC changed: PM AC auto"));
00223 break;
00224
00225 case 0x1:
00226 _interface->displayText(i18n("Power management mode AC changed: PM AC manual"));
00227 break;
00228
00229 default:
00230 _interface->displayText(i18n("Power management mode AC changed: PM AC unknown"));
00231 break;
00232 }
00233 }
00234
00235
00236 if (thinkpad_state.powermgt_battery != last_thinkpad_state.powermgt_battery) {
00237 switch(thinkpad_state.powermgt_battery) {
00238 case 0x4:
00239 _interface->displayText(i18n("Power management mode battery changed: PM battery high"));
00240 break;
00241
00242 case 0x2:
00243 _interface->displayText(i18n("Power management mode battery changed: PM battery auto"));
00244 break;
00245
00246 case 0x1:
00247 _interface->displayText(i18n("Power management mode battery changed: PM battery manual"));
00248 break;
00249
00250 default:
00251 _interface->displayText(i18n("Power management mode battery changed: PM battery unknown"));
00252 break;
00253 }
00254 }
00255
00256
00257 if (thinkpad_state.wireless_toggle != last_thinkpad_state.wireless_toggle) {
00258 showToggleMessage(i18n("Wireless LAN is enabled"), i18n("Wireless LAN is disabled"), thinkpad_state.wireless_toggle == 1);
00259 }
00260
00261
00262 if (thinkpad_state.bluetooth_toggle != last_thinkpad_state.bluetooth_toggle) {
00263 showToggleMessage(i18n("Bluetooth is enabled"), i18n("Bluetooth is disabled"), thinkpad_state.bluetooth_toggle == 1);
00264 }
00265
00266 return pollResult;
00267 }
00268
00269
00270 int ThinkPadMonitor::progress() const {
00271 return m_progress;
00272 }
00273
00274 QString ThinkPadMonitor::message() const {
00275
00276
00277 return "";
00278 }
00279
00280 bool ThinkPadMonitor::getNvramState(thinkpad_state_struct* thinkpad_state) {
00281 #ifndef Q_OS_FREEBSD
00282 int file;
00283 unsigned char buffer[114];
00284
00285
00286
00287 if ((file=open(m_nvramFile.toLatin1(), O_RDONLY|O_NONBLOCK)) == -1) {
00288 kError() << "Unable to open device: " << m_nvramFile << endl;
00289 return false;
00290 }
00291
00292
00293 if (read(file, buffer, sizeof(buffer)) != sizeof(buffer)) {
00294 kError() << "Unable to read from device: " << m_nvramFile << endl;
00295 return false;
00296 }
00297
00298
00299 if (close(file) == -1) {
00300 kError() << "Unable to close device %s: " << m_nvramFile << endl;
00301 return false;
00302 }
00303
00304 thinkpad_state->thinkpad_toggle
00305 = (thinkpad_state->thinkpad_toggle & ~0x01) | (( buffer[0x57] & 0x08) >> 3);
00306 thinkpad_state->zoom_toggle
00307 = (thinkpad_state->zoom_toggle & ~0x01) | ((~buffer[0x57] & 0x20) >> 5);
00308 thinkpad_state->display_toggle
00309 = (thinkpad_state->display_toggle & ~0x01) | (( buffer[0x57] & 0x40) >> 6);
00310 thinkpad_state->home_toggle
00311 = (thinkpad_state->home_toggle & ~0x01) | (( buffer[0x56] & 0x01) );
00312 thinkpad_state->search_toggle
00313 = (thinkpad_state->search_toggle & ~0x01) | (( buffer[0x56] & 0x02) >> 1);
00314 thinkpad_state->mail_toggle
00315 = (thinkpad_state->mail_toggle & ~0x01) | (( buffer[0x56] & 0x04) >> 2);
00316 thinkpad_state->thinklight_toggle
00317 = (thinkpad_state->thinklight_toggle & ~0x01) | (( buffer[0x58] & 0x10) >> 4);
00318 thinkpad_state->hibernate_toggle
00319 = (thinkpad_state->hibernate_toggle & ~0x01) | (( buffer[0x58] & 0x01) );
00320 thinkpad_state->display_state
00321 = (( buffer[0x59] & 0x03) );
00322 thinkpad_state->expand_toggle
00323 = (thinkpad_state->expand_toggle & ~0x01) | (( buffer[0x59] & 0x10) >> 4);
00324 thinkpad_state->brightness_level
00325 = (( buffer[0x5E] & 0x07) );
00326 thinkpad_state->brightness_toggle
00327 = (thinkpad_state->brightness_toggle & ~0x01) | (( buffer[0x5E] & 0x20) >> 5);
00328 thinkpad_state->volume_level
00329 = (( buffer[0x60] & 0x0f) );
00330 thinkpad_state->volume_toggle
00331 = (thinkpad_state->volume_toggle & ~0x01) | (( buffer[0x60] & 0x80) >> 7);
00332 thinkpad_state->mute_toggle
00333 = (thinkpad_state->mute_toggle & ~0x01) | (( buffer[0x60] & 0x40) >> 6);
00334 thinkpad_state->powermgt_ac
00335 = (( buffer[0x39] & 0x07) );
00336 thinkpad_state->powermgt_battery
00337 = (( buffer[0x39] & 0x38) >> 3);
00338 #else
00339 u_int n = 0;
00340 size_t len = sizeof(n);
00341
00342 if ( sysctlbyname("dev.acpi_ibm.0.hotkey", &n, &len, NULL, 0) == -1 ) {
00343 kError() << "Unable to read sysctl: dev.acpi_ibm.0.hotkey" << endl;
00344 return false;
00345 }
00346
00347 thinkpad_state->thinkpad_toggle
00348 = (thinkpad_state->thinkpad_toggle & ~0x01) | (( n & (1<<3)) >> 3);
00349 thinkpad_state->zoom_toggle
00350 = (thinkpad_state->zoom_toggle & ~0x01) | (( n & (1<<4)) >> 4);
00351 thinkpad_state->display_toggle
00352 = (thinkpad_state->display_toggle & ~0x01) | (( n & (1<<6)) >> 6);
00353 thinkpad_state->home_toggle
00354 = (thinkpad_state->home_toggle & ~0x01) | (( n & (1<<0)) );
00355 thinkpad_state->search_toggle
00356 = (thinkpad_state->search_toggle & ~0x01) | (( n & (1<<1)) >> 1);
00357 thinkpad_state->mail_toggle
00358 = (thinkpad_state->mail_toggle & ~0x01) | (( n & (1<<2)) >> 2);
00359 thinkpad_state->hibernate_toggle
00360 = (thinkpad_state->hibernate_toggle & ~0x01) | (( n & (1<<7)) >> 7);
00361 thinkpad_state->expand_toggle
00362 = (thinkpad_state->expand_toggle & ~0x01) | (( n & (1<<9)) >> 9);
00363 thinkpad_state->brightness_toggle
00364 = (thinkpad_state->brightness_toggle & ~0x01) | (( n & (1<<10)) >> 10);
00365 thinkpad_state->volume_toggle
00366 = (thinkpad_state->volume_toggle & ~0x01) | (( n & (1<<11)) >> 11);
00367
00368
00369 if ( sysctlbyname("dev.acpi_ibm.0.thinklight", &n, &len, NULL, 0) != -1 )
00370 thinkpad_state->thinklight_toggle = n;
00371 else
00372 kWarning() << "Unable to read sysctl: dev.acpi_ibm.0.thinklight" ;
00373
00374 if ( sysctlbyname("dev.acpi_ibm.0.lcd_brightness", &n, &len, NULL, 0) == -1 ) {
00375 kError() << "Unable to read sysctl: dev.acpi_ibm.0.lcd_brightness" << endl;
00376 return false;
00377 }
00378 thinkpad_state->brightness_level = n;
00379
00380 if ( sysctlbyname("dev.acpi_ibm.0.volume", &n, &len, NULL, 0) == -1 ) {
00381 kError() << "Unable to read sysctl: dev.acpi_ibm.0.volume" << endl;
00382 return false;
00383 }
00384 thinkpad_state->volume_level = n;
00385
00386 if ( sysctlbyname("dev.acpi_ibm.0.mute", &n, &len, NULL, 0) == -1 ) {
00387 kError() << "Unable to read sysctl: dev.acpi_ibm.0.mute" << endl;
00388 return false;
00389 }
00390 thinkpad_state->mute_toggle = n;
00391
00392
00393 if ( sysctlbyname("dev.acpi_ibm.0.wlan", &n, &len, NULL, 0) != -1 )
00394 thinkpad_state->wireless_toggle = n;
00395 else
00396 kWarning() << "Unable to read sysctl: dev.acpi_ibm.0.wlan" ;
00397
00398 if ( sysctlbyname("dev.acpi_ibm.0.bluetooth", &n, &len, NULL, 0) != -1 )
00399 thinkpad_state->bluetooth_toggle = n;
00400 else
00401 kWarning() << "Unable to read sysctl: dev.acpi_ibm.0.bluetooth" ;
00402 #endif
00403 return true;
00404 }
00405
00406 void ThinkPadMonitor::clearStruct(thinkpad_state_struct& thinkpad_state) {
00407 thinkpad_state.thinkpad_toggle = 0;
00408 thinkpad_state.zoom_toggle = 0;
00409 thinkpad_state.display_toggle = 0;
00410 thinkpad_state.home_toggle = 0;
00411 thinkpad_state.search_toggle = 0;
00412 thinkpad_state.mail_toggle = 0;
00413 thinkpad_state.favorites_toggle = 0;
00414 thinkpad_state.reload_toggle = 0;
00415 thinkpad_state.abort_toggle = 0;
00416 thinkpad_state.backward_toggle = 0;
00417 thinkpad_state.forward_toggle = 0;
00418 thinkpad_state.fn_toggle = 0;
00419 thinkpad_state.thinklight_toggle = 0;
00420 thinkpad_state.hibernate_toggle = 0;
00421 thinkpad_state.display_state = 0;
00422 thinkpad_state.expand_toggle = 0;
00423 thinkpad_state.brightness_level = 0;
00424 thinkpad_state.brightness_toggle = 0;
00425 thinkpad_state.volume_level = 0;
00426 thinkpad_state.volume_toggle = 0;
00427 thinkpad_state.mute_toggle = 0;
00428 thinkpad_state.ac_state = 0;
00429 thinkpad_state.powermgt_ac = 0;
00430 thinkpad_state.powermgt_battery = 0;
00431 thinkpad_state.wireless_toggle = 0;
00432 thinkpad_state.bluetooth_toggle = 0;
00433 }
00434
00435 void ThinkPadMonitor::showToggleMessage(QString onMessage, QString offMessage, bool state)
00436 {
00437 QString message;
00438 if (state) {
00439 message = onMessage;
00440 } else {
00441 message = offMessage;
00442 }
00443 _interface->displayText(message);
00444 }
00445
00446 void ThinkPadMonitor::reconfigure(KConfig* config) {
00447 KConfigGroup group = config->group("thinkpad");
00448
00449 m_nvramFile = group.readEntry("nvram", "/dev/nvram");
00450 m_softwareVolume = group.readEntry("softwareVolume", true);
00451 m_run = group.readEntry("run", false);
00452 m_volumeStep = group.readEntry("volumeStep", defaultVolumeStep);
00453 m_buttonThinkpad = group.readEntry("buttonThinkpad", "/usr/bin/konsole");
00454 m_buttonHome = group.readEntry("buttonHome", "/usr/bin/konqueror");
00455 m_buttonSearch = group.readEntry("buttonSearch", "/usr/bin/kfind");
00456 m_buttonMail = group.readEntry("buttonMail", "/usr/bin/kmail");
00457 m_buttonZoom = group.readEntry("buttonZoom", "/usr/bin/ksnapshot");
00458 }
00459
00460 bool ThinkPadMonitor::retrieveVolume() {
00461 bool kmix_error = false;
00462 QDBusReply<int> reply = kmixAdaptor->call("masterVolume");
00463 if( reply.isValid())
00464 m_volume = reply;
00465 else
00466 kmix_error = true;
00467 if (kmix_error) {
00468 if (KToolInvocation::startServiceByDesktopName("kmix")==0) {
00469
00470 reply = kmixAdaptor->call("masterVolume");
00471 if (reply.isValid()) {
00472 m_volume = reply;
00473 kmix_error = false;
00474 kmixWindowAdaptor->call("minimize");
00475 }
00476 }
00477 }
00478 #if 0
00479 DCOPReply reply = kmixClient->call("masterVolume");
00480 if (reply.isValid()) {
00481 m_volume = reply;
00482 } else {
00483 kmix_error = true;
00484 }
00485
00486 if (kmix_error) {
00487 if (KToolInvocation::startServiceByDesktopName("kmix")==0) {
00488
00489 reply = kmixClient->call("masterVolume");
00490 if (reply.isValid()) {
00491 m_volume = reply;
00492 kmix_error = false;
00493 kmixWindow->send("minimize");
00494 }
00495 }
00496 }
00497 #endif
00498
00499 if (kmix_error) {
00500 kError() << "KMilo: ThinkPadMonitor could not access kmix/Mixer0 via dcop" << endl;
00501 return false;
00502 } else {
00503 return true;
00504 }
00505 }
00506
00507 void ThinkPadMonitor::setVolume(int volume) {
00508 if (!retrieveVolume()) {
00509 return;
00510 }
00511
00512 if (volume > 100) {
00513 m_volume = 100;
00514 } else if (volume < 0) {
00515 m_volume = 0;
00516 } else {
00517 m_volume = volume;
00518 }
00519 kmixAdaptor->call("setMasterVolume",m_volume);
00520
00521
00522 if (m_volumeStep != defaultVolumeStep) {
00523 setNvramVolume();
00524 }
00525 m_progress = m_volume;
00526 }
00527
00528 void ThinkPadMonitor::setNvramVolume() {
00529 #ifndef Q_OS_FREEBSD
00530 int file;
00531 char buffer;
00532
00533
00534 if ((file = open(m_nvramFile.toLatin1(), O_RDWR|O_NONBLOCK)) == -1) {
00535 kError() << "Unable to open device " << m_nvramFile << endl;
00536 return;
00537 }
00538
00539
00540 if (lseek(file, 0x60, SEEK_SET) == -1 ) {
00541 kError() << "Unable to seek device " << m_nvramFile << endl;
00542 return;
00543 }
00544
00545
00546 if (read(file, &buffer, sizeof(buffer)) != sizeof(buffer)) {
00547 kError() << "Unable to read from device " << m_nvramFile << endl;
00548 return;
00549 }
00550
00551
00552
00553 thinkpad_state.volume_level = 0x07;
00554 buffer &= 0xf0;
00555 buffer |= thinkpad_state.volume_level;
00556
00557
00558 if (lseek(file, 0x60, SEEK_SET) == -1 ) {
00559 kError() << "Unable to seek device " << m_nvramFile << endl;
00560 return;
00561 }
00562
00563
00564 if (write(file, &buffer, sizeof(buffer)) != sizeof(buffer)) {
00565 kError() << "Unable to write to device " << m_nvramFile << endl;
00566 return;
00567 }
00568
00569 close(file);
00570 #else
00571 u_int n = thinkpad_state.volume_level;
00572
00573 if (sysctlbyname("dev.acpi_ibm.0.volume", NULL, NULL, &n, sizeof(n)))
00574 kError() << "Unable to write sysctl: dev.acpi_ibm.0.volume" << endl;
00575 #endif
00576 }
00577
00578 }
00579
00580 K_PLUGIN_FACTORY(KMiloFactory, registerPlugin<KMilo::ThinkPadMonitor>();)
00581 K_EXPORT_PLUGIN(KMiloFactory("kmilo_thinkpad"))
00582