Блокировка экрана на «Маке» из Си

Как-то я программировал на Си под «Мак» блокировку жестом перед датчиком освещения. Сегодня показывал его коллегам, оказалось ничего не работает — метод, который я использовал для вызова экрана блокировки перестал принимать на вход NULL. Тогда как в интернетах только этот способ везде и упоминается:
CGSCreateLoginSession(NULL);
// теперь выдаёт
// Assertion failed: (sid != NULL), function CGSCreateLoginSession, file Services/Connection/CGSSession.c, line 418.
Возможно в «Эль-Капитане» (последней версии «МакОСи») что-то поменялось — функция CGSCreateLoginSession считается недокументированной, никакой информации о ней мне найти не удалось.

В общем, покапался я в заголовочных файлах, подбором и интуицией родил код, который блокирует экран и под «Эль-Капитаном», заодно и утилиту свою поправил:
// получаем словарик с атрибутами текущей сессии
CFDictionaryRef dict = CGSCopyCurrentSessionDictionary();
// выбираем из него идентификатор текущей сессии, получаем его в виде int
CFNumberRef number = (CFNumberRef) CFDictionaryGetValue(dict, CFSTR("kCGSSessionIDKey"));
CGSSessionID currentSession;
CFNumberGetValue(number, kCFNumberIntType, &currentSession);
// блокируем текущую сессию
CGSCreateLoginSession(&currentSession);
Обновлённая версия лежит там же — на «Гитхабе».
3 комментария
21 октября 2015 17:23

Где это я был?

Где я был (73.84КиБ) Нацарапал утилитку на «Баше», которая помогает узнать к каким вайфай-сетям цеплялся ваш «Мак» в последнее время. Утилита требует подключения к интернету.
2 комментария
13 октября 2015 08:14

Чудны́ дела твои, PHP

var_dump(array_pop(range(0,5)));
// Strict Standards: Only variables should be passed by reference in Command line code on line 1
// int(5)

var_dump(array_pop( (range(0,5)) ));
// int(5)
В первом случае ПХП обращает наше внимание на то, что функция array_pop требует передачи значения по ссылке, что возможно только для переменной. Поскольку мы передаём результат работы функции, то логично, что ПХП в этом месте нам на это пеняет. Но если обернуть функцию в скобки, то интерпретатор уже всё устраивает.

Я не смотрел байт-код, поэтому не знаю причины точно, но мне кажется всё дело в том, что во втором случае примитивный парсер ПХП просто не узнаёт ситуацию, в которой должен выдавать предупреждение.
1 комментарий
16 сентября 2015 15:25

65536

Если какие-то числа часто встречаются в работе, то всё, связанное с ними в остальной жизни, тоже очень эффективно запоминается. У программистов это, в основном, степени двойки, возможно марки старых процессоров и сопроцессоров Интела (например, 8080, 386, 387, 486), какие-то константные адреса (например, я со времён «Спектрума» помню адрес 23755 — начало программы на Бейсике в памяти).

Когда я начал заниматься историей, к этому всему добавились ещё числа. Пока у меня это только года. 1834 — второй год восьмой ревизии, 1850 — девятая ревизия и год, когда Бугульминский уезд перешёл из Оренбургской губернии в Самарскую и так далее.

Поэтому, увидев на часах 18:34, я улыбаюсь этому числу, как старому знакомому — я его за последние годы видел на тысячах, а то и десятках тысяч архивных листах. Номер 386 на авто так же привлекает моё внимание, так же как номер квартиры 256 (степень двойки). Для меня это очень особенные числа — события связаные с ними, как будто сами ложаться в память.

А в ваших профессии и хобби есть такие числа?
10 комментариев
20 августа 2015 07:46

«Кремниевая долина»

