Оконная поддержка в системе Mithril

                    Никитин А.Г., Недоря А.Е.

               -----------------------------------

0. Введение

     Основными   принципами,   заложенными   в   основу  оконного
интерфейса     системы    Mithril    [1]    являются    поддержка
"полиэкранности",  "расширяемости",  переносимость и максимальная
независимость  от  деталей  реализации  аппартной части поддержки
графического вывода.

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

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

     Тогда  для  работы и переноса системы должен быть реализован
только  набор  необходимых  драйверов для каждого типа физических
экранов,  используемых  в  заданной конфигурации системы Mithril.
Аппаратно  зависимые  части  драйверов  физических  экранов,  как
правило  должны  быть  переписаны  при переносе системы на другую
платформу для обеспечения их достаточной эффективности.

1. Общая структура оконной подсистемы

     Оконная  поддержка  системы  Мithril полностью реализует все
вышеописанные  возможности  и  предоставляет максимальную свободу
при   проектировании   пользовательских   интерфейсов   различных
направлений.   В   описываемой   оконной  подсистеме  отсутствуют
ограничения  на  возможные направления передачи управления внутри
оконной иерархии и на рисование графических примитивов, связанные
с  положением  и  текущим  состоянием  окна. Ограничения на набор
возможных  примитивов  рисования  может  наложить только неполная
реализация  драйвера экрана. Отсутствие таких ограничений выгодно
отличает  систему  Mithril от ее предшественника - системы Oberon
[2].

     В   основании  всей оконной  структуры  лежит  "супер-окно",
непосредственно  на  котором  расположены его под-окна (потомки),
между   которыми  определено  отношение  "выше/ниже".  Каждое  из
под-окон супер-окна может иметь свои под-окна с тем же отношением
между  ними. Эти новые под-окна в свою очередь также могуть иметь
свои  под-окна  и  т.д.  По  отношению  к  окну,  на  котором оно
расположено  (окну-родителю),  под-окно  может  находиться в двух
состояниях  "открытом"  или  "закрытом".  В первом оно становится
видимым,  если  окно-родитель  само  видимо, во втором наоборот -
невидимым. При отображении от потомков видимы только те их части,
которые попадают в видимые области окна-родителя.

     Окна,  расположенные  непосредственно  на супер-окне, всегда
открыты  и каждое из них соответствует физическому экрану. Причем
их  размеры совпадают с физическим разрешением экрана, поэтому на
них   не   распространяются  операции  изменения  размеров  окна.
Физическое  разрешение  при  этом может быть разным для различных
типов экранов. Поэтому окна-экраны расположены логически на одной
прямой  (у  всех  них  ордината левого нижнего угла равна 0). При
увеличении  набора  драйверов экранов, очередной экран может быть
добавлен  слева  или  справа  от  уже установленных. Во избежание
возможного  перекрытия  между  окнами-экранами действие процедура
измения координат на них не распространяется.

     За  исключением  выделенных  окон-экранов  и супер-окна, все
равноправны  между  собой  и  на них распространяются все базовые
операции.   При  создании  нового  окна  всегда  указывается  его
размеры,  окно-родитель,  на  котором оно создается, и координаты
относительно  окна-родителя.  Это  позволяет  создавать  внутрнне
сложно  устроенные  конструкции со индивидуальной схемой передачи
управления между их частями.

2. Ядро

     Ядро  оконной  системы состоит из модулей Screens и Windows,
определяющих  несколько  базовых  понятий. Далее развитие системы
происходит  в  результате  расширения  этих  базовых понятий (или
классов). Система целиком написана на языке Оберон-2 [3].

2.1. Модуль Screens

     Модуль  Screens  определяет  базовый  тип  Screen  - драйвер
экрана,  задающий  атрибуты  экрана:  его размеры, число битов на
пиксел,  палитру  и  т.д. Каждый конкретный драйвер экрана должен
задать   значения  этих  атрибутов,  реализовать  набор  операций
графики  и  конкретную  реализацию  курсора для данного экрана. С
каждым  конкретным экраном должно быть связано непустое множество
курсоров.

     Кроме  того,  модуль  поддерживает список зарегистрированных
драйверов экрана, содержит процедуры регистрации драйвера, поиска
драйвера по имени и процедуру установки курсора.

     Тип  TOOL  /инструмент  рисования/ задает область рисования,
