123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- = СОБЫТИЯ =
- В mc используется система событий, основанная на технологии быстрых бинарных деревьев.
- == Вступление ==
- Система событий, в первую очередь, призвана отвязать на уровне исходных текстов источник события и обработчик.
- Например, в модуле VFS используется функция вывода сообщений, которая определена в исходных текстах (не в lib).
- В lib определение этой функции будет затруднительным (потому что функция делает множество вызовов на другие
- функции из src). В данном случае можно представить эту функцию как обработчик события, а сам процесс вывода
- сообщения от VFS на экран как непосредственно событие. В начале запуска mc функцию вывода сообщений необходимо
- зарегистрировать как обработчик события; в последствии при необходимости выдачи сообщений нужно просто вызвать
- событие, абсолютно не волнуясь, привязан ли обработчик к событию, а также какая именно функция сейчас является
- обработчиком события (вероятна ситуация, при которой позже загрузился плагин и переопределил этот обработчик
- событий на себя).
- === Использование ===
- Не везде и не всегда применимы события. Самое трудное порой бывает решить, это должна быть простая функция или это
- должно быть событие. Наиболее правильным будет заменить все функции (или части switch(){case:}), используемые при
- обработке кейбиндингов. Все кейбиндинги описаны в lib/keybind.h
- Вторым аргументом при выборе решения (обработчик события или функция) должна стать мысль, что функцию в обработчик
- события "превратить" легко; обратный процесс (обработчик в функцию) будет значительно сложнее - хотя бы потому,
- что на одном событии может "висеть" множество обработчиков, и каждый может зависеть от работы предыдущего.
- Третьим аргументом при выборе в пользу обработчиков событий могут стать плагины (когда появятся). В данном случае
- события - это способ дать плагинам доступ к внутренним ресурсам приложения, не "пуская" эти плагины в низкоуровневое
- API. Всё, что нужно будет знать "плагинам" - какое событие с какой структурой надо вызвать и как именно вызвать
- (#include "lib/event.h").
- == Структура ==
- В общем виде подсистему событий можно представить так:
- ------------------------------------ }
- |Группа1 Группа2 ... ГруппаN| } Группы событий (GTree)
- ------------------------------------- }
- | | |
- /|\ /|\ /|\
- / | \ / | ... ... событиеN }
- / | \ / ... }
- / | \ ... } События, разбитые на группы
- | | событие3 } (GTree для каждой группы)
- | событие2 }
- событие1 }
- | | | |
- f1f2...fN } список обработчиков события (GPtrArray на каждое событие)
- Такая схема позволяет группировать события и выполнять более одного обработчика на одно событие.
- == Требования к обработчикам событий ==
- Любой каллбэк должен быть вида:
- gboolean mc_event_callback_func_t (const gchar *event_group, const gchar *event_name, gpointer init_data, gpointer event_data);
- где:
- event_group:
- название группы, в которой было инициировано событие
- event_name:
- название события. Вместе с названием группы событий позволяют точно идентифицировать событие.
- Эти параметры могут быть полезны в случае если обработчик события привязан сразу к нескольким событиям
- и необходимо различать различные события (например, в функции логгирования)
- init_data:
- Произвольные данные, предоставляемые обработчикам события. Эти данные указываются при добавлении обработчика
- к событию (инициализационные данные).
- event_data:
- Данные, предоставляемые обработчику событий в момент возникновения события.
- Каллбэк должен вернуть TRUE, чтобы разрешить исполниться всем последующим за ним каллбэкам в данном событии; либо
- FALSE если необходимо немедленно прекратить дальнейшую обработку события (оставшиеся каллбэки не вызываются).
- Если для одного и того же события будет привязано множество каллбэков, то порядок из исполнения будет следующим:
- "Последним добавился - первым выполнился". Это позволяет в последствии переопределять стандартные обработчики событий
- (например, в плагинах).
- === Передача параметров в обработчики событий. Возврат результатов ===
- Из-за унификации обработчиков событий стало невозможным передать определённое количество параметров и
- получить результат выполнения. Если передачу одного параметра (или приём одного результата от обработчика события)
- можно произвести простым приведением типа на одну переменную при вызове событий, то при передаче нескольких параметров
- (или при получении нескольких результатов) такой метод неприменим.
- Для решения этой проблемы можно передавать ранее определённые структуры по универсальному указателю event_data.
- Все структуры, используемые в обработчиках событий, должны быть определены в файле lib/event-types.h.
- У данного метода (передача параметров указателем на структуру) есть как достоинства, так и недостатки.
- Достоинства:
- * произвольное количество параметров и их типов;
- * произвольное количество возвращаемых значений и их типов.
- Недостатки:
- * вероятность ошибки: вызов события с неправильной структурой. В данном случае обработчик приведёт указатель к той
- структуре, на которую он рассчитан. При этом возможны весёлые глюки с отладкой (особенно если нет сразу segfault);
- * необходимость иметь ранее определённые структуры для того, чтобы и обработчик события, и инициатор события могли бы
- без проблем "общаться" между собой.
- == Примеры использования ==
- === Логгирование ===
- Рассмотрим пример временного каллбэка, который просто логгирует порядок вызова определённых событий (например,
- для выявления зацикливаний).
- Сам каллбэк:
- gboolean mc_temp_event_logger (const gchar *event_group, const gchar *event_name, gpointer init_data, gpointer data)
- {
- (void) init_data;
- (void) data;
- mc_log("Event: %s:%s",event_group,event_name);
- return TRUE;
- }
- Добавляем его в src/event_init.c в виде записей к инициализационной структуре
- перед строчкой "{NULL, NULL, NULL, NULL}":
- {MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", mc_temp_event_logger, NULL},
- {MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", mc_temp_event_logger, NULL},
- {MCEVENT_GROUP_CORE, "clipboard_text_to_file", mc_temp_event_logger, NULL},
- {MCEVENT_GROUP_CORE, "clipboard_text_from_file", mc_temp_event_logger, NULL},
- ...(тут любые другие события, которые необходимо мониторить)...
|