mirror of
https://github.com/MidnightCommander/mc
synced 2024-12-23 21:06:52 +03:00
130 lines
10 KiB
Plaintext
130 lines
10 KiB
Plaintext
|
= СОБЫТИЯ =
|
|||
|
|
|||
|
В 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},
|
|||
|
...(тут любые другие события, которые необходимо мониторить)...
|
|||
|
|