цвет,  начало  относительных  координат и операцию /or, xor, bic,
replace/.

TYPE
  TOOL*    = RECORD
               ...
             END;

  Cursor*  = POINTER TO CrsDesc;

  CrsDesc* = RECORD
               name*: Str.String;
               x-,y-: INTEGER;
               on-  : BOOLEAN;
               .....
             END;

  Screen  = POINTER TO ScrDesc;
  ScrDesc = RECORD
              name-: ARRAY 32 OF CHAR;
              type-: ARRAY 32 OF CHAR;
              W-,H-: INTEGER;
              ....
              crs-: Cursor;
              clist-: Cursor;
              next-: Screen;
            END;

PROCEDURE (s: Screen) erase;
PROCEDURE (s: Screen) dot(t: TOOL; x,y: INTEGER);
PROCEDURE (s: Screen) line(t: TOOL; x0,y0,x1,y1: INTEGER);
 .......
PROCEDURE (s: Screen) crs_move(x,y: INTEGER);
PROCEDURE (s: Screen) crs_flip(on: BOOLEAN);
PROCEDURE (s: Screen) crs_in_block(b: BLOCK): BOOLEAN;

PROCEDURE (s: Screen) get_color(c: INTEGER; VAR r,g,b: INTEGER);
PROCEDURE (s: Screen) set_color(c: INTEGER;     r,g,b: INTEGER);

PROCEDURE insert(s: Screen);
PROCEDURE find(name: ARRAY OF CHAR): Screen;

PROCEDURE set_crs(s: Screen; name: ARRAY OF CHAR);

2.2. Модуль Windows

     Выше  модуля  Screens в иерархии импорта-экспорта расположен
модуль   Windows,   в  котором  определяются  тип  Window,  набор
примитивов  графики  для  окна,  операции модификации атрибутов и
иерархии  окон. Каждое окно обладает набором атрибутов (ссылка на
окно-родителя,  координаты,  размеры,  т.д).  При расширении окна
могут   быть  переопределены  методы  управления  окном  (handle,
handle_mouse, handle_key), создания копии окна (copy), реакции на
изменение   размеров   окна   (resizer)   и   спосов  перерисовки
прямоугольного участка окна (refresh).

     Для  обеспечения правильной передачи управления определяется
специальное   расширение  окна  Frame.  Головной  модуль  системы
Mithril   гарантирует,  что  метод  управления  (handle*)  всегда
изначально  вызывается  для  объектов  типа  Frame. Каждый объект
этого   типа   передает   (в   случае  необходимости)  управление
какому-либо из своих под-окон.

     Для включения некоторого окна (возможно расширенного типа) в
иерархию  окон должна быть вызвана процедура make, которая, кроме
того, определяет координаты и размеры окна, а также устанавливает
его "родителя".

     Изменение размеров или положения окна делается при помощи
процедур move, moveandresize и resize.

     В  модуле  реализован  набор  процедур, изменяющих положение
окна  в  общей  иерархии  под-окон  своего  окна-родителя:
     ontop, onbottom - делают окно самым верхним или самым нижним
на окне-родителе соответственно;
     putover,  putunder - помещают окно v над/под окном u, причем
они должны иметь одного родителя;
     pass  -  заменяет  родителя окна v на окно desk, при этом на
новом родителе окно v будет иметь теже относительные координаты.

     Процедура  open  открывает  окно,  т.е. делает его и все его
"открытые"  под-окна"  видимыми, если окно-родитель видимо, тогда
как,  close  делает  его  и  всех  его  потомков  невидимыми. Оба
этих действия не изменяют положение окна в иерархии.

     Все выше перечисленнные операции неприменимы к окнам-экранам.
Окно-экран всегда открыто.

     Процедура  gravity  позволяет  для  левого нижнего и правого
верхнего углов окна установить признаки, определяющие до какой из
точек  привязки  на  окне-родителе  будет автоматически сохранено
расстояние  при  изменении размеров окна-родителя. Такой подход к
проблеме  изменения  размеров  и  местоположения под-окон гораздо
шире и богаче, чем принятый в системе X-Windows.

     Точки  привязки,  их  всего  девять,  определяются по тем же
