Текущий раздел:

Описания RDS

Руководство программиста

Глава 2. Создание моделей блоков

§2.13. Вызов функций блоков

§2.13.2. Прямой вызов функции одного блока

Полный исходный текст на языке C++ для библиотеки (DLL) с моделями блоков, иллюстрирующих прямой вызов функции одного блока. Библиотека содержит две модели:

- PlusMinus – модель блока, увеличивающего и уменьшающего свой выход по щелчкам мыши и нажатию клавиш (см. §2.12.1, теперь к блоку можно подключать поле ввода для точного задания значения);
- SimpleJoystick – модель блока, изображающего двухкоординатную рукоятку, которую пользователь может перемещать мышью (см. §2.12.2, теперь у блоку можно подключать поля ввода для точного задания координат рукоятки).

Изменения относительно предыдущих версий этих моделей выделены цветом.

      // Прямой вызов функции блока
      #include <windows.h>
      #include <RdsDef.h>
      // Подготовка описаний сервисных функций
      #define RDS_SERV_FUNC_BODY GetInterfaceFunctions
      #include <RdsFunc.h>

      //========== Главная функция DLL ==========
      int WINAPI DllMain(HINSTANCE /*hinst*/,
                               unsigned long reason,
                               void* /*lpReserved*/)
      { if(reason==DLL_PROCESS_ATTACH) // Загрузка DLL
          { // Получение доступа к функциям RDS
            if(!GetInterfaceFunctions())
              RDS_SERV_ERROR_MSGW // Сообщение: старая версия RDS
          }
        return 1;
      }
      //========= Конец главной функции =========

      // Глобальная переменная для уникального идентификатора функции
      // "Common.ControlValueChanged"
      int ControlValueChangedId=0;
      // Функция обратного вызова для "Common.ControlValueChanged"
      BOOL RDSCALL ControlValChanged_Callback(
        RDS_PPOINTDESCRIPTION /*src*/,
        RDS_PPOINTDESCRIPTION dest,
        LPVOID /*data*/)
      { // Вызов функции "Common.ControlValueChanged" у блока на другом
        // конце связи
        rdsCallBlockFunction(dest->Block,ControlValueChangedId,NULL);
        // Возвращаем TRUE – не останавливаем перебор блоков
        return TRUE;
      }
      //=========================================
      // Функция уведомления соседей об изменении значения
      void ControlValueChangedCall(BOOL *pCancelCall)
      { // В параметре pCancelCall передается указатель на флаг
        // блокировки вызова функции
        if(*pCancelCall) // Вызов заблокирован
          return;

        // Принудительно передаем данные выходов блока, модель которого
        // сейчас выполняется, по связям
        rdsActivateOutputConnections(NULL,TRUE);

        // Взводим флаг блокировки перед вызовом функций
        *pCancelCall=TRUE;
        // Перебираем все простые блоки, соединенные с выходами текущего
        // (для каждого будет вызвана ControlValChanged_Callback)
        rdsEnumConnectedBlocks(NULL,
                               RDS_BEN_OUTPUTS | RDS_BEN_TRACELINKS,
                               ControlValChanged_Callback,
                               NULL);
        // Сбрасываем флаг блокировки после вызова функций
        *pCancelCall=FALSE;
      }
      //=========================================

      //====================================================================
      // Уменьшающий/увеличивающий выход блок
      //====================================================================

      //====== Класс личной области данных ======
      class TPlusMinusData
      { public:
          int KeyPlus;       // Клавиша увеличения
          DWORD ShiftsPlus;  // и ее флаги
          int KeyMinus;      // Клавиша уменьшения
          DWORD ShiftsMinus; // и ее флаги

          // Флаг блокировки повторных вызовов
          // функции "Common.ControlValueChanged"
          BOOL NoCall;
          int Setup(void);    // Функция настройки клавиш
          void SaveBin(void); // Сохранение параметров
          int LoadBin(void);  // Загрузка параметров

          // Конструктор класса
          TPlusMinusData(void)
            { KeyPlus=ShiftsPlus=KeyMinus=ShiftsMinus=0;
              NoCall=FALSE; // Исходно флаг блокировки сброшен
              if(ControlValueChangedId==0) // Регистрация функции
                ControlValueChangedId=
                     rdsRegisterFunction("Common.ControlValueChanged");
            };
      };
      //=========================================

      // Функция задания клавиш
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      int TPlusMinusData::Setup(void)
      { RDS_HOBJECT window; // Идентификатор вспомогательного объекта
        BOOL ok;            // Пользователь нажал "OK"
        // Создание окна
        window=rdsFORMCreate(FALSE,-1,-1,"Плюс/минус");
        // Добавление полей ввода
        rdsFORMAddEdit(window,0,1,RDS_FORMCTRL_HOTKEY,
                       "Клавиша увеличения:",80);
        rdsFORMAddEdit(window,0,2,RDS_FORMCTRL_HOTKEY,
                       "Клавиша уменьшения:",80);
        // Занесение исходных значений кодов клавиш в поля ввода
        rdsSetObjectInt(window,1,RDS_FORMVAL_VALUE,KeyPlus);
        rdsSetObjectInt(window,1,RDS_FORMVAL_HKSHIFTS,ShiftsPlus);
        rdsSetObjectInt(window,2,RDS_FORMVAL_VALUE,KeyMinus);
        rdsSetObjectInt(window,2,RDS_FORMVAL_HKSHIFTS,ShiftsMinus);
        // Открытие окна
        ok=rdsFORMShowModalEx(window,NULL);
        if(ok)
          { // Нажата кнопка OK - запись кодов клавиш в класс
            KeyPlus=rdsGetObjectInt(window,1,RDS_FORMVAL_VALUE);
            ShiftsPlus=rdsGetObjectInt(window,1,RDS_FORMVAL_HKSHIFTS);
            KeyMinus=rdsGetObjectInt(window,2,RDS_FORMVAL_VALUE);
            ShiftsMinus=rdsGetObjectInt(window,2,RDS_FORMVAL_HKSHIFTS);
          }
        // Уничтожение окна
        rdsDeleteObject(window);
        // Возвращаемое значение
        return ok?RDS_BFR_MODIFIED:RDS_BFR_DONE;
      }
      //=========================================

      // Сохранение параметров
      void TPlusMinusData::SaveBin(void)
      { BYTE tag; // Переменная для байта тега

        tag=1; // Тег 1 - клавиша увеличения
        rdsWriteBlockData(&tag,sizeof(tag));
        rdsWriteBlockData(&KeyPlus,sizeof(KeyPlus));
        rdsWriteBlockData(&ShiftsPlus,sizeof(ShiftsPlus));

        tag=2; // Тег 2 - клавиша уменьшения
        rdsWriteBlockData(&tag,sizeof(tag));
        rdsWriteBlockData(&KeyMinus,sizeof(KeyMinus));
        rdsWriteBlockData(&ShiftsMinus,sizeof(ShiftsMinus));

        tag=0; // Тег 0 - конец данных
        rdsWriteBlockData(&tag,sizeof(tag));
      }
      //=========================================

      // Загрузка параметров
      int TPlusMinusData::LoadBin(void)
      { BYTE tag;

        for(;;) // Цикл до тех пор, пока данные не кончатся
          { // Читаем байт тега
            if(!rdsReadBlockData(&tag,sizeof(tag)))
              break; // Тег не считан - данные кончились
            // Анализируем считанный тег
            switch(tag)
              { case 0: // Конец данных блока
                  return RDS_BFR_DONE; // Загрузка успешно завершена
                case 1: // Данные клавиши увеличения
                  rdsReadBlockData(&KeyPlus,sizeof(KeyPlus));
                  rdsReadBlockData(&ShiftsPlus,sizeof(ShiftsPlus));
                  break;
                case 2: // Данные клавиши уменьшения
                  rdsReadBlockData(&KeyMinus,sizeof(KeyMinus));
                  rdsReadBlockData(&ShiftsMinus,sizeof(ShiftsMinus));
                  break;
                default: // Неопознанный тег
                  return RDS_BFR_ERROR; // Сообщаем RDS об ошибке
              }
          }
        // Данные кончились до тега 0 - сообщаем об ошибке
        return RDS_BFR_ERROR;
      }
      //=========================================

      // Увеличение/уменьшение значения по щелчку и клавишам
      extern "C" __declspec(dllexport)
        int RDSCALL PlusMinus(int CallMode,
                          RDS_PBLOCKDATA BlockData,
                          LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define v (*((RDSINT32 *)(pStart+2*RDS_VSZ_S)))
      #define input (*((RDSINT32 *)(pStart+2*RDS_VSZ_S+RDS_VSZ_I)))

        // Вспомогательная — указатель на структуру события мыши
        RDS_PMOUSEDATA mouse;
        // Вспомогательная — указатель на структуру события клавиатуры
        RDS_PKEYDATA key;
        // Указатель на личную область данных блока,приведенный к
        // правильному типу
        TPlusMinusData *data=(TPlusMinusData*)(BlockData->BlockData);

        switch(CallMode)
          { // Проверка типа статических переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSII}")?
                RDS_BFR_BADVARSMSG:RDS_BFR_DONE;

            // Реакция на нажатие кнопки мыши
            case RDS_BFM_MOUSEDOWN:
              // Приведение ExtParam к нужному типу
              mouse=(RDS_PMOUSEDATA)ExtParam;
              if(mouse->Button==RDS_MLEFTBUTTON)
                { // Нажата левая кнопка
                  // Проверяем, есть ли у блока картинка (получаем описание блока)
                  RDS_BLOCKDESCRIPTION descr;
                  descr.servSize=sizeof(descr);
                  rdsGetBlockDescription(BlockData->Block,&descr);
                  if(descr.Flags & RDS_BDF_HASPICTURE)
                    { // Картинка есть – определяем идентификатор
                      // элемента под курсором
                      int id=rdsGetMouseObjectId(mouse);
                      v+=id;
                    }
                  else if(mouse->y<mouse->Top+mouse->Height/2)
                    v++; // В верхней половине блока - увеличиваем
                  else
                    v--; // В нижней половине блока — уменьшаем
                  // Взводим сигнал готовности
                  Ready=1;
                  // Принудительно передаем в соседние блоки
                  ControlValueChangedCall(&(data->NoCall));
                }
              break;

            case RDS_BFM_INIT: // Инициализация
              BlockData->BlockData=new TPlusMinusData();
              break;
            case RDS_BFM_CLEANUP:// Очистка данных
              delete data;
              break;
            case RDS_BFM_SETUP: // Настройка параметров
              return data->Setup();
            case RDS_BFM_SAVEBIN:// Сохранение параметров
              data->SaveBin();
              break;
            case RDS_BFM_LOADBIN:// Загрузка параметров
              return data->LoadBin();

            // Реакция на нажатие клавиши
            case RDS_BFM_KEYDOWN:
              // Приведение ExtParam к нужному типу
              key=(RDS_PKEYDATA)ExtParam;
              // Сравнение нажатой клавиши с клавишами уменьшения
              // и увеличения
              if(key->KeyCode==data->KeyPlus &&
                 key->Shift==data->ShiftsPlus)
                { v++; Ready=1; }
              else if(key->KeyCode==data->KeyMinus &&
                      key->Shift==data->ShiftsMinus)
                { v--; Ready=1; }
              // Принудительно передаем в соседние блоки
              if(Ready)
                ControlValueChangedCall(&(data->NoCall));
              break;

            // Такт расчета
            case RDS_BFM_MODEL:
              if(v==input) // Новое значение равно старому
                { Ready=0; // Не передаем по связям
                  break;
                }
              v=input; // Передаем значение входа на выход
              // Принудительно передаем в соседние блоки
              ControlValueChangedCall(&(data->NoCall));
              break;
            // Реакция на вызов функции блока
            case RDS_BFM_FUNCTIONCALL:
              if(((RDS_PFUNCTIONCALLDATA)ExtParam)->Function==
                                         ControlValueChangedId)
                { // Вызвана"Common.ControlValueChanged"
                  if(data->NoCall) // Взведен флаг блокировки
                    break;
                  if(v==input) // Новое значение равно старому -
                    break;     // передавать не нужно
                  v=input; // Передаем значение входа на выход
                  Ready=1; // Взводим сигнал готовности
                  // Принудительно передаем в соседние блоки
                  ControlValueChangedCall(&(data->NoCall));
                }
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef input
      #undef v
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================

      //============================================================================
      // Двухкоординатная рукоятка
      //============================================================================

      //====== Класс личной области данных ======
      class TSimpleJoystick
      { private:
          // Центр круга (рукоятки) до начала перетаскивания
          int OldHandleX,OldHandleY;
          // Координаты курсора на момент начала перетаскивания
          int OldMouseX,OldMouseY;
          // Флаги фиксации одной из координат
          BOOL LockX,LockY;
          // Идентификаторы добавленных пунктов меню
          RDS_MENUITEM MenuLockX,MenuLockY;
        public:
          // Настроечные параметры блока
          COLORREF BorderColor;       // Цвет рамки блока
          COLORREF FieldColor;        // Цвет прямоугольника
          COLORREF HandleColor;       // Цвет круга в покое
          COLORREF MovingHandleColor; // Цвет круга при таскании
          COLORREF GrayedColor;       // Цвет недоступной области
          int HandleSize;             // Диаметр круга

          // Флаг блокировки повторных вызовов
          // функции "Common.ControlValueChanged"
          BOOL NoCall;
          // Реакция на нажатие кнопки мыши
          int MouseDown(RDS_PMOUSEDATA mouse,double x,double y,DWORD *pFlags);
          // Реакция на перемещение курсора мыши
          void MouseMove(RDS_PMOUSEDATA mouse,double *px,double *py);
          // Рисование изображения блока
          void Draw(RDS_PDRAWDATA draw,double x,double y,BOOL moving);
          // Реакция на выбор добавленного пункта меню
          void MenuFunction(RDS_PMENUFUNCDATA MenuData);
          // Реакция на изменение размеров блока
          void Resizing(RDS_PRESIZEDATA ResData);
          // Ограничение входных значений
          void LimitInputValues(double *px_in,double *py_in,double x,double y);
          // Конструктор класса
          TSimpleJoystick(void)
            { BorderColor=0;          // Черная рамка
              FieldColor=0xffffff;    // Белое поле
              HandleColor=0xff0000;   // Синий круг
              MovingHandleColor=0xff; // Красный при таскании
              HandleSize=20;          // Диаметр круга
              GrayedColor=0x7f7f7f;   // Серый
              LockX=LockY=FALSE;      // Фиксация выключена
              // Создание пунктов меню
              // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
              //   в противном случае необходимо использовать версии функций
              //   с суффиксом "W" и символьные константы с префиксом "L"
              MenuLockX=rdsRegisterContextMenuItem("Фиксировать X",1,0);
              MenuLockY=rdsRegisterContextMenuItem("Фиксировать Y",2,0);

              NoCall=FALSE; // Исходно флаг блокировки сброшен
              if(ControlValueChangedId==0) // Регистрация функции
                ControlValueChangedId=
                     rdsRegisterFunction("Common.ControlValueChanged");
            };
          // Деструктор класса
          ~TSimpleJoystick()
            { // Уничтожение пунктов меню
              rdsUnregisterMenuItem(MenuLockX);
              rdsUnregisterMenuItem(MenuLockY);
            };
      };
      //=========================================

      // Рисование изображения блока
      void TSimpleJoystick::Draw(RDS_PDRAWDATA draw,
                                 double x,double y,BOOL moving)
      { int hx,hy,cx,cy;
        RECT r;
        int hR=HandleSize*draw->DoubleZoom/2; // Радиус круга-рукоятки

        // Если размер блока - нулевой, рисовать негде
        if(draw->Height==0 || draw->Width==0)
          return;

        // Рисование поля блока
        rdsXGSetPenStyle(0,PS_SOLID,1,BorderColor,R2_COPYPEN);
        rdsXGSetBrushStyle(0,RDS_GFS_SOLID,FieldColor);
        rdsXGRectangle(draw->Left,draw->Top,
          draw->Left+draw->Width,draw->Top+draw->Height);

        // Вычисление центра прямоугольника блока
        cx=draw->Left+draw->Width/2;
        cy=draw->Top+draw->Height/2;

        // Вычисление координат центра круга-рукоятки
        hx=cx+x*draw->Width/2;
        hy=cy-y*draw->Height/2;

        // Установка области отсечения
        r.left=draw->Left+1;
        r.top=draw->Top+1;
        r.right=draw->Left+draw->Width-1;
        r.bottom=draw->Top+draw->Height-1;
        rdsXGSetClipRect(&r);

        // Рисование ограничений
        if(LockX||LockY) // Фиксируется одна из координат
          { // Установка серого цвета заливки
            rdsXGSetBrushStyle(0,RDS_GFS_SOLID,GrayedColor);
            if(LockX) // Фиксируется X
              { rdsXGFillRect(r.left,r.top,hx-hR,r.bottom);	// Слева
                rdsXGFillRect(hx+hR,r.top,r.right,r.bottom);// Справа
              }
            else // Фиксируется Y
              { rdsXGFillRect(r.left,r.top,r.right,hy-hR);	// Сверху
                rdsXGFillRect(r.left,hy+hR,r.right,r.bottom);// Снизу
              }
          }

        // Линии перекрестия
        rdsXGMoveTo(cx,draw->Top);
        rdsXGLineTo(cx,draw->Top+draw->Height);
        rdsXGMoveTo(draw->Left,cy);
        rdsXGLineTo(draw->Left+draw->Width,cy);

        // Рисование круга (цвет зависит от параметра moving)
        rdsXGSetPenStyle(RDS_GFSTYLE,PS_NULL,0,0,0);
        rdsXGSetBrushStyle(0,RDS_GFS_SOLID,
          moving?MovingHandleColor:HandleColor);
        rdsXGEllipse(hx-hR,hy-hR,hx+hR+1,hy+hR+1);

        // Отмена отсечения
        rdsXGSetClipRect(NULL);
      }
      //=========================================

      // Реакция на нажатие кнопки мыши
      int TSimpleJoystick::MouseDown(RDS_PMOUSEDATA mouse,
                                     double x,double y,DWORD *pFlags)
      { int hx,hy,cx,cy,
            hR=HandleSize*mouse->DoubleZoom/2; // Радиус круга

        // Если размер - нулевой, реакция не имеет смысла
        if(mouse->Height==0 || mouse->Width==0)
          return RDS_BFR_DONE;
        // Если нажата не левая кнопка, перетаскивать не надо
        // Разрешаем в этом случае вызов контекстного меню блока
        if(mouse->Button!=RDS_MLEFTBUTTON)
          return RDS_BFR_SHOWMENU;

        // Координаты цента блока
        cx=mouse->Left+mouse->Width/2;
        cy=mouse->Top+mouse->Height/2;
        // Координаты центра круга-рукоятки
        hx=cx+x*mouse->Width/2;
        hy=cy-y*mouse->Height/2;

        // Проверка попадания курсора в круг
        if(abs(mouse->x-hx)<=hR && abs(mouse->y-hy)<=hR)
          { // Курсор попал в круг
            // Запоминаем координаты центр круга на момент
            // начала перетаскивания
            OldHandleX=hx;
            OldHandleY=hy;
            // Координаты курсора на начало перетаскивания
            OldMouseX=mouse->x;
            OldMouseY=mouse->y;
            // Взводим флаг захвата мыши
            *pFlags|=RDS_MOUSECAPTURE;
          }
        // Курсор не попал в рукоятку - захватывать мышь
        // и подготавливать перетаскивание не нужно
        return RDS_BFR_DONE;
      }
      //=========================================

      // Реакция на перемещение курсора мыши
      void TSimpleJoystick::MouseMove(RDS_PMOUSEDATA mouse,
                                      double *px,double *py)
      { int hx,hy,cx,cy;

        // Если размер - нулевой, реакция не имеет смысла
        if(mouse->Height==0 || mouse->Width==0)
          { *px=*py=0.0;
            return;
          }

        // Новые координаты центра рукоятки
        hx=OldHandleX+(mouse->x-OldMouseX);
        hy=OldHandleY+(mouse->y-OldMouseY);

        // Координаты центра блока
        cx=mouse->Left+mouse->Width/2;
        cy=mouse->Top+mouse->Height/2;

        // По новым координатам центра рукоятки вычисляем соответствующие
        // им вещественные значения выходов, ограничивая их
        // диапазоном [-1...1]
        if(!LockX)
          { *px=2.0*(hx-cx)/mouse->Width;
            if(*px>1.0) *px=1.0;
            else if(*px<-1.0) *px=-1.0;
          }
        if(!LockY)
          { *py=-2.0*(hy-cy)/mouse->Height;
            if(*py>1.0) *py=1.0;
            else if(*py<-1.0) *py=-1.0;
          }
      }
      //=========================================

      // Функция реакции на выбор одного из пунктов меню
      void TSimpleJoystick::MenuFunction(RDS_PMENUFUNCDATA MenuData)
      { switch(MenuData->Function)
          { case 1: // Выбран пункт "Фиксировать X"
              LockX=!LockX; // Переключаем флаг фиксации X
              LockY=FALSE;  // Отключаем фиксацию Y
              break;
            case 2: // Выбран пункт "Фиксировать Y"
              LockY=!LockY; // Переключаем флаг фиксации Y
              LockX=FALSE;  // Отключаем фиксацию X
              break;
          }
        // Установка галочек у пунктов меню в зависимости от
        // флагов фиксации координат
        rdsSetMenuItemOptions(MenuLockX,LockX?RDS_MENU_CHECKED:0);
        rdsSetMenuItemOptions(MenuLockY,LockY?RDS_MENU_CHECKED:0);
      }
      //=========================================

      // Реакция на изменение размеров блока
      void TSimpleJoystick::Resizing(RDS_PRESIZEDATA ResData)
      {
        if(ResData->HorzResize && (!ResData->VertResize))
          // Перетаскивается левый или правый маркер
          ResData->newHeight=ResData->newWidth;
        else if((!ResData->HorzResize) && ResData->VertResize)
          // Перетаскивается верхний или нижний маркер
          ResData->newWidth=ResData->newHeight;
        else // Перетаскивается угловой маркер или размер задан точно
          { // Вычисляем среднее арифметическое
            int avg=(ResData->newWidth+ResData->newHeight)/2;
            // Присваиваем его ширине и высоте
            ResData->newWidth=ResData->newHeight=avg;
          }
      }
      //=========================================

    // Ограничение значений входов блока
      void TSimpleJoystick::LimitInputValues(double *px_in,double *py_in,
                                             double x,double y)
      { // px_in, py_in - указатели на входы блока
        // x, y - текущие значения выходов
        if(LockX) // Координата x зафиксирована
          *px_in=x; // Присваиваем входу x_in зафиксированное значение
        else // Ограничиваем x_in диапазоном [-1...1]
          { if(*px_in<-1.0) *px_in=-1.0;
            else if(*px_in>1.0) *px_in=1.0;
          }
        if(LockY) // Координата y зафиксирована
          *py_in=y; // Присваиваем входу y_in зафиксированное значение
        else // Ограничиваем y_in диапазоном [-1...1]
          { if(*py_in<-1.0) *py_in=-1.0;
            else if(*py_in>1.0) *py_in=1.0;
          }
      }
      //=========================================

      // Функция модели блока
      extern "C" __declspec(dllexport)
        int E SimpleJoystick(int CallMode,
                RDS_PBLOCKDATA BlockData,
                LPVOID ExtParam)
      {
      // Макроопределения для статических переменных
      #define pStart  ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define x (*((double *)(pStart+2*RDS_VSZ_S)))
      #define y (*((double *)(pStart+2*RDS_VSZ_S+RDS_VSZ_D)))
      #define x_in (*((double *)(pStart+2*RDS_VSZ_S+2*RDS_VSZ_D)))
      #define y_in (*((double *)(pStart+2*RDS_VSZ_S+3*RDS_VSZ_D)))
      #define x_in_ok (*((char *)(pStart+2*RDS_VSZ_S+4*RDS_VSZ_D)))
      #define y_in_ok (*((char *)(pStart+3*RDS_VSZ_S+4*RDS_VSZ_D)))
        // Вспомогательная переменная - указатель на личную область,
        // приведенный к правильному типу
        TSimpleJoystick *data=(TSimpleJoystick*)(BlockData->BlockData);
        switch(CallMode)
          { // Инициализация
           case RDS_BFM_INIT:
              BlockData->BlockData=new TSimpleJoystick();
              break;
            // Очистка
            case RDS_BFM_CLEANUP:
              delete data;
              break;
            // Проверка допустимости типов переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSDDDDSS}")?
                RDS_BFR_BADVARSMSG:RDS_BFR_DONE;
            // Нажатие кнопки мыши
            case RDS_BFM_MOUSEDOWN:
              return data->MouseDown((RDS_PMOUSEDATA)ExtParam,x,y,
                &(BlockData->Flags));
            // Отпускание кнопки мыши
            case RDS_BFM_MOUSEUP:
              // Снятие захвата мыши
              RDS_SETFLAG(BlockData->Flags,RDS_MOUSECAPTURE,FALSE);
              break;
            // Перемещение курсора мыши
            case RDS_BFM_MOUSEMOVE:
              // Проверка: включен ли захват мыши
              if(BlockData->Flags & RDS_MOUSECAPTURE)	// Включен
                { // Запоминаем старые значения
                   double oldX=x,oldY=y;
                  // Вызываем функцию реакции
                  data->MouseMove((RDS_PMOUSEDATA)ExtParam,&x,&y);
                  Ready=1;	// Взводим сигнал готовности
                  // Если значения изменились, передаем соседям
                  if(oldX!=x || oldY!=y)
                    ControlValueChangedCall(&(data->NoCall));
                }
              break;
            // Рисование
            case RDS_BFM_DRAW:
              data->Draw((RDS_PDRAWDATA)ExtParam,x,y,
              BlockData->Flags & RDS_MOUSECAPTURE);
              break;

            // Выбор пользователем добавленного пункта меню
            case RDS_BFM_MENUFUNCTION:
              data->MenuFunction((RDS_PMENUFUNCDATA)ExtParam);
              break;

            // Изменение размеров блока
            case RDS_BFM_RESIZE:
            case RDS_BFM_RESIZING:
              data->Resizing((RDS_PRESIZEDATA)ExtParam);
              break;

            // Один такт расчета
            case RDS_BFM_MODEL:
              if(x_in_ok==0 && y_in_ok==0)
                { // Связанные сигналы не взведены
                  Ready=0; // Сбрасываем сигнал готовности
                  break;
                }
              if(x==x_in && y==y_in)
                { // На входах те же значения, что и на выходах
                  Ready=x_in_ok=y_in_ok=0; // Сбрасываем все сигналы
                  break;
                }
              // Ограничиваем входные значения
              data->LimitInputValues(&x_in,&y_in,x,y);
              // В зависимости от того, какой связанный сигнал взведен,
              // копируем соответствующие входы в выходы
              if(x_in_ok) x=x_in;
              if(y_in_ok) y=y_in;
              x_in_ok=y_in_ok=0; // Сбрасываем связанные сигналы
              // Передаем данные соединенным блокам
              ControlValueChangedCall(&(data->NoCall));
              break;
            // Реакция на вызов функции
            case RDS_BFM_FUNCTIONCALL:
              if(((RDS_PFUNCTIONCALLDATA)ExtParam)->Function==
              ControlValueChangedId)
                { // Вызвана "Common.ControlValueChanged"
                  if(data->NoCall) // Вызов заблокирован
                    break;
                  // Ограничиваем входные значения
                  data->LimitInputValues(&x_in,&y_in,x,y);
                  // Если связанные сигналы не взведены - не реагируем
                  if(x_in_ok==0 && y_in_ok==0)
                    break;
                  // Если значения входов те же, что и у выходов -
                  // не реагируем
                  if(x_in==x && y_in==y)
                    break;
                  // Копируем входы в выходы согласно связанным сигналам
                  if(x_in_ok) x=x_in;
                  if(y_in_ok) y=y_in;
                  x_in_ok=y_in_ok=0; // Сбрасываем связанные сигналы
                  Ready=1;           // Взводим сигнал готовности
                  // Передаем данные соединенным блокам
                  ControlValueChangedCall(&(data->NoCall));
                }
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef y_in_ok
      #undef x_in_ok
      #undef y_in
      #undef x_in
      #undef y
      #undef x
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================