📄 fancontrol.cpp
字号:
// --------------------------------------------------------------
//
// Thinkpad Fan Control
//
// --------------------------------------------------------------
//
// This program and source code is in the public domain.
//
// The author claims no copyright, copyleft, license or
// whatsoever for the program itself (with exception of
// WinIO driver). You may use, reuse or distribute it's
// binaries or source code in any desired way or form,
// Useage of binaries or source shall be entirely and
// without exception at your own risk.
//
// --------------------------------------------------------------
#include "fancontrol.h"
//-------------------------------------------------------------------------
// constructor
//-------------------------------------------------------------------------
FANCONTROL::FANCONTROL(HINSTANCE hinstapp)
: hwndDialog(NULL),
CurrentMode(-1),
PreviousMode(-1),
Cycle(5),
CurrentIcon(-1),
hThread(NULL),
FanBeepFreq(440),
FanBeepDura(50),
ReadErrorCount(0),
MaxReadErrors(10),
pTaskbarIcon(NULL),
MinimizeToSysTray(FALSE),
StartMinimized(FALSE),
MinimizeOnClose(FALSE),
ActiveMode(false),
FinalSeen(false)
{
int i= 0;
char buf[256]= "";
// clear title strings
setzero(this->Title, sizeof(this->Title));
setzero(this->Title2, sizeof(this->Title2));
setzero(this->LastTitle, sizeof(this->LastTitle));
setzero(this->CurrentStatus, sizeof(this->CurrentStatus));
setzero(this->IgnoreSensors, sizeof(this->IgnoreSensors));
this->IconLevels[0]= 50; // yellow icon level
this->IconLevels[1]= 55; // orange icon level
this->IconLevels[2]= 60; // red icon level
// initial default "smart" table
setzero(this->SmartLevels, sizeof(this->SmartLevels));
this->SmartLevels[i].temp= 50; this->SmartLevels[i].fan= 0; i++;
this->SmartLevels[i].temp= 55; this->SmartLevels[i].fan= 3; i++;
this->SmartLevels[i].temp= 60; this->SmartLevels[i].fan= 5; i++;
this->SmartLevels[i].temp= 65; this->SmartLevels[i].fan= 7; i++;
this->SmartLevels[i].temp= 70; this->SmartLevels[i].fan= 128; i++;
this->SmartLevels[i].temp= -1; this->SmartLevels[i].fan= 0; i++;
this->hwndDialog= ::CreateDialogParam(hinstapp,
MAKEINTRESOURCE(9000),
HWND_DESKTOP,
(DLGPROC)BaseDlgProc,
(LPARAM)this);
if (this->hwndDialog) {
::GetWindowText(this->hwndDialog, this->Title, sizeof(this->Title));
strcat(this->Title, " V" FANCONTROLVERSION);
::SetWindowText(this->hwndDialog, this->Title);
::SetWindowLong(this->hwndDialog, GWL_USERDATA, (ULONG)this);
::SendDlgItemMessage(this->hwndDialog, 8112, EM_LIMITTEXT, 256, 0);
::SendDlgItemMessage(this->hwndDialog, 9200, EM_LIMITTEXT, 4096, 0);
::SetDlgItemText(this->hwndDialog, 8310, "7");
// config file
this->ReadConfig("fancontrol.ini");
// taskbaricon (keep code after reading config)
if (this->MinimizeToSysTray) {
this->pTaskbarIcon= new TASKBARICON(this->hwndDialog, 10, "Thinkpad Fan Control");
}
// read current fan control status and set mode buttons accordingly
if (this->ActiveMode==2) {
this->CurrentMode= 2;
}
else
if (this->ReadEcStatus(&this->State)) {
if (this->State.FanCtrl & 0x80) {
this->CurrentMode= 1;
}
else {
this->CurrentMode= 3;
char buf[16]= "";
sprintf(buf, "%d", this->State.FanCtrl & 0x3f);
::SetDlgItemText(this->hwndDialog, 8310, buf);
}
}
this->ModeToDialog(this->CurrentMode);
this->PreviousMode= 1;
// enable/disable mode radiobuttons
::EnableWindow(::GetDlgItem(this->hwndDialog, 8300), this->ActiveMode);
::EnableWindow(::GetDlgItem(this->hwndDialog, 8301), this->ActiveMode);
::EnableWindow(::GetDlgItem(this->hwndDialog, 8302), this->ActiveMode);
::EnableWindow(::GetDlgItem(this->hwndDialog, 8310), this->ActiveMode);
// make it call HandleControl initially
::PostMessage(this->hwndDialog, WM__GETDATA, 0, 0);
::SetTimer(this->hwndDialog, 1, this->Cycle*1000, NULL); // fan update
::SetTimer(this->hwndDialog, 2, 500, NULL); // title update
if (!this->MinimizeToSysTray)
::ShowWindow(this->hwndDialog, TRUE);
if (this->StartMinimized)
::ShowWindow(this->hwndDialog, SW_MINIMIZE);
}
}
//-------------------------------------------------------------------------
// destructor
//-------------------------------------------------------------------------
FANCONTROL::~FANCONTROL()
{
if (this->hThread) {
::WaitForSingleObject(this->hThread, 2000);
this->hThread= NULL;
}
if (this->pTaskbarIcon) {
delete this->pTaskbarIcon;
this->pTaskbarIcon= NULL;
}
if (this->hwndDialog)
::DestroyWindow(this->hwndDialog);
}
//-------------------------------------------------------------------------
// mode integer from mode radio buttons
//-------------------------------------------------------------------------
int
FANCONTROL::CurrentModeFromDialog()
{
BOOL modetpauto= ::SendDlgItemMessage(this->hwndDialog, 8300, BM_GETCHECK, 0L, 0L),
modefcauto= ::SendDlgItemMessage(this->hwndDialog, 8301, BM_GETCHECK, 0L, 0L),
modemanual= ::SendDlgItemMessage(this->hwndDialog, 8302, BM_GETCHECK, 0L, 0L);
if (modetpauto)
this->CurrentMode= 1;
else
if (modefcauto)
this->CurrentMode= 2;
else
if (modemanual)
this->CurrentMode= 3;
else
this->CurrentMode= -1;
return this->CurrentMode;
}
void
FANCONTROL::ModeToDialog(int mode)
{
::SendDlgItemMessage(this->hwndDialog, 8300, BM_SETCHECK, mode==1, 0L);
::SendDlgItemMessage(this->hwndDialog, 8301, BM_SETCHECK, mode==2, 0L);
::SendDlgItemMessage(this->hwndDialog, 8302, BM_SETCHECK, mode==3, 0L);
}
//-------------------------------------------------------------------------
// process main dialog
//-------------------------------------------------------------------------
int FANCONTROL::ProcessDialog()
{
MSG qmsg, qmsg2;
int dlgrc= -1;
if (this->hwndDialog) {
for (;;) {
BOOL nodlgmsg= FALSE;
::GetMessage(&qmsg, NULL, 0L, 0L);
// control movements
if (qmsg.message!=WM__DISMISSDLG && IsDialogMessage(this->hwndDialog, &qmsg)) {
continue;
}
qmsg2= qmsg;
TranslateMessage(&qmsg);
DispatchMessage(&qmsg);
if (qmsg2.message==WM__DISMISSDLG && qmsg2.hwnd==this->hwndDialog) {
dlgrc= qmsg2.wParam;
break;
}
}
}
return dlgrc;
}
//-------------------------------------------------------------------------
// dialog window procedure (map to class method)
//-------------------------------------------------------------------------
ULONG CALLBACK
FANCONTROL::BaseDlgProc(HWND hwnd, ULONG msg, WPARAM mp1, LPARAM mp2)
{
ULONG rc= FALSE;
FANCONTROL *This= (FANCONTROL*)GetWindowLong(hwnd, GWL_USERDATA);
if (This)
rc= This->DlgProc(hwnd, msg, mp1, mp2);
return rc;
}
//-------------------------------------------------------------------------
// dialog window procedure as class method
//-------------------------------------------------------------------------
ULONG
FANCONTROL::DlgProc(HWND hwnd, ULONG msg, WPARAM mp1, LPARAM mp2)
{
ULONG rc= 0, ok, res;
switch (msg) {
case WM_INITDIALOG:
// placing code here will NOT work!
// (put it into BaseDlgProc instead)
break;
case WM_TIMER:
switch (mp1) {
case 1: // update fan state
::PostMessage(this->hwndDialog, WM__GETDATA, 0, 0);
break;
case 2: // update window title
res= this->IsMinimized();
if (res && strcmp(this->LastTitle, this->Title2)!=0) {
::SetWindowText(this->hwndDialog, this->Title2);
strcpy(this->LastTitle, this->Title2);
}
else
if (!res && strcmp(this->LastTitle, this->Title)!=0) {
::SetWindowText(this->hwndDialog, this->Title);
strcpy(this->LastTitle, this->Title);
}
if (this->pTaskbarIcon) {
this->pTaskbarIcon->SetTooltip(this->Title2);
int icon= -1;
if (this->CurrentModeFromDialog()==1) {
icon= 10; // gray
}
else {
icon= 11; // blue
for (int i= 0; i<ARRAYMAX(this->IconLevels); i++) {
if (this->MaxTemp>=this->IconLevels[i]) {
icon= 12+i; // yellow, orange, red
}
}
}
if (icon!=this->CurrentIcon && icon!=-1) {
this->pTaskbarIcon->SetIcon(icon);
this->CurrentIcon= icon;
}
}
break;
default:
break;
}
break;
case WM_COMMAND:
if (HIWORD(mp1)==BN_CLICKED || HIWORD(mp1)==EN_CHANGE) {
int cmd= LOWORD(mp1);
if (cmd>=8300 && cmd<=8302 || cmd==8310) { // radio button or manual speed entry
::PostMessage(hwnd, WM__GETDATA, 0, 0);
}
else
switch (cmd) {
case 5001: // bios
case 5002: // smart
this->ModeToDialog(cmd-5000);
break;
case 5010: // show window
::ShowWindow(this->hwndDialog, TRUE);
::SetForegroundWindow(this->hwndDialog);
break;
case 5020: // end program
// don't close if we can't set the fan back to bios controlled
if (!this->ActiveMode || this->SetFan("On close", 0x80, true)) {
::PostMessage(hwnd, WM__DISMISSDLG, IDCANCEL, 0); // exit from ProcessDialog()
}
break;
}
}
break;
case WM_CLOSE:
if (this->MinimizeOnClose && this->MinimizeToSysTray)
::ShowWindow(this->hwndDialog, SW_MINIMIZE);
else
::PostMessage(this->hwndDialog, WM_COMMAND, 5020, 0); // End Program
rc= TRUE;
break;
case WM_MOVE:
case WM_SIZE:
if (mp1==SIZE_MINIMIZED && this->MinimizeToSysTray) {
::ShowWindow(this->hwndDialog, FALSE);
}
rc= TRUE;
break;
case WM_DESTROY:
break;
//
// USER messages
//
case WM__GETDATA:
if (!this->hThread)
this->hThread= this->CreateThread(FANCONTROL_Thread, (ULONG)this);
break;
case WM__NEWDATA:
if (this->hThread) {
::CloseHandle(this->hThread);
this->hThread= 0;
}
ok= mp1; // equivalent of "ok= this->ReadEcStatus(&this->State);" via thread
if (ok) {
this->ReadErrorCount= 0;
this->HandleData();
}
else {
this->Trace("Warning: Can't read Status (possible conflict with other software)");
this->ReadErrorCount++;
// after so many consecutive read errors, try to switch back to bios mode
if (this->ReadErrorCount>this->MaxReadErrors) {
this->ModeToDialog(1);
ok= this->SetFan("Max. Errors", 0x80);
if (ok) {
::SendMessage(this->hwndDialog, WM_CLOSE, 0, 0);
}
}
}
break;
case WM__TASKBAR:
switch (mp2) {
case WM_LBUTTONDOWN:
break;
case WM_LBUTTONUP:
{
BOOL
isshift= ::GetAsyncKeyState(VK_SHIFT) & 0x8000,
isctrl= ::GetAsyncKeyState(VK_CONTROL) & 0x8000;
int action= -1;
// some fancy key dependent stuff could be done here.
}
break;
case WM_LBUTTONDBLCLK:
::ShowWindow(this->hwndDialog, TRUE);
::SetForegroundWindow(this->hwndDialog);
break;
case WM_RBUTTONDOWN:
{
MENU m(5000);
int mode= this->CurrentModeFromDialog();
if (mode==1)
m.CheckMenuItem(5001);
else
if (mode==2)
m.CheckMenuItem(5002);
m.Popup(this->hwndDialog);
}
break;
}
rc= TRUE;
break;
default:
break;
}
return rc;
}
//-------------------------------------------------------------------------
// reading the EC status may take a while, hence do it in a thread
//-------------------------------------------------------------------------
int
FANCONTROL::WorkThread()
{
int ok= this->ReadEcStatus(&this->State);
::PostMessage(this->hwndDialog, WM__NEWDATA, ok, 0);
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -