Настройка темы Emacs на основе внешнего освещения

Ссылка на оригинал - https://matthewbilyeu.com/blog/2018-04-09/setting-emacs-theme-based-on-ambient-l…, автор публикации - Matt Bilyeu

Я сижу рядом с окном на работе. В солнечные дни легче увидеть светлую тему редактора, а когда небо темное, темная тема становится легче для моих глаз. Поэтому я решил сделать так, чтобы мой MacBook автоматически переключал тему Emacs на основе показаний датчика внешней освещенности.

Демонстрация переключения темы в действии

Это решение состоит из двух частей: исполняемый файл командной строки для чтения данных с датчика, а затем небольшая функция elisp для переключения тем.

Программа ниже взята со StackOverflow и немного изменена. Она получает службу AppleLMUController IO, затем, когда служба готова, печатает данные датчика освещенности в stdout и выходит.

// lmutracker.mm
//
// clang -o lmutracker lmutracker.mm -framework IOKit -framework CoreFoundation

#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);
}

Сопровождающий elisp-код будет вызывать этот исполняемый файл по таймеру и менять тему в зависимости от показаний света.

(setq current-theme "dark")
(defconst light-theme 'majapahit-light)
(defconst dark-theme 'majapahit-dark)

;; будет применять темную тему, если комната темная, и светлую тему, если комната светлая
(defun change-theme-for-lighting ()
  (let* ((current-light-sensor-reading
          (string-to-number
           (shell-command-to-string "./lmutracker"))))
    (if (< current-light-sensor-reading 100000)
        (when (not (string-equal current-theme "dark"))
          (load-theme dark-theme 1)
          (setq current-theme "dark"))
      (when (not (string-equal current-theme "light"))
        (load-theme light-theme 1)
        (setq current-theme "light")))))

;; вероятно, нужно запускать реже, чем каждую секунду
(run-with-timer 0 1 #'change-theme-for-lighting)

Хотите больше полезных советов? Смотрите и подписывайтесь на наш канал! Здесь я публикую лучшие советы для пользователей Андроид, Windows, iOS и Mac OS. Также вы можете задать мне любой вопрос, подписавшись на канал.

Наш канал в Telegram