четверг, 22 апреля 2010 г.

Гестуры в xwem

Совсем забыл про классную фукнциональность в xwem — гестуры aka strokes. Это когда рисуешь что-нибудь на экране, а у тебя выполняется какая-нибудь команда. В родном (S)XEmacs есть поддержка, но она конечно далека от идела с точки зрения пользовательского интерфейса, зато дало нужную функциональность для реализации гестур в xwem.

Вот список гестур, которыми я пользуюсь:

global strokes

Глобальные гестуры

xemacs strokes

Гестуры локальные для xemacs фреймов

gv strokes

Гестуры локальные для ghostview приложений

opera strokes

Гестуры локальные для бравзера opera

firefox strokes

Гестуры локальные для бравзера firefox

Скачать эти гестуры можно тут.

Как видно из скринов, гестуры в xwem достаточно прокаченные: они могут запускать команды, произвольные e-lisp выражения, эмулировать нажатия кнопок, а также вводить в текущее приложение всякие строки (на скринах нет такого варианта гестуры).

суббота, 3 апреля 2010 г.

SXEmacs живёт

После некоторого затишья проект SXEmacs продолжил развитие. Появились merge-requestы и что-то всё таки делается. Залили мои патчи к FFI, которые реализуют новый тип safe-string в помощь дядьке, который пишет FFI к libdbus. Так же в Wand-mode я добавил поддержку модного liquid scale, кто не знает, то это выглядит вот так

Кстати, я наконец-то придумал как можно использовать wand-button в стандартном customize!

воскресенье, 7 февраля 2010 г.

Интеграция SXEmacs с Mac OS

Немного сомневаюсь, хочу посоветоваться с пользователями GNU Emacs под Mac OS. Есть идея и возможность реализовать в SXEmacs FFI к libobjc. Только не очень представляю как его потом можно полезно использовать.

Вообще я хочу сделать чтобы эпловский multitouch мог работать в SXEmacs (конкретно в Wand-mode). Я не представляю как реализован multitouch и что нужно для того, чтобы он работал. Просветите пожалуйста если кто в курсе.

Ещё хотел бы посоветоваться какая именно интеграция с Mac OS была бы полезна в Emacs? Мне пока на ум приходит только использование нативных виджетов для M-x customize RET, чтобы выглядело как настройки у других приложений под Mac OS. Само собой ещё growl и голос.

Ещё хотелось бы, чтобы Slife.app каким-нибудь образом брал данные из запущенного Emacs (потому что отчёты Slife.app типа "95% времени работает приложение X11.app" меня как-то не возбуждают), но как это сделать вообще без понятия.

Если наработается хоть пусть и небольшая интеграция с Mac OS, то это будет очень большим подспорьем для привлечения нужных людей (возможно даже из проекта GNU Emacs ;))) к реализации нативного redisplay в SXEmacs под Mac OS.

среда, 16 декабря 2009 г.

Генератор кнопок

В (S)XEmacs есть классный package, называется xpm-button.el. Он умеет на лету генерировать картинку с кнопкой. Всё вроде бы хорошо, но «красота» сгенерированных кнопок повергает в уныние. Решил написать достаточно гибкий генератор красивых кнопок используя Wand-mode. По мере написания генератора, добавлял необходимый функционал в Wand-mode. Вот что в итоге получилось:

(wand-button 150 32 "Wand Button"
             :bg "cornflowerblue" :fg "white"
             :round 8.0 :depth 1 :yoff 8 ; :type 'pressed
             :wand-font (make-wand-font :family "Zapfino"))

→ button1

(wand-button 100 20 "Wand Button"
             :bg "gray80" :fg "black" :round 10.0
             :contrast 0.5 :depth 2 ; :type 'pressed
             :wand-font (make-wand-font :family "Copperplate"))

→ button2