Пока отхожу от операции, смотрю сериал «Кремниевая долина» и дабы не заржавел мозг, сделал расшифровку надписи на футболке Эрлиха — одного из героев сериала, на трёх языках: Перле, Пайтоне и Тикле. Листинг один для трёх языков:
#\
"@{[$\=qq{\n},\
print map {chr $_} (0b1000010, 0b1101001, 0b1110100, 0b1100011, 0b1101111, 0b1101001, 0b1101110)]}";#\
__END__ = 1;\
print(''.join(chr(b) for b in (0b1000010, 0b1101001, 0b1110100, 0b1100011, 0b1101111, 0b1101001, 0b1101110))); #\
"""
foreach name {01000010 01101001 01110100 01100011 01101111 01101001 01101110} {
    puts -nonewline [binary format B* $name]
}
puts {} ;#"""
Дольше всего с Перлом возился, пока не вспомнил, что у него есть вычисление выражений внутри строк через конструкцию @{[…]}. Нужно рассказать как всё работает?
Комментировать
10 июля 2015 18:29

Политкорректный Unicode 8

Я всё ждал, когда это появится, и вот наконец в восьмом Юникоде обнаружились модификаторы цвета кожи. Модификаторов в Юникоде — пруд пруди, это специальные символы, которые сами по себе ничего не значат — они привносят что-то новое в символ, с которым комбинируются. Простой пример — модификатор ударения, вот вам ударная буква «й»: «й́». Модификаторы цвета (57.52КиБ) Вот восьмой Юникод вводит модификаторы цвета (на картинке выше), при комбинировании с которыми цветные «эмоджи», которые уже давно входят в Юникод, могут менять свой цвет: Смена цвета (12.86КиБ) Юникод всё усложняется и усложняется, чувствую, однажды они придут к чем-то похожему на ХТМЛ+ЦСС — будут базовые элементы и стилевые таблицы для их изменения.
6 комментариев
6 июля 2015 08:09

Изучаю TCL

Учу «Тикль» потихонечку — вечерами, урывками. Язык нравится очень простым синтаксисом, который позволяет делать крутые штуки — всё в языке считается строкой, а вопросом интерпретации строки занимается код. Это даёт возможность, например, расширять язык по необходимости.

Попробую пояснить как это работает. В данном случае все три параметра, передаваемых команде puts эквивалентны:
puts Hello; # это строка
puts "Hello"; # это тоже строка
puts {Hello}; # и это строка!
То есть фигурные скобки — это не «блок кода», а строка, правда со своими особыми правилами интерполяции. Это, вместе с конструкциями изменения уровня выполнения и позволяет создавать собственные дополнения к языку. Например, добавил в язык конструкцию unless (это такой if, только проверяется не истинность, а ложность условия):
proc unless {condition body} {
    ;# uplevel — конструкция, позволяющая выполнить блок выше по стеку
    ;# то есть, в данном случае, мы выполняем код в контектсе вызвавшего
    ;# процедуру unless кода, что позволяет увидеть все переменные, которые там определены
    uplevel [list if !($condition) $body]
}

set var false

;# Это наша новая команда
unless { $var } {
     puts "False: $var" ;# напечатает «False: false»
}

set var true

;# А это обычный if, которые есть в языке
if { $var } {
    puts "True: $var" ;#  напечатает «True: true»
}
Надо бы, конечно, что-то пописа́ть, чтобы лучше понять язык, задумки есть, скоро приступлю.
11 комментариев
26 июня 2015 08:31

Tcl/tk и Retina

О, оказывается, можно «ретинизировать» интерпретатор Тикля — сделать так, чтобы программы на этом языке хорошо смотрелись на маковской ретине (см. скриншот). Ретинизированный Тикль (24.29КиБ) Для этого нужно скачать программу Retinizer и бросить в неё Wish.app, который находится в /System/Library/Frameworks/Tk.framework/Versions/Current/Resources/
Комментировать
23 июня 2015 19:38

Странный PHP

А вот ПХП, в отличие от ДжаваСкрипта действительно странный. Давайте посмотрим на такой вот код и его результат:
$ php -a
Interactive shell

php > $a = 1; echo $a + $a++;
3
php > $a = 1; echo $a + $a + $a++;
3
Как видите, в том и другом случае у нас один результат — «3». Даже первая «тройка», казалось бы, противоречит здравому смыслу, а вторая — тем более. Что же происходит? Давайте разбираться.

Как работает первый пример?

Операция сложения левоассоциативна — разбор агрументов начинается слева направо. Двигаясь таким образом, парсер видит выражение в котором две операции — сложение и постинкремент, у постинкремента приоритет выше, поэтому вычисляется сначала он — возвращая в качестве значения «1» и увеличивая переменную на единицу, потом вычисляется операция сложения, складывая полученную единицу с двойкой (так как постинкремент увеличил значение переменной на единицу). Получается «три».

Похожим образом обрабатывается умножение вместе со сложением: 2 + 2 * 2 = 6, а не 8, потому что умножение имеет более высокий приоритет.

Во втором случае всё происходит похожим образом, но чуть иначе — парсер, обрабатывая левоассоциативное сложение, берёт первые два аргумента, складывает их, получает «двойку», двигается дальше, видит двойку, сложение и постинкремент переменной. Постинкремент более приоритетный, он его вычисляет раньше, возвращая в сложение «единицу», значение переменной увеличивается, но его уже никто не использует — складываются числа «2» (от предыдущего сложения) и «1» (вернул постинкремент). Получается «три».

В байт-кодах всё перечисленное выглядит следующим образом: Байт-коды (23.90КиБ) Тут с восьмой строки начинается второй пример (правда присваивание единицы во втором примере я опустил — как видите второй операции ASSIGN нет).
12 комментариев
17 июня 2015 16:45

«Странный» JS

В ДжаваСкрипте полно странностей, да, но популярная картинка, которая получила сейчас широкое хождение (не буду её приводить, просто суть перепишу) имеет отношение только к безграмотности её авторов. Суть такова:
[] + [] // массив + массив
"" // результат — пустая строка

[] + {} // массив + объект
"[object Object]" // В результате получаем объект? Ну ок

{} + [] // объект + массив
0 // 0? Реально?

{} + {} // объект + объект
NaN // Not a Number? WAT?
Выглядит очень всё ооочень странно, если думать, что происходит именно то, что написано в коментариях справа. На самом деле происходящее неверно интерпретировано авторами.

В ДжаваСкрипте фигурные скобки, помимо объекта, задают ещё и блок кода. В данном случае все «объекты» слева — на самом деле блоки кода. Откроем консоль и попробуем это доказать: Вот что происходит на самом деле (9.90КиБ) Как видите, в левый блок можно без труда добавить код и станет очевидно, что это не описание объекта. Так два последних обсуждаемых примера распадаются на две независимые команды — определение пустого блока кода, который ничего не выполнит и унарную операция «плюс» над пустым объектом или массивом.

С массивом, возвращающим пустоту при суммировании (конкатенации) всё ещё проще — в данном случае производится попытка вызвать метод toString, который у массива выводит строку его значений через запятую. Так как значений нет, строка пустая.

В общем, ничего сильно странного не происходит.
42 комментария
16 июня 2015 07:05

PHP/FI

На ПХП я начинал программировать с третьей версии, предыдущую, вторую, никогда в глаза не видел. Занялся сегодня археологией, что интересно, интерпретатор собрался под «Мак» без каких-либо проблем, а я уже настроился его патчить.

По описанию, вторая версия — удивительно бедный язык, я попытался написать подобие функции var_dump (которой тогда в языке не было), описал в комментариях с чем столкнулся. Надо сказать, некоторые вещи очень сильно удивили.

Вообще, я хотел написать одну функцию, а отступ для вывода скаляра передавать вторым параметром, но оказалось, во-первых, что не указанный параметр при первом вызове сохраняет своё состояние — в каждый следующий вывод он попадает через какую-то магию (причём это работает не всегда), а во-вторых, если не указать второй параметр, то он получит значение первого, а первый станет не заданным (isset вернёт «ноль», булевского типа нет, поэтому «ноль»).

