О чем речь (цитаты хайпующего в настоящий момент в лентах новостей deepseekа):

  1. Quansheng UV-K5 — это недорогая портативная радиостанция с поддержкой диапазонов VHF и UHF, широким функционалом, возможностью модификации прошивки и программирования, популярная среди радиолюбителей и профессионалов.
  2. Прошивка — это изменение или обновление встроенного программного обеспечения радиостанции, которое влияет на ее базовую функциональность.
  3. Программирование — это настройка параметров работы радиостанции в рамках ее текущей прошивки.

В сети полно инструкций про Windows, я коротко расскажу про работу со станцией под Linux

1. Кабель.

Для прошивки и программирования подходит один и тот же кабель. Самый обычный, совместимый с популярными аналоговыми Баофенгами вроде Baofeng BF-888S.

Кабель работает под Linux из коробки, определяется как USB Serial, цитата из вывода команды dmesg:

[   69.062664] usb 3-1: new full-speed USB device number 4 using xhci_hcd

...

[   69.274720] usb 3-1: ch341-uart converter now attached to ttyUSB0

Если у пользователя не хватает прав для работы с портом, скорее всего поможет добавление в  группу dialout (команда sudo usermod -a -G dialout $USER) и последующий ребут. 

2. Прошивка

Технологии дошли до того, что прошить радиостанцию (в том числе под Linux) можно прямо из браузера, но душа ретрограда требует консольной утилиты, и такая есть — k5tool.

Утилита на писана на C#, для ее работы нужно накатить в систему пакет mono-runtime (для debian-подобных дистрибутивов — командой sudo apt install mono-runtime), после чего утилиту можно запускать скриптом k5tool из корня скачанного git-репозитория.

Порт ttyUSB0 утилита распознает сама, указывать его требуется.

Далее нужно выбрать прошивку. Я выбрал прошивку egzumer, как достаточно функциональную, с хорошей документацией и прозрачной разработкой на githubе.

Перед манипуляциями рекомендуется забекапиться.

  1. Сделаем бекап калибровки:
    k5tool -rdee 0x1E00 0x0200 eeprom-calib.raw
  2. И бекап памяти eeprom целиком:
    k5tool -rdee
  3. После чего прошьем прошивку (это действие нужно проводить в особом режиме — рацию нужно включить с зажатой клавишей PTT, все остальные действия в статье работают на включенной обычным образом рации):
    k5tool -wrflash ./egzumer_v0.22.packed.bin
Готово, можно слушать дальнобойщиков на 27,135 МГц!

3. Программирование

Для программирования радиостанций под Linux есть популярная графическая программа — chirp. В сети много информации про плагины к ней для различных кастомных прошивок, но в первую очередь — смотрите документацию на выбранную прошивку. Так, с прошивкой egzumer, все просто — свежий chirp поддерживает ее из коробки (уже имеет встроенный плагин), в том числе позволяет настраивать специфичные для этой кастомной прошивки параметры. То, что chirp распознал прошивку, можно увидеть в окне Radio -> Download from radio... по надписи "Quansheng UV-K5 egzumer".

Официальная документация chirp рекомендует использовать для установки менеджер пакетов pipx, я же ограничился Debianовским aptом:

  1. Установил зависимости (полный список зависимостей см. в requirements.txt):
    sudo apt install python3-wxgtk4.0 python3-serial python3-requests python3-suds python3-yattag python3-lark
  2. После чего chirp можно запускать командой ./chirpwx.py из корня скачанного git-репозитория chirp.

Ссылки по теме UV-K5 


 Пожалуй, главный совет: покупая устройство Xiaomi, которое вы хотите использовать в составе умного дома, крепитесь! Теперь расскажу о своем опыте с парой таких устройств. Все манипуляции проводил со стандартным (не модифицированным) приложением Mi Home из play-маркета.

Xiaomi Smartmi Humidifier 2

Барабан с вентилятором от Xiaomi, который, в отличие от многочисленных ультразвуковых коллег, позволяет использовать воду из-под крана. Речь о второй версии, без экрана, уже снятой с производства (думаю, что совет актуален и для обновленных версий). У меня китайская версия аппарата (ее можно отличить по китайской вилке), отсюда и проблема.
  1. Стандартной рекомендации для китайских устройств Xiaomi — выставить материковый Китай в приложении Mi Home, для увлажнителя недостаточно. Ошибка с неподдерживаемым регионом остается.
  2. Добавить аппарат в Mi Home можно только установив на телефоне часовой пояс Китая. После этого аппарат остается в приложении Mi Home, обновляются его параметры (температура, влажность), но управлять им через Mi Home можно только с выставленным китайским временем, что, конечно, не удобно.
  3. Спасет отца демократии приложение Умный дом от Яндекса, с которым нужно связать аккаунт Mi Home. Тогда увлажнитель появится среди устройств умного дома Яндекса и через приложение Яндекса (а заодно и через Алису) им можно будет управлять и из российского часового пояса.

Xiaomi Mijia Smart Pet Feeder 2 (MJWSQ02) CN

Кормушка, конечно, тоже китайской версии. Здесь не нужно выставлять китайский часовой пояс, достаточно выбрать материковый Китай в настройках региона Mi Home. Также рекомендуется установить английский язык в приложении, т.к. русский перевод не полный и непереведенные сущности будут отображаться на китайском.
С этим устройством мне откровенно не повезло. Mi Home устройство находило, конфигурировало, но на этапе подключения аппарата к WiFi-сети Mi Home выдавало ошибку "Connection timed out". Интересно, что роутер даже видел недолгое подключение аппарата (кормушка отображалась в его web-интерфейсе). И на дисплее кормушки значок WiFi на несколько секунд горел не мигая, а затем гас. Будто кормушка успешно подключалась к WiFi, но затем не могла достучаться до серверов Xiaomi. Что делал:
  1. Безуспешно пробовал — отключена 5Ггц сеть (кормушка поддерживает только 2.4 Ггц), сменены SSID и пароль на короткие слова из маленьких латинских букв, сменен режим Wifi (на фиксированный 802.11n), перебраны режимы WPA/WPA2/WPA+WPA2, шифрования AES/TKIP, DNS-сервера Яндекса, Google и Cloudflare, разные каналы — ничего из этого не помогло.
  2. Попробовал раздать интернет с телефона — и, о чудо, кормушка успешно подключилась!
  3. Виноват провайдер или роутер? Воткнул в ПК, который подключен к роутеру, Wifi-свисток и раздал с него интернет. С ним кормушка также заработала! Перепроверил — подключился с ноута к этой же импровизированной сети — ПК не добавил каких-то новых левых DNS-серверов, используется DNS провайдера. Значит провайдер не виноват, все дело — в роутере.
  4. Роутер у меня — SmartBox Turbo+ от Beeline, хотя провайдер давно уже другой, но роутеры от Билайна — универсальны и хороши по отношению цена/качества, менять роутер не хотелось. Решил установить на него OpenWRT. Обращу внимание, что при установке OpenWRT советуйтесь в первую очередь с официальным вики. Например, для моего роутера в сети полно устаревших инструкций, которые предлагают менять загрузчик, меж тем как технологии уже дошли до того, что OpenWRT в два клика шьется через стоковый билайновский web-интерфейс.
  5. Затем настроил новую прошивку (см. неплохую статью про минимальную настройку).
  6. И вуоля, кормушка полноценно заработала! Т.е. имеется какая-то несовместимость кормушки со стоковой прошивкой билайновского роутера.
  7. Кормушка автоматом подхватывается и в приложении Умного дома Яндекса, хотя там возможности работы с ней сильно ограничены по сравнению с Mi Home.

UPDATE МАРТ 2020: Яндекс актуализировал описание на официальном сайте, следует ориентироваться на него.

2020-й год, Yandex.Browser версии 19.12.3.332, Debian 10 Buster и сайт с DRM-видео, на примере Кинопоиск HD.

Трейлеры воспроизводятся, т.е. можно предположить, что с кодеками все в порядке. Однако при попытке воспроизведения купленного фильма видим: «Пожалуйста, подождите, идет настройка браузера для проигрывания видео. Для ручной настройки ознакомьтесь с инструкцией».

В своей инструкции Яндекс и авторы других прошлогодних инструкций предлагают распаковать libwidevinecdm.so в /opt/yandex/browser-beta/. Не помогает! Утилита lsof показывает, что файл libwidevinecdm.so браузером не открыт, а с помощью strace выясняется, что Яндекс.Браузер ищет  библиотеку в каталоге /opt/yandex/browser-beta/WidevineCdm/_platform_specific/.

Вот исправленная рабочая версия инструкции:


  1. Проверьте версию браузера. Если версия ниже 18.11.1, обновите браузер;
  2. Скачайте архив плагина Widevine Content Decryption Module;
  3. Извлеките из архива файл libwidevinecdm.so;
  4. Разместите его по пути
    /opt/yandex/browser-beta/WidevineCdm/_platform_specific/linux_x64/
    ;
  5. Измените права доступа к файлу libwidevinecdm.so на 644.

Yet another субъективный сравнительный обзор музыкальных стриминговых сервисов.

Блюдя тематику (GNU/Linux) блога сравнивались только мобильные приложения для Android и десктопные web-интерфейсы.

В сравнении не участвует Spotify из-за своей скорости принятия корпоративных решений, потому что он уже более пяти лет скоро-скоро запустится в России. Также не участвуют сервисы Google из-за вечного бардака. Не понятно, на какой сервис можно положиться (Google Play Music/YouTube Music), какой выживет, а какой будет закопан, как были закопаны Picasa, Блокнот, Google Reader и так далее.

Параметр для сравнения Яндекс.Музыка Deezer
Синхронизация текущего списка воспроизведения Главный провал Яндекса: перейдя с одного устройства на другое, приходится заново искать то, что только что играло UPD от февраля 2020 г.: Яндекс выкатил таки функцию под названием «Музыка продолжается» В Deezer действительно можно прийти домой и одним кликом запустить на ПК то, что только играло в наушниках на телефоне. Причем возможность эта отключается в настройках.
Поиск Поиск у Яндекса лучше, даже по иностранной музыке Поиск уступает Яндексу
Присутствие альбомов Явно больше альбомов русских исполнителей. Из зарубежных исполнителей по сравнению с Deezer не удалось найти (понятно, что выборка очень субъективна) только пару альбомов каверов. Немного меньше русской музыки. Что хуже: иногда неправильно проставлены даты выходов альбомов русских исполнителей, кажется вместо дат выходов указаны даты добавления в каталог (лейбла/самого Deezerа/...)
Гибкость рекомендаций У Яндекса всегда под рукой радио по исполнителю, альбому (чего у Deezer нет), треку, субъективно разнообразней тематические подборки Миксы только по исполнителю и отдельному треку
Качество Mp3 320 kb/s Mp3 320 kb/s на обычных подписках и flac на подписке HiFi за отдельную плату
Интерфейс Андроид-приложения Отличный Субъективно приложение для Андроид выглядит неаккуратным
Web-интерфейс Не идеальный, очередь воспроизведения не под рукой Отличный в целом, явно указывается качество в kb/s
Запуск/остановка воспроизведения медиаклавишей клавиатуры Запуск воспроизведения работает раз через три
UPD от марта 2020 г.: Яндекс таки починил прошлогодний баг — теперь все ОК
Просто работает
Дополнительные плюшки подписки Скидки на различные сервисы Яндекса Доп. плюшек нет
Семейная подписка Дороже (299 руб.), меньшее количество человек (4) Дешевле (255 руб.), большее количество человек (6), но есть и минус — на семью единый аккаунт с единым логином-паролем с шестью профилями, т.е. подписку получится делить лишь между людьми, которым вы полностью доверяете (действительно подписка именно семейная)
Цена индивидуальной версии 169₽/месяц, 1690₽/год, но есть акционные варианты за 99₽/месяц и 990₽/год 169₽/месяц, 1690₽/год


Устройство клавиатуры можно найти по имени в каталоге "/dev/input/by-id/". Возможно, нужно будет сменить владельца на текущего пользователя:
sudo chown user:user /dev/input/by-id/usb-Logitech_USB_Keyboard-event-kbd 
Далее для снятия дампа в hex-виде предлагаю такую замысловатую команду:
sleep 1 ; hexdump -e '24/1 " %02X" "\n"' /dev/input/by-id/usb-Logitech_USB_Keyboard-event-kbd /dev/input/by-id/usb-Logitech_USB_Keyboard-event-kbd & sleep 5 ; pkill hexdump
Здесь через 1 секунду (sleep 1, чтобы нажатие Enter не попало в дамп) начнется захват данных с клавиатуры, будет выводиться по 24 байта на строку (24/1), еще через 5 секунд (sleep 5) захват будет остановлен (pkill hexdump, чтобы мы смогли вновь пользоваться клавиатурой после снятия дампа).
Пример вывода, где пару раз была нажата кнопка F1:
sleep 1 ; hexdump -e '24/1 " %02X" "\n"' /dev/input/by-id/usb-Logitech_USB_Keyboard-event-kbd /dev/input/by-id/usb-Logitech_USB_Keyboard-event-kbd & sleep 5 ; pkill hexdump
[1] 14693
 15 87 FC 5D 00 00 00 00 40 CA 07 00 00 00 00 00 04 00 04 00 3A 00 07 00
 15 87 FC 5D 00 00 00 00 40 CA 07 00 00 00 00 00 01 00 3B 00 01 00 00 00
 15 87 FC 5D 00 00 00 00 40 CA 07 00 00 00 00 00 00 00 00 00 00 00 00 00
 15 87 FC 5D 00 00 00 00 3E C4 08 00 00 00 00 00 04 00 04 00 3A 00 07 00
 15 87 FC 5D 00 00 00 00 3E C4 08 00 00 00 00 00 01 00 3B 00 00 00 00 00
 15 87 FC 5D 00 00 00 00 3E C4 08 00 00 00 00 00 00 00 00 00 00 00 00 00
 16 87 FC 5D 00 00 00 00 37 C4 08 00 00 00 00 00 04 00 04 00 3A 00 07 00
 16 87 FC 5D 00 00 00 00 37 C4 08 00 00 00 00 00 01 00 3B 00 01 00 00 00
 16 87 FC 5D 00 00 00 00 37 C4 08 00 00 00 00 00 00 00 00 00 00 00 00 00
 16 87 FC 5D 00 00 00 00 75 60 09 00 00 00 00 00 04 00 04 00 3A 00 07 00
 16 87 FC 5D 00 00 00 00 75 60 09 00 00 00 00 00 01 00 3B 00 00 00 00 00
 16 87 FC 5D 00 00 00 00 75 60 09 00 00 00 00 00 00 00 00 00 00 00 00 00
Input-устройства выдают данные пакетами. Каждый пакет — структура input_event:
struct input_event {
 struct timeval time;
 __u16 type;
 __u16 code;
 __s32 value;
};
Структура имеет размер 24 байта (на 64-битной системе), именно поэтому выше мы выводили дамп по 24 байта на строку.

Первое поле структуры input_event — также структура, timeval. Использовалась 64-битная система, поэтому timeval состояла из двух 64-битных (по 8 байт) целых чисел — значения секунд и микросекунд. По первым 8 байтам каждой строки дампа видно, как меняется значение секунд (почти не меняется, только первый байт поменялся с 15 на 16, когда секунда "тикнула"). Следующие 8 байт каждой строки отвечают за микросекунды, а потому меняются хаотично (нажатие/отпускание кнопки F1 приходилось на произвольную долю секунд).

А самое интересное происходит в последних восьми байтах каждой строки дампа. Сначала идет двухбайтное поле type — его значение менялось с 4 (EV_MSC — некая дополнительная информация, разбирать ее не будем) на 1 (EV_KEY — непосредственно событие кнопки клавиатуры) и 0 (EV_SYN — разделитель).

Разберем события EV_KEY, их было всего 4:
 15 87 FC 5D 00 00 00 00 40 CA 07 00 00 00 00 00 01 00 3B 00 01 00 00 00
 15 87 FC 5D 00 00 00 00 3E C4 08 00 00 00 00 00 01 00 3B 00 00 00 00 00
 16 87 FC 5D 00 00 00 00 37 C4 08 00 00 00 00 00 01 00 3B 00 01 00 00 00
 16 87 FC 5D 00 00 00 00 75 60 09 00 00 00 00 00 01 00 3B 00 00 00 00 00
За полем event следует также двухбайтное поле code — код клавиши. В нашем случае — 3B в hex или 59 в десятичном виде. Смотрим файл input-event-codes.h. Действительно, 59 — это код клавиши F1.

И, наконец, последнее поле — четырехбайтное value. Здесь все просто: значение 1 соответствовало нажатию клавиши F1, значение 0 — отпусканию.

Итак, есть роутер WRT54GL (а может быть и другой какой) с прошивкой DD-WRT. Wi-Fi работает (скорее всего годами) и в какой-то момент без видимых причин перестает. Помогает отключение аутентификации. 

Первый раз после выключения аутентификации и повторного включения роутер мой продолжил работать как нужно, но через несколько месяцев (возможно после кратковременного отключения электричества?) проблема повторилась. На этот раз кратковременное отключение аутентификации ситуацию не исправляло. Без аутентификации — Wi-Fi работает, с аутентификацией — нет.

В сети было найдено несколько обсуждений описанной проблемы, но ответ содержал лишь один тред (да еще и на второй странице) на форуме DD-WRT. Проблема в том, что логи исчерпали место на устройстве!

Решение просто как 2x2: Веб-интерфейс → Status → WAN → Data Administration → Delete.

В позапрошлом посте рассказывалось о создании экземпляров объектов GObject по имени класса, где, кроме всего прочего, было сказано:
Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type... и вызова этого метода.
Все почти так и есть, но в коде GtkBuilder случайно был найден довольно изящный и портабельный механизм поиска foo_bar_get_type. Идея простая: нужно создать объект GModule не для динамически загружаемого модуля, а для самой программы. Для чего в метод g_module_open(const gchar *file_name, GModuleFlags flags) вместо имени файла нужно передать NULL:
file_name — the name of the file containing the module, or NULL to obtain a GModule representing the main program itself.
Пример на Vala на основе идей из GtkBuilder (механизм получения типа по имени класса иллюстрирует функция type_from_name_lazy):
public abstract class Animal : Object
{
  public abstract string to_string();
}

public class Cow : Animal
{
  public override string to_string()
  {
    return "I'm a cow!";
  }
}

public class Dog : Animal
{
  public override string to_string()
  {
    return "I'm a dog!";
  }
}

