logo
Литература_1 / photon_old

Рабочие процедуры

Рабочая процедура выполняется всякий раз, когда для Вашего приложения нет сообщений, на которые надо реагировать. В каждом цикле петли обработки событий Photon'а эта процедура вызывается, если не получены никакие сообщения (лучше, чем блокироваться на ожидании последующих сообщений функцией MsgReceive() ). Эта процедура будет выполняться очень часто, так что стремитесь сохранить её как можно более короткой.

 Если Ваше рабочая процедура изменяет изображение, вызывайте функцию PtFlush(), чтобы гарантировать обновление изображения.

См. раздел "Потоки и рабочие процедуры" ниже, если Вы пишете рабочую процедуру для многопоточной программы.

Рабочие процедуры собираются в стек; когда Вы регистрируете рабочую процедуру, она помещается наверх стека. Вызывается только рабочая процедура, расположенная в вершине стека. Если вы удаляете рабочую процедуру, находящуюся в вершине стека, вызывается эта, что расположена сразу под ней.

Рабочая процедура сама по себе является функцией ответной реакции, получающей единственный void* параметр – client_data. Этот параметр client_data представляет собой данные [вернее, указатель на данные. – Прим. пер.], которые Вы присоединяете к рабочей процедуре, когда регистрируете её в библиотеке виджета. Вы должны создать структуру данных для рабочей процедуры, которая содержит вся её информацию о состоянии, и предоставить её как client_data.

Чтобы зарегистрировать, или добавить, рабочую процедуру, вызовите функциюPtAppAddWorkProc():

PtWorkProcId_t * PtAppAddWorkProc( PtAppContext_t app_context,

PtWorkProc_t work_func,

void *data );

параметрами которой являются:

PtAppAddWorkProc() возвращает указатель на структуру типа PtWorkProcId_t, которая идентифицирует рабочую процедуру. Чтобы удалить рабочую процедуру, когда она уже больше не нужна, вызывайте PtAppRemoveWorkProc():

void PtAppRemoveWorkProc( PtAppContext_t app_context,

PtWorkProcId_t *WorkProc_id );

передавая ей тот же контекст приложения и указатель, возвращённый функцией PtApppAddWorkProc().

Реальный пример использования рабочей процедуры слишком велик, чтобы размещать его здесь, поэтому вот простой итеративный пример. Рабочая процедура отсчитывает большое число, периодически обновляя надпись, чтобы отражать ход её исполнения.

#include <Pt.h>

typedef struct workDialog {

PtWidget_t *widget;

PtWidget_t *label;

PtWidget_t *ok_button;

} WorkDialog_t;

typedef struct countdownClosure {

WorkDialog_t *dialog;

int value;

int maxvalue;

int done;

PtWorkProcId_t *work_id;

} CountdownClosure_t;

WorkDialog_t *create_working_dialog(PtWidget_t *parent) {

PhDim_t dim;

PtArg_t args[3];

int nargs;

PtWidget_t *window, *group;

WorkDialog_t *dialog = (WorkDialog_t *) malloc(sizeof(WorkDialog_t));

if (dialog) {

dialog->widget = window = PtCreateWidget(PtWindow, parent, 0, NULL);

nargs = 0;

PtSetArg(&args[nargs], Pt_ARG_GROUP_ORIENTATION, Pt_GROUP_VERTICAL, 0);

nargs++;

PtSetArg(&args[nargs], Pt_ARG_GROUP_VERT_ALIGN, Pt_GROUP_VERT_CENTER, 0);

nargs++;

group = PtCreateWidget(PtGroup, window, nargs, args);

nargs = 0;

dim.w = 200;

dim.h = 100;

PtSetArg(&args[nargs], Pt_ARG_DIM, &dim, 0); nargs++;

PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING, "Counter: ", 0); nargs++;

dialog->label = PtCreateWidget(PtLabel, group, nargs, args);

PtCreateWidget(PtSeparator, group, 0, NULL);

nargs = 0;

PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING, "Stop", 0); nargs++;

dialog->ok_button = PtCreateWidget(PtButton, group, 1, args);

} // if(dialog)

return dialog;

} // create_working_dialog()

int done(PtWidget_t *w, void *client, PtCallbackInfo_t *call) {

CountdownClosure_t *closure = (CountdownClosure_t *)client;

call = call;

if (!closure->done) { PtAppRemoveWorkProc(NULL, closure->work_id); }

PtDestroyWidget(closure->dialog->widget);

free(closure->dialog);

free(closure);

return (Pt_CONTINUE);

} // done()

int count_cb(void *data) {

CountdownClosure_t *closure = (CountdownClosure_t *)data;

char buf[64];

int finished = 0;

if ( closure->value++ = = 0 || closure->value % 1000 = = 0 ) {

sprintf(buf, "Counter: %d", closure->value);

PtSetResource( closure->dialog->label, Pt_ARG_TEXT_STRING, buf, 0);

}

if ( closure->value = = closure->maxvalue ) {

closure->done = finished = 1;

PtSetResource( closure->dialog->ok_button, Pt_ARG_TEXT_STRING, "Done", 0);

}

return finished ? Pt_END : Pt_CONTINUE;

} // count_cb()