правилам, что и в системе X-Windows.

                NW ----- N ----- NE
                 |       |       |
                 |       |       |
                 W -- Center --- E
                 |       |       |
                 |       |       |
                SW ----- S ----- SE

Модуль содержит две процедуры для определения окна по координатам:
     this  -  выдает  самое верхнее открытое окно по абсолютным
координатам (координатам на супер-экране).
     locate  -  выдает  самое  верхнее  открытое  окно из его
непосредственных потомков, уже по относительным координатам.

     Модуль  содержит  процедуры принудительной перерисовки окна,
части  окна  и  окна  вместе  со  всеми  его  потомками /refresh,
refreshbox,   refreshall/   и   набор   процедур,  для  рисования
графических примитивов и текстов в окне.

     Рисование  производится  в  координатах  окна.  При этом нет
никаких  ограничений связанных с положением этого окна в иерархии
окон.

TYPE
  Window* = POINTER TO WndDesc;
  WndDesc* = RECORD (Storage.Object)
    x-,y-,w-,h- : INTEGER;
    desk-       : Window;
    top-,bottom-: Window;
    ....
    PROCEDURE (F: Window) handle*(VAR M: Message);
    PROCEDURE (F: Window) handle_key*(ch: CHAR);
    PROCEDURE (F: Window) handle_mouse*(x,y: INTEGER; keys: SET);

    PROCEDURE (v: Window) copy*(VAR d: Window);
    PROCEDURE (v: Window) resizer*(dw,dh: INTEGER);
    PROCEDURE (v: Window) refresh*(x,y,w,h: INTEGER);
  END;

TYPE
  Frame* = POINTER TO FrmDesc;
  FrmDesc* = RECORD (WndDesc)
              ....
             END;

3. Построение интерфейса

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

     В   предложенном  ниже  примере  переопределены  только  три
базовых метода refresh, copy и handle_mouse и обявлен метод act -
реакция  на  нажатие  кнопки  мыши  на  иконке. Каждое дальнейшее
расширение этого типа реализовать его в зависимости от назначения
иконки.

TYPE
  Icon = POINTER TO IcnDesc;
  IcnDesc = RECORD (Windows.WndDesc)
    pressed-: BOOLEAN;
    ....
    PROCEDURE (i: Icon) act*(keys: SET);
    PROCEDURE (i: Icon) copy*(): Windows.Window;
    PROCEDURE (i: Icon) handle_mouse*(x,y: INTEGER; keys: SET);
  END;

PROCEDURE pressed(i: Icon; on: BOOLEAN);

     Следующее расширение переопределяет метод act таким образом,
что  при  нажатию  кнопки  мыши  на иконке этого типа исполняется
команда,  хранимая в стандартном формате системы Mithril в строке
cmd.

TYPE
  ComIcon = POINTER TO IccDesc;
  IccDesc = RECORD (IcnDesc)
    cmd*: Str.String;
  END;

     Размер  текста  модуля,  где  описаны  оба  этих  типа  и их
релизация, составляет всего 90 строк.

     При   реализации  скроллеров,  например,  были  использованы
об'екты  расширенного типа Icon. Что существенно сократило размер
текста модуля их реализующего.

4. Заключение

     Проделанная     работа    продемонстрировала    преимущества
проектирования  подобных  систем  на языке Оберон-2 в сравнении с
Модулой-2.  Для  иллюстрации: реализация подобной оконной системы
на  Модуле-2  занимает  2500  сток,  тогда как на Обероне-2 всего
1300.   Причем   Оберон-2   предоставляет   богатые   возможности
расширения и более эффективную реализацию.

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

     Применение  метода  handle  позволяет  существенно расширить
набор  операций  над окнами и сложно устроенными конструкциями из
них.

     В  заключении  хотелось  бы выразить особую благодарность за
ценные  идеи  и  помощь, оказанную при разработке общей концепции
оконной подсистемы Д.Н. Кузнецову (Leo).

Литература:

1. Недоря А.Е., Никитин А.Г.
   Mithril - переносимая Оберон-2 система

2. J.Gutknecht
   The Oberon Guide: System Release 1.2
   Report 138, ETH Zurich, 1990.

3. H.Mossenbock, N.With
   The Programming Language Oberon-2
   Structured Programming (1991) 12: 177-195