Added manual for events (Russian and English).

Signed-off-by: Slava Zanko <slavazanko@gmail.com>
This commit is contained in:
Slava Zanko 2011-02-24 17:32:00 +02:00
parent f40887c3c6
commit 7df04e95e2
2 changed files with 282 additions and 0 deletions

129
lib/event/event-ru.txt Normal file
View File

@ -0,0 +1,129 @@
= СОБЫТИЯ =
В 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},
...(тут любые другие события, которые необходимо мониторить)...

153
lib/event/event.txt Normal file
View File

@ -0,0 +1,153 @@
= EVENTS =
The subsystem of events used in mc is based on fast binary trees engine.
== Introduction ==
Subsystem of events is primarily designed to detach event source and event
handler in source code level. For example, VFS module uses function to show
messages, which is defined in the source code (not in the lib). In the lib,
definition of this function will be difficult (because the function does
a lot of calls of other functions from src). In this case, the transform
of this function to event handler is needed, and the display messages process
can be used as an event. Function as event handler should be registered
at early stage of mc start. Later just call the event, absolutely without
worrying whether the handler is tied to the event, and which function
is an event handler now (in some situation, plugin will load and reassign
this event handler to itself).
=== Usage ===
Events are applicable in any case. Sometimes it is hard to decide whether
it should be a common function or it should be an event handler. The replacement
of all functions used in keybindings process to event handler is good choice
(or parts of the 'switch () {case:}' in keybindings handlers). All keybindings
are described in lib/keybind.h.
The second argument to choose the solution (event handler or function) should be
thought whether that transformation of function to the event handler is easy,
the inverse process (handler to function) would be more difficult because
one event can have multiple handlers and each handler may depend to another.
A third argument in the choice in favor of the event handlers can be a plug-ins
(in future). In this case events is a way to give access to internal application
resources without providing a low-level API. All plug-ins need is to know what and how
call the event with proper structure type (#include "lib/event.h").
== Structure ==
In general, the subsystem of events can be represented as following:
------------------------------------ }
|Group1 Group2 ... GroupN| } Event groups (GTree)
------------------------------------- }
| | |
/|\ /|\ /|\
/ | \ / | ... ... eventN }
/ | \ / ... }
/ | \ ... } Events by groups
| | event3 } (GTree for any group)
| event2 }
event1 }
| | | |
f1f2...fN } list of event handlers (GPtrArray for any event)
This scheme allows to group events, and perform several handlers for one event.
== Requirements for event handlers ==
The following function prototype is event handler:
gboolean mc_event_callback_func_t (
const gchar *event_group,
const gchar *event_name,
gpointer init_data,
gpointer event_data
);
where:
event_group:
name of the group, where event was initiated
event_name:
event name. event_name ans event_group uniquely identify an event.
These parameters can be useful if event handler is tied to several events
and the distinguish between different events (for example, function of logging)
is required.
init_data:
Arbitrary data, provided to the event handler.
This data is provided by adding a handler to the event (the initialization data).
event_data:
Data provided to the handler when the event occurred.
Handler should return TRUE to allow running all other handlers tied to this event;
or FALSE if it is necessary to stop further processing of event (the remaining
handlers are not called).
If one event will have multiple handlers, the order of execution is "Last added - first
executed". This allows to override the standard event handlers (eg, in plug-ins).
=== Passing parameters to event handlers. Returning rezults ==
Due to the unification of the event handlers, there is no possibility to pass
a certain number of parameters and get the results of execution. Pass of a single
parameter (or get one result of an event handler) can be made as simple type casting
for one variable when event is called. But this way isn't applicable if pass
of several parameters (or get multiple return values) is required.
To solve this problem, you can pass the previously defined structure as universal
pointer event_data. All structures used in the event handlers should be defined
in the lib/event-types.h.
This way (the pass parameters as pointer to structure) has advantages and disadvantages.
Advantages:
* any number of parameters and their types;
* any number of return values and their types.
Disadvantages:
* probability of error: call the event with the wrong structure. In this case,
the handler will cast pointer to the structure on which it was designed.
At this point funny bugs and very long debugging process (especially if segfault
doesn't occur immediately) are possible;
* in order for an event handler and the initiator of the event to "communicate"
with each other, previously defined structures is needed.
== Examples ==
=== Logging ===
Consider the example of a temporary handler which simply logged the order
of certain events (for example, to detect infinite loop).
Here event handler:
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;
}
Add the following lines into src/event_init.c before "{NULL, NULL, NULL, NULL}" line
as one record to the initialization structure.
{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},
...(there any other events which you want to monitor)...