Skip to content

Instantly share code, notes, and snippets.

@areinisc
Last active September 3, 2018 13:20
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save areinisc/26560a375a397f6085f2228c9411d1d2 to your computer and use it in GitHub Desktop.
Save areinisc/26560a375a397f6085f2228c9411d1d2 to your computer and use it in GitHub Desktop.
PHOTOMETRY --- doom-emacs -- automatic theme adjustment based on ambient light

Adding automatic light/dark theme switching to doom-emacs running on a mac with ambient light sensor.

Idea adapted from: Matt Bilyeu https://matthewbilyeu.com/blog/2018-04-09/setting-emacs-theme-based-on-ambient-light

Installation

  1. Make the lmutracker executable using clang and place it in your custom doom directory:
    $ cd ~/.doom.d/    # start in your custom doom directory, wherever that is for you.
    $ touch lmutracker.mm # copy in the contents of this file however you like
    $ clang -o lmutracker lmutracker.mm -framework IOKit -framework CoreFoundation  # this makes the executable
    
  2. Add the relevant elisp to your doom config (e.g. "~/.doom.d/config.el") Edit values for light-theme and dark-theme to match your preferences.
  3. Restart emacs and watch the theme automatically adjust to ambient light. By default, light sensor is probed every 10seconds. Use keybind SPC t p to toggle photometry on/off.
;; photometry
;; I want to be able to toggle "photometry" (automatic theme switching),
;; but I don't know how to properly build a module or properly code in elisp.
;; Here I start by defining an integer variable that tracks the state of
;; photometry being on or off.
(defvar photometry-mode 0) ; photometry is recorded as "off"
;; Set light and dark theme choices here!
(defconst light-theme 'doom-solarized-light)
(defconst dark-theme 'doom-nova)
(defun photometry ()
"Function for sensing light and changing themes based on apparent brightness
as reported through lmutracker executable."
(let* ((current-light-sensor-reading
(string-to-number
(shell-command-to-string
;; this assumes you put the `lmutracker` executable
;; in your doom-config directory ("~/.doom.d/" or "~/.config/doom/")
(concat doom-private-dir "lmutracker")))))
(if (< current-light-sensor-reading 100000) ; test if environment is low-light
(unless (eq doom-theme dark-theme) ; if theme is not yet dark
(setq doom-theme dark-theme) ; change to dark theme
(doom//reload-theme))
(when (eq doom-theme dark-theme) ; if theme is dark
(setq doom-theme light-theme) ; change to light theme
(doom//reload-theme)))))
(defun photometry/toggle ()
"Toggle photometry on/off. Photometry is a function that changes the theme
over time based on ambient light sensor readings."
(interactive)
(if (zerop photometry-mode)
(and (setq photometry-mode (1+ photometry-mode))
(run-with-timer 0 10 #'photometry)) ; integer controls the update interval
(and (setq photometry-mode (1- photometry-mode))
(cancel-function-timers 'photometry))))
;; Add keybind so photometry can be toggled with `SPC t p`
(map! (:leader
(:desc "toggle" :prefix "t"
:desc "Photometry" :n "p" #'photometry/toggle)))
;; When on mac, use photometry for automatic theme adjustment
(when (eq system-type 'darwin)
(setq doom-theme dark-theme) ; starting (dark) theme
(photometry/toggle)) ; toggle photometry on!
// lmutracker.mm
//
// clang -o lmutracker lmutracker.mm -framework IOKit -framework CoreFoundation
//
// copied from Matt Bilyeu <https://matthewbilyeu.com/blog/2018-04-09/setting-emacs-theme-based-on-ambient-light>
#include <mach/mach.h>
#import <IOKit/IOKitLib.h>
#import <CoreFoundation/CoreFoundation.h>
static double updateInterval = 0.1;
static io_connect_t dataPort = 0;
void updateTimerCallBack(CFRunLoopTimerRef timer, void *info) {
kern_return_t kr;
uint32_t outputs = 2;
uint64_t values[outputs];
kr = IOConnectCallMethod(dataPort, 0, nil, 0, nil, 0, values, &outputs, nil, 0);
if (kr == KERN_SUCCESS) {
printf("%8lld", values[0]);
exit(0);
}
if (kr == kIOReturnBusy) {
return;
}
mach_error("I/O Kit error:", kr);
exit(kr);
}
int main(void) {
kern_return_t kr;
io_service_t serviceObject;
CFRunLoopTimerRef updateTimer;
serviceObject = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleLMUController"));
if (!serviceObject) {
fprintf(stderr, "failed to find ambient light sensors\n");
exit(1);
}
kr = IOServiceOpen(serviceObject, mach_task_self(), 0, &dataPort);
IOObjectRelease(serviceObject);
if (kr != KERN_SUCCESS) {
mach_error("IOServiceOpen:", kr);
exit(kr);
}
setbuf(stdout, NULL);
updateTimer = CFRunLoopTimerCreate(kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + updateInterval, updateInterval,
0, 0, updateTimerCallBack, NULL);
CFRunLoopAddTimer(CFRunLoopGetCurrent(), updateTimer, kCFRunLoopDefaultMode);
CFRunLoopRun();
exit(0);
}
@idoo
Copy link

idoo commented Sep 3, 2018

it's doesn't works anymore on new Macbooks (with touchbar)

https://stackoverflow.com/a/52150583/1397675

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment