[Mac] 根据环境光自动调整主题

环境变暗时用暗色主题,变亮时用亮色主题:

(除了关灯,用手挡住 Macbook 上的摄像头的位置也行。)

2018-04-11 21_46_11

文中用的是 run-with-timer 定期监测,感觉会拖慢 Emacs,我没试过。我自己写了个用 Emacs Subprocess + Filter 的方法,应该会好些。下面的代码仅供参考,如果你打算运行,得先把

  • sanityinc-tomorrow-eighties
  • sanityinc-tomorrow-day

改成你想要的暗色和亮色主题。

(defun auto-theme-mode-filter (_proc output)
  (let ((current-light-sensor-reading (string-to-number output))
        (current-theme (car custom-enabled-themes))
        (dark-theme 'sanityinc-tomorrow-eighties)
        (light-theme 'sanityinc-tomorrow-day))
    (cond ((/= (length output) 8))      ; printf("%8lld", values[0]);
          ((and (< current-light-sensor-reading 100000)
                (not (eq current-theme dark-theme)))
           (disable-theme current-theme)
           (enable-theme dark-theme))
          ((and (>= current-light-sensor-reading 100000)
                (not (eq current-theme light-theme)))
           (disable-theme current-theme)
           (enable-theme light-theme)))))

(define-minor-mode auto-theme-mode
  "Automatically set Emacs theme based on ambient light."
  :global t
  (let* ((buf " *auto-theme-mode*")
         (proc (get-buffer-process buf)))
    (if auto-theme-mode
        (or (and proc (eq 'run (process-status proc)))
            (let ((process-connection-type nil))
              (set-process-filter
               (start-process
                "lmutracker"
                buf
                "/bin/sh"
                "-c"
                "while true; do lmutracker && sleep 1; done")
               #'auto-theme-mode-filter)))
      (and proc (kill-process proc)))))
5 个赞

棒极了,比我那个预习设置时间段切换主题的强多了

坐火车的时候会怎样,光线不停变化,屏幕闪烁?

把手放到摄像头位置晃一晃,就会不停地来回换主题

如果只换 Emacs 的 theme,不换终端的 theme,会很难看。

请问这个需要打开摄像头吗

找到动态切换 iTerm2 Color Preset 的方法(快捷键):

看看有没有可能在 Emacs 中调用。

搞这种东西,最好了解一下"施密特触发器" 的工作原理.

2 个赞

不需要,只是 MacBook 的环境光传感器在摄像头边上。

可以连同 iTerm2 主题一起切换了:

(前 → 后)

如果只换 Emacs 主题,效果是这样的:

@xuchunyang 大佬,好像最新的macos不能发访 ambient light sensor了,方便时可以更新一下代码吗?谢谢!ps:macos big sur 11.5.1 emacs 27.2

原文引用的原帖有新的代码:macos - How do you programmatically access the ambient light sensor on Mac OS X 10.5+? - Stack Overflow

改了一下输出搬过来:

// lmutracker.mm
//
// clang -o lmutracker lmutracker.mm -F /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks -framework Foundation -framework IOKit -framework CoreFoundation -framework BezelServices

#include <mach/mach.h>
#import <Foundation/Foundation.h>
#import <IOKit/IOKitLib.h>
#import <IOKit/hidsystem/IOHIDServiceClient.h>

typedef struct __IOHIDEvent *IOHIDEventRef;

#define kAmbientLightSensorEvent 12

#define IOHIDEventFieldBase(type) (type << 16)

extern "C" {
  IOHIDEventRef IOHIDServiceClientCopyEvent(IOHIDServiceClientRef, int64_t, int32_t, int64_t);
  double IOHIDEventGetFloatValue(IOHIDEventRef, int32_t);

  IOHIDServiceClientRef ALCALSCopyALSServiceClient(void);
}

static double updateInterval = 0.1;
static IOHIDServiceClientRef client;
static IOHIDEventRef event;

void updateTimerCallBack(CFRunLoopTimerRef timer, void *info) {
  double value;

  event = IOHIDServiceClientCopyEvent(client, kAmbientLightSensorEvent, 0, 0);

  value = IOHIDEventGetFloatValue(event, IOHIDEventFieldBase(kAmbientLightSensorEvent));

  printf("%8f", value);

  CFRelease(event);
}

int main(void) {
  kern_return_t kr;
  CFRunLoopTimerRef updateTimer;

  client = ALCALSCopyALSServiceClient();
  if (client) {
    event = IOHIDServiceClientCopyEvent(client, kAmbientLightSensorEvent, 0, 0);
  }
  if (!event) {
    fprintf(stderr, "failed to find ambient light sensors\n");
    exit(1);
  }

  CFRelease(event);

  setbuf(stdout, NULL);
  printf("%8f", 0.0);

  updateTimer = CFRunLoopTimerCreate(kCFAllocatorDefault,
                  CFAbsoluteTimeGetCurrent() + updateInterval, updateInterval,
                  0, 0, updateTimerCallBack, NULL);
  CFRunLoopAddTimer(CFRunLoopGetCurrent(), updateTimer, kCFRunLoopDefaultMode);
  CFRunLoopRun();

  exit(0);
}

1 个赞