(wand-button 100 40 (encode-coding-string "привет" 'utf-8)
             :bg "palegreen" :fg "green" :round 0.0
             :depth 4 :yoff -2 ; :type 'pressed
             :wand-font (make-wand-font :family "Arial Black"
                                        :size 20))

→ button2

Интересно будет этот функционал внедрить в Emacs widgets, а то кнопки там сделаны через очень большую задницу.

ReST source Скачать оригинал

пятница, 27 ноября 2009 г.

Wand-mode - новые возможности

Wand-mode развивается, с тех пор как я сделал описание, было добавлено несколько интересных возможностей. Их я и опишу.

Имя файла в имени буфера

Откроем какую-нибудь картинку: C-x C-f lg-tree.jpg RET, получаем примерно следующее:

orig image

Замечаем, что в mode-line отображается имя буфера, которое теперь содержит имя оригинального файла. Удобно при переключении с помощью C-x C-b.

Новая операция: modulate

Фотка сама по себе классная, но природа выглядит как-то уныло, немного приукрасим, с помощью команды o modulate RET saturation RET 60 RET, получим результат:

sat inc 60 percents

60% increased saturation

Уже намного лучше.

Поддержка IPTC профиля

Хочется добавить информацию о том, что это там на фотке. Воспользуемся редактором IPTC профиля. Добавим IPTC теги с помощью p caption RET lg climbes trees RET и p keywords RET js-kit, corporative RET, получим:

caption and keywords added

Caption and Keywords added

Pixel color picker

Интересно какого цвета у меня трусы, кликнем на них и увидим:

pixel color picker

Pixel color picker

Видим координаты пикселя, по которому мы кликнули, его цвет и разложение по RGB. Удобно.

Глобальные операции

Иногда нужно просмотреть несколько картинок применяя конкретный набор операций, например увеличение. Для этого теперь есть команда фиксирования операций, по умолчанию висит на O, нажмём её и перейдём к следующей картинке в директории с помощью SPC:

global operations

Global operations applied

Видим список глобальных операций, а также замечаем, что они были автоматически применены к картинке. Однако для этой картинки увеличение saturation не подходит. Отменим с помощью undo (C-/):

operations canceled

Operations canceled

Чтобы сбросить глобальные операции нужно нажать C-u O.

Редактор свойств изображения

Иногда нужно подправить EXIF данные у изображения. Лично я не пользуюсь этой возможностью (мне хватает IPTC профиля), но есть один знакомый фотограф, который попросил эту функциональность в Wand-mode. Мне не сложно за пару пива. Вызвать редактор можно с помощью кнопки e. Он отфильтрует свойства согласно переменной Wand-mode-properties-pattern ("^exif:" по умолчанию).

Новая операция: grayscale

Существует множество методик преобразования картинки в чёрно-белую. Один из них, например, полностью удалить saturation с помощью o modulate RET saturation RET -100 RET, что часто приводит к желаемому результату. Всё же была добавлена возможность поменять colormap на grayscale — o grayscale RET

Новая операция: fit to size

Иногда нужно изменить размеры изображения, чтобы она влазила в определённый размер. Теперь это можно сделать с помощью o fit to size RET. Эта операция запросит ширину, высоту и сохраняя соотношение сторон изменит размер. Есть также пользовательская переменная Wand-mode-scaler, которая отвечает за то, какой преобразователь будет использован. Вы можете создавать свои собственные преобразователи с помощью Wand-make-scaler; неплохой настройкой может быть:

(setq Wand-mode-scaler (Wand-make-scaler :GaussianFilter 1))

Документация к Wand-mode-scaler и Wand-make-scaler содержит исчерпывающую информацию о преобразователях.

ReST source Скачать оригинал

четверг, 12 ноября 2009 г.

Судоку для (S)XEmacs

Не спалось как-то ночью и мягкой подруги не было под рукой. Решил поиграть во что-нибудь. Тетрис слишком активный, сокобан уже прошёл весь, World of Goo перегревает комп — решил погонять в судоку. Зашёл на websudoku.com и начал играть, всё бы хорошо, но это не в SXEmacs и недоступно offline. Решил написать sudoku.el для игры в судоку под SXEmacs. Поискал в инете и нашёл sudoku.el для (X)Emacs. Скачал, запустил и не обрадовался, посмотрел код — вообще расстроился. В итоге ночь провёл за переписыванием sudoku.el, чтобы удовлетворить свои нужды, а именно:

  • Удобный, красивый и информативный интерфейс
  • Возможность скачивания судок с websudoku.com и menneske.no
  • Возможность печати судок
  • Возможность редактирования судок
  • Возможность сохранения/загрузки судок
  • Наличие карандашей для сложных техник и экспериментов
  • Возможность решать только реально сложные задачи, т.е. авто-заполнение (полу)очевидных полей

Пример

Запускаем M-x sudoku RET:

sudoku-mode

Сразу видим кучу очевидных полей для заполнения, например, R5C5 (где курсор) и понятно дело лениво их все заполнять, поэтому нажимаем a, получаем:

autoinsert

Теперь действительно сложная задача, но сразу видно интересность значения 4, поэтому берём карандаш1 — p (от pencil) и применяем технику Nishio начиная с R8C9, получаем:

pencil
  1. R8C9 -> 4 (начало Nishio)
  2. R2C3 -> 4 (единственное место в C3 где может быть 4)
  3. R3C7 -> 4 (единственное место в R1 где может быть 4)
  4. в восьмом квадрате некуда ставить 4

то есть в R8C9 не может стоять 4. C-u p и отменяем карандаш. Теперь мы знаем, что 4 стоит в R2C9:

won

Хорошая судочка, пожалуй сыну подойдёт, распечатаем её нажав P, на бумаге это выглядит вот так:

printed

Скачать

Скачать можно с http://lgarc.narod.ru/xemacs/sudoku.el

Замечания, баги, фиксы присылайте на lg AT sxemacs.org


1В режиме карандаша авто-заполнение отключается

ReST source Скачать оригинал

среда, 30 сентября 2009 г.

Говорящий SXEmacs

Баловался тут давеча с разными способами более тесной интеграции SXEmacs с Mac OS. Сделал (а точнее стащил из GNU Emacs) интересную функцию, с помощью которой можно запускать различные сценарии AppleScript:

(defun do-applescript (&rest script-lines)
  (let ((script (mapconcat 'identity script-lines "\n"))
        start cmd return)
    (while (string-match "\n" script)
      (setq script (replace-match "\r" t t script)))
    (while (string-match "\"" script start)
      (setq start (+ 2 (match-beginning 0))
            script (replace-match "\\\"" t t script)))
    (setq cmd (concat "osascript -e \"\"\"" script "\"\"\""))
    (setq return (shell-command-to-string cmd))
    (concat "\"" return "\"")))

Применения этой процедуры очевидны. Например можно заставить Growl отображать сообщения SXEmacs:

(defun growl (title message)
  (do-applescript
   "tell application \"GrowlHelperApp\""
   (format "notify with name \"SXEmacs Notification\" title %S description %S application name \"SXEmacs\" image from location \"file:///opt/local/share/sxemacs-22.1.10/etc/sxemacs-icon.png\"" title message)
   "end tell"))

Но, что более интересно, можно научить SXEmacs разговаривать, используя VoiceOver!

(defvar lg-say-voices
  '((agnes . "Agnes") (albert . "Albert") (alex . "Alex")
    (badnews . "BadNews") (bahh . "Bahh") (bells . "Bells")
    (boing . "Boing") (bruce . "Bruce") (bubbles . "Bubbles")
    (cellos . "Cellos") (deranged . "Deranged") (fred . "Fred")
    (goodnews . "GoodNews") (hysterical . "Hysterical") (junior . "Junior")
    (kathy . "Kathy") (organ . "Organ") (princess . "Princess")
    (ralph . "Ralph") (trinoids . "Trinoids") (vicki . "Vicki")
    (victoria . "Victoria") (whisper . "Whisper") (zarvox . "Zarvox")))

(defun lg-say-the-text (text &optional voice)
  (let* ((sv (cdr (assq voice lg-say-voices)))
         (svoice (if sv (format " using %S" sv) ""))
         ;; " -> ' in order to avoid osascript confusion
         (nt (replace-in-string text "\"" "'")))
    (do-applescript (format "say %S%s" nt svoice))
    (growl "Text via VoiceOver" "Done!")))

(defun lg-say-region-or-buffer (arg)
  "Say contents of the buffer or region.
If prefix ARG is specified then use custom voice."
  (interactive "P")
  (let ((voice (and current-prefix-arg
                    (intern
                     (completing-read "Voice: "
                      (mapcar #'(lambda (v)
                                  (list (symbol-name (car v))))
                              lg-say-voices)
                      nil t))))
        (text (if (region-active-p)
                  (buffer-substring (region-beginning) (region-end))
                (buffer-substring))))
    (lg-say-the-text text voice)))

(define-key global-map (kbd "C-c s") 'lg-say-region-or-buffer)

Далее, можно например реализовать процедуру, которая будет читать тело письма в gnus:

(defun lg-gnus-say-article-body ()
  "Say body of the current gnus article."
  (interactive)
  (let ((atxt (with-current-buffer gnus-article-buffer
                (save-restriction
                  (article-goto-body)
                  (buffer-substring (point) (point-max))))))
    (lg-say-the-text atxt)))

Единственная проблема с данной реализацией это то, что SXEmacs блокирует во время выполнения сценария AppleScript. Нужно расширить процедуру do-applescript, чтобы она поддерживала параметр IN-BACKGROUND.

ReST source Скачать оригинал

суббота, 8 августа 2009 г.

MacOS clipboarding

В Mac OS есть отличное приложение — X11.app. Реальный и работающий X сервер. Оно позволило при переходе с FreeBSD на MacOS практически не менять среду разработки и использовать привычные, старые добрые X приложения, такие как xterm, Inkscape или SXEmacs. Единственный минус это отсутствие внятного межклипбордного взаимодействия между Cocoa приложениями и X приложениями, особенно бесило, что я не могу выделить что-то в SXEmacs и вставить в Firefox. Давеча нашёл способ как это можно сделать:

(when (eq system-type 'darwin)
  ;; Make C-w, M-y interact with Mac OS X clipboard
  (defun lg-paste-from-osx ()
    (shell-command-to-string "pbpaste"))
  (defun lg-paste-to-osx (text &optional push)
    (let* ((process-connection-type nil)
           (proc (start-process "pbcopy" nil "pbcopy")))
      (process-send-string proc text)
      (process-send-eof proc)))
  (setq interprogram-paste-function 'lg-paste-from-osx)
  (setq interprogram-cut-function 'lg-paste-to-osx))

Теперь, после C-w или M-w в SXEmacs, я могу делать C-v в Cocoa приложениях, а выделив что-то в Cocoa приложениях и скопировав, я могу вставлять в SXEmacs с помощью C-y.

ReST source Скачать оригинал

понедельник, 29 декабря 2008 г.

SXEmacs - behind the scene

Как и обещал приоткрою немного занавес SXEmacs и расскажу, а точнее покажу, как устроена модель разработки SXEmacs. Но сначала про разработчиков:

hroptatyr (aka Sebastian Freundt)
Немец, живёт в Берлине, математик, занимается разработкой систем компьютерной алгебры, зарабатывает на жизнь игрой на бирже, любит мощные компьютеры и большие мониторы, носит очки
lg (aka Zajcev Evgeny)
Гимнаст, работал охранником 6 лет, хорошо владеет рукопашным боем, неплохо обращается с холодным оружием и отлично стреляет. Живёт в лесистой части г. Ульяновск, любит работать в туалете, выпивать, читать книжки и детей
njsf (aka Nelson Ferreira)
Живёт в Америке, любит фотографировать и путешествовать, есть красивая подружка, имеет хорошее чувство юмора.
PeanutHorst
Не совсем разработчик, а скорее противовес в команде SXEmacs, пессимистичен, работает в OpenBox под FreeBSD, часто несёт глупости, тестирует SXEmacs на различных платформах.
JackaLX (aka Steve Youngs)
Создал SXEmacs, домохозяин, имеет кучку детей, держит магазин, торгующий шмотками и вещами с символикой SXEmacs, ждёт когда кто-нибудь переведёт первый доллар в качестве денежного пожертвования на развитие SXEmacs, есть красавица жена Мишель с большой грудью, которая зарабатывает на жизнь семье

Это список активных разработчиков на текущий момент, так что он не совсем полный, но для понимания поднаготщины — хватит.

Итак, скачайте эту картинку и откройте её в просмоторщике в полном формате, т.е. без использования fill-to-width или схожих функций.

Те кому интересны все детали можете скачать SVG исходник

ReST source Скачать оригинал

вторник, 23 декабря 2008 г.

Условия гонки в ASYNEQ

Давайте для начала я покажу вам скриншот моего SXEmacs:

Скриншот

Ничего необычного не замечаете? У меня почти невидимые клеточки. Цвет #C4C4C4 на фоне #CCCCCC да ещё с моим дальтонизмом — я их действительно еле вижу, главное что я знаю, что они там есть и если приглядеться, то я их отчётливо вижу. Особо внимательные заметят, что в каждую клеточку помещается 4 символа по горизонтали и 2 по вертикали.

Теперь немного утомительной истории. Путём наблюдений и экспериментов я пришёл к выводу, что у меня есть некая особенность — проблемы лучше решаются, если во время размышлений вести записи на листочке в клеточку. Первый раз я это заметил решая просто гигантское количество задач по планиметрии в 8 классе. В дальнейшем доходило даже до маразма; для того, чтобы решить одну задачу на вступительных экзаменах в МГУ мне пришлось разлиновать в клеточку выданный листочек. Я всё не мог понять с чем это связано, и вот, когда начал использовать XEmacs в году '97-98, то понял. Как сейчас помню, сидел набирал текст какой-то программы и возникло острое ощущение, что буквы, которые я так старательно набираю, сейчас разбегутся, а вместе с этими буквами разбегутся и вкладываемые мысли. Тогда то я и понял, что хорошие мысли порождаются свободным разумом, но истинно свободный разум также легко теряет мысли как и порождает их, для разума-рабочей лошадки, нужна «клетка». Вот такая вот психология.

Со временем, поэкспериментировав с цветом и размером клеток в окнах (S)XEmacs я нашёл оптимальный (дающий наибольшую продуктивность) для себя вариант — тот, что на картинке.

И вот представьте, сижу я как обычно печатаю текст, пытаясь вложить в него хоть немного смысла, как вдруг, буквы, в прямом смысле этого слова, начинают убегать и смешиваться друг с другом. Всё думаю, доигрался, колёсики уехали. Как ни странно, но первая мысль была именно, что проблемы у меня, а не у SXEmacs. Немного успокоившись задался вопросом почему так произошло, ведь раньше такого не было. Нашёл особенность — использовался flyspell режим, который обычно выключен. Покопавшись в flyspell нашёл особенность, что self-insert команда может породить вызов accept-process-output. Дальнейшее разбирательство привело к ASYNEQ. А текст так и не написал :(

ReST source Скачать оригинал

UPD: Код для сетки

(defface lg-grido-face
  '((((class color) (background dark))
     (:foreground "gray10"))
    (((class color) (background light))
     (:foreground "gray77"))
    (t (:foreground "gray77")))
  "Face for grid.")

(push '("grido" (face-foreground 'lg-grido-face)) xpm-color-symbols)

(defconst lg-square-64x64-xpm
  (concat
   "/* XPM */\n"
   "static char *mini_square_xpm[] = {\n"
   "/* columns rows colors chars-per-pixel */\n"
   "\"64 64 2 1\",\n"
   "\"       c None s background\",\n"
   "\".      c gray77 s grido\",\n"
   "/* pixels */"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"................................................................\"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n,"
   "\"    .                                                           \"\n"
   "\"    .                                                           \"\n"
   "\"    .                                                           \"\n"
   "\"    .                                                           \"\n"
   "\"    .                                                           \"\n"
   "\"    .                                                           \"\n"
   "\"    .                                                           \"\n"
   "\"    .                                                           \"\n"
   "\"    .                                                           \"\n"
   "};"))

(set-face-background-pixmap
 'default
 (make-image-specifier (vector 'xpm :data lg-square-64x64-xpm))) 

понедельник, 22 декабря 2008 г.

SXEmacs - лучший программный продукт года

Решил немного помечтать и вообразить, что нужно SXEmacs, чтобы выиграть приз «Лучший программный продукт года». Было ли вообще такое, чтобы GNU Emacs выигрывал этот приз? Что же необходимо изменить и сделать в SXEmacs, чтобы это событие произошло?

  • Кардинально улучшить подсистему обработки событий (event loop).
  • Реализовать подсистему отображения (redisplay) используя современные средства, например Cairo. Использовать современные библиотеки виджетов, например GTK+.
  • Реализовать нормальную мультиязыковую поддержку.
  • Поймать все утечки памяти при использовании BDWGC и сделать его сборщиком мусора по умолчанию.
  • Реализовать чистую интеграцию различных ЯП, таких как Python, Ruby и др.
  • Улучшить документацию как для разработчиков так и для пользователей.
  • Создать более продвинутый и быстрый сопоставитель текста с образцом на замену регулярных выражений. На базе него сделать, чтобы font-lock работал корректно и быстро.
  • Создать систему совместной разработки на базе SXEmacs.

info

Призываю читателей к дискуссии. Опишите свой взгляд, расскажите каким вы хотели бы видеть ([S]X)Emacs.

Подробное изложение каждой части этого плана я буду публиковать отдельными постами (не обязательно в правильном порядке). Потом их можно будет склеить в одну статейку и оформить как некий путь развития SXEmacs. Сегодня будет небольшой обзор подсистемы событий.

Подсистема событий

Самое удивительное, что сейчас вообще нет этой подсистемы ;). Есть определённые части, выполняющие необходимые действия, но отсутствует целостность, расширяемость и сила. В существующей ПС есть только высокий уровень доступа, нет разделения API на низкий/высокий уровни, поэтому если требуется что-то добавить, то необходимо делать много низкоуровневой мишуры, с помощью копи&паст программирования — утомительно.

В SXEmacs было решено использовать libev как низкоуровневую базу для ПС. Выбор стоял между liboop, libevent, libev и одной проприетарной библиотекой, которую планировалось перелицензировать (уже было получено согласие правообладателя). liboop отпала первой — библиотека практически мертва. libevent является почти de-facto стандартом для создания софта с ПС, но у нескольких разработчиков SXEmacs был негативный (как и позитивный тоже) опыт использования libevent и как раз ограничения, которые существуют в ней плюс просто ошеломляющие результаты тестирования скорости libev в сравнении с libevent не позволили выбрать libevent. Проприетарная библиотека отпала так как она зависела от дополнительной большой библиотеки, а так же в ней не очень удобный API (хотя, видимо, очень мощный) и отсутствовала документация.

Так зачем вообще всё это нужно? А для того, чтобы полностью избавиться от внутренних блокировок в SXEmacs. Блокировки бывают двух типов:

  1. Выполнение elisp кода приостанавливается в ожидании какого-то события.
  2. Блокирует сам вызов библиотечной функции.

К пункту 2 относятся вызовы для разрешения DNS имён, такие как gethostbyname(3) и getaddrinfo(3), вызов connect(2), а также, как это не удивительно, вызов write(2). В GNU Emacs реализован неблокирующий вызов connect если передано ключевое слово :nowait1 в процедуру make-network-process, при этом вызов make-network-process всё же может заблокировать, если заблокировало разыменование имени хоста. В SXEmacs вызов connect всегда блокирует, поэтому open-network-stream всегда блокирует. Что касается write(2), то по настоящему всё же write не блокирует, ибо дескриптор был переведён в неблокирующий режим для неблокирующего чтения, но все Емаксы, получая EWOULDBLOCK от write(2), делают select в ожидании, когда разрешат записать.

К пункту 1 относятся любые вызовы accept-process-output и call-process. Что произойдёт если accept-process-output ожидает данных и в этот момент приходит какое-нибудь событие? Если событие системное, то оно обрабатывается немедленно, если событие специальное или командное2, то оно будет отложено и записано в очередь событий, которые будут обработаны позже.

Асинхронный обработчик событий

В SXEmacs есть такая вещь называется ASYNEQ — это возможность асинхронной обработки событий. Не всякое событие может быть обработано асинхронно. В общем случае асинхронная обработка командных событий ведёт к условиям гонки (race condition)3. Специальные же события без проблем можно обрабатывать асинхронно. Именно по этой причине xlib и xwem работают более предсказуемо под SXEmacs, ибо они порождают просто тонны специальных событий, которые в XEmacs обрабатываются синхронно как и командные.

Часто командные события всё же можно обрабатывать асинхронно. Условия гонки были обнаружены токмо спустя год после того как в SXEmacs появился ASYNEQ. Это говорит о том, что условия гонки крайне редки и сильно зависят от специфики elisp кода, то есть автоматически невозможно определить возникнет ли в конкретном случае race condition или нет. Возможно, необходимо определить переменную asyneq-ignore-command-events, которую можно связать со значением t в особых случаях, когда необходимо запретить асинхронную обработку командных событий.

Как организовать новую ПС

Что мы хотим от новой ПС:

  • Целостность — одно место обработки событий, отсутствие костылей на вроде как при потенциальном блокировании write в process-send-string
  • Простой и мощный API как с низкоуровневыми, так и с высокоуровневыми конструкциями
  • Возможность интеграции других подсистем событий в ПС SXEmacs. Для начала X11 и Glib
Схема ПС

Сх. 1. пример работы ПС SXEmacs (event-loop.svg)

Использовать новую ПС будут:

  • На низком уровне
    • Асинхронное разыменование DNS
    • Остальные потенциально блокирующие вызовы
    • Клей для других ПС
  • На высоком уровне
    • Event API на elisp уровне
    • Таймеры
    • Прогнозируемые предикаты
    • И т.д.

Попробую изобразить в виде схемы, что будет происходить при возникновении командного события, которое порождает вызов open-network-stream. Как видим, событие помещается в очередь событий и когда до него дойдёт очередь, оно будет обработано, что породит вызов процедуры open-network-stream, которой, в свою очередь, необходимо разыменовать имя хоста и подсоединиться к нему, т.е. произвести две потенциально блокирующих операции. За время выполнения open-network-stream мы два раза посетим обработчик событий и в случае, если нам действительно нужно будет ждать, мы сможем обработать ещё кучу событий из очереди.

Именно так я и вижу работу новой ПС в SXEmacs.


1Вроде RMS был ярым противником ключевых слов
2Командные события порождаются действиями пользователя (нажал кнопку, мышкой побаловался и т.д.)
3Не буду описывать подробности, но, поверьте мне, я думал что помешался когда наблюдал эти условия гонки при работе с flyspell. Впрочем, психологическую составляющую этого опыта можно вынести отдельным постом, будет интересно

ReST source Скачать оригинал

Расширение REPL

Возникла тут идея реализовать возможность расширения возможностей REPL в SXEmacs, а особенно его Read части. Но сначала про Print часть.

Пользовательская печать

В XEmacs уже давно были предпосылки к созданию пользовательской печати elisp-объектов. Так, например, при задании структуры с помощью defstruct можно было указать :print-function — процедуру, которая осуществляет печать объекта данной структуры. Уже давно существовала магическая переменная custom-print-functions со следующим напутствием:

This variable is not used at present, but it is defined in hopes that a future Emacs interpreter will be able to use it.1

—custom-print-functions (cl.el)

В SXEmacs это напутствие было выполнено и появилась возможность пользовательской печати. Это моментально решило проблему печати сложно-рекурсивных структур, которая до этого решалась с помощью изменения переменных print-level и print-length.

Уже существуют пакеты, которые используют возможность пользовательской печати. Использовать эту возможность крайне легко:

(defstruct (test-prs
            (:print-function
             (lambda (tp s pl)
               (princ (format "#<test-prs %s>"
                              (test-prs-name tp)) s))))
  type              ; type of struct: `frs', `snd', ..
  (name "default")  ; name of object
  state             ; state of object
  plist             ; user defined properties list
  )
(make-test-prs :name "Done")
==> #<test-prs Done> 

Пользовательское чтение

Раз у нас есть пользовательская печать, то почему бы нам не иметь пользовательского чтения. В SXEmacs была реализована возможность пользовательского чтения под кодовым названием ureaders (от user readers). В новой встроенной (built-in) переменной ureaders хранится список пользовательских читалок. Пользовательская читалка это пара, состоящая из имени и функции-читалки от одного аргумента, которая возвращает elisp-объект или порождает ошибку. Приведу пример. Допустим, мы хотим чтобы следующая конструкция работала:

(setq mm '#<test ao -- 1
                 test -- 2
                 val -- "test">)
==> ((val . "test") (test . 2) (ao . 1)) 

реализуем:

(defun my-test-reader (input)
  (flet ((string-trim (s)
           (replace-in-string s "\\(^[ \t]+\\|[ \t]+$\\)" "")))
    (let ((ss (mapcar 'string-trim (split-string input "\n")))
          (rv nil))
      (dolist (s ss)
        (let ((rw (mapcar 'string-trim (split-string s "--"))))
          (push (cons (intern (car rw)) (read (cadr rw)))
                rv)))
      rv)))

(push '("test" . my-test-reader) ureaders) 

На первый взгляд эта возможность кажется не совсем нужной, ведь у нас и так есть мощнейшее средство — макросы. Это так, конечно, но в связке с пользовательской печатью почти прямой доступ к кишкам чтения выглядит интересно. Позже я продемонстрирую захватывающие вещи, которые можно реализовать с помощью ureaders.


1Эта переменная пока не используется и была создана в надежде, что в будущем интерпретатор Emacs сможет её использовать.

ReST source Скачать оригинал

пятница, 19 декабря 2008 г.

Просмотр страничных файлов в Wand-mode

Недавно в Wand-mode была добавлена возможность просмотра страничных файлов, таких как PDF, EPS, MPEG и т.д., у которых есть несколько страниц. Навигация по страницам до ужаса проста:

  • PgDown - Следующая страница
  • PgUp - Предыдущая страница
  • Home - Первая страница
  • End - Последняя страница
  • g или M-g - Переход на выбранную страницу. Передать номер страницы можно посредством универсального аргумента или ввести интерактивно.

Конечно, Wand-mode это вам не xpdf или gv, но для быстренького ознакомления с документом или быстренького выковыривания нужной картинки из книжки или видео вполне сгодится.

Текущая страница и общее количество страниц будет отображено на экране если у вас non-nil Wand-mode-show-fileinfo.

Заметим, что операции над картинкой будут осуществляться только для текущей страницы, а не для всех страниц файла.

ReST source Скачать оригинал

Wand-mode - алгоритм устранения красных глаз

Внимание!

Для корректного отображения этой страницы вашему браузеру необходима поддержка формата MathML.

Как и обещал напишу про то, как улучшить алгоритм устранения красных глаз у Wand-mode и как его тестировать. Возможно, что найдутся желающие его поковырять и улучшить ибо я не эстет да ещё и дальтоник к тому же, так что доволен тем, что есть и сейчас.

Описание алгоритма

Вкратце, проходим по всем пикселям выделенной области и смотрим на их красноту. Если пиксел достаточно красный, то заменяем его на тёмный. Затем немного размываем пикселы лежащие внутри эллипса, вписанного в выделенную область. Определение красности пиксела и его замена на более тёмный происходит в функции Wand-fix-red-pixels. Аргумент PIXELS, который она принимает — это список троек, где каждая тройка имеет вид (RED GREEN BLUE). RED, GREEN и BLUE принимают значения от 0 до 255.

После того как пикселы подправлены краснота уйдёт, но кое-где могут остаться резкие перепады цвета. Для того чтобы их сгладить мы накладываем эллиптическую маску (ибо зрак, в основном, круглой формы ;)) и применяем размытие по Гауссу с радиусом, который нам выдаст функция Wand-mode-redeye-blur-radius.

С чего начать

Неплохим началом в ковырянии алгоритма может стать функция Wand-mode-redeye-blur-radius. Это должна быть такая хитрая функция, которая даёт небольшие значения для маленьких входных и очень медленно растёт. Сейчас это убогая WH16 , где W — ширина выделенной области, а H — высота.

Тестирование улучшенного алгоритма

Для начала скачайте вот эту картинку с примерами красных глаз. Затем для тестирования вам понадобится следующая команда:

(defun Wand-test-redeye (arg)
  "Apply redeye reduction algorithm to ARG's region."
  (interactive "p")
  (let ((pp '(((41 41 349 302) (39 34 86 307)
               (44 42 354 58) (47 42 94 151))      ; first region
              ((29 31 315 676) (36 33 108 660)
               (86 79 168 468))                    ; second region
              ((28 26 335 885) (26 25 147 930)
               (21 18 267 774) (21 18 129 792))    ; third region
              ((26 26 343 1219) (28 29 87 1161)
               (25 18 294 1044) (23 18 127 1036))  ; fourth region
              ((17 15 431 1468) (18 17 376 1463)
               (17 20 310 1468) (17 20 232 1477)
               (13 15 116 1458) (13 15 25 1466)
               (23 25 253 1336) (21 25 132 1378))  ; fifth region
              )))
    (mapc (lambda (reg)
            (Wand-operation-apply 'redeye-remove image-wand reg))
          (nth (1- arg) pp))
    (Wand-redisplay))) 

Откройте картинку redeye-samples.jpg с помощью M-x Wand-display RET. Тестировать алгоритм устранения красных глаз можно с помощью команды C-u <NUM> M-x Wand-test-redeye RET, где <NUM> это номер области над которой нужно произвести тестирование. Как видно из кода всего есть 5 областей. После выполнения команды проверьте результаты с помощью программы для увеличения, я предпочитаю Lupe — быстро работает и вообще отличный софт. Если для всех областей результаты схожи с тем что справа, то у вас неплохо получилось.

Присылайте результаты в список рассылки sxemacs-devel@, для лучшего варианта предусмотрен приз ;)

ReST source Скачать оригинал

среда, 10 декабря 2008 г.

Логотип SXEmacs

Удивительно, но у SXEmacs нет логотипа в формате векторной графики, а тот, что есть в растре, по части качества просто отвратительный.

Я попробовал тут свои силы в создании лого в Inkscape. Вот, что из этого вышло:

Лого

и логотип для бета версии

Бета Лого

Вы можете также скачать исходный sxemacs.svg

Надеюсь Стиву понравится и оный станет официальным логотипом SXEmacs.

ReST source Скачать оригинал

вторник, 9 декабря 2008 г.

SXEmacs - Unix-way

Bloated software

Часто приходится слышать, что Емакс это не Unix-way, но так как он огромен, то в Емакс можно всё, иногда даже говорят, что Емакс это целая операционная система.

Да, GNU Emacs огромен, ибо подход выбран доставлять до пользователя максимально укомплектованный дистрибутив. В SXEmacs выбран другой подход — предоставить пользователю небольшой дистрибутив и лёгкий способ установки пакетов. Напомню, что в SXEmacs (в отличии от XEmacs), при поддержке ffi-curl, можно начать устанавливать пакеты без наличия каких-либо предустановленных пакетов. Так каким образом в дистрибутиве размером 9Mb может быть всё? Его там нет. Зато в Unix-like системе есть всё.

Вернёмся к изначальному вопросу. Емакс это и есть Unix-way в чистом виде и в Емакс можно всё потому и только потому, что в Unix-like системе можно всё, то есть Емакс это добротный унифицированный пользовательский интерфейс к Unix-like системе.

Давеча, экспериментируя с отображением видео, я добился таки показа видео в обычном glyph посредством xlib-ffi, но далеко не был удовлетворён. Я нутром чувствовал неправильность, хотя результаты были просто потрясающими. А смущало меня наличие mplayer — отличного средства для проигрывания видео. Памятуя совет из SICP — мысли отталкиваясь от желаемого (wishful thinking)1, я всё мечтал, чтобы видео в glyphе отображал mplayer, а не сам SXEmacs. И в итоге додумал как это можно сделать! Этот способ работает как в SXEmacs так и в XEmacs, хотя в XEmacs есть небольшие ограничения. Итак, код:

(defun lg-video-glyph (file &optional w h)
  "Create video glyph to play FILE.
Optionally you can specify width and height by passing W and H args."
  (let* ((gg (make-glyph [subwindow]))
         (prc (start-process "mplayer" nil "mplayer"
                             "-wid" (int-to-string
                                     (subwindow-xid
                                      (glyph-image-instance gg)))
                             (expand-file-name file))))
    (resize-subwindow
     (glyph-image-instance gg) (or w 200) (or h 140))
    (put gg 'mplayer-proc prc)
    gg))

(setq gg (lg-video-glyph "~/video/kids/kung-fu-panda.mp4"))
(set-extent-end-glyph
 (make-extent (point) (point))
 gg) 

конечно, укажите не ~/video/kids/kung-fu-panda.mp4, а реальный какой-нибудь файл с видео, который у вас есть. Появившийся, после выполнения s-exp с set-extent-end-glyph, glyph можно изменять в размерах при помощи:

(resize-subwindow (glyph-image-instance gg) WIDTH HEIGHT) 

Заметим также, что если навести мышкой на этот glyph, то можно управлять программой mplayer как обычно, т.е. делать перемотку, паузить, менять громкость и т.д. Должен работать и OSD.

В XEmacs вы можете обнаружить, что glyph становится микроскопическим (20x20) если изменить размер фрейма XEmacs. Эта неприятность была исправлена в SXEmacs.

Есть и ещё проблема. Как нам определить размер видео фрейма, чтобы выставить размеры glyphа, соблюдающие пропорцию? Можно, конечно, разобрать вывод программы mplayer, но что если формат вывода будет изменён2? Нет, нужно что-то более подходящее. В SXEmacs есть такая вещь как media-stream, воспользуемся этой возможностью:

(make-media-stream :file "~/crypt/video/kung-fu-panda.mp4" 'ffmpeg)
=>
#<media-stream :kind #<file "/home/lg/crypt/video/kung-fu-panda.mp4">
  #<media-substream :type #<video mov,mp4,m4a,3gp,3g2,mj2
                            (h264), 720x304 (2.37/1)>>
  #<media-substream :type #<audio mov,mp4,m4a,3gp,3g2,mj2
                            (mpeg4aac), stereo, 48000 Hz, 16 Bit>>
  driven by ffmpeg> 

как видим, информация о размере фрейма у нас имеется, в моём случае это 720x304.

У многих может возникнуть резонный вопрос «зачем в SXEmacs отображать видео?», некоторые даже могут подумать, что это идиотизм. Но представим, что есть «сумасшедший», который решил написать клиента для обмена сообщениями, работающего под SXEmacs. Причём сообщением может быть не только коротенький текст, но и голос или видео+голос или даже объект векторной графики. mplayer умеет принимать данные из stdin, поэтому вырисовывается неплохая схема медиа потоков.

SXEmacs media streams

Схема медиа потоков в SXEmacs

Опять же может возникнуть вопрос зачем такой клиент в SXEmacs, ведь есть XXX. Да, как отдельный компонент, возможно, задающие вопрос правы, но если этот клиент будет частью системы для совместной разработки (collaborative development), то уже не так очевидно. Понятно, что в системе совместной разработки должны присутствовать компоненты:

  • Мощный текстовый редактор ;)
  • Средство для совместного редактирования файлов
  • Средство для обмена сообщениями: текстовыми, голосовыми, видео
  • Средства для мозгового штурма — совместная доска для рисования, писания, черкания и т.д.
  • Система контроля версий

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

Основной же вывод из примера с видео-glyph я хочу сделать, что SXEmacs — это и есть Unix-way как он задумывался изначально.


1В русском переводе SICP «wishful thinking» переведено как «мечтать не вредно».
2Стандартизированный ввод и вывод программ это тоже один из аспектов Unix-way.

ReST source Скачать оригинал