int push_button_cb(PtWidget_t *w, void *client, PtCallbackInfo_t *call) {

PtWidget_t *parent = (PtWidget_t *)client;

WorkDialog_t *dialog;

w = w; call = call;

dialog = create_working_dialog(parent);

if (dialog) {

CountdownClosure_t *closure = (CountdownClosure_t *) malloc(sizeof(CountdownClosure_t));

if (closure) {

PtWorkProcId_t *id;

closure->dialog = dialog;

closure->value = 0;

closure->maxvalue = 200000;

closure->done = 0;

closure->work_id = id =

PtAppAddWorkProc(NULL, count_cb, closure);

PtAddCallback(dialog->ok_button, Pt_CB_ACTIVATE, done, closure);

PtRealizeWidget(dialog->widget);

} // if (closure)

} // if (dialog)

return (Pt_CONTINUE);

}

int main(int argc, char *argv[]) {

PhDim_t dim;

PtArg_t args[3];

int n;

PtWidget_t *window;

PtCallback_t callbacks[] = {{push_button_cb, NULL}};

char Helvetica14b[MAX_FONT_TAG];

if (PtInit(NULL) = = -1) PtExit(EXIT_FAILURE);

dim.w = 200;

dim.h = 100;

PtSetArg(&args[0], Pt_ARG_DIM, &dim, 0);

if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT, 1, args)) = = NULL)

PtExit(EXIT_FAILURE);

callbacks[0].data = window;

n = 0;

PtSetArg(&args[n++], Pt_ARG_TEXT_STRING, "Обратный отсчёт...", 0);

/* Используется шрифт Helvetica 14-пунктовый жирный, если тот доступен */

if (PfGenerateFontName("Helvetica", PF_STYLE_BOLD, 14, Helvetica14b) = = NULL) {

perror("невозможно сгенерировать имя шрифта");

}

else PtSetArg(&args[n++], Pt_ARG_TEXT_FONT, Helvetica14b, 0);

PtSetArg(&args[n++], Pt_CB_ACTIVATE, callbacks, sizeof(callbacks)/sizeof(PtCallback_t));

PtCreateWidget(PtButton, window, n, args);

PtRealizeWidget(window);

PtMainLoop();

return (EXIT_SUCCESS);

}

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

Параметр closure содержит указатель на диалог, текущее значение счётчика и значение, до которого ведётся отсчёт. Когда это значение будет достигнуто, рабочая процедура изменит надпись на кнопке диалога и подсоединит ответную реакцию, которая снесёт напрочь диалог в целом, когда кнопка будет нажата. Будучи так вынужденной, рабочая процедура вернёт Pt_END, чтобы быть удалённой.

Функция done() вызывается, если пользователь останавливает рабочую процедуру или если она завершает свою работу. Эта функция удаляет диалог, связанный с рабочей процедурой, и удаляет рабочую процедуру, если та была остановлена пользователем (т.е. она не выполнилась вплоть до завершения).

Если Вы запустите этот пример на исполнение, Вы можете обнаружить ещё одну из возможностей рабочих процедур – они вытесняют одна другую. Когда Вы добавляете новую рабочую процедуру, она вытесняет все остальные. Новая рабочая процедура отработает только один раз, пока не завершится или не будет удалена. После этого возобновится исполнение рабочей процедуры, исполнявшейся до этого. Это проиллюстрировано в вышеприведенном примере – в случае, когда пользователь нажимает кнопку "Count Down..." до того, как отсчёт завершится. Создаётся новый диалог отсчёта, и этот отсчёт будет исполняться вместо первого, пока не выполнится.

Уровень модульности этой вытесняемости – на уровне вызова функции. Когда функция ответной реакции для рабочей процедуры вернёт управление, эта рабочая процедура может быть вытеснена другой рабочей процедурой.

    1. Потоки

Приложения Photon'а являются приложениями, управляемыми событиями и базирующимися на ответных реакциях; каждый раз, когда приходит событие, вызывается соответствующая ответная реакция и обрабатывает это событие, и затем управление возвращается в петлю событий на ожидание следующего события. В связи с такой структурой большинство приложений Photon'а являются однопоточными. Библиотека Photon'а позволяет Вам использовать потоки, но путём минимизации накладных расходов для однопоточных приложений. Библиотека Photon'а является скорее "дружественной по отношению к потокам", чем полностью потоко-безопасной, как функции printf() и malloc(), являющиеся потоко-безопасными.

 Не отменяйте поток, который может исполнять функцию библиотеки Photon'а или ответную реакцию (поскольку библиотека, возможно, должна выполнить какую-нибудь очистку, когда ответная реакция вернёт управление).

Этот раздел включает:

Yandex.RTB R-A-252273-3
Yandex.RTB R-A-252273-4