Руководство программиста в Photon (953920), страница 42
Текст из файла (страница 42)
Функция PtResizeEventMsg() не уменьшит буфер сообщений меньше определённого минимального размера. Это потому, что библиотека виджета хочет продолжить функционировать.
Пример – регистрация сообщений об ошибках
Следующий фрагмент кода показывает, как неспециализированный обработчик ввода может быть использован для реагирования на сообщения от других пользователей, регистрирующих ошибки. Когда одно из таких сообщений приходит, приложение отображает содержание сообщения в многострочном текстовом виджете (Этот пример предполагает, что где-то в другом месте задекларировано log_message).
int input_proc(void *client_data, int rcvid, void *msg, size_t msglen) {
struct log_message *log = (struct log_message *)msg;
/* Обработка только регистрирующих (log) сообщений */
if (log->type == LOG_MSG) {
PtWidget_t *text = (PtWidget_t *)client_data;
struct log_message header;
int msg_offset = offsetof(struct log_message, msg);
int log_msglen;
int status;
/* Смотрим: если весь наш заголовок в буфере - это оно */
if (msglen < msg_offset) {
/* Читаем во всём заголовке */
if (MsgRead(rcvid, &header, msg_offset, 0) = = -1) {
status = errno;
MsgError( rcvid, status);
return Pt_HALT; /* отпускаем*/
}
log = &header;
}
log_msglen = msg_offset+log->msg_len;
/* Смотрим, всё ли сообщение в буфере */
if (msglen < log_msglen) {
struct log_message *log_msg = (struct log_message *)alloca(log_msglen);
/* Читаем остаток сообщения из пространства стека */
if (log_msg = = NULL || MsgRead( rcvid, log_msg, log_msglen, 0) = = -1) {
status = errno;
MsgError( rcvid, status);
return Pt_HALT; /* отпускаем */
}
log = log_msg;
}
add_msg(text, log);
status = 0;
MspReply( rcvid, 0, 0, 0);
}
return Pt_HALT;
}
Это приложение регистрирует функцию input_proc() как обработчик ввода для обработки не-Photon'овских сообщений от каких-либо других процессов. Функция input_proc() вначале проверяет тип пришедшего сообщения. Если обработчик ввода не является ответственным за этот тип сообщения, он немедленно возвращает управление. Это важно, поскольку будут вызваны также и какие-либо другие неспециализированные обработчики ввода, которые были зарегистрированы, и только один из них будет нести ответственность по данному сообщению. Если тип полученного сообщения – регистрирующее сообщение, функция убеждается, что Photon прочёл в свой буфер событий сообщение целиком. Это можно определить, проверив длину сообщения, представленную как msglen в обработчике ввода. Если часть сообщения в буфере событий отсутствует, в памяти выделяется буфер сообщения и вызывается функция MsgRead(), чтобы получить сообщение целиком. Затем функция input_proc() вызывает функцию add_msg(), чтобы добавить сообщение в текстовый виджет, и выдаёт ответ на сообщение.
Когда input_proc() завершает свою работу, она возвращает значение Pt_HALT. Это указывает Photon'овской библиотеке виджета не удалять обработчик ввода.
Импульсы Photon'а
[Прим. пер. Импульсы Photon'а – это отнюдь не импульсы QNX 6, а прокси QNX 4. Именно этот механизм.]
В дополнении к синхронному обмену сообщений, Photon поддерживает импульсы. Процесс, желающий уведомить другой процесс, но не желающий при этом ожидать ответа, может использовать импульсы Photon'а. Например, сервер может использовать импульс, чтобы общаться с клиентом в ситуации, когда отсылка сообщения может оставить их обоих SEND-блокированными (и поэтому попавшими в тупик). Импульс Photon'а определяется по его отрицательному идентификатору процесса, который может быть использован в качестве аргумента pid функции PtAppAddInput(). Этот идентификатор процесса является локальным для Вашего приложения. Если Вы хотите, чтобы другой процесс прислал Вам импульс, Вы должны "взвести" импульс, используя функцию PtPulseArm(). Это создаёт объект типа PtPulseMsg_t, который может быть послан другому процессу в сообщении. Другой процесс затем будет способен посылать импульсы, вызывая функцию MsgDeliverEvent().
В OC QNX версии 6 тип PtPulseMsg_t является структурой sigevent. Биты в msg.sigev_value.sival_int, которые соответствуют _NOTIFY_COND_MASK, сброшены, но могут быть установлены приложением, отсылающим импульс. Более подробно см. в описании функции ionotify() "Справочника библиотечных функций QNX 6".
PtPulseArm() (описанная в "Справочнике библиотечных функций Photon'а ") просто берёт структуру sigevent. Функции PtPulseArmFd() и PtPulseArmPid() существуют для совместимости с более ранними версиями OC QNX и Photon microGUI.
Давайте посмотрим код, который Вам надо написать, чтобы поддерживать импульс в:
-
Приложении Photon'а, которое получает импульсы
-
Приложении Photon'а, которое выпускает импульсы
Приложение Photon'а, получающее импульсы
Это адресат импульсов Photon'а, который делает большую часть подготовительной работы. Ему следует
-
Создать импульс
-
Взвести импульс
-
Отослать сообщение об импульсе процессу, который будет этот импульс испускать
-
Зарегистрировать обработчик ввода для сообщений импульса
-
Послать импульс самому себе, если это необходимо
-
Удалить импульс, когда он больше не будет нужен
В нижеприведенных разделах обсуждается каждый шаг, и далее следует пример.
Перед своим завершением процесс-получатель импульсов должен дать указание процессу, посылающему импульсы, перестать это делать.
Создание импульса
Чтобы создать импульс Photon'а, вызовите функцию PtAppCreatePulse():
pid_t PtAppCreatePulse(PtAppContext_t app, int priority);
аргументами которой являются:
app Адрес контекста приложения, структуры типа PtAppContext_t, которая управляет всеми данными, связанными с приложением. Вам следует передавать NULL в качестве этого аргумента, так чтобы использовался контекст по умолчанию.
priority приоритет импульса. Если он равен –1, используется приоритет вызывающей программы.
PtAppCreatePulse() возвращает идентификатор импульса, который является отрицательным числом, но никогда не равен –1. Это конец импульса на стороне получателя.
Взведение импульса
Взведение импульса заполняет структуру sigevent, которая может использоваться в большинстве вызовов QNX-функций, принимающих этот тип в качестве аргумента.
Нет никакой ошибки в том, чтобы иметь больше одного процесса-источника одного и того же импульса, несмотря на то, что получатель не будет в состоянии определить, какой процесс его послал.
Чтобы взвести импульс, вызовите функцию PtPulseArm(). Её прототип:
int PtPulseArm( PtAppContext_t app, pid_t pulse, struct sigevent *msg );
и аргументами являются:
app Указатель на PtAppContext_t структуру, определяющую текущий контекст приложения (обычно NULL)
pulse Импульс, созданный функцией PtAppCreatePulse()
msg указатель на сообщение импульса, созданное функцией. Это конец импульса на стороне отправителя, и мы должны будем отослать его этому процессу, как это описано ниже.
Эта функция возвращает указатель на идентификатор сообщения импульса, который понадобится нам позже.
Пересылка сообщения импульса испускателю импульсов
Метод, который Вы используете для отсылки сообщения импульса, зависит от процесса, который будет испускать импульсы:
-
Для менеджеров ресурсов:
ionotify(fd, _NOTIFY_ACTION_ARM, _NOTIFY_COND_INPUT, &pulsemsg);
-
Для любого другого типа процесса:
/*Создание своего собственного формата сообщения: */
msg.pulsemsg=pulsemsg;
MsgSendv(channel_id, &msg, msg_parts, &rmsg, rmsg_parts);
Регистрация обработчика ввода
Регистрация обработчика ввода для импульса похожа на регистрацию обработчика ввода для сообщения; см. раздел "Добавление обработчика ввода" выше в этой главе. Передайте идентификатор импульса, возвращённый функцией PtAppCreatePulse(), как параметр pid в функции ptAppAddInput().
Аргумент rcvid для обработчика ввода не будет иметь обязательно то же значение, что и идентификатор импульса: он сопоставляет идентификатор импульса в битах, определённых в _NOTIFY_DATA_MASK (см. описание ionotify() в "Справочнике библиотечных функций QNX 6"), но остальные биты берутся из полученного импульса QNX.
Посылка импульса самому себе
Если приложению требуется отослать импульс самому себе, оно может вызвать функцию PtAppPulseTrigger():
int PtAppPulseTrigger(PtAppContext_t app, pid_t pulse);
Параметрами этой функции являются структура типа PtAppContext_t, определяющая контекст приложения (обычно NULL), и идентификатор импульса, возвращённый функцией PtAppCreatePulse().
Удаление импульса
Когда ВАше приложение больше не нуждается в импульсе, он может быть удалён вызовом функции PtAppDeletePulse():
int PtAppDeletePulse(PtAppContext_t app, pid_t pulse_pid);
параметрами которой являются структура типа PtAppContext_t, определяющая контекст приложения (обычно NULL), и идентификатор импульса, возвращённый функцией PtAppCreatePulse().
Пример – очередь сообщений
Это приложение, получающее импульсы Photon'а. Оно открывает очередь сообщений (/dev/mqueue/testqueue по умолчанию), устанавливает импульс, и использует функцию mq_notify(), чтобы отдать ему импульс, когда в очереди сообщений есть нечто для прочтения:
/* Стандартные хеадеры */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <mqueue.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
/* Инструментальные хеадеры */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Локальные хеадеры */
#include "abimport.h"
#include "proto.h"
mqd_t mqd = -1;
struct sigevent sigev;
static void readqueue( void ) {
static unsigned counter;
unsigned mprio;
ssize_t msize;
char mbuf[ 4096 ];
while ( ( msize = mq_receive( mqd, mbuf, sizeof(mbuf), &mprio ) ) >= 0 ) {
char hbuf[ 40 ];
PtTextModifyText( ABW_mtext, 0, 0, -1, hbuf,
sprintf( hbuf, "Msg #%u (prio %d):\n", ++counter, mprio ));
PtTextModifyText( ABW_mtext, 0, 0, -1, mbuf, msize );
}
if ( errno != EAGAIN ) perror( "mq_receive" );
} // Функции readqueue()
static int input_fun( void *data, int rcvid, void *message, size_t mbsize ) {
readqueue();
if ( mq_notify( mqd, &sigev ) == -1 ) perror( "mq_notify" );
return Pt_HALT;
} // Функции input_fun()
pid_t pulse;
/* Строка опций приложения */
const char ApOptions[] = AB_OPTIONS ""; /* Добавьте Ваши опции в "" */
int init( int argc, char *argv[] ) {
if (( pulse = PtAppCreatePulse( NULL, -1 ) ) = = 0
|| PtAppAddInput( NULL, pulse, input_fun, NULL ) = = NULL ) {
fputs( "Инициализация не удалась\n", stderr );
exit( EXIT_FAILURE );
}
PtPulseArm( NULL, pulse, &sigev );
/* предотвращает предупреждения (варнинги) об отсутствии ссылок */
argc = argc, argv = argv;
return( Pt_CONTINUE );
} // Функции init()
int open_queue( PtWidget_t *link_instance, ApInfo_t *apinfo, PtCallbackInfo_t *cbinfo ) {
const char *name;
PtArg_t arg;
if ( mqd >= 0 ) mq_close( mqd );
PtSetArg( &arg, Pt_ARG_TEXT_STRING, &name, 0 );
PtGetResources( ABW_qname, 1, &arg );
if ( ( mqd = mq_open( name, O_RDONLY | O_CREAT | O_NONBLOCK, S_IRUSR | S_IWUSR,
NULL ) ) < 0 )
perror( name );
else
if ( mq_notify( mqd, &sigev ) == -1 ) {
perror( "mq_notify" );
mq_close( mqd );
mqd = -1;
}
else
readqueue();
/* предотвращает предупреждения (варнинги) об отсутствии ссылок */
link_instance = link_instance, apinfo = apinfo;
cbinfo = cbinfo;
return( Pt_CONTINUE );
} // Функции open_queue()
Приложение Photon, отправляющее импульсы
Приложение Photon'а, собирающееся отправлять импульсы, должно:
-
Иметь обработчик ввода для сообщений от того приложения, которое собирается получать эти импульсы. Этот обработчик ввода создаётся так, как описано ранее в разделе "Получение сообщений QNX" этой главы. Ему необходимо обрабатывать сообщения, содержащие сообщение импульса, и сообщения, указывающие ему прекратить выдачу импульсов.
Сохраните rcvid из сообщения, которое содержит сообщение импульса – этот идентификатор получателя понадобится Вам при отправке импульса.
-
Отправлять импульсы, вызывая функцию MsgDeliverEvent().
Обработка сигналов
Если Вашему приложению необходимо обрабатывать сигналы, Вам понадобится установить обработчик сигналов. Проблема заключается в том. что Вы не можете вызвать функции Photon'а из обработчика сигнала, поскольку библиотека виджетов не является сигнало-безопасной или рентабельной (повторно входимой).
Чтобы обойти эту проблему, библиотека Photon'а включает обработчик сигнала. Вы регистрируете функцию обработки сигнала, и Photon вызывает её после того, как
-
Photon'овский обработчик сигнала вернул управление
и
-
завершилась вся обработка текущего виджета.
Обрабатывая сигналы таким способом, Вы не получаете строгой производительности реального времени, поскольку Ваша функция обработки сигнала не вызывается немедленно, тотчас.
Добавление функции обработки сигналов
Чтобы добавить функцию обработки сигнала, используйте функцию PtAppAddSignalProc(). Обычно Вы будете вызывать её в
-
инициализирующей функции Вашего приложения
или
-
установочной функции для окна.
Вам понадобится подключить хеадер <signal.h>. Синтаксис функции ptAppAddSignalProc() следующий:
int PtAppAddSignalProc( PtAppContext_t app,
sigset_t const *set,
PtSignalProc_t func,