2 * wmthrottle- A dockapp to throttle CPU via ACPI status
3 * Copyright (C) 2002 Thomas Nemeth <tnemeth@free.fr>
5 * Based on work by Seiichi SATO <ssato@sh.rim.or.jp>
6 * Copyright (C) 2001,2002 Seiichi SATO <ssato@sh.rim.or.jp>
7 * and on work by Mark Staggs <me@markstaggs.net>
8 * Copyright (C) 2002 Mark Staggs <me@markstaggs.net>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 #include "backlight_on.xpm"
35 #include "backlight_off.xpm"
42 #define FREE(data) {if (data) free (data); data = NULL;}
46 #define WINDOWED_BG ". c #AEAAAE"
47 #define MAX_HISTORY 16
48 #define CPUNUM_NONE -1
51 # define ACPIDEV "/proc/acpi/info"
54 typedef struct AcpiInfos
{
59 typedef enum { LIGHTOFF
, LIGHTON
} light
;
68 char *tpercent
[]={"100","88","75","63","50","38","25","13"};
69 static char *display_name
= "";
70 static char *light_color
= NULL
; /* back-light color */
71 static unsigned update_interval
= 1;
72 // temperature threshold to slow down cpu
73 static int temperature_slowdown
= 80;
74 // current temperature
76 static light backlight
= LIGHTOFF
;
77 static char *notif_cmd
= NULL
;
78 static unsigned alarm_level
;
79 static char* suspend_cmd
= NULL
;
80 static char* standby_cmd
= NULL
;
81 static unsigned switch_authorized
= True
;
82 static unsigned performance_flag
= False
;
83 unsigned temperature_flag
= False
;
84 unsigned temperature_delta
= 1;
85 static int statecount
= 0;
86 static int currstate
= 0;
87 static AcpiInfos cur_acpi_infos
;
90 # ifndef ACPI_32_BIT_SUPPORT
91 # define ACPI_32_BIT_SUPPORT 0x0002
98 static void switch_light();
99 static void draw_pcdigit(AcpiInfos infos
);
100 static void draw_statusdigit(AcpiInfos infos
);
101 static void draw_pcgraph(AcpiInfos infos
);
102 static void throttle_speed(int x
);
103 static void highlight_but(AcpiInfos infos
);
104 static void parse_arguments(int argc
, char **argv
);
105 static void print_help(char *prog
);
106 static void acpi_getinfos(AcpiInfos
*infos
);
107 static int acpi_exists();
108 static int my_system (char *cmd
);
110 int acpi_read(AcpiInfos
*i
);
111 int acpi_get_statecount();
112 int acpi_read_temp(int *temp
);
113 void throttle_by_temp(int threshold
);
118 int main(int argc
, char **argv
) {
122 XpmColorSymbol colors
[2] = { {"Back0", NULL
, 0}, {"Back1", NULL
, 0} };
127 sa
.sa_handler
= SIG_IGN
;
129 sa
.sa_flags
= SA_NOCLDWAIT
;
133 sigemptyset(&sa
.sa_mask
);
134 sigaction(SIGCHLD
, &sa
, NULL
);
136 /* Parse CommandLine */
137 parse_arguments(argc
, argv
);
139 /* Check for ACPI support */
140 if (!acpi_exists()) {
142 fprintf(stderr
, "No ACPI support in kernel\n");
144 fprintf(stderr
, "Unable to access ACPI info\n");
149 /* Initialize Application */
150 acpi_getinfos(&cur_acpi_infos
);
151 statecount
= acpi_get_statecount();
152 dockapp_open_window(display_name
, PACKAGE
, SIZE
, SIZE
, argc
, argv
);
153 dockapp_set_eventmask(ButtonPressMask
);
157 colors
[0].pixel
= dockapp_getcolor(light_color
);
158 colors
[1].pixel
= dockapp_blendedcolor(light_color
, -24, -24, -24, 1.0);
162 /* change raw xpm data to pixmap */
163 if (dockapp_iswindowed
)
164 backlight_on_xpm
[1] = backlight_off_xpm
[1] = WINDOWED_BG
;
166 if (!dockapp_xpm2pixmap(backlight_on_xpm
, &backdrop_on
, &mask
, colors
, ncolor
)) {
167 fprintf(stderr
, "Error initializing backlit background image.\n");
170 if (!dockapp_xpm2pixmap(backlight_off_xpm
, &backdrop_off
, NULL
, NULL
, 0)) {
171 fprintf(stderr
, "Error initializing background image.\n");
174 if (!dockapp_xpm2pixmap(parts_xpm
, &parts
, NULL
, colors
, ncolor
)) {
175 fprintf(stderr
, "Error initializing parts image.\n");
180 if (!dockapp_iswindowed
) dockapp_setshape(mask
, 0, 0);
181 if (mask
) XFreePixmap(display
, mask
);
183 /* pixmap : draw area */
184 pixmap
= dockapp_XCreatePixmap(SIZE
, SIZE
);
186 /* Initialize pixmap */
187 if (backlight
== LIGHTON
)
188 dockapp_copyarea(backdrop_on
, pixmap
, 0, 0, SIZE
, SIZE
, 0, 0);
190 dockapp_copyarea(backdrop_off
, pixmap
, 0, 0, SIZE
, SIZE
, 0, 0);
192 dockapp_set_background(pixmap
);
197 if (dockapp_nextevent_or_timeout(&event
, update_interval
* 1000)) {
199 switch (event
.type
) {
201 but_stat
= CheckMouseRegion(event
.xbutton
.x
, event
.xbutton
.y
);
203 case -1: switch_light(); break;
204 case 8: temperature_flag
= !temperature_flag
; break;
206 throttle_speed(but_stat
);
213 if( temperature_flag
&& temperature_slowdown
< 80 )
214 throttle_by_temp( acpi_read_temp( &temp
));
222 /* called by timer */
223 static void update() {
224 static light pre_backlight
;
225 static Bool in_alarm_mode
= False
;
227 acpi_getinfos(&cur_acpi_infos
);
230 if (backlight
== LIGHTON
)
231 dockapp_copyarea(backdrop_on
, pixmap
, 0, 0, 58, 58, 0, 0);
233 dockapp_copyarea(backdrop_off
, pixmap
, 0, 0, 58, 58, 0, 0);
236 draw_pcdigit(cur_acpi_infos
);
237 draw_statusdigit(cur_acpi_infos
);
238 draw_pcgraph(cur_acpi_infos
);
239 highlight_but(cur_acpi_infos
);
242 dockapp_copy2window(pixmap
);
246 /* called when mouse button pressed */
247 static void switch_light() {
251 dockapp_copyarea(backdrop_on
, pixmap
, 0, 0, 58, 58, 0, 0);
254 backlight
= LIGHTOFF
;
255 dockapp_copyarea(backdrop_off
, pixmap
, 0, 0, 58, 58, 0, 0);
260 acpi_getinfos(&cur_acpi_infos
);
261 draw_pcdigit(cur_acpi_infos
);
262 draw_statusdigit(cur_acpi_infos
);
263 draw_pcgraph(cur_acpi_infos
);
266 dockapp_copy2window(pixmap
);
269 static void draw_pcdigit(AcpiInfos infos
) {
272 int num
= infos
.throttle_perc
;
274 if (num
< 0) num
= 0;
277 v10
= (num
- v100
* 100) / 10;
278 v1
= (num
- v100
* 100 - v10
* 10);
280 if (backlight
== LIGHTON
) xd
= 50;
283 dockapp_copyarea(parts
, pixmap
, v1
* 5 + xd
, 40, 5, 9, 17, 45);
285 dockapp_copyarea(parts
, pixmap
, v10
* 5 + xd
, 40, 5, 9, 11, 45);
287 dockapp_copyarea(parts
, pixmap
, 5 + xd
, 40, 5, 9, 5, 45);
288 dockapp_copyarea(parts
, pixmap
, 0 + xd
, 40, 5, 9, 11, 45);
292 static void highlight_but(AcpiInfos infos
) {
295 int x
= infos
.throttle_state
;
300 dockapp_copyarea(parts
, pixmap
, xd
, 58, 12, 12, 5 + xd
, 3);
304 dockapp_copyarea(parts
, pixmap
, xd
, 70, 12, 12, 5 + xd
, 15);
310 static void draw_statusdigit(AcpiInfos infos
) {
314 if (backlight
== LIGHTON
) {
319 if (infos
.throttle_state
> 4)
320 dockapp_copyarea(parts
, pixmap
, 1 + xd
, 50, 6 ,6 ,34 ,46);
321 if (infos
.throttle_state
<= 4)
322 dockapp_copyarea(parts
, pixmap
, 7 + xd
, 50, 6 ,6 ,34 ,46);
323 if(infos
.throttle_state
< 3)
324 dockapp_copyarea(parts
, pixmap
, 13 + xd
, 50, 6 ,6 ,34 ,46);
328 static void draw_pcgraph(AcpiInfos infos
) {
331 int num
= infos
.throttle_perc
/ 6.25 ;
333 if (num
< 0) num
= 0;
335 if (backlight
== LIGHTON
) xd
= 102;
338 for (nb
= 0 ; nb
< num
; nb
++)
339 dockapp_copyarea(parts
, pixmap
, xd
, 0, 2, 9, 6 + nb
* 3, 33);
343 static void parse_arguments(int argc
, char **argv
) {
346 for (i
= 1; i
< argc
; i
++) {
347 if (!strcmp(argv
[i
], "--help") || !strcmp(argv
[i
], "-h")) {
348 print_help(argv
[0]), exit(0);
349 } else if (!strcmp(argv
[i
], "--version") || !strcmp(argv
[i
], "-v")) {
350 printf("%s version %s\n", PACKAGE
, VERSION
), exit(0);
351 // new code for performance
352 } else if (!strcmp(argv
[i
], "--performance") || !strcmp(argv
[i
], "-p")) {
353 performance_flag
= True
;
354 } else if (!strcmp(argv
[i
], "--display") || !strcmp(argv
[i
], "-d")) {
355 display_name
= argv
[i
+ 1];
357 } else if (!strcmp(argv
[i
], "--backlight") || !strcmp(argv
[i
], "-bl")) {
359 } else if (!strcmp(argv
[i
], "--light-color") || !strcmp(argv
[i
], "-lc")) {
360 light_color
= argv
[i
+ 1];
362 } else if (!strcmp(argv
[i
], "--interval") || !strcmp(argv
[i
], "-i")) {
364 fprintf(stderr
, "%s: error parsing argument for option %s\n",
365 argv
[0], argv
[i
]), exit(1);
366 if (sscanf(argv
[i
+ 1], "%i", &integer
) != 1)
367 fprintf(stderr
, "%s: error parsing argument for option %s\n",
368 argv
[0], argv
[i
]), exit(1);
370 fprintf(stderr
, "%s: argument %s must be >=1\n",
371 argv
[0], argv
[i
]), exit(1);
372 update_interval
= integer
;
374 } else if (!strcmp(argv
[i
], "--alarm") || !strcmp(argv
[i
], "-a")) {
376 fprintf(stderr
, "%s: error parsing argument for option %s\n",
377 argv
[0], argv
[i
]), exit(1);
378 if (sscanf(argv
[i
+ 1], "%i", &integer
) != 1)
379 fprintf(stderr
, "%s: error parsing argument for option %s\n",
380 argv
[0], argv
[i
]), exit(1);
381 if ( (integer
< 0) || (integer
> 100) )
382 fprintf(stderr
, "%s: argument %s must be >=0 and <=100\n",
383 argv
[0], argv
[i
]), exit(1);
384 alarm_level
= integer
;
386 } else if (!strcmp(argv
[i
], "--windowed") || !strcmp(argv
[i
], "-w")) {
387 dockapp_iswindowed
= True
;
388 } else if (!strcmp(argv
[i
], "--broken-wm") || !strcmp(argv
[i
], "-bw")) {
389 dockapp_isbrokenwm
= True
;
390 } else if (!strcmp(argv
[i
], "--notify") || !strcmp(argv
[i
], "-n")) {
391 notif_cmd
= argv
[i
+ 1];
393 // temperature threshold for cpu downclocking
394 } else if (!strcmp(argv
[i
], "--temperature") || !strcmp(argv
[i
], "-t")) {
396 fprintf(stderr
, "%s: error parsing argument for option %s\n",
397 argv
[0], argv
[i
]), exit(1);
398 if (sscanf(argv
[i
+ 1], "%i", &integer
) != 1)
399 fprintf(stderr
, "%s: error parsing argument for option %s\n",
400 argv
[0], argv
[i
]), exit(1);
402 fprintf(stderr
, "%s: argument %s must be >=1\n",
403 argv
[0], argv
[i
]), exit(1);
404 temperature_slowdown
= integer
;
405 temperature_flag
= True
;
407 // temperature "delta" value for state transitions
408 } else if ( !strcmp(argv
[i
], "-e")) {
410 fprintf(stderr
, "%s: error parsing argument for option %s\n",
411 argv
[0], argv
[i
]), exit(1);
412 if (sscanf(argv
[i
+ 1], "%i", &integer
) != 1)
413 fprintf(stderr
, "%s: error parsing argument for option %s\n",
414 argv
[0], argv
[i
]), exit(1);
416 fprintf(stderr
, "%s: argument %s must be >=1\n",
417 argv
[0], argv
[i
]), exit(1);
418 temperature_delta
= integer
;
419 temperature_flag
= True
;
422 fprintf(stderr
, "%s: unrecognized option '%s'\n", argv
[0], argv
[i
]);
423 print_help(argv
[0]), exit(1);
429 static void print_help(char *prog
)
431 printf("Usage : %s [OPTIONS]\n"
432 "%s - Window Maker dockapp for changing acpi throttle/performance states of cpu\n"
433 " -d, --display <string> display to use\n"
434 " -bl, --backlight turn on back-light\n"
435 " -lc, --light-color <string> back-light color(rgb:6E/C6/3B is default)\n"
436 " -i, --interval <number> number of secs between updates (1 is default)\n"
437 " -h, --help show this help text and exit\n"
438 " -v, --version show program version and exit\n"
439 " -w, --windowed run the application in windowed mode\n"
440 " -bw, --broken-wm activate broken window manager fix"
441 " (use this if you have problems running it in your window manager)\n"
442 " -p, --performance use acpi performance instead of throttling\n"
443 " -t, --temperature temperature threshold to start downclocking"
444 " (to avoid fan noise)\n"
445 " -e temperature difference that will cause a state transition"
449 * ? -f, --file : configuration file
453 static void throttle_speed(int x
) {
457 if( performance_flag
) {
458 if(fd
= fopen("/proc/acpi/processor/CPU0/performance","w")) {
462 fprintf(stderr
,"Could not change performance state\n");
464 if(fd
= fopen("/proc/acpi/processor/CPU0/throttling","w")) {
469 fprintf(stderr
,"Could not throttle machine\n");
473 static void acpi_getinfos(AcpiInfos
*infos
) {
475 #if defined(linux) || defined(solaris)
479 (acpi_read(&temp_info
))
483 fprintf(stderr
, "Cannot read ACPI information: %i\n");
490 if (access(ACPIDEV
, R_OK
))
497 static int my_system (char *cmd
) {
499 extern char **environ
;
501 if (cmd
== 0) return 1;
503 if (pid
== -1) return -1;
512 execve ("/bin/sh", argv
, environ
);
522 int acpi_read(AcpiInfos
*i
) {
530 buf
=(char *)malloc(sizeof(char)*512);
532 // new performance code
533 if( performance_flag
) {
534 if ((fd
= fopen("/proc/acpi/processor/CPU0/performance", "r"))){
537 if(ptr
= strstr(buf
,"active state:")) {
539 sscanf(ptr
,"%d",&i
->throttle_state
);
543 if ((fd
= fopen("/proc/acpi/processor/CPU0/throttling", "r"))) {
546 if(ptr
= strstr(buf
,"active state:")) {
548 sscanf(ptr
,"%d",&i
->throttle_state
);
553 sscanf(tpercent
[cur_acpi_infos
.throttle_state
],"%d",&i
->throttle_perc
);
557 // get cpu temperature
558 int acpi_read_temp(int *temp
) {
563 tmp
= (char *)malloc(sizeof(char)*2);
565 buf
=(char *)malloc(sizeof(char)*512);
567 if ((fd
= fopen("/proc/acpi/thermal_zone/THRM/temperature", "r"))){
571 if(ptr
= strstr(buf
,"temperature:")) {
574 strncpy( tmp
, ptr
, 2 );
575 sscanf(tmp
,"%d", &temp
); //&i->throttle_state);
583 // throttle according to the current temperature
584 void throttle_by_temp(int cur_temp
) {
588 // calculate new state
589 if( cur_temp
< temperature_slowdown
)
591 else if( cur_temp
== temperature_slowdown
)
593 else if( cur_temp
>= temperature_slowdown
+ ( statecount
- 2 ) * temperature_delta
)
594 state
= statecount
- 1;
596 state
= 1 + ( cur_temp
- temperature_slowdown
) / temperature_delta
;
599 throttle_speed( state
);
602 //get acpi state count (seems to work for throttling, too)
603 int acpi_get_statecount() {
609 buf
=(char *)malloc(sizeof(char)*512);
611 if( performance_flag
) {
612 if ((fd
= fopen("/proc/acpi/processor/CPU0/performance", "r"))){
615 if(ptr
= strstr(buf
,"state count:")) {
617 sscanf(ptr
,"%d",&retcode
);
621 if ((fd
= fopen("/proc/acpi/processor/CPU0/throttling", "r"))) {
624 if(ptr
= strstr(buf
,"state count:")) {
626 sscanf(ptr
,"%d",&retcode
);