lightweight X11 utility to dim the screen and/or keyboard backlight when idle

respond to SIGUSR1 and SIGUSR2 by immediately dimming or brightening

+166 -46
+38 -7
README.md
··· 9 9 10 10 **xdimmer** 11 11 \[**-a**] 12 + \[**-b** *brighten steps*] 12 13 \[**-d**] 13 14 \[**-k**] 14 15 \[**-n**] 15 16 \[**-p** *percent*] 16 - \[**-s** *steps*] 17 + \[**-s** *dim steps*] 17 18 \[**-t** *timeout*] 18 19 19 20 # DESCRIPTION ··· 25 26 backlight is dimmed to 26 27 *percent* 27 28 in 28 - *steps* 29 + *dim steps* 29 30 steps, unless the 30 31 *-n* 31 32 option was specified. 32 33 Once keyboard or mouse input is detected, the screen backlight is restored 33 - to its previous brightness value. 34 + to its previous brightness value in 35 + *brighten steps* 36 + steps. 34 37 35 38 On OpenBSD, if the 36 39 *-k* ··· 54 57 > Change backlights according to ambient light sensor lux readings. 55 58 > Currently only supported on OpenBSD. 56 59 60 + **-b** *steps* 61 + 62 + > Number of steps to take while restoring backlight. 63 + > The default is 64 + > `5` 65 + > steps. 66 + 57 67 **-d** 58 68 59 69 > Print debugging messages to stdout. ··· 65 75 66 76 **-n** 67 77 68 - > Do not adjust the screen backlight (can only be used if 69 - > *-k* 70 - > is used). 78 + > Do not adjust the screen backlight when idle. 71 79 72 80 **-p** *percent* 73 81 ··· 90 98 > `120` 91 99 > seconds. 92 100 101 + # SIGNALS 102 + 103 + `SIGINT` 104 + 105 + > **xdimmer** 106 + > will exit, attempting to brighten the screen and/or keyboard before 107 + > exiting if they are currently in a dimmed state. 108 + 109 + `SIGUSR1` 110 + 111 + > **xdimmer** 112 + > will immediately dim the screen and/or keyboard (depending on 113 + > *-k* 114 + > and 115 + > *-n* 116 + > options). 117 + 118 + `SIGUSR2` 119 + 120 + > **xdimmer** 121 + > will immediately brighten the screen and/or keyboard if they are 122 + > in a dimmed state. 123 + 93 124 # AUTHORS 94 125 95 126 **xdimmer** 96 127 was written by 97 128 joshua stein <[jcs@jcs.org](mailto:jcs@jcs.org)>. 98 129 99 - OpenBSD 6.2 - September 21, 2017 130 + OpenBSD 6.6 - August 27, 2019
+19
xdimmer.1
··· 78 78 The default is 79 79 .Dv 120 80 80 seconds. 81 + .Sh SIGNALS 82 + .Bl -tag -width "SIGUSR1" -compact 83 + .It Dv SIGINT 84 + .Nm 85 + will exit, attempting to brighten the screen and/or keyboard before 86 + exiting if they are currently in a dimmed state. 87 + .Pp 88 + .It Dv SIGUSR1 89 + .Nm 90 + will immediately dim the screen and/or keyboard (depending on 91 + .Ar -k 92 + and 93 + .Ar -n 94 + options). 95 + .Pp 96 + .It Dv SIGUSR2 97 + .Nm 98 + will immediately brighten the screen and/or keyboard if they are 99 + in a dimmed state. 81 100 .Sh AUTHORS 82 101 .Nm 83 102 was written by
+109 -39
xdimmer.c
··· 63 63 OP_SET, 64 64 }; 65 65 66 + enum { 67 + MSG_EXIT = 1, 68 + MSG_DIM, 69 + MSG_BRIGHTEN, 70 + }; 71 + 66 72 static const struct als_setting { 67 73 char *label; 68 74 int min_lux; ··· 82 88 }; 83 89 84 90 void xloop(void); 85 - void set_alarm(XSyncAlarm *, XSyncTestType, unsigned long); 91 + void set_alarm(XSyncAlarm *, XSyncTestType); 86 92 void bail(int); 93 + void sigusr1(int); 94 + void sigusr2(int); 87 95 void stepper(float, float, int, int); 88 96 float backlight_op(int, float); 89 97 float kbd_backlight_op(int, float); ··· 91 99 void als_fetch(void); 92 100 void usage(void); 93 101 int XPeekEventOrTimeout(Display *, XEvent *, unsigned int); 94 - int exitmsg[2]; 102 + int pipemsg[2]; 95 103 96 104 extern char *__progname; 97 105 ··· 116 124 static Atom backlight_a = 0; 117 125 static XSyncCounter idler_counter = 0; 118 126 static int exiting = 0; 127 + static int force_dim = 0; 128 + static int force_brighten = 0; 119 129 static int debug = 0; 120 130 #define DPRINTF(x) { if (debug) { printf x; } }; 121 131 ··· 222 232 223 233 signal(SIGINT, bail); 224 234 signal(SIGTERM, bail); 235 + signal(SIGUSR1, sigusr1); 236 + signal(SIGUSR2, sigusr2); 225 237 226 - /* setup a pipe to wait for an exit message from bail() */ 227 - pipe(exitmsg); 238 + /* setup a pipe to wait for messages from signal handlers */ 239 + pipe(pipemsg); 228 240 229 241 xloop(); 230 242 ··· 262 274 * fire an XSyncAlarmNotifyEvent when IDLETIME counter reaches 263 275 * dim_timeout seconds 264 276 */ 265 - set_alarm(&idle_alarm, XSyncPositiveComparison, dim_timeout * 1000); 277 + set_alarm(&idle_alarm, XSyncPositiveComparison); 266 278 267 279 for (;;) { 268 280 XEvent e; 269 281 XSyncAlarmNotifyEvent *alarm_e; 282 + int do_dim = 0, do_brighten = 0; 270 283 271 284 DPRINTF(("waiting for next event\n")); 272 285 ··· 280 293 if (exiting) 281 294 break; 282 295 283 - XNextEvent(dpy, &e); 296 + if (force_dim) { 297 + do_dim = force_dim; 298 + } else if (force_brighten) { 299 + do_brighten = force_brighten; 300 + } else { 301 + XNextEvent(dpy, &e); 284 302 285 - if (!dim_screen && !dim_kbd) 286 - continue; 303 + if (!dim_screen && !dim_kbd) 304 + continue; 287 305 288 - if (e.type != (sync_event + XSyncAlarmNotify)) { 289 - DPRINTF(("got event of type %d\n", e.type)); 290 - continue; 291 - } 306 + if (e.type != (sync_event + XSyncAlarmNotify)) { 307 + DPRINTF(("got event of type %d\n", e.type)); 308 + continue; 309 + } 292 310 293 - alarm_e = (XSyncAlarmNotifyEvent *)&e; 311 + alarm_e = (XSyncAlarmNotifyEvent *)&e; 294 312 295 - if (alarm_e->alarm == idle_alarm) { 296 - DPRINTF(("idle counter reached %dms, dimming\n", 297 - XSyncValueLow32(alarm_e->counter_value))); 313 + if (alarm_e->alarm == idle_alarm) { 314 + DPRINTF(("idle counter reached %dms, dimming\n", 315 + XSyncValueLow32(alarm_e->counter_value))); 316 + do_dim = 1; 317 + } else if (alarm_e->alarm == reset_alarm) { 318 + DPRINTF(("idle counter reset, brightening\n")); 319 + do_brighten = 1; 320 + } 321 + } 298 322 299 - /* fire reset_alarm when idle counter resets */ 300 - set_alarm(&reset_alarm, XSyncNegativeComparison, 301 - (dim_timeout * 1000) - 1); 323 + if (do_dim && !dimmed) { 324 + set_alarm(&reset_alarm, XSyncNegativeTransition); 302 325 303 326 if (dim_screen) 304 327 backlight = backlight_op(OP_GET, 0); 305 328 if (dim_kbd) 306 329 kbd_backlight = kbd_backlight_op(OP_GET, 0); 307 330 308 - stepper(dim_pct, 0, dim_steps, 1); 331 + stepper(dim_pct, 0, force_dim ? 1 : dim_steps, 1); 309 332 dimmed = 1; 310 - 311 - set_alarm(&reset_alarm, XSyncNegativeComparison, 312 - (dim_timeout * 1000) - 1); 313 333 } 314 - else if (alarm_e->alarm == reset_alarm) { 315 - DPRINTF(("idle counter reset, brightening\n")); 316 - 334 + else if (do_brighten && dimmed) { 317 335 if (use_als) 318 336 als_fetch(); 319 337 320 - set_alarm(&idle_alarm, XSyncPositiveComparison, 321 - dim_timeout * 1000); 338 + set_alarm(&idle_alarm, XSyncPositiveComparison); 322 339 323 - stepper(backlight, kbd_backlight, brighten_steps, 0); 340 + stepper(backlight, kbd_backlight, 341 + force_brighten ? 1 : brighten_steps, 0); 324 342 dimmed = 0; 325 343 } 344 + 345 + force_dim = force_brighten = 0; 326 346 } 327 347 328 - DPRINTF(("restoring backlight to %f / %f before exiting\n", backlight, 329 - kbd_backlight)); 330 - stepper(backlight, kbd_backlight, brighten_steps, 0); 348 + if (dimmed) { 349 + DPRINTF(("restoring backlight to %f / %f before exiting\n", 350 + backlight, kbd_backlight)); 351 + stepper(backlight, kbd_backlight, brighten_steps, 0); 352 + } 331 353 } 332 354 333 355 void 334 - set_alarm(XSyncAlarm *alarm, XSyncTestType test, unsigned long milliseconds) 356 + set_alarm(XSyncAlarm *alarm, XSyncTestType test) 335 357 { 336 358 XSyncAlarmAttributes attr; 359 + XSyncValue value; 337 360 unsigned int flags; 361 + int64_t cur_idle; 362 + 363 + XSyncQueryCounter(dpy, idler_counter, &value); 364 + cur_idle = ((int64_t)XSyncValueHigh32(value) << 32) | 365 + XSyncValueLow32(value); 366 + DPRINTF(("cur idle %lld\n", cur_idle)); 338 367 339 368 attr.trigger.counter = idler_counter; 340 - attr.trigger.value_type = XSyncAbsolute; 341 369 attr.trigger.test_type = test; 342 - XSyncIntToValue(&attr.trigger.wait_value, milliseconds); 370 + attr.trigger.value_type = XSyncRelative; 371 + XSyncIntsToValue(&attr.trigger.wait_value, dim_timeout * 1000, 372 + (unsigned long)(dim_timeout * 1000) >> 32); 343 373 XSyncIntToValue(&attr.delta, 0); 344 374 345 375 flags = XSyncCACounter | XSyncCATestType | XSyncCAValue | XSyncCADelta; ··· 690 720 void 691 721 bail(int sig) 692 722 { 723 + int msg = MSG_EXIT; 724 + 693 725 if (exiting) 694 726 exit(0); 695 727 ··· 700 732 * polling. 701 733 */ 702 734 DPRINTF(("got signal %d, trying to exit\n", sig)); 703 - write(exitmsg[1], &exitmsg, 1); 735 + write(pipemsg[1], &msg, 1); 704 736 exiting = 1; 705 737 } 706 738 739 + void 740 + sigusr1(int sig) 741 + { 742 + int msg = MSG_DIM; 743 + 744 + DPRINTF(("got signal %d, forcing dim\n", sig)); 745 + write(pipemsg[1], &msg, 1); 746 + } 747 + 748 + void 749 + sigusr2(int sig) 750 + { 751 + int msg = MSG_BRIGHTEN; 752 + 753 + DPRINTF(("got signal %d, forcing brighten\n", sig)); 754 + write(pipemsg[1], &msg, 1); 755 + } 756 + 707 757 int 708 758 XPeekEventOrTimeout(Display *dpy, XEvent *e, unsigned int msecs) 709 759 { 710 760 struct pollfd pfd[2]; 761 + int msg = 0; 711 762 712 763 while (!XPending(dpy)) { 713 764 memset(&pfd, 0, sizeof(pfd)); 714 765 pfd[0].fd = ConnectionNumber(dpy); 715 766 pfd[0].events = POLLIN; 716 - pfd[1].fd = exitmsg[0]; 767 + pfd[1].fd = pipemsg[0]; 717 768 pfd[1].events = POLLIN; 718 769 719 770 switch (poll(pfd, 2, msecs == 0 ? INFTIM : msecs)) { ··· 726 777 return 0; 727 778 default: 728 779 if (pfd[1].revents) { 729 - DPRINTF(("%s: got exit message\n", __func__)); 730 - exiting = 1; 780 + read(pipemsg[0], &msg, 1); 781 + switch (msg) { 782 + case MSG_EXIT: 783 + DPRINTF(("%s: got pipe message: exit\n", 784 + __func__)); 785 + exiting = 1; 786 + break; 787 + case MSG_DIM: 788 + DPRINTF(("%s: got pipe message: dim\n", 789 + __func__)); 790 + force_dim = 1; 791 + break; 792 + case MSG_BRIGHTEN: 793 + DPRINTF(("%s: got pipe message: " 794 + "brighten\n", __func__)); 795 + force_brighten = 1; 796 + break; 797 + default: 798 + DPRINTF(("%s: junk on msg pipe: 0x%x\n", 799 + __func__, msg)); 800 + } 731 801 return 1; 732 802 } else if (pfd[0].revents) { 733 803 DPRINTF(("%s: got X event\n", __func__));