habrahabr

Systemicus чаcть 2: GUI

  • воскресенье, 25 мая 2014 г. в 03:10:43
http://habrahabr.ru/post/223979/



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


Общие итоги


Как и обещал в прошлой теме нашел тонкое место (точнее места) в производительности — ими оказались планировщик (циклический) и ошибка двойной перерисовки всех окон.
Анти-итогом стала проблема размера исполняемого файла оболочки (explorer.exe). Я надеялся вместить весь код оболочки в одну секцию PE (4096 байт), но пока этого не получилось. Секция кода на данный момент составляет 4800 байт (сам файлик весит почти 10 Кб :-( ). Возможно, в будущем удастся немного сократить размер, но т.к. в оболочке многое еще нужно будет добавить, то размер explorer.exe может достигнуть даже 16 килобайт.

Принцип работы


Сразу отмечу, что скупой платит дважды. Из-за моего желания сэкономить лишних килобайт оперативной памяти, я изначально отклонил путь создания байтовой маски экрана (где каждому пикселю соответствует байт с номером окна [окон предусмотрено до 256]). И проблема не только в экономии памяти, но и в экономии ресурсов процессора, ведь в отсутствии данной технологии нету необходимости перед выводом каждого пикселя экрана проверять его принадлежность к определенному окну. Но… когда дело дошло до клиппинга (т.е. отрисовки только части окна, которое лежит под другим окном) я понял, как глубоко ошибся. Благо было бы, если рисуемое окно лежит только под одним окошком — там клиппинг можно вычислить — всего-то прямоугольная область. А что делать, если рисуемое окно перекрывают самым непотребным образом сразу несколько других окон и рисуемая область оказывается весьма причудливой формы?
В общем, вернулся назад и ввел байтовую маску экрана. Расход памяти увеличился, зато уменьшилось количество кода (вычислять стало легче, хотя это не значит, что повысилась скорость).

Итак, всё просто до безобразия. Есть байтовая маска экрана, где каждому пикселю присваивается нулевое значение либо иное, если точка принадлежит окну. Есть структура окон, она у меня такого вида:

WNDLIST_ITEM:
   .handle      dd 0   ; +00
   .x           dd 0   ; +04
   .y           dd 0   ; +08
   .width       dd 0   ; +12
   .height      dd 0   ; +16
   .flags       dd 0   ; +20
   .rsrc        dd 0   ; +24
   .parent      dd 0   ; +28
   .wbmp        dd 0   ; +32
   .caption     dd 0   ; +36
   ; if DESKTOP: db Leftbuttonstate  ; +36
   ;             db RightButtonState ; +37
   ;             dw reserved         ; +38
   .clickx      dw 0   ; +40
   .clicky      dw 0   ; +42
   .winstyle    dd 0
                db 16 dup 0   

Значения parent и winstyle оказались неиспользуемыми и потому зарезервированными (как и 16 байт после). Имея эти данные мы можем построить окно в скрытом буфере wbmp (технология двойной буферизации). В целях экономии памяти в буфере всё рисуется 8-битным цветом (пока используется только 16), т.е. 1 байт на точку, а при выводе на экран этот цвет преобразуется в 24/32-разрядный при помощи таблицы сопоставления значений цветов.

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

Теперь об элементах окна. Для всех элементов всех окон существует всего одна общая глобальная таблица элементов, каждый элемент которой содержит ссылку на окно (1 байт, т.к. окон всего 256 максимум), позиция относительно окна и размеры элемента (по 2 байта), байт состояния и 2 двойных слова — ссылка (на текст, например для кнопок или другая инофрмация для других элементов) и extras (иконка для кнопки или другие данные в зависимости от типа элемента). Байт состояния: младшие 4 бита указывают на тип элемента (кнопка, чекбокс, текстовое поле и т.п.), старшие на его состояние (активен ли, hover).

Главный процесс explorer'а получает событие от драйвера мышки. Что происходит: по координатам указателя ищется окно. Окну передается параметры указателя, а именно нажата ли клавиша, если нажата, то это нажатие или отпускание клавиши. При нажатии вначале активизируем окно, т.е. делаем его активным и перерисовываем на переднем плане. Далее по глобальной таблице элементов ищем все элементы, принадлежащие окну, а среди них — подпадающие под координаты мышки. Если нашли, то в соответствии с параметрами указателя мышки обрабатываем (рисуем, перерисовываем и т.п.).
Если же указатель находится вне окон, то это событие также обрабатывается. В данном случае, если нажат клавиша на рабочем столе, а меню Пуск открыто — то нужно его закрыть. В дальнейшем эти события нужны будут для создания ярлыков и других функций рабочего стола.

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

План на ближайшее будущее


В текущей реализации осталось много проблем, в частности, иногда появляются ошибки при отрисовке курсора мышки после клика по рабочему столу — копия курсора остается на столе)) Есть и некоторые другие проблемы, впрочем небольшие.
Из важного — полноценный элемент textarea с прокруткой текста, взаимосвязь радиокнопок одного имени (т.е. включение только одной кнопки в один момент времени для группы radiobuttons). Это из сложностей. Интересной, но не очень трудной задачей остается привязка приложений к их окнам, т.к. сейчас это всё тестовые окна, выводимые самой оболочкой.

Есть идея реализации терминала созданием окна и копированием в него содержимого из адреса 0xB8000 (естественно, превращая каждый символ в площадь пикселей 8*16).

А далее — портирование пары-тройки приложений, в первую очередь fasmw, какой-нибудь маленькой игрушки и, наверное, калькулятора. После чего отпишусь уважаемому хабрасообществу.

И напоследок небольшое видео. Нету, видео не выложу, т.к. CamStudio очень затормаживает Qemu. Выкладываю образ для запуска Qemu. Выбирайте Partition 2, режим любой, но а 640*480 есть небольшая проблема с фоном) И не ругайте, это АЛЬФА… много багов. Ругать будете за бета-версию :-) Также, прошу прощения за стиль и ошибки (если есть), уже глубокая ночь…

Ссылка на файл: http://nebka.ru/files/647-0.02_qemu.7z