1 /* tpb - program to use the IBM ThinkPad(tm) special keys
2 * Copyright (C) 2002-2005 Markus Braun <markus.braun@krawel.de>
3 *
4 * This file is part of tpb.
5 *
6 * tpb is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * tpb is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with tpb; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /* includes {{{ */
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <sys/ioctl.h>
25 #include <sys/wait.h>
26 #include <locale.h>
27 #include <signal.h>
28 #include <sys/soundcard.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include "config.h"
36
37 #ifdef __FreeBSD__
38 #include <sys/file.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/sysctl.h>
42 #ifdef __i386__
43 #include <machine/apm_bios.h>
44 #endif
45 #endif
46
47 #if ENABLE_NLS
48 #include <libintl.h>
49 #endif /* ENABLE_NLS */
50
51 #ifdef HAVE_LIBXOSD
52 #include <xosd.h>
53 #endif /* HAVE_LIBXOSD */
54
55 #ifdef HAVE_LIBX11
56 #include <X11/Xlib.h>
57 #endif /* HAVE_LIBX11 */
58
59 #include "cfg.h"
60 #include "tpb.h"
61 /* }}} */
62
63 /* RCS version string for later identification using rcs ident {{{ */
64 #ifndef lint
65 static volatile const char *RCSid = "@(#) $Id: tpb.c,v 1.31 2005/07/18 14:15:59 mbr Exp $";
66 #endif /* lint */
67 /* }}} */
68
69 /* global variables {{{ */
70 extern config cfg;
71 /* }}} */
72
73 /* function prototypes {{{ */
74 #ifdef ENABLE_NLS
75 void init_i18n(void);
76 #endif /* ENABLE_NLS */
77 void daemonize(void);
78 #ifdef HAVE_LIBXOSD
79 xosd *init_xosd(void);
80 #endif /* HAVE_LIBXOSD */
81 #ifdef HAVE_LIBX11
82 Display *init_xgrabkey(void);
83 #endif /* HAVE_LIBX11 */
84 int get_nvram_state(t_thinkpad_state *thinkpad_state);
85 int get_apm_state(t_thinkpad_state *thinkpad_state);
86 #ifdef HAVE_LIBX11
87 int xgrabkey(t_thinkpad_state *thinkpad_state, Display *display);
88 #endif /* HAVE_LIBX11 */
89 int fork_app(char * cmd);
90 void set_nvram_volume_level(t_thinkpad_state *thinkpad_state);
91 unsigned int change_volume(int change);
92 int apmiser_running(void);
93 #ifdef HAVE_LIBX11
94 int *xerrorhandler(Display *display, XErrorEvent *event);
95 #endif /* HAVE_LIBX11 */
96 void sig_chld_handler(int signo);
97 /* }}} */
98
main(int argc,char ** argv)99 int main(int argc, char **argv) /* {{{ */
100 {
101 t_thinkpad_state thinkpad_state, last_thinkpad_state;
102 unsigned int vol = 0;
103 unsigned int display_state = 1;
104 char *home;
105 char *cfg_file;
106 char *msg = "";
107 char callback_cmd[CALLBACK_CMD_LENGTH];
108 struct sigaction signal_action;
109 #ifdef HAVE_LIBXOSD
110 xosd *osd_ptr = NULL;
111 #endif /* HAVE_LIBXOSD */
112 #ifdef HAVE_LIBX11
113 Display *display = NULL;
114 #endif /* HAVE_LIBX11 */
115
116 /* zero thinkpad_state */
117 memset(&thinkpad_state, 0, sizeof(thinkpad_state));
118
119 /* register my own handler for SIG_CHLD */
120 signal_action.sa_handler = sig_chld_handler;
121 signal_action.sa_flags = SA_NOCLDSTOP;
122 sigemptyset(&signal_action.sa_mask);
123 if (sigaction(SIGCHLD, &signal_action, NULL) < 0)
124 {
125 fprintf(stderr, _("Unable to register signal handler:"));
126 perror(NULL);
127 _exit(-1);
128 }
129
130 /* initialize i18n */
131 #ifdef ENABLE_NLS
132 init_i18n();
133 #endif /* ENABLE_NLS */
134
135 /* initialize config */
136 init_cfg(&cfg);
137
138 /* read global config file */
139 parse_cfg_file(&cfg, GLOBAL_CONFIG_FILE);
140
141 /* read user config file */
142 if((home = getenv("HOME")) != NULL) {
143 cfg_file = (char *)malloc(strlen(home) + strlen("/") + strlen(PRIVATE_CONFIG_FILE) + 1);
144 strcpy(cfg_file, home);
145 strcat(cfg_file, "/");
146 strcat(cfg_file, PRIVATE_CONFIG_FILE);
147 parse_cfg_file(&cfg, cfg_file);
148 free(cfg_file);
149 }
150
151 /* parse options */
152 parse_option(&cfg, argc, argv);
153
154 /* become a daemon if requested */
155 if(cfg.daemon == STATE_ON) {
156 daemonize();
157 }
158
159 /* initialize osd */
160 #ifdef HAVE_LIBXOSD
161 osd_ptr = init_xosd();
162 #endif /* HAVE_LIBXOSD */
163
164 #ifdef HAVE_LIBX11
165 /* initialize key grabbing */
166 if(cfg.xevents == STATE_ON) {
167 display = init_xgrabkey();
168 }
169 #endif /* HAVE_LIBX11 */
170
171 /* to initialize struct */
172 memset(&last_thinkpad_state, 0x00, sizeof(t_thinkpad_state));
173 if(get_nvram_state(&thinkpad_state) != 0) {
174 #ifdef __FreeBSD__
175 fprintf(stderr, _("Neither acpi_ibm(4) nor nvram(4) driver loaded. Exiting..."));
176 #endif
177 _exit(1);
178 }
179 if(cfg.apm == STATE_ON) {
180 get_apm_state(&thinkpad_state);
181 }
182 #ifdef HAVE_LIBX11
183 if(cfg.xevents == STATE_ON) {
184 xgrabkey(&thinkpad_state, display);
185 }
186 #endif /* HAVE_LIBX11 */
187
188
189 /* initialize volume */
190 if(cfg.mixer == STATE_ON) {
191 set_nvram_volume_level(&thinkpad_state);
192 vol = change_volume(0);
193 }
194 else {
195 vol = thinkpad_state.volume_level * 100 / 14;
196 }
197
198 while(0 == 0) {
199 /* sleep for polltime */
200 usleep(cfg.polltime);
201
202 /* save last state and get new one */
203 memcpy(&last_thinkpad_state, &thinkpad_state, sizeof(t_thinkpad_state));
204
205 if(get_nvram_state(&thinkpad_state) != 0) {
206 _exit(1);
207 }
208 if(cfg.apm == STATE_ON) {
209 get_apm_state(&thinkpad_state);
210 }
211 #ifdef HAVE_LIBX11
212 if(cfg.xevents == STATE_ON) {
213 xgrabkey(&thinkpad_state, display);
214 }
215 #endif /* HAVE_LIBX11 */
216
217 /* check if anything changed */
218 if(memcmp(&last_thinkpad_state, &thinkpad_state, sizeof(t_thinkpad_state)) == 0) {
219 continue;
220 }
221
222 /* determine the state of the Thinkpad button {{{ */
223 if(thinkpad_state.thinkpad_toggle != last_thinkpad_state.thinkpad_toggle &&
224 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
225 if(cfg.verbose == STATE_ON) {
226 puts(_("ThinkPad button pressed"));
227 }
228 if(cfg.tpb_cmd != NULL) {
229 if(fork_app(cfg.tpb_cmd) != 0) {
230 _exit(0);
231 }
232 }
233 if(cfg.callback != NULL) {
234 snprintf(callback_cmd, sizeof(callback_cmd), "%s thinkpad pressed", cfg.callback);
235 if(fork_app(callback_cmd) != 0) {
236 _exit(0);
237 }
238 }
239 } /* }}} */
240
241 /* determine the state of zoom {{{ */
242 if(thinkpad_state.zoom_toggle != last_thinkpad_state.zoom_toggle) {
243 if(cfg.verbose == STATE_ON) {
244 printf(_("Zoom is %s\n"), thinkpad_state.zoom_toggle == 1 ? _("on") : _("off"));
245 }
246 if(cfg.callback != NULL) {
247 snprintf(callback_cmd, sizeof(callback_cmd), "%s zoom %s", cfg.callback, thinkpad_state.zoom_toggle == 1 ? "on" : "off");
248 if(fork_app(callback_cmd) != 0) {
249 _exit(0);
250 }
251 }
252 #ifdef HAVE_LIBXOSD
253 if(osd_ptr != NULL &&
254 ((cfg.osd == STATE_OFF && cfg.osdzoom == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdzoom != STATE_OFF))) {
255 xosd_display(osd_ptr, 0, XOSD_string, thinkpad_state.zoom_toggle == 1 ? _("Zoom on") : _("Zoom off"));
256 xosd_display(osd_ptr, 1, XOSD_string, "");
257 }
258 #endif /* HAVE_LIBXOSD */
259 } /* }}} */
260
261 /* determine the state of the home button {{{ */
262 if(thinkpad_state.home_toggle != last_thinkpad_state.home_toggle &&
263 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
264 if(cfg.verbose == STATE_ON) {
265 puts(_("Home button pressed"));
266 }
267 if(cfg.home_cmd != NULL) {
268 if(fork_app(cfg.home_cmd) != 0) {
269 _exit(0);
270 }
271 }
272 if(cfg.callback != NULL) {
273 snprintf(callback_cmd, sizeof(callback_cmd), "%s home pressed", cfg.callback);
274 if(fork_app(callback_cmd) != 0) {
275 _exit(0);
276 }
277 }
278 } /* }}} */
279
280 /* determine the state of the search button {{{ */
281 if(thinkpad_state.search_toggle != last_thinkpad_state.search_toggle &&
282 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
283 if(cfg.verbose == STATE_ON) {
284 puts(_("Search button pressed"));
285 }
286 if(cfg.search_cmd != NULL) {
287 if(fork_app(cfg.search_cmd) != 0) {
288 _exit(0);
289 }
290 }
291 if(cfg.callback != NULL) {
292 snprintf(callback_cmd, sizeof(callback_cmd), "%s search pressed", cfg.callback);
293 if(fork_app(callback_cmd) != 0) {
294 _exit(0);
295 }
296 }
297 } /* }}} */
298
299 /* determine the state of the mail button {{{ */
300 if(thinkpad_state.mail_toggle != last_thinkpad_state.mail_toggle &&
301 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
302 if(cfg.verbose == STATE_ON) {
303 puts(_("Mail button pressed"));
304 }
305 if(cfg.mail_cmd != NULL) {
306 if(fork_app(cfg.mail_cmd) != 0) {
307 _exit(0);
308 }
309 }
310 if(cfg.callback != NULL) {
311 snprintf(callback_cmd, sizeof(callback_cmd), "%s mail pressed", cfg.callback);
312 if(fork_app(callback_cmd) != 0) {
313 _exit(0);
314 }
315 }
316 } /* }}} */
317
318 /* determine the state of the wireless button {{{ */
319 if(thinkpad_state.wireless_toggle != last_thinkpad_state.wireless_toggle &&
320 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
321 if(cfg.verbose == STATE_ON) {
322 puts(_("Wireless button pressed"));
323 }
324 if(cfg.wireless_cmd != NULL) {
325 if(fork_app(cfg.wireless_cmd) != 0) {
326 _exit(0);
327 }
328 }
329 if(cfg.callback != NULL) {
330 snprintf(callback_cmd, sizeof(callback_cmd), "%s wireless pressed", cfg.callback);
331 if(fork_app(callback_cmd) != 0) {
332 _exit(0);
333 }
334 }
335 } /* }}} */
336
337 /* determine the state of the favorites button {{{ */
338 if(thinkpad_state.favorites_toggle != last_thinkpad_state.favorites_toggle) {
339 if(cfg.verbose == STATE_ON) {
340 puts(_("Favorites button pressed"));
341 }
342 if(cfg.favorites_cmd != NULL) {
343 if(fork_app(cfg.favorites_cmd) != 0) {
344 _exit(0);
345 }
346 }
347 if(cfg.callback != NULL) {
348 snprintf(callback_cmd, sizeof(callback_cmd), "%s favorites pressed", cfg.callback);
349 if(fork_app(callback_cmd) != 0) {
350 _exit(0);
351 }
352 }
353 } /* }}} */
354
355 /* determine the state of the reload button {{{ */
356 if(thinkpad_state.reload_toggle != last_thinkpad_state.reload_toggle) {
357 if(cfg.verbose == STATE_ON) {
358 puts(_("Reload button pressed"));
359 }
360 if(cfg.reload_cmd != NULL) {
361 if(fork_app(cfg.reload_cmd) != 0) {
362 _exit(0);
363 }
364 }
365 if(cfg.callback != NULL) {
366 snprintf(callback_cmd, sizeof(callback_cmd), "%s reload pressed", cfg.callback);
367 if(fork_app(callback_cmd) != 0) {
368 _exit(0);
369 }
370 }
371 } /* }}} */
372
373 /* determine the state of the abort button {{{ */
374 if(thinkpad_state.abort_toggle != last_thinkpad_state.abort_toggle) {
375 if(cfg.verbose == STATE_ON) {
376 puts(_("Abort button pressed"));
377 }
378 if(cfg.abort_cmd != NULL) {
379 if(fork_app(cfg.abort_cmd) != 0) {
380 _exit(0);
381 }
382 }
383 if(cfg.callback != NULL) {
384 snprintf(callback_cmd, sizeof(callback_cmd), "%s abort pressed", cfg.callback);
385 if(fork_app(callback_cmd) != 0) {
386 _exit(0);
387 }
388 }
389 } /* }}} */
390
391 /* determine the state of the backward button {{{ */
392 if(thinkpad_state.backward_toggle != last_thinkpad_state.backward_toggle) {
393 if(cfg.verbose == STATE_ON) {
394 puts(_("Backward button pressed"));
395 }
396 if(cfg.backward_cmd != NULL) {
397 if(fork_app(cfg.backward_cmd) != 0) {
398 _exit(0);
399 }
400 }
401 if(cfg.callback != NULL) {
402 snprintf(callback_cmd, sizeof(callback_cmd), "%s backward pressed", cfg.callback);
403 if(fork_app(callback_cmd) != 0) {
404 _exit(0);
405 }
406 }
407 } /* }}} */
408
409 /* determine the state of the forward button {{{ */
410 if(thinkpad_state.forward_toggle != last_thinkpad_state.forward_toggle) {
411 if(cfg.verbose == STATE_ON) {
412 puts(_("Forward button pressed"));
413 }
414 if(cfg.forward_cmd != NULL) {
415 if(fork_app(cfg.forward_cmd) != 0) {
416 _exit(0);
417 }
418 }
419 if(cfg.callback != NULL) {
420 snprintf(callback_cmd, sizeof(callback_cmd), "%s forward pressed", cfg.callback);
421 if(fork_app(callback_cmd) != 0) {
422 _exit(0);
423 }
424 }
425 } /* }}} */
426
427 /* determine the state of the fn button {{{ */
428 if(thinkpad_state.fn_toggle != last_thinkpad_state.fn_toggle) {
429 if(cfg.verbose == STATE_ON) {
430 puts(_("Fn button pressed"));
431 }
432 if(cfg.fn_cmd != NULL) {
433 if(fork_app(cfg.fn_cmd) != 0) {
434 _exit(0);
435 }
436 }
437 if(cfg.callback != NULL) {
438 snprintf(callback_cmd, sizeof(callback_cmd), "%s fn pressed", cfg.callback);
439 if(fork_app(callback_cmd) != 0) {
440 _exit(0);
441 }
442 }
443 } /* }}} */
444
445 /* determine the state of ThinkLight {{{ */
446 if(thinkpad_state.thinklight_toggle != last_thinkpad_state.thinklight_toggle) {
447 if(cfg.verbose == STATE_ON) {
448 printf(_("ThinkLight is %s\n"), thinkpad_state.thinklight_toggle == 1 ? _("on") : _("off"));
449 }
450 if(cfg.callback != NULL) {
451 snprintf(callback_cmd, sizeof(callback_cmd), "%s thinklight %s", cfg.callback, thinkpad_state.thinklight_toggle == 1 ? "on" : "off");
452 if(fork_app(callback_cmd) != 0) {
453 _exit(0);
454 }
455 }
456 #ifdef HAVE_LIBXOSD
457 if(osd_ptr != NULL &&
458 ((cfg.osd == STATE_OFF && cfg.osdthinklight == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdthinklight != STATE_OFF))) {
459 xosd_display(osd_ptr, 0, XOSD_string, thinkpad_state.thinklight_toggle == 1 ? _("ThinkLight on") : _("ThinkLight off"));
460 xosd_display(osd_ptr, 1, XOSD_string, "");
461 }
462 #endif /* HAVE_LIBXOSD */
463 } /* }}} */
464
465 /* determine the state of Bluetooth {{{ */
466 if(thinkpad_state.bluetooth_toggle != last_thinkpad_state.bluetooth_toggle) {
467 if(cfg.verbose == STATE_ON) {
468 printf(_("Bluetooth is %s\n"), thinkpad_state.bluetooth_toggle == 1 ? _("on") : _("off"));
469 }
470 if(cfg.callback != NULL) {
471 snprintf(callback_cmd, sizeof(callback_cmd), "%s bluetooth %s", cfg.callback, thinkpad_state.bluetooth_toggle == 1 ? "on" : "off");
472 if(fork_app(callback_cmd) != 0) {
473 _exit(0);
474 }
475 }
476 #ifdef HAVE_LIBXOSD
477 if(osd_ptr != NULL) {
478 xosd_display(osd_ptr, 0, XOSD_string, thinkpad_state.bluetooth_toggle == 1 ? _("Bluetooth enabled") : _("Bluetooth disabled"));
479 xosd_display(osd_ptr, 1, XOSD_string, "");
480 }
481 #endif /* HAVE_LIBXOSD */
482 } /* }}} */
483
484 /* determine the state of display {{{ */
485 if((thinkpad_state.display_toggle != last_thinkpad_state.display_toggle ||
486 thinkpad_state.display_state != last_thinkpad_state.display_state) &&
487 thinkpad_state.hibernate_toggle == last_thinkpad_state.hibernate_toggle) {
488
489 /* Some thinkpads have no hardware support to switch lcd/crt. They also
490 * don't reflect the current state in thinkpad_state.display_state. So, if
491 * thinkpad_state.display_toggle changes, but thinkpad_state.display_state does
492 * not change, simulate the display state.
493 */
494 if(thinkpad_state.display_toggle != last_thinkpad_state.display_toggle &&
495 thinkpad_state.display_state == last_thinkpad_state.display_state) {
496 display_state = display_state % 3 + 1;
497 }
498 else {
499 display_state = thinkpad_state.display_state;
500 }
501
502 switch (display_state & 0x03) {
503 case 0x1:
504 msg = _("LCD on, CRT off");
505 snprintf(callback_cmd, sizeof(callback_cmd), "%s display lcd", cfg.callback);
506 break;
507
508 case 0x2:
509 msg = _("LCD off, CRT on");
510 snprintf(callback_cmd, sizeof(callback_cmd), "%s display crt", cfg.callback);
511 break;
512
513 case 0x3:
514 msg = _("LCD on, CRT on");
515 snprintf(callback_cmd, sizeof(callback_cmd), "%s display both", cfg.callback);
516 break;
517 }
518 if(cfg.verbose == STATE_ON) {
519 printf(_("Display changed: %s\n"), msg);
520 }
521 if(cfg.callback != NULL) {
522 if(fork_app(callback_cmd) != 0) {
523 _exit(0);
524 }
525 }
526 #ifdef HAVE_LIBXOSD
527 if(osd_ptr != NULL &&
528 ((cfg.osd == STATE_OFF && cfg.osddisplay == STATE_ON) || (cfg.osd == STATE_ON && cfg.osddisplay != STATE_OFF))) {
529 xosd_display(osd_ptr, 0, XOSD_string, _("Display"));
530 xosd_display(osd_ptr, 1, XOSD_string, msg);
531 }
532 #endif /* HAVE_LIBXOSD */
533 } /* }}} */
534
535 /* determine the state of hv expansion {{{ */
536 if(thinkpad_state.expand_toggle != last_thinkpad_state.expand_toggle) {
537 if(cfg.verbose == STATE_ON) {
538 printf(_("HV Expansion is %s\n"), (thinkpad_state.expand_toggle & 0x01) == 1 ? _("on") : _("off"));
539 }
540 if(cfg.callback != NULL) {
541 snprintf(callback_cmd, sizeof(callback_cmd), "%s expand %s", cfg.callback, thinkpad_state.expand_toggle == 1 ? "on" : "off");
542 if(fork_app(callback_cmd) != 0) {
543 _exit(0);
544 }
545 }
546 #ifdef HAVE_LIBXOSD
547 if(osd_ptr != NULL &&
548 ((cfg.osd == STATE_OFF && cfg.osdhvexpansion == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdhvexpansion != STATE_OFF))) {
549 xosd_display(osd_ptr, 0, XOSD_string, thinkpad_state.expand_toggle == 1 ? _("HV Expansion on") : _("HV Expansion off"));
550 xosd_display(osd_ptr, 1, XOSD_string, "");
551 }
552 #endif /* HAVE_LIBXOSD */
553 } /* }}} */
554
555 /* determine the state of the brightness buttons {{{ */
556 if(thinkpad_state.brightness_level != last_thinkpad_state.brightness_level) {
557 if(cfg.verbose == STATE_ON) {
558 printf(_("Brightness changed: Level %d\n"), thinkpad_state.brightness_level * 100 / 7);
559 }
560 if(cfg.callback != NULL) {
561 snprintf(callback_cmd, sizeof(callback_cmd), "%s brightness %d", cfg.callback, thinkpad_state.brightness_level * 100 / 7);
562 if(fork_app(callback_cmd) != 0) {
563 _exit(0);
564 }
565 }
566 }
567 #ifdef HAVE_LIBXOSD
568 if(thinkpad_state.brightness_toggle != last_thinkpad_state.brightness_toggle) {
569 if(osd_ptr != NULL &&
570 ((cfg.osd == STATE_OFF && cfg.osdbrightness == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdbrightness != STATE_OFF))) {
571 xosd_display(osd_ptr, 0, XOSD_string, _("Brightness"));
572 xosd_display(osd_ptr, 1, XOSD_percentage, thinkpad_state.brightness_level * 100 / 7);
573 }
574 }
575 #endif /* HAVE_LIBXOSD */ /* }}} */
576
577 /* determine the state of the volume buttons {{{ */
578 if(thinkpad_state.volume_level != last_thinkpad_state.volume_level) {
579 if(cfg.mixer == STATE_ON) {
580 /* if we use the DEFAULT_MIXERSTEPS, we don't need to modify the nvram */
581 if(cfg.mixersteps == DEFAULT_MIXERSTEPS) {
582 vol = change_volume((1. * MAX_VOLUME / cfg.mixersteps * thinkpad_state.volume_level) - vol);
583 }
584 else
585 {
586 vol = change_volume(1. * MAX_VOLUME / cfg.mixersteps * ((int)thinkpad_state.volume_level - (int)last_thinkpad_state.volume_level)); /* thinkpad_state.volume_level-last_thinkpad_state.volume_level gives the direction */
587 set_nvram_volume_level(&thinkpad_state);
588 }
589 }
590 else {
591 vol = thinkpad_state.volume_level * 100 / 14;
592 }
593 if(cfg.verbose == STATE_ON) {
594 printf(_("Volume changed: Level %d\n"), vol);
595 }
596 if(cfg.callback != NULL) {
597 snprintf(callback_cmd, sizeof(callback_cmd), "%s volume %d", cfg.callback, vol);
598 if(fork_app(callback_cmd) != 0) {
599 _exit(0);
600 }
601 }
602 }
603 #ifdef HAVE_LIBXOSD
604 /* show volume every time a volume button is pressed and not mute */
605 if(thinkpad_state.volume_toggle != last_thinkpad_state.volume_toggle &&
606 thinkpad_state.mute_toggle == 0) {
607 if(osd_ptr != NULL &&
608 ((cfg.osd == STATE_OFF && cfg.osdvolume == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdvolume != STATE_OFF))) {
609 xosd_display(osd_ptr, 0, XOSD_string, _("Volume"));
610 xosd_display(osd_ptr, 1, XOSD_percentage, vol);
611 }
612 }
613 #endif /* HAVE_LIBXOSD */ /* }}} */
614
615 /* determine the state of the mute button {{{ */
616 if(thinkpad_state.mute_toggle != last_thinkpad_state.mute_toggle) {
617 if(cfg.verbose == STATE_ON) {
618 printf("%s\n", thinkpad_state.mute_toggle == 1 ? _("Mute on") : _("Mute off"));
619 }
620 if(cfg.callback != NULL) {
621 snprintf(callback_cmd, sizeof(callback_cmd), "%s mute %s", cfg.callback, thinkpad_state.mute_toggle == 1 ? "on" : "off");
622 if(fork_app(callback_cmd) != 0) {
623 _exit(0);
624 }
625 }
626 }
627 #ifdef HAVE_LIBXOSD
628 if(thinkpad_state.mute_toggle != last_thinkpad_state.mute_toggle ||
629 (thinkpad_state.volume_toggle != last_thinkpad_state.volume_toggle && last_thinkpad_state.mute_toggle == 1)) {
630 if(cfg.mixer == STATE_ON) {
631 if(thinkpad_state.mute_toggle == 1) {
632 change_volume(-vol); /* mute */
633 }
634 else {
635 change_volume(vol); /* unmute */
636 }
637 }
638
639 if(osd_ptr != NULL &&
640 ((cfg.osd == STATE_OFF && cfg.osdvolume == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdvolume != STATE_OFF))) {
641 if(thinkpad_state.mute_toggle == 1) {
642 xosd_display(osd_ptr, 0, XOSD_string, _("Mute on"));
643 xosd_display(osd_ptr, 1, XOSD_percentage, 0);
644 }
645 else {
646 xosd_display(osd_ptr, 0, XOSD_string, _("Mute off"));
647 xosd_display(osd_ptr, 1, XOSD_percentage, vol);
648 }
649 }
650 }
651 #endif /* HAVE_LIBXOSD */ /* }}} */
652
653 /* determine the state of power {{{ */
654 if(thinkpad_state.ac_state != last_thinkpad_state.ac_state) {
655 if(cfg.verbose == STATE_ON) {
656 printf(_("Power line changed: %s\n"), thinkpad_state.ac_state == 1 ? _("AC connected") : _("AC disconnected"));
657 }
658 if(cfg.callback != NULL) {
659 snprintf(callback_cmd, sizeof(callback_cmd), "%s ac_power %s", cfg.callback, thinkpad_state.ac_state == 1 ? "connected" : "disconnected");
660 if(fork_app(callback_cmd) != 0) {
661 _exit(0);
662 }
663 }
664 #ifdef HAVE_LIBXOSD
665 if(osd_ptr != NULL &&
666 ((cfg.osd == STATE_OFF && cfg.osdpowermgt == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdpowermgt != STATE_OFF))) {
667 xosd_display(osd_ptr, 0, XOSD_string, thinkpad_state.ac_state == 1 ? _("AC connected") : _("AC disconnected"));
668 xosd_display(osd_ptr, 1, XOSD_string, "");
669 }
670 #endif /* HAVE_LIBXOSD */
671 } /* }}} */
672
673 /* determine power management mode AC {{{ */
674 if(thinkpad_state.powermgt_ac != last_thinkpad_state.powermgt_ac) {
675 switch(thinkpad_state.powermgt_ac) {
676 case 0x4:
677 msg = _("PM AC high");
678 snprintf(callback_cmd, sizeof(callback_cmd), "%s powermgt_ac high", cfg.callback);
679 break;
680
681 case 0x2:
682 msg = _("PM AC auto");
683 snprintf(callback_cmd, sizeof(callback_cmd), "%s powermgt_ac auto", cfg.callback);
684 break;
685
686 case 0x1:
687 msg = _("PM AC manual");
688 snprintf(callback_cmd, sizeof(callback_cmd), "%s powermgt_ac manual", cfg.callback);
689 break;
690
691 default:
692 msg = _("PM AC unknown");
693 break;
694 }
695 if(cfg.verbose == STATE_ON) {
696 printf(_("Power management mode AC changed: %s\n"), msg);
697 }
698 if(cfg.callback != NULL) {
699 if(fork_app(callback_cmd) != 0) {
700 _exit(0);
701 }
702 }
703 #ifdef HAVE_LIBXOSD
704 if(osd_ptr != NULL &&
705 ((cfg.osd == STATE_OFF && cfg.osdpowermgt == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdpowermgt != STATE_OFF))) {
706 if (cfg.powermgt == STATE_ON || (cfg.powermgt == STATE_AUTO && apmiser_running() == 0)) {
707 xosd_display(osd_ptr, 0, XOSD_string, msg);
708 xosd_display(osd_ptr, 1, XOSD_string, "");
709 }
710 }
711 #endif /* HAVE_LIBXOSD */
712 } /* }}} */
713
714 /* determine power management mode battery {{{ */
715 if(thinkpad_state.powermgt_battery != last_thinkpad_state.powermgt_battery) {
716 switch(thinkpad_state.powermgt_battery) {
717 case 0x4:
718 msg = _("PM battery high");
719 snprintf(callback_cmd, sizeof(callback_cmd), "%s powermgt_battery high", cfg.callback);
720 break;
721
722 case 0x2:
723 msg = _("PM battery auto");
724 snprintf(callback_cmd, sizeof(callback_cmd), "%s powermgt_battery auto", cfg.callback);
725 break;
726
727 case 0x1:
728 msg = _("PM battery manual");
729 snprintf(callback_cmd, sizeof(callback_cmd), "%s powermgt_battery manual", cfg.callback);
730 break;
731
732 default:
733 msg = _("PM battery unknown");
734 break;
735 }
736 if(cfg.verbose == STATE_ON) {
737 printf(_("Power management mode battery changed: %s\n"), msg);
738 }
739 if(cfg.callback != NULL) {
740 if(fork_app(callback_cmd) != 0) {
741 _exit(0);
742 }
743 }
744 #ifdef HAVE_LIBXOSD
745 if(osd_ptr != NULL &&
746 ((cfg.osd == STATE_OFF && cfg.osdpowermgt == STATE_ON) || (cfg.osd == STATE_ON && cfg.osdpowermgt != STATE_OFF))) {
747 if (cfg.powermgt == STATE_ON || (cfg.powermgt == STATE_AUTO && apmiser_running() == 0)) {
748 xosd_display(osd_ptr, 0, XOSD_string, msg);
749 xosd_display(osd_ptr, 1, XOSD_string, "");
750 }
751 }
752 #endif /* HAVE_LIBXOSD */
753 } /* }}} */
754 }
755
756 _exit(0); /* never reached */
757 } /* }}} */
758
759 #ifdef ENABLE_NLS
760 /* initialize the i18n system */
init_i18n(void)761 void init_i18n(void) /* {{{ */
762 {
763 setlocale (LC_ALL, "");
764 bindtextdomain (PACKAGE, LOCALEDIR);
765 textdomain (PACKAGE);
766
767 return;
768 } /* }}} */
769 #endif /* ENABLE_NLS */
770
771 /* when called, the application becomes a daemon */
daemonize(void)772 void daemonize(void) /* {{{ */
773 {
774 int fdsc, fdsc_max;
775
776 /* code inspired by comp.unix.programmer FAQ */
777 switch (fork())
778 {
779 case 0:
780 break;
781 case -1:
782 fprintf(stderr, _("Unable to fork daemon:"));
783 perror(NULL);
784 _exit(-1);
785 default:
786 _exit(0); /* exit the original process */
787 }
788
789 /* create a new session */
790 if (setsid() < 0) {
791 _exit(-1);
792 }
793
794 switch (fork())
795 {
796 case 0:
797 break;
798 case -1:
799 fprintf(stderr, _("Unable to fork daemon:"));
800 perror(NULL);
801 _exit(-1);
802 default:
803 _exit(0);
804 }
805
806 if(cfg.verbose == STATE_ON) {
807 puts(_("Daemon started."));
808 }
809
810 /* change to root directory */
811 chdir("/");
812
813 /* close all file descriptors */
814 for (fdsc = 0, fdsc_max = sysconf(_SC_OPEN_MAX); fdsc < fdsc_max; fdsc++) {
815 close(fdsc);
816 }
817
818 /* open /dev/null as file descriptor 0 */
819 open("/dev/null",O_RDWR);
820
821 /* duplicate file descriptor 0 to file descriptor 1 (next free) */
822 dup(0);
823
824 /* duplicate file descriptor 0 to file descriptor 2 (next free) */
825 dup(0);
826
827 return;
828 } /* }}} */
829
830 #ifdef HAVE_LIBXOSD
831 /* initialize the on-screen-display */
init_xosd(void)832 xosd *init_xosd(void) /* {{{ */
833 {
834 xosd *osd_ptr = NULL;
835
836 #if HAVE_LIBXOSD_VERSION >= 20000
837 osd_ptr = xosd_create(DEFAULT_OSDLINES);
838 #elif HAVE_LIBXOSD_VERSION >= 10000
839 osd_ptr = xosd_init(DEFAULT_OSDFONT, DEFAULT_OSDCOLOR, DEFAULT_OSDTIMEOUT,
840 DEFAULT_OSDPOS, DEFAULT_OSDVERTICAL, DEFAULT_OSDSHADOW, DEFAULT_OSDLINES);
841 #elif HAVE_LIBXOSD_VERSION >= 700
842 osd_ptr = xosd_init(DEFAULT_OSDFONT, DEFAULT_OSDCOLOR, DEFAULT_OSDTIMEOUT,
843 DEFAULT_OSDPOS, DEFAULT_OSDVERTICAL, DEFAULT_OSDSHADOW);
844 #endif /* HAVE_LIBXOSD_VERSION */
845
846 if(osd_ptr == NULL) {
847 fprintf(stderr, _("Unable to initialize xosd. Running without onsceen display.\n"));
848 }
849 else {
850 /* initialize font */
851 if(xosd_set_font(osd_ptr, cfg.osdfont) != 0) {
852 if(xosd_set_font(osd_ptr, DEFAULT_OSDFONT) != 0) {
853 /* even the default font is not available */
854 fprintf(stderr, _("Invalid xosd font \"%s\". Even the default font \"%s\" "),
855 cfg.osdfont, DEFAULT_OSDFONT);
856 fprintf(stderr, _("is not available. Please add an existing font to your %s file.\n"),
857 GLOBAL_CONFIG_FILE);
858 fprintf(stderr, _("Running without onsceen display.\n"));
859
860 #if HAVE_LIBXOSD_VERSION >= 20000
861 /* Disabled because of a bug in xosd lib
862 * xosd_destroy(osd_ptr);
863 */
864 #else /* HAVE_LIBXOSD_VERSION */
865 /* Disabled because of a bug in xosd lib
866 * xosd_uninit(osd_ptr);
867 */
868 #endif /* HAVE_LIBXOSD_VERSION */
869
870 return NULL;
871 }
872 else {
873 /* we run with the default font */
874 fprintf(stderr, _("Invalid xosd font \"%s\". Running with the default \"%s\".\n"),
875 cfg.osdfont, DEFAULT_OSDFONT);
876 }
877 }
878
879 /* initialize color */
880 if(xosd_set_colour(osd_ptr, cfg.osdcolor) != 0) {
881 fprintf(stderr, _("Invalid xosd color \"%s\". Running with the default \"%s\".\n"),
882 cfg.osdcolor, DEFAULT_OSDCOLOR);
883 xosd_set_colour(osd_ptr, DEFAULT_OSDCOLOR);
884 }
885
886 /* initialize timeout */
887 if(xosd_set_timeout(osd_ptr, cfg.osdtimeout) != 0) {
888 fprintf(stderr, _("Invalid xosd timeout \"%d\". Running with the default \"%d\".\n"),
889 cfg.osdtimeout, DEFAULT_OSDTIMEOUT);
890 xosd_set_timeout(osd_ptr, DEFAULT_OSDTIMEOUT);
891 }
892
893 /* initialize position */
894 if(xosd_set_pos(osd_ptr, cfg.osdpos) != 0) {
895 fprintf(stderr, _("Invalid xosd position \"%d\". Running with the default \"%d\".\n"),
896 cfg.osdpos, DEFAULT_OSDPOS);
897 xosd_set_pos(osd_ptr, DEFAULT_OSDPOS);
898 }
899
900 #if HAVE_LIBXOSD_VERSION >= 20000
901 /* initialize horizontal offset */
902 if(xosd_set_horizontal_offset(osd_ptr, cfg.osdhorizontal) != 0) {
903 fprintf(stderr, _("Invalid horizontal xosd offset \"%d\". Running with the default \"%d\".\n"),
904 cfg.osdhorizontal, DEFAULT_OSDHORIZONTAL);
905 xosd_set_horizontal_offset(osd_ptr, DEFAULT_OSDHORIZONTAL);
906 }
907
908 /* initialize vertical offset */
909 if(xosd_set_vertical_offset(osd_ptr, cfg.osdvertical) != 0) {
910 fprintf(stderr, _("Invalid vertical xosd offset \"%d\". Running with the default \"%d\".\n"),
911 cfg.osdvertical, DEFAULT_OSDVERTICAL);
912 xosd_set_vertical_offset(osd_ptr, DEFAULT_OSDVERTICAL);
913 }
914 #else /* HAVE_LIBXOSD_VERSION */
915 /* initialize vertical offset */
916 if(xosd_set_offset(osd_ptr, cfg.osdvertical) != 0) {
917 fprintf(stderr, _("Invalid vertical xosd offset \"%d\". Running with the default \"%d\".\n"),
918 cfg.osdvertical, DEFAULT_OSDVERTICAL);
919 xosd_set_offset(osd_ptr, DEFAULT_OSDVERTICAL);
920 }
921 #endif /* HAVE_LIBXOSD_VERSION */
922
923 /* initialize shadow offset */
924 if(xosd_set_shadow_offset(osd_ptr, cfg.osdshadow) != 0) {
925 fprintf(stderr, _("Invalid shadow xosd offset \"%d\". Running with the default \"%d\".\n"),
926 cfg.osdshadow, DEFAULT_OSDSHADOW);
927 xosd_set_shadow_offset(osd_ptr, DEFAULT_OSDSHADOW);
928 }
929
930 #if HAVE_LIBXOSD_VERSION >= 20200
931 /* initialize shadow color */
932 if(xosd_set_shadow_colour(osd_ptr, cfg.osdshadowcolor) != 0) {
933 fprintf(stderr, _("Invalid xosd shadow color \"%s\". Running with the default \"%s\".\n"),
934 cfg.osdshadowcolor, DEFAULT_OSDSHADOWCOLOR);
935 xosd_set_shadow_colour(osd_ptr, DEFAULT_OSDSHADOWCOLOR);
936 }
937
938 /* initialize outline offset */
939 if(xosd_set_outline_offset(osd_ptr, cfg.osdoutline) != 0) {
940 fprintf(stderr, _("Invalid outline xosd width \"%d\". Running with the default \"%d\".\n"),
941 cfg.osdoutline, DEFAULT_OSDOUTLINE);
942 xosd_set_outline_offset(osd_ptr, DEFAULT_OSDOUTLINE);
943 }
944
945 /* initialize outline color */
946 if(xosd_set_outline_colour(osd_ptr, cfg.osdoutlinecolor) != 0) {
947 fprintf(stderr, _("Invalid xosd outline color \"%s\". Running with the default \"%s\".\n"),
948 cfg.osdoutlinecolor, DEFAULT_OSDOUTLINECOLOR);
949 xosd_set_outline_colour(osd_ptr, DEFAULT_OSDOUTLINECOLOR);
950 }
951 #endif /* HAVE_LIBXOSD_VERSION */
952
953 #if HAVE_LIBXOSD_VERSION >= 10000
954 /* initialize alignment */
955 if(xosd_set_align(osd_ptr, cfg.osdalign) != 0) {
956 fprintf(stderr, _("Invalid xosd alignment \"%d\". Running with default \"%d\".\n"),
957 cfg.osdalign, DEFAULT_OSDALIGN);
958 xosd_set_align(osd_ptr, DEFAULT_OSDALIGN);
959 }
960 #endif /* HAVE_LIBXOSD_VERSION */
961 }
962
963 return osd_ptr;
964 } /* }}} */
965 #endif /* HAVE_LIBXOSD */
966
967 #ifdef HAVE_LIBX11
968 /* initialize X for grabbing key events */
init_xgrabkey(void)969 Display *init_xgrabkey(void) /* {{{ */
970 {
971 char *display_name = NULL;
972 Display *display;
973
974 /* set up our own error handler */
975 XSetErrorHandler ((XErrorHandler) xerrorhandler);
976
977 /* get the name of the current X display */
978 display_name = XDisplayName(NULL);
979 if(display_name == NULL) {
980 fprintf(stderr, _("Can't get display name, X may not be running!\n"));
981 _exit(1);
982 }
983
984 /* open the X display */
985 display = XOpenDisplay(display_name);
986 if(!display) {
987 fprintf(stderr, _("Can't open display, X may not be running!\n"));
988 _exit(1);
989 }
990
991 /* allow X events */
992 XAllowEvents(display, AsyncKeyboard, CurrentTime);
993
994 /* select the input */
995 XSelectInput (display, DefaultRootWindow(display), KeyPressMask);
996
997 /* set up the keys to grab */
998 if ((cfg.home_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_HOME , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
999 if ((cfg.search_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_SEARCH , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
1000 if ((cfg.mail_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_MAIL , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
1001 if ((cfg.favorites_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_FAVORITES , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
1002 if ((cfg.reload_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_RELOAD , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
1003 if ((cfg.abort_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_ABORT , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
1004 if ((cfg.backward_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_BACKWARD , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
1005 if ((cfg.forward_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_FORWARD , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
1006 if ((cfg.fn_cmd != NULL) || (cfg.callback != NULL)) XGrabKey(display, KEYCODE_FN_KEY , AnyModifier, DefaultRootWindow(display), False, GrabModeAsync, GrabModeAsync);
1007
1008 return(display);
1009 } /* }}} */
1010 #endif /* HAVE_LIBX11 */
1011
1012 /* get the current state from the nvram */
get_nvram_state(t_thinkpad_state * thinkpad_state)1013 int get_nvram_state(t_thinkpad_state *thinkpad_state) /* {{{ */
1014 {
1015 static int fdsc = -1; /* -1 -> file not opened */
1016 #ifdef __FreeBSD__
1017 u_int n = 0;
1018 size_t len = sizeof(n);
1019 if ( fdsc != -1 || sysctlbyname("dev.acpi_ibm.0.hotkey", &n, &len, NULL, 0) == -1 ) {
1020 #endif
1021 unsigned char buffer[114];
1022 struct {
1023 int pos;
1024 int len;
1025 } pos_len[] = {
1026 { 0x39, 0x01 },
1027 { 0x56, 0x04 },
1028 { 0x5e, 0x01 },
1029 { 0x60, 0x01 },
1030 { 0x00, 0x00 } /* end */
1031 };
1032 int pos_len_idx = 0;
1033
1034 /* open nvram for reading */
1035 if(fdsc == -1) { /* if not already opened, open nvram */
1036 if((fdsc=open(cfg.nvram, O_RDONLY|O_NONBLOCK)) == -1) {
1037 fprintf(stderr, _("Unable to open device %s: "), cfg.nvram);
1038 perror(NULL);
1039 return -1;
1040 }
1041 }
1042
1043 /* Read only the interesting bytes from nvram to reduce the CPU consupmtion of tpb */
1044 /* The kernel nvram driver reads byte-by-byte from nvram, so just reading interesting bytes reduces the amount of inb() calls */
1045 while (pos_len[pos_len_idx].pos != 0x0) {
1046 if(lseek(fdsc, pos_len[pos_len_idx].pos, SEEK_SET) != pos_len[pos_len_idx].pos ) {
1047 fprintf(stderr, _("Unable to seek in device %s: "), cfg.nvram);
1048 perror(NULL);
1049 return -1;
1050 }
1051 if(read(fdsc, buffer+pos_len[pos_len_idx].pos, pos_len[pos_len_idx].len) != pos_len[pos_len_idx].len) {
1052
1053 fprintf(stderr, _("Unable to read from device %s: "), cfg.nvram);
1054 perror(NULL);
1055 return -1;
1056 }
1057 pos_len_idx++;
1058 }
1059
1060 thinkpad_state->thinkpad_toggle = (thinkpad_state->thinkpad_toggle & ~0x01) | (( buffer[0x57] & 0x08) >> 3);
1061 thinkpad_state->zoom_toggle = (thinkpad_state->zoom_toggle & ~0x01) | ((~buffer[0x57] & 0x20) >> 5);
1062 thinkpad_state->display_toggle = (thinkpad_state->display_toggle & ~0x01) | (( buffer[0x57] & 0x40) >> 6);
1063 thinkpad_state->home_toggle = (thinkpad_state->home_toggle & ~0x01) | (( buffer[0x56] & 0x01) );
1064 thinkpad_state->search_toggle = (thinkpad_state->search_toggle & ~0x01) | (( buffer[0x56] & 0x02) >> 1);
1065 thinkpad_state->mail_toggle = (thinkpad_state->mail_toggle & ~0x01) | (( buffer[0x56] & 0x04) >> 2);
1066 thinkpad_state->wireless_toggle = (thinkpad_state->wireless_toggle & ~0x01) | (( buffer[0x56] & 0x20) >> 5);
1067 thinkpad_state->thinklight_toggle = (thinkpad_state->thinklight_toggle & ~0x01) | (( buffer[0x58] & 0x10) >> 4);
1068 thinkpad_state->hibernate_toggle = (thinkpad_state->hibernate_toggle & ~0x01) | (( buffer[0x58] & 0x01) );
1069 thinkpad_state->display_state = (( buffer[0x59] & 0x03) );
1070 thinkpad_state->expand_toggle = (thinkpad_state->expand_toggle & ~0x01) | (( buffer[0x59] & 0x10) >> 4);
1071 thinkpad_state->brightness_level = (( buffer[0x5E] & 0x07) );
1072 thinkpad_state->brightness_toggle = (thinkpad_state->brightness_toggle & ~0x01) | (( buffer[0x5E] & 0x20) >> 5);
1073 thinkpad_state->volume_level = (( buffer[0x60] & 0x0f) );
1074 thinkpad_state->volume_toggle = (thinkpad_state->volume_toggle & ~0x01) | (( buffer[0x60] & 0x80) >> 7);
1075 thinkpad_state->mute_toggle = (thinkpad_state->mute_toggle & ~0x01) | (( buffer[0x60] & 0x40) >> 6);
1076 thinkpad_state->powermgt_ac = (( buffer[0x39] & 0x07) );
1077 thinkpad_state->powermgt_battery = (( buffer[0x39] & 0x38) >> 3);
1078
1079 return 0;
1080 #ifdef __FreeBSD__
1081 }
1082 thinkpad_state->thinkpad_toggle = (thinkpad_state->thinkpad_toggle & ~0x01) | (( n & (1<<3)) >> 3);
1083 thinkpad_state->zoom_toggle = (thinkpad_state->zoom_toggle & ~0x01) | (( n & (1<<4)) >> 4);
1084 thinkpad_state->display_toggle = (thinkpad_state->display_toggle & ~0x01) | (( n & (1<<6)) >> 6);
1085 thinkpad_state->home_toggle = (thinkpad_state->home_toggle & ~0x01) | (( n & (1<<0)) );
1086 thinkpad_state->search_toggle = (thinkpad_state->search_toggle & ~0x01) | (( n & (1<<1)) >> 1);
1087 thinkpad_state->mail_toggle = (thinkpad_state->mail_toggle & ~0x01) | (( n & (1<<2)) >> 2);
1088 thinkpad_state->hibernate_toggle = (thinkpad_state->hibernate_toggle & ~0x01) | (( n & (1<<7)) >> 7);
1089 thinkpad_state->expand_toggle = (thinkpad_state->expand_toggle & ~0x01) | (( n & (1<<9)) >> 9);
1090 thinkpad_state->brightness_toggle = (thinkpad_state->brightness_toggle & ~0x01) | (( n & (1<<10)) >> 10);
1091 thinkpad_state->volume_toggle = (thinkpad_state->volume_toggle & ~0x01) | (( n & (1<<11)) >> 11);
1092
1093 /* Don't fail if the wlan, bluetooth or thinklight sysctls are not present. They are generated dynamically */
1094 if ( sysctlbyname("dev.acpi_ibm.0.wlan", &n, &len, NULL, 0) != -1 )
1095 thinkpad_state->wireless_toggle = n;
1096
1097 if ( sysctlbyname("dev.acpi_ibm.0.bluetooth", &n, &len, NULL, 0) != -1 )
1098 thinkpad_state->bluetooth_toggle = n;
1099
1100 if ( sysctlbyname("dev.acpi_ibm.0.thinklight", &n, &len, NULL, 0) != -1 )
1101 thinkpad_state->thinklight_toggle = n;
1102
1103 if ( sysctlbyname("dev.acpi_ibm.0.lcd_brightness", &n, &len, NULL, 0) == -1 )
1104 return -1;
1105 thinkpad_state->brightness_level = n;
1106
1107 if ( sysctlbyname("dev.acpi_ibm.0.volume", &n, &len, NULL, 0) == -1 )
1108 return -1;
1109 thinkpad_state->volume_level = n;
1110
1111 if ( sysctlbyname("dev.acpi_ibm.0.mute", &n, &len, NULL, 0) == -1 )
1112 return -1;
1113 thinkpad_state->mute_toggle = n;
1114
1115 return 0;
1116 #endif
1117 } /* }}} */
1118
1119 /* get the current state from the apm subsystem */
get_apm_state(t_thinkpad_state * thinkpad_state)1120 int get_apm_state(t_thinkpad_state *thinkpad_state) /* {{{ */
1121 {
1122 static int fdsc = -1; /* -1 -> file not opened */
1123 #ifndef __FreeBSD__
1124 unsigned int i;
1125 char buffer[38];
1126 char *tokens[9];
1127
1128 /* Read the state of the ac line from proc filesystem.
1129 * Documentation of /proc/apm from linux kernel (/usr/src/linux/arch/i386/kernel/apm.c)
1130 *
1131 * 0) Linux driver version (this will change if format changes)
1132 * 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
1133 * 2) APM flags from APM Installation Check (0x00):
1134 * bit 0: APM_16_BIT_SUPPORT
1135 * bit 1: APM_32_BIT_SUPPORT
1136 * bit 2: APM_IDLE_SLOWS_CLOCK
1137 * bit 3: APM_BIOS_DISABLED
1138 * bit 4: APM_BIOS_DISENGAGED
1139 * 3) AC line status
1140 * 0x00: Off-line
1141 * 0x01: On-line
1142 * 0x02: On backup power (BIOS >= 1.1 only)
1143 * 0xff: Unknown
1144 * 4) Battery status
1145 * 0x00: High
1146 * 0x01: Low
1147 * 0x02: Critical
1148 * 0x03: Charging
1149 * 0x04: Selected battery not present (BIOS >= 1.2 only)
1150 * 0xff: Unknown
1151 * 5) Battery flag
1152 * bit 0: High
1153 * bit 1: Low
1154 * bit 2: Critical
1155 * bit 3: Charging
1156 * bit 7: No system battery
1157 * 0xff: Unknown
1158 * 6) Remaining battery life (percentage of charge):
1159 * 0-100: valid
1160 * -1: Unknown
1161 * 7) Remaining battery life (time units):
1162 * Number of remaining minutes or seconds
1163 * -1: Unknown
1164 * 8) min = minutes; sec = seconds
1165 */
1166
1167 /* open /proc/apm */
1168 if(fdsc == -1) { /* if not already opened, open apm */
1169 if((fdsc = open("/proc/apm", O_RDONLY)) == -1) {
1170 return -1;
1171 }
1172 }
1173
1174 /* seek to the beginning of the file */
1175 if(lseek(fdsc, 0, SEEK_SET) != 0 ) {
1176 return -1;
1177 }
1178
1179 /* read apm state */
1180 if(read(fdsc, buffer, sizeof(buffer)) != sizeof(buffer)) {
1181 return -1;
1182 }
1183
1184 /* tokenize the apm string */
1185 tokens[0] = strtok(buffer, " ");
1186 for(i = 1 ; i < sizeof(tokens)/sizeof(char*) ; i++) {
1187 tokens[i] = strtok(NULL, " ");
1188 }
1189
1190 /* determine the AC line status */
1191 switch(strtol(tokens[3], NULL, 16)) {
1192 case 0x00:
1193 thinkpad_state->ac_state = STATE_OFF;
1194 break;
1195
1196 case 0x01:
1197 thinkpad_state->ac_state = STATE_ON;
1198 break;
1199 }
1200 #else
1201 u_long addr;
1202 size_t len = sizeof(addr);
1203
1204 if ( fdsc != -1 || sysctlbyname("hw.acpi.acline", &addr, &len, NULL, 0) == -1 ) {
1205 #ifdef __i386__
1206 // Try APM
1207 if(fdsc == -1) { /* if not already opened, open apm */
1208 if((fdsc = open("/dev/apm", O_RDONLY)) == -1)
1209 return -1;
1210 }
1211
1212 struct apm_info info;
1213 if (ioctl(fdsc, APMIO_GETINFO, &info) == -1)
1214 return -1;
1215
1216 if (info.ai_acline > 2)
1217 return -1;
1218
1219 addr = info.ai_acline;
1220 #endif
1221 }
1222 thinkpad_state->ac_state = (addr == 1) ? STATE_ON : STATE_OFF;
1223 #endif
1224 return 0;
1225 } /* }}} */
1226
1227 #ifdef HAVE_LIBX11
1228 /* get key events from X */
xgrabkey(t_thinkpad_state * thinkpad_state,Display * display)1229 int xgrabkey(t_thinkpad_state *thinkpad_state, Display *display) /* {{{ */
1230 {
1231 XEvent event;
1232
1233 while(XPending(display) > 0) {
1234 XNextEvent(display, &event);
1235
1236 if (event.xkey.type == KeyPress) { /* needed, because KeyReleases are also received, even if XSelectInput only specifies KeyPressMask!! */
1237 switch(event.xkey.keycode) {
1238 case KEYCODE_HOME:
1239 thinkpad_state->home_toggle ^= 0x02;
1240 break;
1241
1242 case KEYCODE_SEARCH:
1243 thinkpad_state->search_toggle ^= 0x02;
1244 break;
1245
1246 case KEYCODE_MAIL:
1247 thinkpad_state->mail_toggle ^= 0x02;
1248 break;
1249
1250 case KEYCODE_FAVORITES:
1251 thinkpad_state->favorites_toggle ^= 0x02;
1252 break;
1253
1254 case KEYCODE_RELOAD:
1255 thinkpad_state->reload_toggle ^= 0x02;
1256 break;
1257
1258 case KEYCODE_ABORT:
1259 thinkpad_state->abort_toggle ^= 0x02;
1260 break;
1261
1262 case KEYCODE_BACKWARD:
1263 thinkpad_state->backward_toggle ^= 0x02;
1264 break;
1265
1266 case KEYCODE_FORWARD:
1267 thinkpad_state->forward_toggle ^= 0x02;
1268 break;
1269
1270 case KEYCODE_FN_KEY:
1271 thinkpad_state->fn_toggle ^= 0x02;
1272 break;
1273
1274 default:
1275 break;
1276 }
1277 }
1278 }
1279
1280 return 0;
1281 } /* }}} */
1282 #endif /* HAVE_LIBX11 */
1283
1284 /* fork an application */
fork_app(char * cmd)1285 int fork_app(char * cmd) /* {{{ */
1286 {
1287 enum mode_t {SKIP, IN_QUOTED, IN_UNQUOTED};
1288
1289 char *cmd_cpy;
1290 int cmd_len;
1291 char *args[CALLBACK_CMD_ARGS];
1292 int ptr;
1293 int argc;
1294 enum mode_t mode;
1295 char quote_char;
1296 int fdsc, fdsc_max;
1297
1298 switch(fork()) {
1299 case -1:
1300 return -1;
1301 break;
1302
1303 case 0:
1304 ptr = 0;
1305 argc = 0;
1306 mode = SKIP;
1307 quote_char = '"';
1308 memset(args, 0, sizeof(args));
1309 cmd_cpy = strdup(cmd);
1310 cmd_len = strlen(cmd_cpy);
1311
1312 /* close ALL file descriptors except stdin, stdout and stderr */
1313 for (fdsc = 3, fdsc_max = sysconf(_SC_OPEN_MAX); fdsc < fdsc_max; fdsc++) {
1314 close(fdsc);
1315 }
1316
1317 /* generate an array of cmd_cpy string */
1318 while (ptr < cmd_len) {
1319 switch (mode) {
1320 /* skip leading whitespaces */
1321 case SKIP:
1322 if (cmd_cpy[ptr] != ' ' && cmd_cpy[ptr] != '\t') {
1323 if (cmd_cpy[ptr] == '"' || cmd_cpy[ptr] == '\'') {
1324 mode = IN_QUOTED;
1325 quote_char = cmd_cpy[ptr];
1326 ptr++;
1327 }
1328 else {
1329 mode = IN_UNQUOTED;
1330 }
1331 args[argc] = &cmd_cpy[ptr];
1332 argc++;
1333 }
1334 ptr++;
1335 break;
1336
1337 /* within a quoted string */
1338 case IN_QUOTED:
1339 if (cmd_cpy[ptr] == quote_char) {
1340 cmd_cpy[ptr] = '\0';
1341 mode = SKIP;
1342 }
1343 ptr++;
1344 break;
1345
1346 /* within a unquoted string */
1347 case IN_UNQUOTED:
1348 if (cmd_cpy[ptr] == ' ' || cmd_cpy[ptr] == '\t') { /* this is the end of the string */
1349 cmd_cpy[ptr] = '\0';
1350 mode = SKIP;
1351 }
1352 ptr++;
1353 break;
1354 }
1355 }
1356
1357 setsid(); /* children should not be killed if tpb ends */
1358 chdir(getenv("HOME")); /* try to change to users home directory */
1359 execv(args[0], args);
1360
1361 /* should never be reached, execv only returns on error */
1362 fprintf(stderr, _("Unable to fork application \"%s\".\n"), args[0]);
1363 fprintf(stderr, _("HINT:\n"));
1364 fprintf(stderr, _("With tpb version 0.6.2 or later you need to specify applications\n"));
1365 fprintf(stderr, _("using the whole path to the executable program.\n"));
1366 free(cmd_cpy);
1367 _exit(0);
1368 return 127;
1369 break;
1370
1371 default:
1372 return 0;
1373 break;
1374 }
1375 return -1; /* never reached */
1376 } /* }}} */
1377
1378 /* set the volume level in the nvram */
set_nvram_volume_level(t_thinkpad_state * thinkpad_state)1379 void set_nvram_volume_level(t_thinkpad_state *thinkpad_state) /* {{{ */
1380 {
1381 int fdsc;
1382 char buffer;
1383
1384 /* only use writeback to nvram when cfg.mixersteps is different from DEFAULT_MIXERSTEPS */
1385 if(cfg.mixersteps != DEFAULT_MIXERSTEPS) {
1386 #ifdef __FreeBSD__
1387 u_int n = thinkpad_state->volume_level;
1388
1389 if (sysctlbyname("dev.acpi_ibm.0.volume", NULL, NULL, &n, sizeof(n)) != 0)
1390 fprintf(stderr, _("Unable to set volume sysctl"));
1391 else {
1392 #endif
1393 /* open nvram */
1394 if((fdsc = open(cfg.nvram, O_RDWR|O_NONBLOCK)) == -1) {
1395 fprintf(stderr, _("Unable to open device %s: "), cfg.nvram);
1396 perror(NULL);
1397 fprintf(stderr, _("To use mixersteps other than %d you need write access to %s.\n"), cfg.mixersteps, cfg.nvram);
1398 _exit(1);
1399 }
1400
1401 /* jump to volume section */
1402 if(lseek(fdsc, 0x60, SEEK_SET) == -1 ) {
1403 fprintf(stderr, _("Unable to seek device %s: "), cfg.nvram);
1404 perror(NULL);
1405 _exit(1);
1406 }
1407
1408 /* read nvram */
1409 if(read(fdsc, &buffer,sizeof(buffer)) != sizeof(buffer)) {
1410 fprintf(stderr, _("Unable to read from device %s: "), cfg.nvram);
1411 perror(NULL);
1412 _exit(1);
1413 }
1414
1415 thinkpad_state->volume_level = 0x07; /* set volume_level to the value we write back to nvram */
1416 buffer &= 0xf0;
1417 buffer |= thinkpad_state->volume_level;
1418
1419 /* jump to volume section */
1420 if(lseek(fdsc, 0x60, SEEK_SET) == -1 ) {
1421 fprintf(stderr, _("Unable to seek device %s: "), cfg.nvram);
1422 perror(NULL);
1423 _exit(1);
1424 }
1425
1426 /* write std value for volume */
1427 if(write(fdsc, &buffer, sizeof(buffer)) != sizeof(buffer)) {
1428 fprintf(stderr, _("Unable to write to device %s: "), cfg.nvram);
1429 perror(NULL);
1430 _exit(1);
1431 }
1432
1433 close(fdsc);
1434 #ifdef __FreeBSD__
1435 }
1436 #endif
1437 }
1438 return;
1439
1440 } /* }}} */
1441
1442 /* change the volume level of the OSS mixer device */
change_volume(int change)1443 unsigned int change_volume(int change) /* {{{ */
1444 {
1445 int mixer;
1446 unsigned int volume;
1447 unsigned int left,right;
1448
1449 /* open mixer */
1450 if((mixer = open(cfg.mixerdev, O_RDWR)) == -1) {
1451 fprintf(stderr, _("Unable to open mixer device %s: "), cfg.mixerdev);
1452 perror(NULL);
1453 _exit(1);
1454 }
1455
1456 /* read mixer volume */
1457 if(ioctl(mixer, SOUND_MIXER_READ_VOLUME, &volume) == -1) {
1458 fprintf(stderr, _("Unable to read volume from mixer device %s: "), cfg.mixerdev);
1459 perror(NULL);
1460 _exit(1);
1461 }
1462
1463 /* adjust volume */
1464 if(((int)(volume & 0xff) + change) < 0) {
1465 left = 0;
1466 }
1467 else {
1468 left = (volume & 0xff) + change;
1469 if(left > MAX_VOLUME) {
1470 left = MAX_VOLUME;
1471 }
1472 }
1473 if((((int)(volume >> 8) & 0xff) + change) < 0) {
1474 right = 0;
1475 }
1476 else {
1477 right = ((volume >> 8) & 0xff) + change;
1478 if(right > MAX_VOLUME) {
1479 right = MAX_VOLUME;
1480 }
1481 }
1482 volume = left | (right << 8);
1483
1484 /* write volume back to mixer */
1485 if(ioctl(mixer, SOUND_MIXER_WRITE_VOLUME, &volume) == -1) {
1486 fprintf(stderr, _("Unable to write volume to mixer device %s: "), cfg.mixerdev);
1487 perror(NULL);
1488 _exit(1);
1489 }
1490
1491 /* close mixer device */
1492 if(close(mixer) == -1 ) {
1493 fprintf(stderr, _("Unable to close mixer device %s: "), cfg.mixerdev);
1494 perror(NULL);
1495 _exit(1);
1496 }
1497
1498 /* calc volume percentage and return it */
1499 return ((left + right) / 2) * 100 / MAX_VOLUME;
1500
1501 } /* }}} */
1502
1503 /* check if apmiser is running */
apmiser_running(void)1504 int apmiser_running(void) /* {{{ */
1505 {
1506 /* code inspired by comp.unix.programmer FAQ */
1507 char line[133];
1508 char *linep;
1509 char *token;
1510 char *cmd = NULL;
1511 FILE *fp;
1512
1513 /* open command like a file */
1514 fp = popen("ps -e 2>/dev/null", "r");
1515 if (fp == NULL) {
1516 return 0;
1517 }
1518
1519 /* get header */
1520 if (fgets(line, sizeof(line), fp) == NULL) {
1521 pclose(fp);
1522 return 0;
1523 }
1524
1525 /* determine column of command name */
1526 linep = line;
1527 while(cmd == NULL)
1528 {
1529 if ((token = strtok(linep, " \t\n")) == NULL) {
1530 pclose(fp);
1531 return 0;
1532 }
1533 linep = NULL;
1534
1535 if (strcmp("COMMAND", token) == 0 || strcmp("CMD", token) == 0) {
1536 cmd = token;
1537 }
1538 }
1539
1540 /* check if any of the commands is apmiser */
1541 while(fgets(line, sizeof line, fp) != NULL) {
1542 if (strstr(strtok(cmd, " \t\n"), "apmiser") != NULL) {
1543 pclose(fp);
1544 return 1;
1545 }
1546 }
1547
1548 pclose(fp);
1549
1550 return 0;
1551 } /* }}} */
1552
1553 #ifdef HAVE_LIBX11
1554 /* handler for X errors */
xerrorhandler(Display * display,XErrorEvent * event)1555 int *xerrorhandler(Display *display, XErrorEvent *event) /* {{{ */
1556 {
1557 static int called = 0;
1558
1559 if (called == 0) {
1560 called = 1;
1561 fputs(_("There is already a application grabbing some ThinkPad keys.\n"), stderr);
1562 fputs(_("Possibly you restarted tpb too fast.\n"), stderr);
1563 }
1564
1565 return(NULL);
1566 } /* }}} */
1567 #endif /* HAVE_LIBX11 */
1568
1569 /* handler for SIG_CHLD */
sig_chld_handler(int signo)1570 void sig_chld_handler(int signo) /* {{{ */
1571 {
1572 int status;
1573 waitpid(-1, &status, WNOHANG);
1574 return;
1575 } /* }}} */
1576
1577 /* vim600:set fen:set fdm=marker:set fdl=0: */
1578