wmclockmon: update change-log
[dockapps.git] / wmthrottle / src / throttle.c
blob39bbe8316e39882d2cb101e19ca90e511898449e
1 /*
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.
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
30 #include <stdbool.h>
32 #include <signal.h>
33 #include "dockapp.h"
34 #include "backlight_on.xpm"
35 #include "backlight_off.xpm"
36 #include "parts.xpm"
38 #ifdef linux
39 #include <sys/stat.h>
40 #endif
42 #define FREE(data) {if (data) free (data); data = NULL;}
44 #define SIZE 58
45 #define MAXSTRLEN 512
46 #define WINDOWED_BG ". c #AEAAAE"
47 #define MAX_HISTORY 16
48 #define CPUNUM_NONE -1
50 # ifdef linux
51 # define ACPIDEV "/proc/acpi/info"
52 # endif
54 typedef struct AcpiInfos {
55 int throttle_state;
56 int throttle_perc;
57 } AcpiInfos;
59 typedef enum { LIGHTOFF, LIGHTON } light;
62 Pixmap pixmap;
63 Pixmap backdrop_on;
64 Pixmap backdrop_off;
65 Pixmap parts;
66 Pixmap mask;
67 int but_stat = -1;
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
75 static int temp = 0;
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;
89 #ifdef linux
90 # ifndef ACPI_32_BIT_SUPPORT
91 # define ACPI_32_BIT_SUPPORT 0x0002
92 # endif
93 #endif
96 /* prototypes */
97 static void update();
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);
109 #ifdef linux
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);
114 #endif
115 int count;
118 int main(int argc, char **argv) {
121 XEvent event;
122 XpmColorSymbol colors[2] = { {"Back0", NULL, 0}, {"Back1", NULL, 0} };
123 int ncolor = 0;
124 struct sigaction sa;
125 FILE *fp;
127 sa.sa_handler = SIG_IGN;
128 #ifdef SA_NOCLDWAIT
129 sa.sa_flags = SA_NOCLDWAIT;
130 #else
131 sa.sa_flags = 0;
132 #endif
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()) {
141 #ifdef linux
142 fprintf(stderr, "No ACPI support in kernel\n");
143 #else
144 fprintf(stderr, "Unable to access ACPI info\n");
145 #endif
146 exit(1);
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);
154 init_dock();
156 if (light_color) {
157 colors[0].pixel = dockapp_getcolor(light_color);
158 colors[1].pixel = dockapp_blendedcolor(light_color, -24, -24, -24, 1.0);
159 ncolor = 2;
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");
168 exit(1);
170 if (!dockapp_xpm2pixmap(backlight_off_xpm, &backdrop_off, NULL, NULL, 0)) {
171 fprintf(stderr, "Error initializing background image.\n");
172 exit(1);
174 if (!dockapp_xpm2pixmap(parts_xpm, &parts, NULL, colors, ncolor)) {
175 fprintf(stderr, "Error initializing parts image.\n");
176 exit(1);
179 /* shape window */
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);
189 else
190 dockapp_copyarea(backdrop_off, pixmap, 0, 0, SIZE, SIZE, 0, 0);
192 dockapp_set_background(pixmap);
193 dockapp_show();
195 /* Main loop */
196 while (1) {
197 if (dockapp_nextevent_or_timeout(&event, update_interval * 1000)) {
198 /* Next Event */
199 switch (event.type) {
200 case ButtonPress:
201 but_stat = CheckMouseRegion(event.xbutton.x, event.xbutton.y);
202 switch(but_stat) {
203 case -1: switch_light(); break;
204 case 8: temperature_flag = !temperature_flag; break;
205 default:
206 throttle_speed(but_stat);
207 break;
209 default: break;
211 } else {
212 /* Time Out */
213 if( temperature_flag && temperature_slowdown < 80 )
214 throttle_by_temp( acpi_read_temp( &temp ));
215 update();
219 return 0;
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);
229 /* all clear */
230 if (backlight == LIGHTON)
231 dockapp_copyarea(backdrop_on, pixmap, 0, 0, 58, 58, 0, 0);
232 else
233 dockapp_copyarea(backdrop_off, pixmap, 0, 0, 58, 58, 0, 0);
235 /* draw digit */
236 draw_pcdigit(cur_acpi_infos);
237 draw_statusdigit(cur_acpi_infos);
238 draw_pcgraph(cur_acpi_infos);
239 highlight_but(cur_acpi_infos);
241 /* show */
242 dockapp_copy2window(pixmap);
246 /* called when mouse button pressed */
247 static void switch_light() {
248 switch (backlight) {
249 case LIGHTOFF:
250 backlight = LIGHTON;
251 dockapp_copyarea(backdrop_on, pixmap, 0, 0, 58, 58, 0, 0);
252 break;
253 case LIGHTON:
254 backlight = LIGHTOFF;
255 dockapp_copyarea(backdrop_off, pixmap, 0, 0, 58, 58, 0, 0);
256 break;
259 /* redraw digit */
260 acpi_getinfos(&cur_acpi_infos);
261 draw_pcdigit(cur_acpi_infos);
262 draw_statusdigit(cur_acpi_infos);
263 draw_pcgraph(cur_acpi_infos);
265 /* show */
266 dockapp_copy2window(pixmap);
269 static void draw_pcdigit(AcpiInfos infos) {
270 int v100, v10, v1;
271 int xd = 0;
272 int num = infos.throttle_perc;
274 if (num < 0) num = 0;
276 v100 = num / 100;
277 v10 = (num - v100 * 100) / 10;
278 v1 = (num - v100 * 100 - v10 * 10);
280 if (backlight == LIGHTON) xd = 50;
282 /* draw digit */
283 dockapp_copyarea(parts, pixmap, v1 * 5 + xd, 40, 5, 9, 17, 45);
284 if (v10 != 0)
285 dockapp_copyarea(parts, pixmap, v10 * 5 + xd, 40, 5, 9, 11, 45);
286 if (v100 == 1) {
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) {
294 int xd;
295 int x = infos.throttle_state;
298 if (x < 4) {
299 xd = x*12;
300 dockapp_copyarea(parts, pixmap, xd, 58, 12, 12, 5 + xd, 3);
302 else {
303 xd = (x-4)*12;
304 dockapp_copyarea(parts, pixmap, xd, 70, 12, 12, 5 + xd, 15);
310 static void draw_statusdigit(AcpiInfos infos) {
311 int xd = 0;
312 int y = 31;
314 if (backlight == LIGHTON) {
315 y = 40;
316 xd = 50;
318 /* draw digit */
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) {
329 int xd = 100;
330 int nb;
331 int num = infos.throttle_perc / 6.25 ;
333 if (num < 0) num = 0;
335 if (backlight == LIGHTON) xd = 102;
337 /* draw digit */
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) {
344 int i;
345 int integer;
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];
356 i++;
357 } else if (!strcmp(argv[i], "--backlight") || !strcmp(argv[i], "-bl")) {
358 backlight = LIGHTON;
359 } else if (!strcmp(argv[i], "--light-color") || !strcmp(argv[i], "-lc")) {
360 light_color = argv[i + 1];
361 i++;
362 } else if (!strcmp(argv[i], "--interval") || !strcmp(argv[i], "-i")) {
363 if (argc == i + 1)
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);
369 if (integer < 1)
370 fprintf(stderr, "%s: argument %s must be >=1\n",
371 argv[0], argv[i]), exit(1);
372 update_interval = integer;
373 i++;
374 } else if (!strcmp(argv[i], "--alarm") || !strcmp(argv[i], "-a")) {
375 if (argc == i + 1)
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;
385 i++;
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];
392 i++;
393 // temperature threshold for cpu downclocking
394 } else if (!strcmp(argv[i], "--temperature") || !strcmp(argv[i], "-t")) {
395 if (argc == i + 1)
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);
401 if (integer < 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;
406 i++;
407 // temperature "delta" value for state transitions
408 } else if ( !strcmp(argv[i], "-e")) {
409 if (argc == i + 1)
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);
415 if (integer < 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;
420 i++;
421 } else {
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"
446 " (default: 1)\n",
447 prog, prog);
448 /* OPTIONS SUPP :
449 * ? -f, --file : configuration file
453 static void throttle_speed(int x) {
455 FILE *fd;
457 if( performance_flag ) {
458 if(fd = fopen("/proc/acpi/processor/CPU0/performance","w")) {
459 fprintf(fd,"%d",x);
460 fclose(fd);
461 } else
462 fprintf(stderr,"Could not change performance state\n");
463 } else {
464 if(fd = fopen("/proc/acpi/processor/CPU0/throttling","w")) {
465 fprintf(fd,"%d",x);
466 fclose(fd);
468 else
469 fprintf(stderr,"Could not throttle machine\n");
473 static void acpi_getinfos(AcpiInfos *infos) {
474 if (
475 #if defined(linux) || defined(solaris)
476 (acpi_read(infos))
477 #else
478 # ifdef freebsd
479 (acpi_read(&temp_info))
480 # endif
481 #endif
483 fprintf(stderr, "Cannot read ACPI information: %i\n");
484 exit(1);
489 int acpi_exists() {
490 if (access(ACPIDEV, R_OK))
491 return 0;
492 else
493 return 1;
497 static int my_system (char *cmd) {
498 int pid;
499 extern char **environ;
501 if (cmd == 0) return 1;
502 pid = fork ();
503 if (pid == -1) return -1;
504 if (pid == 0) {
505 pid = fork ();
506 if (pid == 0) {
507 char *argv[4];
508 argv[0] = "sh";
509 argv[1] = "-c";
510 argv[2] = cmd;
511 argv[3] = 0;
512 execve ("/bin/sh", argv, environ);
513 exit (0);
515 exit (0);
517 return 0;
521 #ifdef linux
522 int acpi_read(AcpiInfos *i) {
523 FILE *fd;
524 int retcode = 0;
525 int capacity,remain;
526 char *buf;
527 char *ptr;
528 char stat;
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"))){
535 fread(buf,512,1,fd);
536 fclose(fd);
537 if(ptr = strstr(buf,"active state:")) {
538 ptr += 26;
539 sscanf(ptr,"%d",&i->throttle_state);
542 } else {
543 if ((fd = fopen("/proc/acpi/processor/CPU0/throttling", "r"))) {
544 fread(buf,512,1,fd);
545 fclose(fd);
546 if(ptr = strstr(buf,"active state:")) {
547 ptr += 26;
548 sscanf(ptr,"%d",&i->throttle_state);
552 free(buf);
553 sscanf(tpercent[cur_acpi_infos.throttle_state],"%d",&i->throttle_perc);
554 return retcode;
557 // get cpu temperature
558 int acpi_read_temp(int *temp) {
559 FILE *fd;
560 char *ptr;
561 char *buf;
562 char *tmp;
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"))){
568 fread(buf,512,1,fd);
569 fclose(fd);
571 if(ptr = strstr(buf,"temperature:")) {
573 ptr += 25;
574 strncpy( tmp, ptr, 2 );
575 sscanf(tmp,"%d", &temp ); //&i->throttle_state);
578 free( buf );
579 free( tmp );
580 return temp;
583 // throttle according to the current temperature
584 void throttle_by_temp(int cur_temp ) {
586 int state;
588 // calculate new state
589 if( cur_temp < temperature_slowdown )
590 state = 0;
591 else if( cur_temp == temperature_slowdown )
592 state = 1;
593 else if( cur_temp >= temperature_slowdown + ( statecount - 2 ) * temperature_delta )
594 state = statecount - 1;
595 else
596 state = 1 + ( cur_temp - temperature_slowdown ) / temperature_delta;
598 // change state
599 throttle_speed( state );
602 //get acpi state count (seems to work for throttling, too)
603 int acpi_get_statecount() {
604 FILE *fd;
605 int *retcode;
606 char *buf;
607 char *ptr;
609 buf=(char *)malloc(sizeof(char)*512);
611 if( performance_flag ) {
612 if ((fd = fopen("/proc/acpi/processor/CPU0/performance", "r"))){
613 fread(buf,512,1,fd);
614 fclose(fd);
615 if(ptr = strstr(buf,"state count:")) {
616 ptr += 25;
617 sscanf(ptr,"%d",&retcode);
620 } else {
621 if ((fd = fopen("/proc/acpi/processor/CPU0/throttling", "r"))) {
622 fread(buf,512,1,fd);
623 fclose(fd);
624 if(ptr = strstr(buf,"state count:")) {
625 ptr += 25;
626 sscanf(ptr,"%d",&retcode);
630 free(buf);
631 return retcode;
633 #endif