int main()
{
  Animal a;
  Type type;

  a = new Dog(); //< Явно создаем Dog, имя типа зарегистрировано в системе типов.
  print("1) %s\n", a.to_string());

  if((type = Type.from_name("Dog")) != Type.INVALID)
  {
    a = Object.new(type) as Animal;
    print("2) %s\n", a.to_string());
  }
  else
    print("2) Failed to get type!\n");

  if((type = Type.from_name("Cow")) != Type.INVALID)
  {
    a = Object.new(type) as Animal;
    print("3) %s\n", a.to_string());
  }
  else
  {
    print("3) Failed to get type! Trying type_from_name_lazy...\n");

    if((type = type_from_name_lazy("Cow")) != Type.INVALID)
    {
      a = Object.new(type) as Animal;
      print("3) %s\n", a.to_string());
    }
    else
      print("3) Failed to get type!\n");
  }

  return 0;
}

[CCode (has_target = false)]
delegate Type TypeGetFunc();

Type type_from_name_lazy(string name)
{
  void *func = null;

  if(Module.open(null, 0).symbol(ascii_camel_to_snake(name) + "_get_type", out func))
    return ((TypeGetFunc)func)();
  else
    return Type.INVALID;
}

string ascii_camel_to_snake(string input)
{
  var sb = new StringBuilder();

  for(int i = 0; input[i] != '\0'; i++)
  {
    if(
      ( input[i].isupper() && i > 0 && !input[i - 1].isupper() ) ||
      ( (i > 2 && input[i].isupper()) && input[i - 1].isupper() && input[i - 2].isupper() )
      )
      sb.append_c('_');

    sb.append_c(input[i].tolower());
  }

  return sb.str;
}


Вывод программы:
1) I'm a dog!
2) I'm a dog!
3) Failed to get type! Trying type_from_name_lazy...
3) I'm a cow!
И немного про Windows

Лично у меня данный пример как есть в Windows не заработал (собирал компилятором mingw). Не удавалось найти указатель на функцию cow_get_type. Опытным путем выяснилось, что работает такой механизм при соблюдении двух условий:
  1. Объект должен быть реализован в отдельной dll;
  2. До попытки поиска указателя должна быть вызвана хотя бы одна функция из этой dll.

Итак, есть проект на Си/Vala, который собирается под 64bit Windows с помощью GCC (MinGW). Есть желание использовать в проекте библиотеку Intel Math Kernel Library (Intel MKL), но нет желания собирать все это дело с помощью Visual Studio.

Если попробуем просто слинковать lib и dll-файлы из MKL со своим проектом (передав их линковщику через gcc -l), то увидим ошибку вида:

   ...
    C:\Views\dockd_headers\lip__mgls_nt\mgls_nt\src\release\mgls64.lib(./x64/release/lpclsdll.obj):(.text[?messageBox@@YAXPEAD@...]+0xcb): undefined reference to `__security_check_cookie'
    C:\Views\dockd_headers\lip__mgls_nt\mgls_nt\src\release\mgls64.lib(./x64/release/lpclsdll.obj):(.xdata[$unwind$?messageBox@@YAXPEAD@...]+0xc): undefined reference to `__GSHandlerCheck'
    ... 

Как видно, линковщик не может найти символы __GSHandlerCheck и __security_check_cookie.

На sourceforge.net есть обсуждение, где приведены вышеобозначенные ошибки и даже есть идеи, в какую сторону копать.

Я же расскажу, что делать, по шагам:
  1. Искомые символы можно найти в библиотеках libcmt.lib и BufferOverflowU.lib соответственно. В свою очередь библиотеки найти можно в составе различных девелоперских продуктов от Microsoft, в том числе бесплатных. Я взял WinDDK 7600.16385.1.
  2. Просто так слинковать их со своим проектом опять же не получится, т.к. libcmt.lib библиотека жирная и будет конфликтовать с внутренностями mingw:
       ...
    C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-mingw_helpers.o):mingw_helpers.c:(.text+0x0): multiple definition of `_decode_pointer'
    C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/startup/mt/objfre/amd64/tidtable.obj):(.text[_decode_pointer]+0x0): first defined here
    C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-mingw_helpers.o): In function `_encode_pointer':
    C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/mingw_helpers.c:26: multiple definition of `_encode_pointer'
    C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/startup/mt/objfre/amd64/tidtable.obj):(.text[_encode_pointer]+0x0): first defined here
    C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-pesect.o): In function `_ValidateImageBase':
    C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/pesect.c:31: multiple definition of `_ValidateImageBase'
    C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/misc/mt/objfre/amd64/pesect.obj):(.text[_ValidateImageBase]+0x0): first defined here
    C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-pesect.o): In function `_FindPESection':
    C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/pesect.c:51: multiple definition of `_FindPESection'
    C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/misc/mt/objfre/amd64/pesect.obj):(.text[_FindPESection]+0x0): first defined here
    C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-pesect.o): In function `_IsNonwritableInCurrentImage':
    C:/repo/mingw-w64-crt-git/src/mingw-w64/mingw-w64-crt/crt/pesect.c:173: multiple definition of `_IsNonwritableInCurrentImage'
    C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/misc/mt/objfre/amd64/pesect.obj):(.text[_IsNonwritableInCurrentImage]+0x0): first defined here
    C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/5.2.0/../../../../x86_64-w64-mingw32/lib/../lib/libmsvcrt.a(dyyns00201.o):(.text+0x0): multiple definition of `_errno'
    C:/msys64/home/user/test/libs/libtest/../mkl/lib/libcmt.lib(d:/5359.obj.amd64fre/minkernel/crts/crtw32/dos/mt/objfre/amd64/dosmap.obj):(.text[_errno]+0x0): first defined here
    
        ... 
  3. Решим вопрос, взяв из libcmt.lib только нужный нам obj-файл. Называется он gshandler.obj, что логично. Для извлечения его из lib-файла нам все же понадобится Visual Studio (я использовал старенькую 2005-ю).
  4. Открываем Visual Studio Command Prompt.
  5. Получаем список obj-файлов в libcmt.lib с помощью утилиты lib.exe:
      lib /list libcmt.lib > libcmt.txt
  6. В получившемся libcmt.txt ищем полный путь к gshandler.obj, у меня он выглядел так:
    d:\5359.obj.amd64fre\minkernel\crts\crtw32\misc\mt\objfre\amd64\gshandler.obj
  7. Теперь можем извлечь его из lib-файла:
    lib /extract:d:\5359.obj.amd64fre\minkernel\crts\crtw32\misc\mt\objfre\amd64\gshandler.obj libcmt.lib
  8. Мне показалось удобным и стройным завернуть извлеченный obj-файл в персональный lib-файл:
    lib /out:gshandler.lib gshandler.obj
  9.  Готово! Линковка проекта someproj с базовым набором dll и lib-файлов MKL в CMake-файле выглядит примерно так:
    set(MKL_LIBRARIES mkl_core mkl_avx  mkl_sequential mkl_intel_lp64_dll BufferOverflowU gshandler)
    target_link_libraries(someproj ${MKL_LIBRARIES})

