Магия с сенсором освещения

Помимо сенсора внезапного движения многие новые «Макбуки» оборудованы сенсором освещённости, он едва заметно располагается рядом с видеокамерой.

Мне интересно стало попробовать поработать и с ним тоже. На этот раз цель была — научиться эффектным жестом блокировать свой ноут. Конечно, ложных срабатываний масса — всё-таки этот сенсор крайне примитивен, он видит только освещённость, но в качестве фокуса годится:

Фокус

По-моему, довольно эффектно смотрится — блокировка экрана магическим пассом.

Код на Си получился чуть более ста строк и на этот раз я распознаю шаблон поведения, он задаётся в подстановке «+vvv000-^^^», что означает «сначала идёт положительное отклонение от среднего, потом в последовательности не менее трёх, отклонение должно уменьшаться, потом не менее трёх раз должно встретиться нулевое отклонение от среднего, потом отрицательное отклонение, потом отклонение должно увеличиваться».

При сканировании чисел с датчика я всегда рассматриваю десять значений от которых считаю среднее и каждое значение рассматриваю как отклонение от этого среднего, собственно, вот код:

// Магические пассы, улавливаемые через датчик освещённости
// Written by Evgeny Stepanischev Jun 2013
// gcc lightlock.c -framework IOKit -framework ApplicationServices -O3 -o lightlock

#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <unistd.h>

#define WINDOWSIZE 10
#define GESTURES "+vvv000-^^^"
#define UPDATEINTERVAL .01

const kGetSensorReadingID = 0;
int makeReaction(uint64_t current);
static io_connect_t port = 0;

void macLock () {
    CGSCreateLoginSession(NULL);
}

void updateTimerCallBack(CFRunLoopTimerRef timer, void *info) {
    IOItemCount osize = 2;
    uint64_t values[osize];

    kern_return_t ret = IOConnectCallMethod(port, kGetSensorReadingID,
    NULL, 0, NULL, 0, values, &osize, NULL, 0);

    if (ret == KERN_SUCCESS) {
        if (makeReaction(values[0])) {
            macLock();
        }
    }
    else if (ret != kIOReturnBusy) {
      mach_error("IOConnectCallMethod: ", ret);
      exit(-3);
    }   
}

int main() {
    io_service_t service = IOServiceGetMatchingService(
        kIOMasterPortDefault, IOServiceMatching("AppleLMUController")
    );

    if (service == kIOReturnNoDevice) {
        fprintf(stderr, "Cannot find Ambient Light Sensor\n");
        exit(-1);
    }

    kern_return_t ret = IOServiceOpen(service, mach_task_self(), 0, &port);
    IOObjectRelease(service);

    if (ret != KERN_SUCCESS) {
        mach_error("IOServiceOpen:", ret);
        exit(-2);
    }

    CFRunLoopTimerRef timer = CFRunLoopTimerCreate(
        kCFAllocatorDefault,
        CFAbsoluteTimeGetCurrent() + UPDATEINTERVAL,
        UPDATEINTERVAL, 0, 0, updateTimerCallBack, NULL
    );
    CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
    CFRunLoopRun();

    exit(0);
}

int checkMask(char mask, const int64_t diff, const int64_t prevdiff) {
    switch (mask) {
        case '+': return diff > 0;
        case '-': return diff < 0;
        case '0': return diff == 0;
        case 'v': return prevdiff > diff;
        case '^': return diff > prevdiff;
    }

    return 0;
}

int makeReaction(uint64_t current) {
    static uint64_t window[WINDOWSIZE];
    static uint64_t prevdiff = 0;
    static gesturepos = 0;

    uint64_t average = current;
    int i, notzerocnt = 1;

    for (i = WINDOWSIZE-1; i > 0; i--) {
        if (window[i] = window[i-1]) {
            average += window[i];
            notzerocnt++;
        }
    }

    window[0] = current;
    average /= notzerocnt;

    int64_t diff = average - current;
    if (checkMask(GESTURES[gesturepos], diff, prevdiff)) {
        gesturepos++;
    } else {
        if (!gesturepos || !checkMask(GESTURES[gesturepos-1], diff, prevdiff)) {
            gesturepos = 0;
        }
    }

    prevdiff = diff;
    return gesturepos == sizeof GESTURES - 1;
}

В функции main, в конце стоит магический код, который нужен для того, чтобы вызываемые мною внутренние шестерёнки «Мака» благополучно крутились, иначе функция CGSCreateLoginSession будет ждать когда ей перепадёт процессорного времени.

Ну и традиционно, если кто-то что-то понимает в Си, поругайте код. Заранее спасибо!

6 комментариев

Malbolge

«Программа» на Малболже (70.54КиБ)

В десятой серии сериала «Элементарно» внезапно для меня в сюжете используется эзотерический язык программирования «Малболж». Я им немного интересовался, но программировать на нём так тяжело, что даже простые программы, в основном, сгенерированы компьютером.

Когда я увидел текст этой программы, конечно, сразу загорелся посмотреть что там выводится, но оказалось, что это даже не программа, а просто фрагмент «Hello, world.», да ещё и с несколькими опечатками (первые две ошибки — символы №№ 9 и 15):

('&%:9]!-}|z2Vzwv-,POqponl$Hjig%eB@@>}=<M:9wv^WsU2T|nm-
,jcl(I&%$#"^CB]V?Tx<uVtT^Rpo3Nlf.Jh++Fdbc=CBA9?!

Жаль, можно было бы что-то интересно закодировать. Шерлок из сериала, кстати, говорит, что во всём мире несколько человек умеют программировать на этом языке (что, по всей видимости, правда) и отличают его от случайного текста (что вряд ли, так как глазу зацепиться тут не за что).

5 комментариев