В общем, пришлось сделать две разные функции.
#!php.cgi -q
<?
# однострочные комментарии только такие, два слеша не работают

	/* так описываются функции */
	function var_dump_scalar $var, $indent (
		if ($indent) {
			/* вот так выглядит конкатенация строки — плюс, а не точка */
			/* интерполяции нет, а одинарные кавычки вернут код символа в них */
			$indent = strtr(sprintf("%' '" + $indent + "d", 0), "0", " ");
			/* функции str_repeat тоже нет, приходится вот так выкручиваться */
		} else {
			$indent = "";
		}

		/* всего три типа данных, нет булевого типа, нет ресурсов — fopen вернёт число */
		/* объектов тоже нет, конечно же */
		switch (gettype($var)) {
			case "integer"; /* после case точка с запятой, а не двоеточие */
				echo "%sint(%d)\n" $indent, $var;
				break;
			case "string";
				echo "%sstring(%d) \"%s\"\n" $indent, strlen($var), $var;
				break;
			case "double";
				echo "%sfloat(%f)\n" $indent, $var;
				break;
		}
		/* можно было бы вернуть всё через return, но мне не удалось бы продемонстрировать несколько вещей */
	);

	function var_dump $var (
		/* смотрим — передан ли параметр */
		if (isset($var)) {
			/* пытаемся определить — не массив ли это, отдельного типа нет */
			$cnt = count($var);
			if ($cnt > 1 || key($var) != "0") {
				reset($var);
				/* echo умеет принимать форматирующую строку */
				echo "array(%d) {\n" $cnt;

				/* других способов проверять конец массива нет — только итерировать по длине */
				/* цикла for в языке тоже нет */
				while ($cnt > 0) {
					$key = key($var);
					echo "  [\"%s\"]=>\n" $key;
					next($var);

					/* тут только скаляры, так как массивов, кроме одномерных не бывает */
					var_dump_scalar($var[$key], 2);

					$cnt--;
				}

				echo "}\n";
			} else {
				var_dump_scalar($var, 0);
			}
		} else {
			echo "Warning:  var_dump() expects exactly 1 parameter, 0 given\n";
		}
	);

/* ассоциативный массив */
$b["aaa"] = "aaa";

/* числовой массив, отдельного типа массива нет, любой тип является нулевым элементом массива */
/* конструкции array(...) тоже нет, массивы задаются только так — в строку */
$a = 1;
$a[1] = 2;

/* двухмерных массивов у нас нет, это просто операция слияния массивов */
$a[] = $b;

var_dump($a);

/* Результат работы:
array(3) {
  ["0"]=>
  int(1)
  ["1"]=>
  int(2)
  ["2"]=>
  string(3) "aaa"
}
*/

/* вот так заканчиваются PHP-скрипты, знака вопроса у закрывающего тега нет */
>
Но самый сок, конечно, это две функции — ClearStack и SecureVar. Их я не использовал, просто не придумал как. Первая является костылём к парсеру (почитайте описание, у меня нет сил это описывать), вторая — фильтрует переменные из ГЕТ- (но почему-то не из ПОСТ-) запроса через маску. Секюрити!
2 комментария
13 июня 2015 21:10

Опции просмотрщика PDF в Chrome

Многие, наверное, знают что у плагина Эдоуби для просмотра ПДФ есть специальные параметры, которые передаются фрагментом в урле. Эдоуби публиковала эти параметры в разных документах, а теперь они собраны в специальном ЭрЭфСи под номером 3778.

Я для интереса попробовал эти параметры в «Хроме» (там собственный просмотрщик, поэтому я не удивился бы, если бы они не сработали) и оказалось, что три из них всё же работают:
# увеличение на 50%
http://example.org/sample.pdf#zoom=50
# переход к странице №2
http://example.org/sample.pdf#page=2
# переход к главе «Chapter»
http://example.org/sample.pdf#nameddest=Chapter
Подробнее можно прочитать ЭрЭфСи (в частности, в «zoom» можно указать ещё два параметра). Так же параметры можно комбинировать, это тоже работает — если указать их через амперсанд.
4 комментария
24 мая 2015 09:15