В предыдущем посте «Клонирование GObject» я рассказывал про идею некоего обобщенного кода, не зависимого от конкретной задачи, который бы сохранял объекты Gobject в файлы (типы объектов и значения свойств) и потом воссоздавал последовательность объектов по этим файлам. Предполагалось сохранять в текстовый файл имена классов и порядок объектов, а также имена свойств объектов и их значения.

Идея красивая, но здесь мы сталкиваемся с одним ограничением системы типов GObject (ограничением вполне естественным, т.к. написан GObjecct на Си и вся поддержка ООП реализована на уровне библиотеки): чтобы создать экземпляр класса по имени класса нужно, чтобы этот класс уже был зарегистрирован в системе типов GObject. Т.е. для типа FooBar должен быть заранее вызван метод foo_bar_get_type непосредственно или опосредованно (например, через метод создания вроде foo_bar_new).

Получается: либо типы всех потенциально загружаемых из файла объектов должны быть заранее зарегистрированы, т.е. все (обязательно все) и явно (в каком-то виде) типы должны быть прописаны в коде разработчиком, либо же нужен неизящный и непортабельный механизм вроде поиска в таблице символов исполняемого файла для имени каждого подгружаемого из файла класса соответствующего метода foo_bar_get_type (см. тему на stack overflow) и вызова этого метода.

UPDATE: Найден довольно изящный и портабельный способ поиска метода foo_bar_get_type, см. Создание объектов GObject по имени типа. Часть 2.

Пример на Vala ниже иллюстрирует описанную проблему. Мы можем создать объект типа Dog по имени, т.к. выше уже создан объект типа Dog и класс зарегистрирован в системе типов. Создание объекта типа Cow же завершается неудачей (см. вывод программы после исходного кода).

public abstract class Animal : Object
{
  public abstract string to_string();
}

public class Cow : Animal
{
  public override string to_string()
  {
    return "I'm a cow!";
  }
}

public class Dog : Animal
{
  public override string to_string()
  {
    return "I'm a dog!";
  }
}

int main()
{
  Animal a;
  Type type;

  a = new Dog(); //< Явно создаем Dog, здесь класс и регистрируется в системе типов.
  print("1) %s\n", a.to_string());

  if((type = Type.from_name("Dog")) != 0)
  {
    a = Object.new(type) as Animal;
    print("2) %s\n", a.to_string());
  }
  else
    print("2) Failed to get type!\n");

  if((type = Type.from_name("Cow")) != 0)
  {
    a = Object.new(type) as Animal;
    print("3) %s\n", a.to_string());
  }
  else
    print("3) Failed to get type!\n");

  return 0;
}
Вывод программы:
1) I'm a dog!
2) I'm a dog!
3) Failed to get type!

В одной из программ на работе используется ряд самописных Gtk-виджетов для настройки фильтров изображения. Виджеты лежат в Box'е, добавляются, удаляются и настраиваются пользователем. У каждого типа виджетов свой набор настроек. Все настройки виджеты хранят в своих свойствах.

Чтобы сохранять после перезапуска программы список фильтров и их параметры, была идея сделать некий обобщенны код, не зависимый от конкретной задачи, который бы сохранял объекты Gobject в файлы (типы объектов и значения свойств) и потом воссоздавал последовательность объектов по этим файлам.

В качестве "proof of concept" был написан код, клонирующий объект Gobject. Функция g_object_clone возвращает копию объекта, переданного ей в качестве параметра src. Код основан на примере со StackOverflow. Обращаю внимание, что полноценно копируются только простые свойства, для boxed-типов копируется указатель с инкрементированием счетчика ссылок.

Код gobject_clone.c:
// gcc `pkg-config --libs --cflags glib-2.0 gobject-2.0` gobject_clone.c point.c -o gobject_clone
#include <glib-object.h>
#include "point.h"
#include <stdio.h>
#include <string.h> //< For strcmp.

