From 7df04e95e2a9a60a4d4a4daa1f57aa8ff3713794 Mon Sep 17 00:00:00 2001 From: Slava Zanko Date: Thu, 24 Feb 2011 17:32:00 +0200 Subject: [PATCH] Added manual for events (Russian and English). Signed-off-by: Slava Zanko --- lib/event/event-ru.txt | 129 ++++++++++++++++++++++++++++++++++ lib/event/event.txt | 153 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+) create mode 100644 lib/event/event-ru.txt create mode 100644 lib/event/event.txt diff --git a/lib/event/event-ru.txt b/lib/event/event-ru.txt new file mode 100644 index 000000000..0fbbb98a4 --- /dev/null +++ b/lib/event/event-ru.txt @@ -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}, +...(тут любые другие события, которые необходимо мониторить)... + diff --git a/lib/event/event.txt b/lib/event/event.txt new file mode 100644 index 000000000..a3460facd --- /dev/null +++ b/lib/event/event.txt @@ -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)...