JBIG2

У нас в документообороте всё больше используется формат ПДФ, но с ним есть проблема — документы из некоторых сторонних организаций к нашим клиентам приходят на бумаге, по факсу или каким-то ещё подобным способом, причём объём такой корреспонденции незначительным назвать трудно.

Пока мы такие документы помещаем в ПДФ сканами в хорошем качестве в формате ДжПЕГ. Увы, это довольно расточительно. К счастью, ПДФ поддерживает не один, а целых три формата графики — ДжПЕГ, ДжПЕГ 2000 и ДжБИГ2. На последний я возлагал большие надежды.

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

Ошибки JBIG2 (34.98КиБ) К сожалению, у формата есть серьёзный недостаток, на который я тут же и наткнулся.

Алгоритм устроен так, что он подбирает не просто одинаковые символы, а похожие. Это разумно — принтеры печатают неравномерно из-за бумаги и тонера, при сканировании вносятся искажения, а при передаче факсом их становится ещё больше. Вот тут и приходит беда — кодек может перепутать похожие, но разные символы, что для документа совершенно недопустимо.

Посмотрите, у меня на скриншоте выше — оригинальный ПДФ (слева) и ПДФ, где графика перекодирована в ДжБИГ2. Как видно при кодировании были перепутаны «п» и «и», «п» и «н». Для слов в документах может это ещё и не смертельно, но для цифр в суммах — беда.

Мне неясно можно ли что-то сделать в этом месте, возможно можно подредактировать кодек так, чтобы он реагировал только на 100% совпадение, но тогда, скорее всего, пропадёт всякий смысл использовать ДжБИГ2.

Остаётся ещё вариант с ДжПЕГом 2000, но с ним возможно, выигрыш будет столь незначительным, что затраты на конвертацию просто не окупятся.
18 комментариев
22 мая 2015 09:30

Itertools для PHP: продолжение

Библиотеку itertools для ПХП я всё же переписал: теперь на вход всем функциям можно передавать что угодно итерируемое. Из ограничений осталось только два: функция iter в моём варианте принимает только один аргумент, а у функций product и izip_longest последний аргумент является обязательным.

Вот, кстати, как переписывается программка итерстрип, написанная мной семь лет назад на «Пайтоне», теперь её можно повторить на ПХП почти один-в-один:
require 'itertools.php';
use function itertools\groupby, itertools\xrange, itertools\chain, itertools\repeat;

function ntrim($letter, $n=3, $replby=1)
{
    $shrink_groups = function($letter, $n) {
        foreach (groupby(chain(repeat('', $n), $letter, repeat('', $n))) as list($item, $grp)) {
            $grp = iterator_to_array($grp);

            yield $item || sizeof($grp) < $n ? $grp : '';
        }
    };

    return array_slice(iterator_to_array(chain(...$shrink_groups($letter, $n))), $replby, -$replby);
}

print_r(ntrim(['', 1, 2, 3, '', '', '', '', 4, '', '']));
Увы, на ПХП всё ещё не так компактно (из-за отсутствия генераторных выражений), но уже очень близко.
4 комментария
11 мая 2015 12:17

Upsert в PostgreSQL

Ура, появился коммит, добавляющий к оператору INSERT «Постгреса» конструкцию, которая позволяет использовать его как upsert (в «Оракле» это конструкция MERGE, в «Майэскуэеле» — INSERT… ON DUPLICATE KEY UPDATE):
-- Обновить, если не удалась вставка
INSERT INTO distributors AS d (did, dname) VALUES (8, 'Anvil Distribution')
ON CONFLICT (did) DO UPDATE
SET dname = EXCLUDED.dname || ' (formerly ' || d.dname || ')'
WHERE d.zipcode != '21201';

-- Если не удалась вставка, ничего не делать
INSERT INTO distributors (did, dname) VALUES (10, 'Conrad International')
ON CONFLICT (did) WHERE is_active DO NOTHING;
Счастье-то какое!