// See http://stackoverflow.com/questions/3003655/is-there-a-good-way-to-copy-a-gtk-widget?answertab=votes#tab-top
static GObject *g_object_clone(GObject *src)
{
  GObject *dst;
  GParameter *params;
  GParamSpec **specs;
  guint n, n_specs, n_params;

  specs = g_object_class_list_properties(G_OBJECT_GET_CLASS(src), &n_specs);
  params = g_new0(GParameter, n_specs);
  n_params = 0;

  for (n = 0; n < n_specs; ++n)
    if((specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
    {
      params[n_params].name = g_intern_string(specs[n]->name);
      g_value_init(&params[n_params].value, specs[n]->value_type);
      g_object_get_property(src, specs[n]->name, &params[n_params].value);
      ++n_params;
    }

  dst = g_object_newv(G_TYPE_FROM_INSTANCE(src), n_params, params);
  g_free(specs);
  g_free(params);

  return dst;
}

// Print, clear, print again.
static void psp (Point *p)
{
  g_print("OBJECT %p\n", p);
  g_print("  Before clearing:\n");
  point_print(p);
  g_signal_emit_by_name(p, "clear");
  g_print("  After clearing:\n");
  point_print(p);
  g_print("  ...\n");
}

int main(int argc, char **argv)
{
  g_type_init();

  GArray *array = g_array_new(FALSE, FALSE, sizeof(gint));
  gint val1 = 33, val2 = 44;
  g_array_append_val(array, val1);
  g_array_append_val(array, val2);
  g_array_append_val(array, val1);

  GObject *p1 = g_object_new(TYPE_POINT, "x", 5, "y", 10, "a", array, NULL);
  GObject *p2 = g_object_clone(p1); //< Note: garray uses ref/unref for copy/free.

  psp(POINT(p1));

  g_print("Appending value '%d' to array.\n", val2);
  g_print("  ...\n");
  g_array_append_val(array, val2);

  psp(POINT(p2));

  g_clear_object(&p1);
  g_clear_object(&p2);

  return 0;
}
В качестве объектов в примере взят простенький класс Point с гитхаба за авторством Jacob Gelbman. Код point.h:
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance
#ifndef __POINT_H__
#define __POINT_H__
#include <glib.h>
#include <glib-object.h>

#define TYPE_POINT           (point_get_type             ())
#define POINT(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_POINT, Point))
#define POINT_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST    ((cls), TYPE_POINT, PointClass))
#define IS_POINT(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_POINT))
#define IS_POINT_CLASS(cls)  (G_TYPE_CHECK_CLASS_TYPE    ((cls), TYPE_POINT))
#define POINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS  ((obj), TYPE_POINT, PointClass))

typedef struct _PointClass PointClass;
typedef struct _Point      Point;

struct _PointClass
{
  GObjectClass parent;
  void (*clear) (Point *self);
};

struct _Point
{
  GObject parent;
  gint    x;
  gint    y;
  GArray *array;
};

GType point_get_type(void);
void  point_print(Point *self);

#endif /*__POINT_H__*/

Код point.c:
// https://github.com/zorgnax/gobject-examples/tree/master/02-point-inheritance
#include <glib-object.h>
#include "point.h"

G_DEFINE_TYPE(Point, point, G_TYPE_OBJECT);

enum
{
  PROP_0,
  PROP_ARRAY,
  PROP_X,
  PROP_Y
};

enum
{
  CLEAR,
  LAST_SIGNAL
};

static guint point_signals[LAST_SIGNAL] = {0};


void point_print (Point *self)
{
  g_print("    x: %d, y: %d\n", self->x, self->y);

  g_print("    ar:");
    if(self->array)
    {
      guint i;
      for(i = 0; i < self->array->len; i++)
        g_print(" %d", g_array_index(self->array, gint, i));
    }
    else
      g_print("null");
  g_print("\n");
}

static void point_clear (Point *self)
{
  g_object_set(self, "x", 0, "y", 0, "a", NULL, NULL);
}

static void point_get_property(GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec)
{
  Point *point = POINT(obj);

  switch (prop_id)
  {
    case PROP_ARRAY:
      g_value_set_boxed(value, point->array);
    break;

    case PROP_X:
      g_value_set_int(value, point->x);
    break;

    case PROP_Y:
      g_value_set_int(value, point->y);
    break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
    break;
  }
}

static void point_set_property(GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec)
{
  Point *point = POINT(obj);

  switch (prop_id)
  {
    case PROP_ARRAY:
      if(point->array) g_array_unref(point->array);
      point->array = g_value_dup_boxed(value);
    break;

    case PROP_X:
      point->x = g_value_get_int(value);
    break;

    case PROP_Y:
      point->y = g_value_get_int(value);
    break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
    break;
  }
}

static void point_finalize(GObject *point)
{
  if(POINT(point)->array) g_array_unref(POINT(point)->array);

  G_OBJECT_CLASS(point_parent_class)->finalize(point);
}

static void point_class_init (PointClass *cls)
{
  GObjectClass *g_object_class = G_OBJECT_CLASS(cls);
  GParamSpec *array_param;
  GParamSpec *x_param;
  GParamSpec *y_param;

  g_object_class->get_property = point_get_property;
  g_object_class->set_property = point_set_property;
  g_object_class->finalize = point_finalize;

  cls->clear = point_clear;

  array_param = g_param_spec_boxed(
    "a", "a", "some int array",
    G_TYPE_ARRAY,
    G_PARAM_READWRITE);

  x_param = g_param_spec_int(
    "x", "x", "x loc of point",
    INT_MIN, /* => */ INT_MAX,
    0,
    G_PARAM_READWRITE);

  y_param = g_param_spec_int(
    "y", "y", "y loc of point",
    INT_MIN, /* => */ INT_MAX,
    0,
    G_PARAM_READWRITE);

  g_object_class_install_property(
    g_object_class,
    PROP_ARRAY,
    array_param);

  g_object_class_install_property(
    g_object_class,
    PROP_X,
    x_param);

  g_object_class_install_property(
    g_object_class,
    PROP_Y,
    y_param);

  point_signals[CLEAR] = g_signal_new(
    "clear",                               /* signal_name */
    TYPE_POINT,                            /* itype */
    G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, /* signal_flags */
    G_STRUCT_OFFSET(PointClass, clear),    /* class_offset */
    NULL,                                  /* accumulator */
    NULL,                                  /* accu_data */
    g_cclosure_marshal_VOID__VOID,         /* c_marshaller */
    G_TYPE_NONE,                           /* return_type */
    0);                                    /* n_params */
}

static void point_init(Point *point)
{
  point->array = NULL;
}

Есть такая особенность в GTK, что если положить с помощью gtk_expander_set_label_widget в GtkExpander активируемый виджет вроде GtkCheckButton или GtkSwitch, то активируемый виджет вроде как работает (можно кликнуть по нему и получить статус "active"), но отрисовывается не правильно. Например, в случае GtkCheckButton не рисуется галочка, даже если кнопка в активном состоянии. В принципе даже из названия "label_widget" видно, что разработчиками не закладывалось, что кто-то будет туда класть какой-то активный элемент, максимум GtkLabel или картинку. Однако есть способ заставить активные виджеты работать, если виджет класть в Expander уже после того, как Expander сформирован.

Ниже пример на питоне. Отмечу, что обязательно нужно класть в label_widget GtkBox, а уже в него складывать свои активные виджеты. Функцию hacked_idle можно объявить один раз и использовать для всех Expander'ов.


import gi, sys
from gi.repository import Gtk, GLib


def hacked_idle(expander):
  hbox = expander.get_label_widget()
  expander.set_label_widget(None)
  expander.set_label_widget(hbox)
  return False

def expander_realize_cb(widget):
  GLib.idle_add(hacked_idle, widget)
  return False


class Test:
  def __init__(self):
    window = Gtk.Window()
    window.set_default_size(640, 480)
    window.connect_after("destroy", self.destroy)

    exp = Gtk.Expander()
    exp.connect("realize", expander_realize_cb)
    exp.set_expanded(True)
    window.add(exp)

    hbox = Gtk.Box(Gtk.Orientation.HORIZONTAL, 5)
    exp.set_label_widget(hbox)

    check = Gtk.CheckButton()
    check.set_label("Hi, There!!");
    hbox.add(check)

    switch = Gtk.Switch()
    hbox.add(switch)

    big_label = Gtk.Label()
    big_label.set_markup("<big>Hello, World!!!</big>")
    exp.add(big_label)

    window.show_all()

  def destroy(window, self):
    Gtk.main_quit()

def main():
  app = Test()
  Gtk.main()

if __name__ == "__main__":
    sys.exit(main())

CMake-ковский CPack в Debian “wheezy” не справляется с созданием deb-пакета, если в RPATH прописан $ORIGIN. CMake вываливается с ошибкой вида:
$ORIGIN используется в RPATH (...)  и соответствующий каталог невозможно определить из-за отсутствия подкаталога DEBIAN в корне дерева сборки пакета
Или, для CLANG=C:
CPackDeb: dpkg-shlibdeps: dpkg-shlibdeps: warning: $ORIGIN is used in RPATH of ... and the corresponding directory could not be identified due to lack of DEBIAN sub-directory in the root of   package's build tree
Беда в том, что каталог "debian" есть, но название его состоит из маленьких букв, а не больших, как того хочет dpkg-shlibdeps.

Есть простенький патч для модуля CPackDeb.cmake, решающий эту проблему.

Однако, если нет возможности пропатчить CMake, то обойти проблему можно следующим, хоть и довольно грязным, костылем: прописать установку какого-нибудь файла, можно даже пустого, в каталог /DEBIAN:
install(FILES dirty_fix.txt DESTINATION /DEBIAN)
Вполне допускаю, что на основе этого костыля можно соорудить костыль поизящней, например удалять файл во время сборки, чтобы он не попадал в deb-пакет. В этом смысле совет выше можно рассматривать как направление, куда в дальнейшем копать.

В свежеустановленной Astra Linux не работала функция gtk_show_uri() с URI типа ghelp.

В GError при этом можно было увидеть ошибку:
'Действие не поддерживается'.
Или, с LANG=C:
'Operation not supported'.

Естественно, yelp был установлен.
Вопреки некоторым советам из интернетов, gvfs тут ни при чем.
Проблема решилась установкой пакета desktop-file-utils.

Задача следующая:
  • У вас есть образ раздела (скажем, sda1), но не всего диска (sda). Т.е. без таблицы разделов. Образ сделан с помощью старого доброго dd.
  • Нужно из него создать диск для VirtualBox'а (или другой виртуальной машины, например, VMware).
Решение:
  1. Создаем пока пустой файл myvm.img для будущего образа диска (всего диска, в т.ч. с разделами). Размер файла выбираем исходя из размера образа, но с запасом, чтобы поместилась еще и таблица разделов. Скажем есть образ в 99 Гб, возьмем сильно с запасом и создадим файл под образ диска в 100 Гб:
    dd if=/dev/zero of=myvm.img bs=1G count=0 seek=100
  2. Разметим файл утилитой fdisk (нужно создать раздел и не забыть сохранить изменения командой 'w'):
    fdisk myvm.img
  3. Теперь сделаем полученный образ с разделами доступным в виде устройств в /dev:
    sudo kpartx -a myvm.img
  4. Копируем образ нашего раздела поверх раздела во вновь созданном файле:
    sudo cp sda1.raw /dev/mapper/loop0p1
  5. Можно, чтобы не пропадало место (тот самый "запас"), расширить файловую систему на разделе (учтите, что утилита resize2fs работает только с системами семейства ext{2-4}!):
    sudo resize2fs /dev/mapper/loop0p1
  6. Теперь можно убрать наш файл myvm.img из /dev:
    sudo kpartx -d myvm.img
  7. Получился "сырой" образ диска с таблицей разделов и собственно нашим разделом. Осталось сконвертировать его в формат нужной виртуальной машины. Для VirtualBox:
    VBoxManage convertdd myvm.img myvm.vdi --format VDI
    Для VMware:
    VBoxManage convertdd myvm.img myvm.vmdk --format VMDK
  8. Готово.
 Если на разделе установлена система и необходимо ее загрузить, то можно для начала загрузиться с какого-нибудь Live-образа, подмонтировать раздел и стандартным для этой системы способом восстановить загрузчик.

Ссылки:
  1. Алгоритм создания образа диска из образа раздела в обсуждении на stackexchange.com;
  2. Статья о конвертации 'сырых' образов дисков в формат образов для виртуальных машин (и обратно);
  3. Про восстановление загрузчика grub в треде на askubuntu.com.

Итак, есть планшет Acer Iconia Tab W511 (или другой, столь же кривой, т.е. на x86, но без поддержки Linux). Требуется запустить на нем Linux-программу с графическим интерфейсом (не портированную под win32, в т.ч. под cygwin). Пусть хоть в режиме тонкого клиента.

Решение — XServer под виндой (я выбрал Cygwin/X) и ssh-сервер на Linux-машине.

Алгоритм следующий:
  1. На Linux-машине должен быть поднят ssh-сервер;
  2. На планшете накатываем cygwin вместе с XServer-ом и ssh-клиентом отсюда (см. раздел "Downloading and Installing"). При установке не забываем (иначе придется переставлять cygwin заново) поставить пакеты xorg-server, xinit и openssh (зачем — см. здесь, пункт 15). Есть смысл сразу же накатить еще subversion и wget, чтобы иметь возможность в любой момент доставлять пакеты с помощью apt-cyg;
  3. Запускаем Cygwin Terminal (ярлык на рабочем столе);
  4. В Cygwin Terminal запускаем команду "startxwin.exe". Открывается классический xterm;
  5. В xterm запускаем ssh-клиент примерно так: "ssh -Y user@host", user и host меняем на пользователя и IP Linux-машины;
  6. После логина в ssh получаем командную строку Linux-машины, где можем запускать графические программы с выводом сюда же, на экран планшета (даже мультитач в GIMP'е работает). См. gedit на скриншоте ниже.
UPD: Если канал слаб, то можно попробовать связку NX Client и FreeNX сервер. Само собой, это потребует дополнительных телодвижений (например, установки сервера на Linux-машину, которого, к тому же, может не быть в репозитории).

Библиотека pixman, как известно, документацией пользователей не балует ("There is currently no documentation besides the source code itself").

Поэтому выкладываю пару откомментированных на русском примеров. Примеры из официальной поставки, конкретно версии pixman-0.30. В комментариях могут быть ошибки, неточности и т.п. Тем не менее, вот они:
  • trap-test — вырезает из зеленого изображения трапецию по маске с альфа-каналом;
  • checkerboard — рисует подобие разноцветной шахматной доски и затем трансформирует его (псевдо-3D перспектива).

Код основан на примере "Rotated button" из gtk-demo. Собирается под Gtk-2. Местами код неидеален (например виджет RotatedBin.scale создается вручную в main), но, думаю, вполне может быть полезен.

Скрин:
[Image]

Код:
https://docs.google.com/file/d/0B40UHa6ea9QkdUFTVnRqNnFaTDg/edit

Потребовалось использовать в программе на Vala некоторые функции небезызвестной библиотеки для вычисления быстрого преобразования Фурье fftw3. Для чего был написан простенький vapi-файл. В силу своего, прямо скажем, мизерного пока еще опыта использования Vala, не рекомендую брать данный vapi за эталон, тем не менее выкладываю его на случай, если кому-то он окажется полезен. В vapi прописаны лишь некоторые функции, типы и константы и лишь для работы с float-значениями, однако, думаю, при необходимости не составит никакого труда файл необходимым образом дополнить по аналогии. fftw_complex было принято решение не использовать, не заворачивать в класс, а по-простому работать напрямую с float-массивами (поэтому осторожней с выделением-освобождением памяти).

Сам vapi-файл:

[CCode (cprefix = "fftwf_", cheader_filename = "fftw3.h")]
namespace Fftwf
{
    [CCode(cprefix = "FFTW_")]
    enum Sign
    {
        FORWARD = -1,
        BACKWARD = 1
    }

    [CCode(cprefix = "FFTW_")]
    enum Flag
    {
        MEASURE         = (0U),
        DESTROY_INPUT   = (1U << 0),
        UNALIGNED       = (1U << 1),
        CONSERVE_MEMORY = (1U << 2),
        EXHAUSTIVE      = (1U << 3),
        PRESERVE_INPUT  = (1U << 4),
        PATIENT         = (1U << 5),
        ESTIMATE        = (1U << 6)
    }


    [CCode(cname = "fftwf_plan")]
    public struct Plan : int { } // fake int


    void    destroy_plan(Plan p);
    void    execute(Plan p);
    void    free(float *p);
    float*  malloc(size_t n);
    Plan    plan_dft_1d(int n, float* in_ptr, float* out_ptr, Sign sign, Flag flags);
}


Пример использования:

{
float input[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

float *data;
float *fft_result;
float *ifft_result;
Fftwf.Plan plan_forward, plan_backward;
int i;

data        = Fftwf.malloc( 2 * sizeof(float) * input.length );
fft_result  = Fftwf.malloc( 2 * sizeof(float) * input.length );
ifft_result = Fftwf.malloc( 2 * sizeof(float) * input.length );

plan_forward  = Fftwf.plan_dft_1d( input.length, (void*)data, (void*)fft_result, Fftwf.Sign.FORWARD, Fftwf.Flag.ESTIMATE );
plan_backward = Fftwf.plan_dft_1d( input.length, (void*)fft_result, (void*)ifft_result, Fftwf.Sign.BACKWARD, Fftwf.Flag.ESTIMATE );

/* populate input data */
for(i = 0 ; i < input.length ; i++)
{
    data[2 * i + 0] = input[i];
    data[2 * i + 1] = 0.0f;
}

/* print initial data */
for( i = 0 ; i < input.length ; i++ )
{
    print("data[%d] = { %2.2f, %2.2f }\n",
        i, data[2 * i + 0], data[2 * i + 1] );
}

Fftwf.execute( plan_forward );

/* print fft result */
for(i = 0 ; i < input.length ; i++)
{
    print("fft_result[%d] = { %2.2f, %2.2f }\n",
        i, fft_result[2 * i + 0], fft_result[2 * i + 1] );
}

Fftwf.execute( plan_backward );

/* print ifft result */
for( i = 0 ; i < input.length ; i++ )
{
    print("ifft_result[%d] = { %2.2f, %2.2f }\n",
        i, ifft_result[2 * i + 0] / input.length, ifft_result[2 * i + 1] / input.length );
}

/* free memory */
Fftwf.destroy_plan( plan_forward );
Fftwf.destroy_plan( plan_backward );

Fftwf.free(data);
Fftwf.free(fft_result);
Fftwf.free(ifft_result);
}

В заключение хочу порекомендовать отличное how-to по написанию vapi к не-GObject-библиотекам.

"Хозяйке на заметку": прочитать данные (переменные, целые, с плавающей точкой и т.п.) из бинарного файла на Vala можно и нужно с помощью GVariant, например так:


{
    uint8[] buf = new uint8[sizeof(uint32) + sizeof(uint32) +
        sizeof(double) + sizeof(double)];

    {
        var data_stream = new DataInputStream(file.read());
        data_stream.read(buf);
    }

    Variant variant = Variant.new_from_data<uint8>(
        new VariantType("(uudd)"), buf, true);

    uint32 int1 = variant.get_child_value(0).get_uint32();
    uint32 int2 = variant.get_child_value(1).get_uint32();
    double double1 = variant.get_child_value(2).get_double();
    double double2 = variant.get_child_value(3).get_double();

    stdout.printf("int 1 = %u\n", int1);
    stdout.printf("int 2 = %u\n", int2);
    stdout.printf("double 1 = %f\n", double1);
    stdout.printf("double 2 = %f\n", double2);
}


В гите есть встроенные средства для создания и накладывания патчей. Но по умолчанию git не сохраняет сообщения коммитов "как есть" и изменяет время коммитов на время, когда были наложены патчи. Из-за этого меняются хэши коммитов. Что особенно неприятно если у вас есть два репозитория и один является субмодулем (git submodule) другого (т.к. основной репозиторий проверяет актуальность субмодуля именно по хэшу, который в данном случае не будет совпадать).

Чтобы сохранить комментарии и время коммитов патч нужно формировать командой:
git format-patch -k --stdout $HASH > ./patch_file
Где $HASH — хэш коммита, начиная с которого изменения необходимо сохранить в патче.
Накладывать полученный патч следует командой (одной строкой):
GIT_COMMITTER_NAME='Paul' GIT_COMMITTER_EMAIL='paul@example.com' git am -k --committer-date-is-author-date < patch_file
Где Paul — имя автора коммитов из патча, paul@example.com — адрес его почты (как указано в конфиге гита).

UPD:  если использование патчей не является обязательным условием, а нужен механизм синхронизации репозиториев как таковой, то возможно вам стоит копнуть в сторону git bundle.


 

Copyright © 2007 DamnSmallBlog. Content is licensed under Creative Commons Attribution-Noncommercial.

Design: GeckoandFly and Blogcrowds.