A L S T E V E N S
НННННННННННННННННННННННННННННННННННННННННННННННННННННН
ЪДДДДДДД¬
ЪД¬ ЪД¬ ¦ ЪДДД¬ ¦ ЪД¬
ЪДЩ АД¬ ЪД¬ ЪД¬ ЪДДД¬ ¦ АДДД¬ ЪДДДДД¬ ¦ ¦ АДЩ АДЩ
АД¬ ЪДЩ ¦ ¦ ¦ ¦ ¦ ЪДЩ ¦ ЪД¬ ¦ ¦ ЪД¬ ¦ ¦ ¦ ЪД¬ ЪД¬
¦ АД¬ ¦ АДЩ ¦ ¦ ¦ ¦ АДЩ ¦ ¦ АДЩ ¦ ¦ АДДДЩ ¦ АДЩ
АДДДЩ АДДДДДЩ АДЩ АДДДДДЩ АДДДДДЩ АДДДДДДДЩ
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ MEMORY - RESIDENT UTILITIES, SCREEN I / O ¦
¦ AND PROGRAMMING TECHNIQUES ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
MANAGEMENT INFORMATION SOURCE, INC.
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
ЪДДДДДДДДДДДДД¬
¦ ¦
¦ M I S : ¦
ГДДДДДДДДДДДДДґ
¦ P R E S S ¦
ГДДДДДДДДДДДДДґ
Г - - - - - - ґ
ГДДДДДДДДДДДДДґ
ГДДДДДДДДДДДДДґ
ЖНННННННННННННµ
ФНННННННННННННѕ
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Ал. Стивенс
"ТЕХНИКА ПРОГРАММИРОВАНИЯ НА ТУРБО СИ"
( c ) пеpевод на pусский язык AcademySoft, 1989.
Предисловие..................................................... 5
Обзор разделов.................................................. 7
ГЛАВА 1......................................................... 9
Интерактивное программное обеспечение, управляющее изображением. 9
ГЛАВА 2......................................................... 14
Язык Cи........................................................ 14
Краткая история языка Си....................................... 14
Особенности языка Си........................................... 15
Достоинства языка Си........................................... 17
Одобрение языка Си............................................. 18
Рекомендуемая литература по Си ................................ 18
ГЛАВА 3 ........................................................ 20
Компилятор Турбо Си ........................................... 20
Два Турбо Си .................................................. 21
Настройка интегрированной среды ............................... 21
Редактор Турбо Си ............................................. 22
Компоновщик Турбо Си .......................................... 23
Утилита построителя задач (Make) в Турбо Си ................... 23
Обнаружение ошибок при компиляции и компоновке ................ 24
Программные средства низкого уровня ........................... 24
Начальная установка ........................................... 25
Модели памяти ................................................. 25
Библиотека исходных модулей ................................... 25
Заключение .................................................... 25
ГЛАВА 4 ........................................................ 26
Функции общего назначения ..................................... 26
Исходные модули функций общего назначения ..................... 28
Заключение..................................................... 32
ГЛАВА 5 ........................................................ 33
Экранные окна ................................................. 33
Экранное окно ................................................. 33
Архитектура видеопамяти ....................................... 36
"Снег" и обратный ход луча развертки .......................... 39
Заключение .................................................... 41
ГЛАВА 6 ........................................................ 42
Библиотека оконных функций .................................... 42
Стековые окна ................................................. 42
Слоеные окна .................................................. 43
Оконные функции ............................................... 46
Листинги оконных функций ...................................... 51
Описание программы: twindow.h ................................. 54
Описание программы: twindow.c ................................. 66
Примеры окон .................................................. 69
Перемещение окна ............................................. 69
Подъем и опускание окон ....................................... 72
Назначение заголовков и изменение цветов окна ................. 74
Сравнение стековых и слоеных окон ............................. 76
Перемещение, подъем, скрытие окон, меню, изменение интенсив. .. 77
Резюме ........................................................ 82
ГЛАВА 7 ........................................................ 83
Контекстно-управляемые окна подсказки ......................... 83
Программирование окон подсказки ............................... 84
Текстовый файл окна подсказки ................................. 86
Функции подсказки ............................................. 88
Изменение функциональной клавиши подсказки .................... 88
Изменение функции подсказки ................................... 88
Выключение подсказки .......................................... 89
Исходный листинг: thelp.c ..................................... 89
Описание программы: thelp.c ................................... 91
Пример контекстно-управляемой подсказки ....................... 92
Резюме ........................................................ 94
ГЛАВА 8 ........................................................ 95
Иcпользование данных в окнах .................................. 95
Шаблон ввода данных ........................................... 95
Поле ввода данных ............................................. 96
Позиция ....................................................... 96
Атрибуты ...................................................... 96
Буфер ......................................................... 96
Проверка допустимости значений ................................ 96
Help-информация ............................................... 97
Маска вводимых данных ......................................... 97
Приглашения к вводу в поле (Prompts) .......................... 97
Ввод данных ................................................... 98
Функции сбора данных .......................................... 98
Исходный текст: entry.c ....................................... 103
Описание программы: entry.c ................................... 109
Пример: Ввод данных в определенном порядке .................... 112
Резюме ........................................................ 117
ГЛАВА 9 ........................................................ 118
Оконный текстовый редактор .................................... 118
Команды тестового редактора ................................... 119
Управление курсором ........................................... 119
Постраничная работа ........................................... 120
Команды работы с блоками текста ............................... 120
Команды редактирования ........................................ 121
Функция, реализующая текстовый редактор ....................... 121
Исходный листинг: editor.c .................................... 122
Описание программы: editor.c .................................. 134
Пример: Использование редактора ............................... 138
Резюме ........................................................ 140
ГЛАВА 10 ....................................................... 141
Оконные меню .................................................. 141
Меню .......................................................... 141
Процесс, образующий оконное меню .............................. 142
Функции поддержки меню ........................................ 143
Исходный листинг: tmenu.c ..................................... 144
Описание программы: tmenu.c ................................... 147
Пример оконного меню .......................................... 148
Резюме ........................................................ 151
ГЛАВА 11 ....................................................... 152
Резидентные программы ......................................... 154
Прерывания .................................................... 154
Векторы прерывания ............................................ 154
Аппаратные прерывания ......................................... 155
Программные прерывания ........................................ 155
ДОС - однозадачная операционная система ....................... 155
TSR-программы ................................................. 157
Программы обработки прерываний ................................ 158
Резидентные утилиты ........................................... 158
Что может быть резидентным .................................... 159
Построение TSR-программ ....................................... 160
Превращение программы в резидентную ........................... 161
Резидентна ли уже программа? .................................. 161
Захват прерывания ............................................. 162
Величина TSR-программы ........................................ 163
Переключение контекстов ....................................... 165
Стек .......................................................... 165
Program Segment Prefix (PSP) .................................. 166
Дисковый буфер ................................................ 172
Прерывание от клавиатуры (9) .................................. 173
Прерывание от таймера ......................................... 173
Проблема реентерабельности ДОС ................................ 174
Два стека ДОС ................................................. 174
Системный флажок занятости (0х34) ............................. 174
Прерывание DOSOK .............................................. 175
Дисковое прерывание ROM-BIOS.(0х13) ........................... 176
Прерывание Ctrl-Break в ДОС.(0х23) ............................ 177
Выполнение TSR-программы ...................................... 177
Завершение TSR-программы ...................................... 177
Приостановка и возобновление выполнения TSR-программы ......... 179
Выводы ........................................................ 179
ГЛАВА 12 ....................................................... 180
Построение резидентных программ ............................... 180
Пример TSR-программы - "часы" ................................. 180
Превращение программы в резидентную ........................... 180
Прерывание по делению на ноль ................................. 181
Выполнение обработчика прерываний от таймера .................. 182
Связывание старого вектора прерывания по таймеру .............. 182
Сохранение и переключение контекста стека ..................... 182
Вычисление времени ............................................ 182
Программы TSR-драйвера ........................................ 185
Действия трех программных модулей ............................. 186
Размер TSR-программы .......................................... 186
Присвоение "горячего ключа" ................................... 186
Сигнатура TSR-программы ....................................... 188
Коммуникационные прерывания ................................... 188
Подготовка к резидентности .................................... 189
Обработчик обращения к диску .................................. 190
Обработчик критических ситуаций ............................... 191
Обработчик клавиатуры ......................................... 191
Обработчик таймера ............................................ 191
Обработчик DOSOK .............................................. 191
Выполнение TSR-программы ...................................... 192
Удаление TSR-программы ........................................ 192
Блоки памяти и управляющие блоки памяти ....................... 193
Исходные тексты: popup.c, resident.c .......................... 193
TSR-программа - приложение .................................... 201
Проверка TSR-программ ......................................... 202
Выводы ........................................................ 203
ЭПИЛОГ.......................................................... 203
Предисловие
-----------------------------------------------------------------
Поскольку вы читаете данную книгу, то, вероятно, вы
программируете на языке Си и уже приобрели или собираетесь
приобрести компилятор Турбо Си для своей IBM PC. При чтении от
вас потребуется довольно хорошее знание языка Си, а также DOS -
операционной системы персональных ЭВМ (ПЭВМ) линии IBM PC - и ее
функций. Знание языка ассемблера процессора 8086 и архитектуры
IBM PC желательно, но не обязательно. В книге содержится
множество исходных модулей функций на языке Си, которые помогут
писать программы, работающие с окнами, а также делать ваши
программы резидентными в памяти.
Программы, работающие с окнами, и резидентные в памяти
программные утилиты составляют в настоящее время основное
направление в программировании для IBM PC. По своей природе
персональная ЭВМ является настольной интерактивной (диалоговой)
системой, которая предоставляет пользователю доступ к набору
интерактивных программ. Аппаратура и операционная система
обеспечивают возможность разработки программ, работающих с
окнами и меню, которые появляются на экране по нажатию клавиши.
Большинство пакетов программ, пользующихся в настоящее время
наибольшим спросом у пользователей, применяют хотя бы одно из
этих средств. В данной книге разбираются основы работы с ними и
содержатся исходные тексты функций на языке Си, позволяющие
использовать эти средства в ваших программах. Прочитав эту
книгу и разобрав содержащиеся в ней программы, а также освоив
компилятор Турбо Си и основы программирования на языке Си, вы
будете готовы создавать резидентные программные утилиты,
использующие окна для организации пользовательского интерфейса.
Эта книга содержит сведения о языках программирования, о
развитии программного обеспечения, а также примеры использования
языков программирования для написания интерактивных,
экранно-ориентированных программ для ЭВМ. Не следует думать, что
перед вами очередная книга об IBM PC, но образ этой персональной
ЭВМ постоянно присутствует здесь. Если прежде акроним РС
обозначал определенную ЭВМ, то теперь он обозначает архитектуру
ЭВМ, которая была создана промышленным гигантом и стала
общепризнанной. РС в данной книге не является объектом изучения,
а обозначает некоторый абстрактный объект, который располагается
на вашем столе, работает под управлением MS-DOS и называется РС,
ХТ, АТ или чем-либо совместимым с ними.
В данной книге вы столкнетесь с программами, написанными на
языке Си. Это замечательный язык, и хотя некоторым он не
нравится, но все же большинство программистов его любят. На Си вы
можете создавать программы, которые делают все, что вы пожелаете.
Нет другого такого языка, который бы так же стимулировал к
программированию. Создается впечатление, что остальные языки
программирования воздвигают искусственные препятствия для
творчества, а Си - нет. Использование этого языка позволяет
сократить затраты времени на создание работающих программ. Си
позволяет программировать быстро, эффективно и предсказуемо. Еще
одно преимущество Си заключается в том, что он позволяет
использовать все возможности вашей ЭВМ. Этот язык создан
программистом для использования другими программистами, чего о
других языках программирования сказать нельзя. Кобол был
создан таким, чтобы менеджеры могли разбираться в написанных на
этом языке программах; Бэйсик был создан для непрограммистов;
Фортран - для научных работников; Ада вообще был создан
прямо-таки правительственным комитетом; Пайлот создан для
учителей; Паскаль - для студентов; Лого - для детей; АПЛ - для
марсиан; Форт, Лисп и Пролог - специализированные языки. Один Си
- это язык для программистов.
Турбо Си, о котором идет речь в этой книге, - это пакет,
который создает программную среду для программирования на языке
Си и является первым из компиляторов Си нового поколения. Турбо
Си содержит редактор с возможностью установки его параметров
пользователем, построитель задач, ориентированный на реализацию
программного проекта, "быстрый" компоновщик, а также самый
"быстрый" компилятор Си для РС, которые "погружены" в
интегрированную, оконно-ориентированную программную среду. Турбо
Си также предоставляет возможность работы с библиотекой функций
и расширениями языка Си, что обеспечивается использованием
вспомогательных программ обработки прерываний и других
резидентных в памяти программ. Такое использование законно,
поскольку Borland International - создатель Турбо Си -
является также основным производителем резидентных программных
утилит.
В этой книге содержатся исходные тексты функций, которые вы
можете использовать в своих программах, работающих в режиме
интерактивного взаимодействия с пользователем. Использование
этих функций улучшит пользовательский интерфейс ваших программ.
Они обеспечивают возможности работы с окнами, меню, ввода
данных по установленному шаблону, оконного редактирования
текста, а также создания резидентных программ, которые
вызываются нажатием определенных клавиш.
Кроме описания этих функций в книге излагаются также
аппаратные и программные принципы, которые лежат в основе
создания программ, управляющих выводом изображений и резидентных
программ. Подробно рассматриваются система прерываний,
видеопамять, а также внутренняя организация DOS, включая
множество функций DOS, использование которых необходимо при
создании резидентных программ, но по которым нет документации
или, наоборот, которые распространяются разработчиками и
поставщиками DOS.
Обзор разделов
------------------------------------------------------------------
Глава 1 знакомит с концепцией интерактивных,
экранно-ориентированных программных систем, в которых
организация обмена с пользователем так же важна, как и
прикладное назначение программы.
Глава 2 содержит основные сведения о языке Си.
Глава 3 описывает компилятор Турбо Си и его интегрированную
среду.
Глава 4 знакомит с первой группой функций, использующих
особенности аппаратной архитектуры РС.
Глава 5 объясняет основные принципы работы с окнами,
содержит общие сведения об архитектуре видеосистемы и знакомит с
проблемами, возникающими при создании окон в видеопамяти РС.
Глава 6 представляет читателю библиотеку функций для работы
с окнами. Эти функции могут применяться в пользовательских
программах для отображения различного рода информации, а также
быть основой для создания меню, редакторов и функций ввода
данных по формату, которые разбираются в последующих разделах.
Глава 6 содержит также несколько примеров программ,
иллюстрирующих использование библиотеки функций для работы с
окнами.
Глава 7 описывает контекстно-зависимые информационные окна
(Help) и содержит исходные тексты функций, которые позволят
реализовать эту возможность.
Глава 8 знакомит с использованием окон для ввода данных по
формату; управление вводом при этом осуществляется путем
определения набора полей для ввода данных внутри определенного
окна. Существуют функции, которые позволяют реализовать эту
возможность в ваших программах. В качестве примера приводится
программа диалогового ввода данных.
Глава 9 содержит функцию редактирования текстовой
информации, использующую окна. Описываемая здесь программа
представляет собой текстовый редактор общего назначения для ввода
и редактирования текстов свободного формата. Он имеет множество
команд, присущих большим системам текстовой обработки и
обеспечивающих автоматическое форматирование текста,
автоматический перенос слов, выделение и перемещение фрагментов и
т.д. Приводится также текст программы интерактивной записной
книжки, в которой используется функция редактирования текста.
Глава 10 знакомит с системами меню и содержит ряд функций,
позволяющих создавать один из типов меню, который можно встретить
в серьезных программах: строковое меню в заголовке окна, выбор
каждого из элементов которого вызывает возникновение на экране
нового меню. Для иллюстрации использования такого типа меню
программные модули объединены в единую программу, которая
позволяет с помощью меню выбрать нужный модуль.
Глава 11 знакомит с основами реализации резидентных
программ. По этой проблеме дается исчерпывающая информация.
Приводятся также разъяснения по тем функциям DOS, по которым не
поставляется документация: какие из них можно использовать, а
каких следует избегать и почему. Освещается проблема
реентерабельности DOS и способы ее решения. Упоминается также
проблема параллельно выполняющихся резидентных утилит. В
заключение обсуждаются свойства "однозадачности" DOS и
объясняется, почему не может быть обеспечена надежная защита
резидентных в памяти программ.
Глава 12 на примерах демонстрирует, как можно использовать
Турбо Си для создания резидентных программных утилит. Первый
пример представляет резидентную в памяти утилиту обработки
прерываний по таймеру, которая отображает текущее время в правом
верхнем углу экрана. Также приведена управляющая программа
общего назначения, которая позволит Вам разрабатывать утилиты,
тестировать их в качестве нерезидентных программ в среде Турбо
Си, а затем компоновать их в рабочие резидентные модули. Для
иллюстрации этого процесса программа управления окнами и меню
из главы 10 преобразуется в резидентную программу, которая
выполняется при нажатии "горячей клавиши".
Подводя итог, можно сказать, что данная книга содержит
разъяснения и исходные тексты программ, касающиеся двух наиболее
популярных свойств программного обеспечения для РС -
использования окон и резидентности программ. Пользуясь этими
инструментами и полными возможностями пакета Турбо Си, вы сможете
повысить свою производительность в программировании, а также
сделать свои программы более полезными и "дружественными" для
пользователя.
Сентябрь, 1987
Меррит Айленд, Флорида
ГЛАВА 1
-------
Интерактивное программное обеспечение,
управляющее изображением на экране
------------------------------------------------------------------
Большинство программ для PC, пользующихся в настоящее время
наибольшим спросом, рассчитаны на интерактивный режим работы, при
котором пользователь обменивается с ЭВМ сообщениями в виде
последовательностей нажатий клавиш на клавиатуре и символов на
экране дисплея. При этом программы вывода сообщений пользователю
широко используют возможности видеотерминала РС. Пользователь
реагирует на это набором на клавиатуре соответствующих слов и
чисел. Такой способ общения с ЭВМ стал естественным для нового
поколения пользователей. Он подразумевает развитие стиля
отображения и ввода информации в ЭВМ, который называется
"смотреть и чувствовать" и постоянно используется и развивается
программистами. Французский язык, Кобол, код Морзе являются
средствами общения, для общения с ЭВМ также нужен язык. Можно
считать, что каждая новая программа является новым языком или
диалектом уже существующего. И поскольку в данном случае язык
служит для взаимодействия с ЭВМ, то повышение его эффективности
способствует более полному соответствию намерений пользователя и
действий ЭВМ.
Эти языки, как правило, не разрабатываются специально, а
возникают сами по себе в процессе создания программы. Программист
озабочен обычно другими проблемами: средой программирования,
структурами данных, функциональными алгоритмами, интерфейсом
пользователя. После завершения разработки программы программист
вводит в нее запросы к пользователю и сообщения об ошибках.
Качество пользовательского интерфейса программы зависит от воли
программиста и наличия программных средств, которые помогают
разрабатывать пользовательский интерфейс.
Программисты могут использовать множество различных
технологий для организации обмена между ПЭВМ и пользователем.
Каждая из этих технологий имеет свои области применения, в
которых она более предпочтительна, чем другие. Но все эти
технологии имеют одну общую цель: они обеспечивают средства для
передачи пользователю предназначенной для него информации и для
ввода информации им.
Одна из наиболее простых проблем, с которой вы могли
сталкиваться в своей практике, заключается в организации ввода и
вывода алфавитно-цифровой информации. Вы можете использовать для
этой цели функции printf и scanf и этим ограничиться. Если ЭВМ
включает в свой состав консольный терминал с клавиатурой типа
пишущей машинки, то при этих условиях почти любое программное
обеспечение может считаться обеспеченным пользовательским
интерфейсом. Эта технология была применима по той причине, что
ЭВМ работали под управлением операторов, а пользователями
считались те, кто записывал данные на программных бланках, а
затем читал распечатки. Теперь же ПЭВМ стоит на столе
пользователя, поэтому для их эффективного взаимодействия
пользовательский интерфейс должен быть более сложным, чем ранее.
Поскольку сегодняшний пользователь ЭВМ обычно имеет
профессиональные интересы, выходящие за рамки программирования,
то пользовательский интерфейс должен стимулировать эти интересы,
а не сдерживать их.
В интерактивной системе в определенные моменты времени
программа выводит пользователю необходимую ему информацию и
запрашивает информацию у пользователя путем выдачи
соответствующей подсказки. Затем программа должна находиться в
состоянии ожидания, пока пользователь не введет всю необходимую
информацию. Если программа способна проверять на достоверность
введенную информацию, то после ввода данных пользователем она
может либо продолжить свое выполнение, либо выдать сообщение об
ошибке и ожидать ввода новых значений данных. Если пользователь
не понимает, что от него требуется, то он может запросить у
программы справочную информацию. Умная программа располагает
множеством полезных сообщений, которые выдаются пользователю,
если он запрашивает справочную информацию, и разъясняют ему, что
от него требуется.
Форматы ввода данных могут быть различными, поскольку
существует много различных классов данных. Эти классы данных
могут быть в общем случае разбиты на две категории: команды и
значения данных.
Команда может быть простой, как, например, нажатие одной из
функциональных клавиш, которая в системах текстовой обработки
обозначает переход к новому параграфу. Команда может быть
сложной, как, например, загадочный набор букв, цифр и символов,
что характерно для многих команд MS-DOS. Некоторые команды
запрашиваются программой, как, например, ввод ответов "Y" и
"N", когда ПЭВМ переспрашивает, действительно ли вы хотите
того, что вы от нее требуете. Другие команды вводятся по
инициативе пользователя, когда, например, вы просите систему
текстовой обработки сохранить документ в дисковом файле.
Программа на ПЭВМ не ожидает ввода именно этой команды, но,
тем не менее, принимает ее и выполняет то, что от нее требуется.
Иногда программа может выводить перечень допустимых команд, из
которого вы можете выбрать любую. Этот перечень называется меню.
Значения данных в свою очередь можно разделить на элементы
данных и текстовую информацию. Элементами данных являются данные
определенного формата и назначения. Это, например, значения даты,
имена, адреса, числовые значения, размер одежды, оттенки цвета.
Проверка достоверности вводимых данных для определенного элемента
данных может быть произведена по соответствию их формата, длины и
значения этому элементу данных. В противоположность этому
текстовая информация не имеет определенного формата, длины и
значения. Системы баз данных обычно манипулируют элементами
данных, а системы обработки текстовой информации -
соответственно текстовой информацией.
Интерактивная система должна обладать пользовательским
интерфейсом, который облегчает использование клавиатуры и экрана
для ввода данных различного типа, которые затем обрабатываются
программой. IBM PC имеет такую архитектуру видеосистемы и
клавиатуры, которая обеспечивает возможности для создания
пользовательских интерфейсов различного типа, что уже реализовано
во многих пакетах программ для IBM PC.
Есть различные способы отображения меню, выдачи
информационных сообщений, сообщений об ошибках и запросов на ввод
данных. Также существуют различные способы ввода текстовой
информации и значений данных, поступающих от пользователя. При
разработке языка взаимодействия с пользователем программист может
выбирать из существующего разнообразия способов. Выбор
инструментальных программных средств будет оказывать влияние на
конечный программный продукт. Использование инструментальных
программных средств, обеспечивающих эффективный ввод и
отображение данных, позволяет создавать эффективные программные
системы.
Одним из наиболее популярных в настоящее время способов
организации взаимодействия с пользователем является работа с
окнами. Окном называется, как правило, прямоугольная область на
экране дисплея с видимой границей, изображение в которой
формируется независимо от остальной части экрана. Окна
используются для всех типов взаимодействия с пользователем: для
отображения меню, в качестве областей для ввода значений данных
или текстовой информации, для вывода сообщений и справочной
информации по требованию пользователя.
Другим важным свойством интерактивных систем, существенно
определяющим их качество, является обеспечение для пользователя
возможности быстро переходить от одной задачи к другой без
утомительных выходов в операционную систему. В интегрированной
программной системе такого рода переходы зачастую обеспечиваются
операционной средой, в которой исполняется программа. Однако,
при увеличении числа независимых задач, для которых необходимо
обеспечить в асинхронном режиме быстрый переход от одной
задачи к другой, возможности однозадачной операционной системы
DOS для IBM PC могут быть превышены. Для обеспечения возможности
переключениязадач в этом случае должны использоваться
резидентные в памятипрограммы. Эти программы не обеспечивают
настоящего мультизадачного режима, но позволяют установить
удобный для пользователя режим использования некоторых утилит в
командной среде DOS.
Программы из этой книги образуют библиотеку программных
инструментальных средств, использующих окна для ввода текстовых и
числовых данных, выдачи справочной информации пользователю и
организации меню. Эти программы могут быть сделаны резидентными.
Программные модули библиотеки написаны для компилятора Турбо Си и
предназначены для использования программами, также написанными
для Турбо Си. Для того чтобы использовать программы из этой
книги, вы должны иметь общее представление о DOS и о тех
средствах, которые она предоставляет программисту. В книге
разбираются вопросы внутренней организации DOS, а также некоторые
из ее функций, обеспечивающие резидентность программ и по которым
нет документации. Отличным справочным руководством по программным
средствам DOS и ROM-BIOS является книга "Advanced MS-DOS" Рэя
Дункана (Microsoft Press, 1986). Данная же книга содержит только
сведения, необходимые для изложения вопросов, касающихся
предлагаемой программистам библиотеки. Дункан с иронией
представляет свою книгу, как нечто облегченное, но вы должны
отнестись к ней со всей серьезностью.
Программное обеспечение из этой книги может быть
представлено в виде шести уровней, как на рисунке 1.1. Уровни на
диаграмме располагаются сверху вниз, но изложение материала будет
соответствовать движению от нижних уровней к верхним.
ЪДДДДДДДДДДДДДДДДДДД¬
¦ ¦
Уровень 1 ¦ TSR - драйвер ¦
¦ ¦
АДДДДДДДДДВДДДДДДДДДЩ
¦
¦
ЪДДДДДДДДДБДДДДДДДДДД¬
Уровень 2 ¦ ¦
¦ Загрузочный ¦
¦ модуль ¦
¦ ¦
АДДДДДДДДДВДДДДДДДДДДЩ
¦
Уровень 3 ¦
ЪДДДДДДДДДДДДДДВДДДВДБДВДДДВДДДДВДДД¬
¦ ¦ ¦ ¦ ¦ ¦ ¦
ЪДБДДДДДДДДДДДД¬ ¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ NOTEPAD ¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ ЪДДДДДДДДДДБДБД¬ ¦ ¦ ¦ ¦ ¦
АДВДґ ¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ TEST MOVE ¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ ЪДДДДДДДДДДБДБД¬ ¦ ¦ ¦ ¦
¦ АДВДґ ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ PROMOTE ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ЪДДДДДДДДДДБДБД¬ ¦ ¦ ¦
¦ ¦ АДВДґ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ FAST TEST ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ЪДДДДДДДДДДБДБД¬ ¦ ¦
¦ ¦ ¦ АДВДґ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ SAYINGS ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ ЪДДДДДДДДДДБДДБ¬ ¦
¦ ¦ ¦ ¦ АДВДґ ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦ POETRY ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦ ЪДДДДДДДДДДБДДБД¬
¦ ¦ ¦ ¦ ¦ АДВДґ ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦ ORDENT ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦ АДДДДДДДДДДДДДВДЩ
АДДДБДДДБДДДБДДДБДДДБДДДДДДВДДДДДДДДЩ
¦
Уровень 4 ЪДДДДДДДДДДДДДДВДДДДДДБДДДДДДВДДДДДДДДДДДДДД¬
ЪДДДДБДДДДДД¬ ЪДДДДДБДДДДД¬ ЪДДДДДБДДДДДД¬ ЪДДДДДБДДДД¬
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ Справочник¦ ¦ Редактор ¦ ¦ Меню ¦ ¦ Ввод ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
АДДДДВДДДДДДЩ АДДДДДВДДДДДЩ АДДДДДДВДДДДДЩ АДДДДДВДДДДЩ
АДДДДДДДДДДДДДДБДДДВДДДДДДДДДДБДДДДДДДДДДДДДЩ
¦
ЪДДДДДДДДДДДДБДДДДДДДДДДДД¬
Уровень 5 ¦ ¦
¦ Функции управления ¦
¦ окнами ¦
¦ ¦
АДДДДДДДДДДДДВДДДДДДДДДДДДЩ
¦
ЪДДДДДДДДДДДДБДДДДДДДДДДДДД¬
Уровень 6 ¦ Функции управления ¦
¦ аппаратурой ¦
¦ IBM PC ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 1.1. Уровни программного обеспечения.
Уровень 6 представляет библиотеку функций нижнего уровня,
которые управляют определенными действиями IBM PC.
Уровень 5 представляет библиотеку функций для работы с
окнами, которые управляют размещением, отображением на экране и
сохранением в памяти экранных окон.
Уровень 4 объединяет функции, которые используют окна для
конкретных применений. Эти функции управляют
контекстно-чувствительными окнами со справочной информацией,
окнами редактирования текста, системами меню и окнами для ввода
данных по формату.
Уровень 3 соответствует прикладным программам и представлен
здесь примерами программ, иллюстрирующих применение библиотек
нижнего уровня.
Уровень 2 представляет собой управляющую программу, которая
связывает прикладные программы предыдущего уровня в единую
программу, управляемую с помощью меню. Эта программа может
работать как автономно, так и в качестве резидентной утилиты на
последующем уровне программного обеспечения.
Уровень 1 представляет драйвер TSR, который строит из
транзитных модулей программы, остающиеся резидентными после
завершения выполнения. В данном примере он используется для
объединения программ предыдущих уровней в единую резидентную
утилиту.
Перед тем как погрузиться в глубины программирования
модулей, резидентных в памяти и работающих с окнами, возможно,
вам захочется почитать что-нибудь по языку Си. Вы можете найти
интересующие вас сведения в данной книге. Раздел 2 содержит
краткое изложение истории языка Си и попытку объяснения, почему
же программисты так любят этот язык. Раздел 3 продолжает
рассмотрение компилятора Турбо Си, выбранного для изучения в
данной книге.
ГЛАВА 2
--------
Язык Cи
-----------------------------------------------------------------
Любая книга по Си превозносит этот язык и рассказывает
историю его создания. Настоящий раздел следует этой традиции: в
нем приводится краткая хронология создания Си, описываются
фундаментальные особенности языка, рассказывается о достоинствах
Си как инструмента программирования, а также предпринимается
попытка объяснить, почему же он завоевал такое широкое признание
среди программистов. В конце раздела содержится список справочных
материалов для изучения языка Си.
Краткая история языка Си
-----------------------------------------------------------------
Язык Си был создан в начале 70-х годов Дэннисом Ритчи,
который работал в компании Bell Telephone Laboratories.
Родословная языка Си берет свое начало от языка Алгол и включает
в себя Паскаль и ПЛ/I.
Си был разработан как язык для программирования в новой по
тем временам операционной системе Unix. ОС Unix была написана на
языке ассемблера для ЭВМ PDP-7 и перенесена затем на PDP-11. На
язык Си оказал значительное влияние его предшественник, язык Би,
созданный Кэном Томпсоном, который в свою очередь является
последователем языка BCPL. Язык ВСРL был создан в 1969 г.
Мартином Ричардсом в рамках проекта "Комбинированный язык
программирования" в Кэмбриджском университете в Лондоне. Вскоре
Unix была переписана на языке Си, и в 1974 - 75 годах OC
Unix фирмы Bell Laboratories стала первым коммерческим
продуктом, реализующим идею о том, что операционная система
может быть успешно написана на языке высокого уровня, если этот
язык является достаточно мощным и гибким.
В 1978 г. Брайан Керниган и Дэннис Ритчи написали книгу
"Язык программирования Си" (издательство Prentice-Hаll). Эта
работа, которая в своем кругу называлась "белой книгой" и "K & R"
в остальном мире, стала стандартом описания языка Си. На момент
создания "K & R" существовали компиляторы языка Си для ЭВМ
PDP-11, Interdata 8/32, Honeywell 6000 и IBM 370. В дальнейшем
этот список был продолжен.
В конце 70-х начали появляться трансляторы Си для микроЭВМ
на процессорах 8080 и Z80 с операционной системой СР/M. Скотт
Газери и Джим Гибсон разработали и пустили в продажу Тiny-C
("Крошечный Си") - интерпретатор, основанный на подмножестве
языка Си. Его интерактивная среда программирования очень похожа
на ту, что имеет чрезвычайно популярный транслятор Basic фирмы
Microsoft. В 1980 г. Рон Кэйн создал свой компилятор Small-C
("Малый Си") для ОС СР/М и микропроцессора 8080. Компилятор
Small-C, основанный на подмножестве языка Си, был написан на
самом Small-C. Проблема курицы и яйца была решена, когда Кэйн
создал первую версию компилятора на основе интерпретатора Тiny-C.
Затем Small-C методом раскрутки создал самого себя, когда Кэйн
вместе с другими, используя ранние версии компилятора, сделал
более совершенный компилятор. Small-C компилирует исходный модуль
на языке Си в модуль на языке ассемблера процессора 8080.
Следует отметить, что Кэйн предоставил свой компилятор и его
исходный текст в общественную собственность.
Примерно в это же время Лео Золман представил свой
компилятор BDS-C для СР/М, также основанный на подмножестве языка
Си. Достоинствами этого компилятора были высокая скорость и
возможность совместной компоновки перемещаемых объектных модулей
в загрузочном модуле.
Вскоре после BDS-C были созданы компиляторы, предназначенные
для СР/М и основанные на полном множестве языка Си. Это дало
импульс развитию программирования на Си для микроЭВМ. В 1981
г., в связи с созданием IBM PC, в мире микроЭВМ был сделан
значительный скачок вперед.
После появления IBM PC стали появляться и компиляторы Си для
этой ПЭВМ. Некоторые компиляторы были получены путем
преобразования соответствующих компиляторов для процессора 8080,
другие были разработаны специально для IBM PC. В настоящее
время на рынке представлены по меньшей мере семнадцать
компиляторов языка Си для IBM PC.
В 1983 г. Американский Институт Стандартов (ANSI)
сформировал Технический Комитет X3J11, устав которого
предусматривает создание стандарта языка Си. Стандартизация будет
распространяться не только на язык, но и на программную среду
компилятора, а также на библиотеку стандартных функций. В работе
комитета участвуют представители основных фирм - поставщиков
компиляторов Си, в том числе и для IBM PC, а также многие другие
светила из мира программирования на языке Си. Усилия комитета
X3J11 привлекли внимание средств массовой информации.
Предлагаемый стандарт был опубликован, для того чтобы все
заинтересованные стороны могли ознакомиться с ним и внести свои
предложения. (Сомнительно, что выдающийся программист
заинтересуется языком, который создан комитетом, но, видимо,
комитет делает хорошее дело, совершенствуя язык, созданный
выдающимся программистом).
Поскольку большинство поставщиков компиляторов для IBM PC
участвуют в работе комитета X3J11, то разрабатываемые ими новые
версии компиляторов будут в рамках этого стандарта. (Турбо Си,
один из последних компиляторов для IBM PC, подчиняется
большинству требований стандарта на язык и библиотеку).
Особенности языка Си
-----------------------------------------------------------------
В данной книге не ставится цель научить вас программировать
на языке Си, но она может быть полезной для понимания тех
особенностей Си, которые заставляют столь многих программистов
остановить свой выбор именно на этом языке.
Си является языком функций, типов данных, операторов
присваивания и управления последовательностью вычислений.
Программируя на Си, вы осуществляете обращение к функциям, и
большинство функций возвращают некоторые значения. Значение,
возвращаемое функцией, будь то значение переменной или константа,
может использоваться в операторе присваивания, который изменяет
значение другой переменной. Дополненный операторами управления
последовательностью вычислений (while, for, do, switch), Си
превращается в язык высокого уровня, способствующий хорошему
стилю программирования.
Си имеет небольшой набор типов данных: целые числа, числа с
плавающей запятой, битовые поля и перечислимый тип. В языке Си вы
можете описать переменную типа указатель, который связывается с
объектом, принадлежащим к любому типу данных. Адресная арифметика
языка Си является чувствительной к типу данных того объекта, с
которым связан используемый указатель. Разрешены также указатели
к функциям. Вы можете расширить список типов данных путем
создания структур с иерархической зависимостью входящих в него
типов данных. Каждый тип данных может принадлежать либо к
основному типу, либо к ранее описанному структурному типу.
Объединения напоминают структуры, но определяют различные виды
иерархических зависимостей, в которых данные разных типов
располагаются в памяти.
Допустимо описание массивов данных различных типов, включая
структуры и объединения. Массивы могут быть многомерными.
Функции Си являются рекурсивными по умолчанию. Вы можете,
правда, создать функцию, которая не будет рекурсивной, но сам
язык по своей природе стремится поддерживать рекурсивность и
требует минимальных усилий при программировании рекурсий.
Программа функции на языке Си разбивается на блоки, в каждом
из которых могут быть определены свои собственные локальные
переменные. Блоки могут выбираться для исполнения по результату
выполнения оператора управления последовательностью
вычислений. Блоки могут быть вложенными друг в друга.
Переменные и функции могут быть глобальными для программы,
глобальными для исходного модуля или локальными для блока, в
котором они описаны. Локальные переменные могут быть описаны
таким образом, что они будут сохранять свои значения при всех
обращениях внутри данного блока (статические переменные) или
же будут восприниматься как новые объекты при каждом обращении
(автоматические переменные).
Си позволяет создавать программу в виде нескольких исходных
модулей, которые будут транслироваться независимо. Перемещаемые
объектные модули, соответствующие исходным модулям, компонуются в
единый загрузочный модуль. Эта особенность позволяет компилятору
поддерживать объектные библиотеки многократно используемых
функций и создавать большие программы из множества небольших
исходных модулей.
В языке Си нет операторов ввода/вывода, весь ввод/вывод
выполняется с помощью функций. Вследствие этой особенности языка
Си разработана стандартная библиотека функций. Существование
этого стандарта и составляет главную привлекательность языка Си,
ибо делает программы на Си переносимыми.
Достоинства языка Си
-----------------------------------------------------------------
Переносимость программ, написанных на Си, является наиболее
разрекламированным преимуществом этого языка. Если вы пишете
программу на Си и избегаете при этом использовать расширения
библиотеки, зависящие от конкретного компилятора, или
машинно-зависимые операции, то вы получаете неплохие шансы
(значительно большие, чем при любом другом языке) на успешный
перенос вашей программы в другую программно-аппаратную среду,
включая смену компилятора, операционной системы и ЭВМ.
Приведенные в данной книге программы не претендуют на
переносимость. Библиотека функций для работы с окнами привязана к
архитектуре видеопамяти IBM PC, а функции резидентных в памяти
утилит используют систему прерываний IBM PC, специфические
особенности DOS и библиотеку расширений компилятора Турбо Си,
которая облегчает разработку программ обработки прерываний.
Некоторые функции включают небольшие фрагменты на языке
ассемблера.
Расширяемость языков программирования означает существование
потенциальной возможности внести добавления в язык. Си рассчитан
на расширение по своему замыслу, поскольку содержит очень
небольшое число операторов. Следует помнить, что сам язык
позволяет немногим более, чем изменять значения переменных и
управлять последовательностью выполнения программы. Самое важное
в программах на Си заключено в функциях, а язык сам по себе не
имеет другие внутренние функции, кроме основной функции (функции
main). Первая группа расширений языка Си размещается в
стандартной библиотеке, другие нестандартные расширения
поддерживает конкретный компилятор и, наконец, третья группа
расширений содержится в дополнительных библиотеках функций Си
(как это описано в данной книге). Последняя группа расширений
разрабатывается самим программистом, который создает программы
для многократного использования.
Кроме своей функциональной расширяемости, Си позволяет
расширять стандартный набор типов данных путем определения
структур, объединений и использования операторов typedef.
Программистам особенно нравится краткость выражений,
которыми в Си кодируются алгоритмы. Большинство операторов, будь
то операторы присваивания, условные операторы, обращения к
функциям или выражения, кроме операторов управления
последовательностью выполнения программы, возвращают некоторые
значения. Использование этой особенности языка позволяет
представлять выражения в краткой форме.
Си обеспечивает формирование эффективного машинного кода
программы, что достигается привязкой языков программирования к
структуре памяти и регистровой архитектуре ЭВМ, для которых они
создаются. Си часто характеризуется как переносимый язык
ассемблера высокого уровня. Сама природа языка позволяет
компилятору генерировать эффективный оптимизированный машинный
код.
Одобрение языка Си
----------------------------------------------------------------
Не должно оставаться сомнений по поводу преимуществ языка Си
по сравнению с другими языками программирования. Программисты
любят Си по причинам, указанным выше. Компании по разработке
программного обеспечения любят его за то, что он позволяет
писать программы, не зависящие от конкретной аппаратуры и
операционной системы. Современные менеджеры, многие из которых в
прошлом программисты, учитывают оба этих преимущества. Си
является языком, на котором написано большинство наиболее
популярных в мире пакетов программ.
Рекомендуемая литература по Си
-----------------------------------------------------------------
Ниже приводится список литературы по Си, которая может
помочь больше узнать о языке и способах его использования:
Брайан В., Керниган, Ритчи Деннис М.. Язык программирования
Си. - Prentiсe-Hall, 1978.
Плам Томас. Стандарты и руководящие принципы
программирования на Си. - Plum-Hall, 1982.
Плам Томас. Изучение программирования на Си. - Pluм Hall,
1983.
Кочан Стефен Г. Программирование на Си. - Hayden Book
Cомpany, 1983.
Пардам Джек. Руководство по программированию на Си. - Que
Corporation, 1983.
Харбисон Самюэл, Стил Гай Л. Си: справочное пособие. -
Jr.Таrtan Laboratories, 1984.
Хоган Том. Руководство для программистов по Си. -
Brady,1984.
Хант Вильям Джеймс. Набор инструментальных средств на Си. -
Аddison-Wеsly, 1985.
Плам Томас. Структуры данных в Си. - Plum Hall, 1985.
Компилятор МIX C. - Mix Software, Inc, 1985.
(Этот материал продается вместе с компилятором и отдельной
книгой. Независимо от того, используете вы компилятор или
нет, руководство и справочное пособие по Си будут для вас
полезны).
Газери Скотт Б. Изучение Си и Тiny-C. - Таb Books, 1985.
Рэдклифф Роберт А., Рааб Томас Д. Утилиты обработки данных
на Си. - Sybex, 1986.
Стивенс Ал. Разработка инструментальных средств на Си для
IBM PC. - Brady, 1986.
Пособие по инструментальным средствам Си доктора Добса. -
Brady, 1986.
Стивенс Ал. Разработка баз данных на Си. - МIS:Press,1987.
Джонсон Нельсон. Усовершенствованная графика на Си:
программирование и методы. - Оsborne / McGraw-Hill, 1987.
"Наставник по Си" дискеты #577 и #578 библиотеки PC-SIG
(Этот пакет программ представляет собой обучающую интерактивную
систему, содержащую текстовую информацию и примеры программ,
которые вы можете транслировать с помощью своего транслятора).
ГЛАВА 3
-------
Компилятор Турбо Си
-----------------------------------------------------------------
В декабре 1986 г. небольшая компания под названием Wigard
Software Systems, Inc. объявила о своем переезде из гopoда
Армингтон, штат Массачусетс, в город Монте Серено, штат
Калифорния. Эта компания создала и начала продажу компилятора
Си стоимостью 450 долларов под названием Wizard C.
Wizard C был компилятором, заслуживающим уважения, всегда
получающим хорошие отзывы в обзорах и даже названный, по крайней
мере одним из обозревателей, "лучшим" компилятором Си. Его
достоинства заключались в высокой скорости компиляции,
эффективной оптимизации получаемого кода, соответствии
предложениям стандарта АNSI и большом числе расширений языка,
позволяющих разрабатывать программы обработки прерываний. Эти
расширения заключали в себе функцию прерывания специального
типа, возможность встраивания в тело программы фрагментов на
языке ассемблера, псевдопеременные, с помощью которых из
языка Си выполняется доступ к регистрам микропроцессора.
В феврале 1987 г. фирма Borland International из
Скоттс-Веллей, штат Калифорния, объявила о создании компилятора
Турбо Си, который ожидался с нетерпением после появления его
предшественника, очень удачного компилятора ТурбоПаскаль.
Объявление содержало оценку эффективности, заверенную несколькими
экспертами. Они утверждали, что скорость компиляции Турбо Си
будет достигать 7000 строк в минуту, что превышало скорость
самого быстрого на то время компилятора Си. По мнению экспертов
было достигнуто предельное значение производительности для
компиляторов Си и не ожидалось появление компилятора, который
превзошел бы это значение.
В том же месяце Wizard сделала своe последнee объявление. В
мае 1987 г. появилась версия 1.0 Турбо Си (совместно с T-shirts).
Перчатка была брошена и соревнование объявлено. Турбо Си подавил
всех своими характеристиками.
На самом деле фирма Вorland International приобрела фирму
Wigard Systems для того, чтобы создать Турбо Си. Промышленность
делала предположения о том, когда Borland выйдет на рынок
компиляторов Си, после того, как она уже представила свои
изделия: Турбо-Паскаль, а вслед за ним очень популярный
Турбо-Бейсик. Вместо того, чтобы предпринимать большие усилия,
начиная с нуля, фирма Borland приняла мудрое решение: она купила
лучший компилятор Си и сконцентрировала усилия на том, чтобы
сделать его еще лучше.
На момент анонсирования фирмой Borland своего компилятора на
рынке было представлено 17 компиляторов Си для IBM PC. Несколько
человек решили, что миру нужен еще один. Таковы краткие сведения
о фирме Borland International Филиппа Кана, которые тем не менее
приковывают внимание и захватывают воображение. После
опубликования информации о Турбо Си многие сомневались, немногие
имели представление о нем, но все жаждали увидеть его
собственными глазами. Мир Си готов был принять еще один
компилятор, при условии, что он поступит от Вorland. Само это
состояние ожидания имело очень большое значение. Фирма Borland
начала дело не для того, чтобы создать очередной компилятор Си;
Borland поставила целью изменить представления о том, как должна
выглядеть программная среда для разработки программ на языке Си.
Два Турбо Си
-----------------------------------------------------------------
Турбо Си обозначает два программных изделия: пакет программ,
обеспечивающий выполнение последовательности команд в стиле Unix:
make/compiler/linker, и интегрированную программную среду для
разработки программ.
Пакет программ содержит утилиту make, компилятор tcc и
настраиваемый компоновщик. Последующие версии несомненно будут
включать объектную библиотеку. Компилятор, входящий в пакет,
похож на большинство других компиляторов Си для IBM PC, но
является более быстрым. Программисты, которые предпочтут этот
пакет Турбо Си, найдут все, что им нужно, включая удобный
редактор. Поскольку вы приобрели эту книгу, то, вероятно, вы уже
имеете или собираетесь приобрести Турбо Си. Все, что Вам нужно
знать по этому пакету, содержится в руководстве пользователя и
справочном руководстве.
Интегрированная программная среда представляет собой
программу под названием tc, которая объединяет в себе текстовый
редактор, ориентированный на создание текстов программ на языке
Си, построитель задач, ориентированный на реализацию программного
проекта, и утилиты исполнения программ. В будущем планируется
включение символьного отладчика. Наличие интегрированной среды
выделяет Турбо Си среди конкурентов (также, как и ее безусловно
блестящая реализация).
Интегрированную среду можно считать витриной Турбо Си. Ее
большим достоинством является достигнутый уровень интеграции
между редактором, компилятором и компоновщиком. Находясь в
интегрированной среде, программист может редактировать программу,
транслировать ее, компоновать ее с другими исходными
модулями и библиотеками и запускать на выполнение. Данное
качество является основным для нового поколения компиляторов Си.
Это похоже на то, чего фирма Borland достигла тремя годами
раньше на компиляторе Турбо-Паскаль, но чего не было
достигнуто до этого времени на компиляторе Си. Ожидается, что
основные конкуренты в ближайшем будущем достигнут подобного
уровня.
Настройка интегрированной среды
-----------------------------------------------------------------
Вы имеете возможность настроить интегрированную среду, в
которой все, от цвета изображения на экране до уровня контроля за
ошибками, может быть установлено по вашему требованию. Некоторые
установки производятся при выполнении программы TCINST, другие -
путем использования меню, создаваемых интерактивной системой
ввода интегрированной среды и возникающих в верхней части экрана.
Ниже приводится список параметров, значения которых могут
устанавливаться по требованию заказчика:
- модель памяти: крошечная, малая, средняя, компактная, большая,
огромная;
- соглашение о вызываемых функциях: Си или Паскаль;
- микропроцессор: 8088/8086 или 80186/80286;
- плавающая арифметика: отсутствует, сопроцессор или эмуляция;
- уровень оптимизации;
- уровень контроля за ошибками.
Вы можете выбирать и большее число параметров Турбо Си.
Турбо Си способен осуществлять строгий контроль за ошибками и
подозрительными местами в программе и выдавать предупреждающие
сообщения. Вы можете использовать Устанавочное Меню для
подавления предупреждающих сообщений. Вы можете установить
необходимость соответствия жестким требованиям ANSI или менее
жестким требованиям стандарта, изложенного в книге " K & R". Вы
можете потребовать выдачи предупреждающего сообщения при любом
несоответствии описания функции и прототипа или можете разрешить
неявное описание функции и определение случайных параметров, как
это делается в так называемых K & R-компиляторах.
Возможно, вам захочется изменить цвета изображений на
экране. Вы можете выбрать один из трех цветовых наборов, включая
цветовой набор по умолчанию (слишком ярок), бирюзовый набор
(неприятен) или малиновый набор (просто ужасен). Не отчаивайтесь,
программа TCINST позволит выбрать цвет и яркость для каждого
отдельно определяемого компонента интегрированной среды. Следует
помнить, что в данном обзоре имеются в виду цветовые наборы,
формируемые системой СGA. Вполне возможно, что они вам и
нравятся.
Редактор Турбо Си
-----------------------------------------------------------------
В первом приближении редактор Турбо Си похож на редактор
системы WordStar, работающий в режиме, альтернативном к режиму
"документ". Архитектура этого редактора характерна для многих
других программных изделий Borland, включая программу Sidekick
Notepad и редактор Турбо-Паскаля. Eсли вы умеете работать с
редактором Турбо-Паскаля, то вы умеете работать и с редактором
Турбо Си. Однако в случае приобретения именно этого редактора
пользователи получат некоторые преимущества. Путем
использования программы TCINST вы можете изменить размер окна по
умолчанию и назначение клавиш команд редактирования.
Программисты, которые раньше работали с другим редактором,
оценят предоставляемую им редактором Турбо Си возможность
работы с двумя наборами команд редактирования, что позволяет
избежать многих затруднений. Диапазон изменения параметров
редактора ограничен: определенные функциональные клавиши и
комбинации различных клавиш с клавишей АLT зарезервированы под
"горячие клавиши" и не могут быть задействованы под команды
редактирования.
Редактор Турбо Си не такой мощный, как некоторые специальные
программы редактирования, но вполне отвечает требованиям не
слишком больших задач по редактированию. Редактор имеет
неизменяемое значение интервала для клавиши табуляции,
соответствующее восьми символьным промежуткам. Это неудобно при
работе с исходными текстами программ, представленных в данной
книге, так как их интервалы табуляции соответствуют четырем
символьным промежуткам, что обусловлено ограничениями при
печати. Borland поставляет программу PATCH.COM и несколько
примеров "заплат" на программы Compuserve и BIX. Один из
этих примеров позволяет вам поставить "заплату", которая
устанавливает интервал табуляции на четыре символьных
промежутка, в результате чего редактор становится очень
удобным для программ из этой книги. Возможность изменения
интервала табуляции, вероятно, будет предусмотрена в следующей
реализации Турбо Си.
Компоновщик Турбо Си
-----------------------------------------------------------------
Турбо Си имеет свой собственный компоновщик, который
называется TLINK. Компоновщик используется для связывания
различных объектных модулей, каждый из которых может быть получен
путем трансляции с языков Си, ассемблера и других в единый
загрузочный модуль. Объектные файлы, формируемые Турбо Си,
соответствуют стандарту программы LINK DOS, поэтому они могут
быть скомпонованы с объектными библиотеками для других языков,
включая ассемблер. Основной причиной использования компоновщика
TLINK является его скорость, поскольку TLINK работает значительно
быстрее, чем компоновщик LINK DOS.
Утилита построителя задач (Make) в Турбо Си
-----------------------------------------------------------------
Компилятор Турбо Си имеет утилиту Make, характерную для ОС
Unix и других компиляторов Си для IBM PC. Интегрированная Среда
дает уникальную возможность связывать при разработке программ
исходные и объектные модули с соответствующими им загрузочными
модулями. В этом отношении утилита Make Турбо Си является
традиционной. Однако утилита Make Турбо Си является
частью интегрированной среды и использует файл сопровождения,
называемый "файлом проекта", который является более легким для
чтения и понимания, чем у командной утилиты MAKE. В файле проекта
перечисляются исходные модули, составляющие программу, по одному
в каждой строке. Справа от имени каждого модуля можно указать
другие файлы (например, заголовки), с которыми связаны исходные
модули. Эти файлы заключаются в скобки и отделяются друг от
друга запятыми. Ниже приводится пример записи файла проекта:
myprogram (keys.h, twindow.h)
Если версия модуля myprogram.c старше версии модуля
myprogram.obj либо версии модулей key.h или twindow.h старше
версии модуля myprogram.c, то модуль myprogram.c транслируется в
модуль myprogram.obj. Если версия модуля myprogram.obj старше
версии модуля myprogram.exe, то модуль myprogram.obj компонуется
с соответствующими (зависящими от модели памяти) начальным
объектным файлом и исполняющей библиотекой. Использование
проектной утилиты Make становится насущно необходимым, когда при
формировании загрузочного модуля используются многочисленные
исходные модули на Си, зависящие от различных файлов заголовков.
Вы можете указывать объектные файлы и объектные библиотеки в
проектном файле MAKE. Интегрированная cреда будет включать
объектные файлы без попытки их компиляции и будет отыскивать
соответствующие библиотечные модули для разрешения вызовов
внешних функций.
Обнаружение ошибок при компиляции и компоновке
-----------------------------------------------------------------
При построении задачи в интегрированной среде Турбо Си
производится запись всех сообщений об ошибках и предупреждениях.
После завершения построения задачи сообщения об ошибках и
предупреждениях выводятся в одно окно, в то время, как исходный
текст программы отображается в другом окне. Вы имеете возможность
перемещаться по файлу с исходным текстом программы вперед и
назад, от одной ошибки к другой. Интегрированная среда отображает
каждое сообщение об ошибке и устанавливает курсор в строке, в
которой ошибка была обнаружена. Вы можете внести исправления,
какие считаете нужными, и снова запустить процесс построения
задачи. Программа обнаружения ошибок следит за тем, удаляете или
добавляете вы строки в исходном модуле, и соответствующим образом
корректирует положение курсора.
Программные средства низкого уровня
-----------------------------------------------------------------
Турбо Си включает несколько расширений языка Си, не
обладающих свойством мобильности, доставшиеся в наследство от
Wizard, но которые являются весьма существенными для программного
обеспечения, представленного в данной книге. Эти расширения
содержат программы обработки прерываний и других операций низкого
уровня.
Расширения языка включают функцию типа прерывание, при
вызове которой производится сохранение регистров процессора 8086
и установка регистра сегмента данных на значение сегмента данных
для функции прерывания. Перед тем, как функция возвращает
управление вызывающей программе, содержимое регистров
восстанавливается. Возврат осуществляется с помощью команды IRET
процессора 8086, которая используется для возврата из прерываний.
При включении в программу на Си фрагментов на ассемблере
используется ключевое слово asm. Все, что следует после этого
ключевого слова, поступает непосредственно на обработку
транслятором с ассемблера фирмы Microsoft, который вы должны
иметь, чтобы использовать данную возможность. Включаемые
ассемблерные фрагменты могут использовать имена переменных из
программы на Си. Эта возможность позволит писать функции на
ассемблере, которые не будут зависеть от используемой модели
памяти. Программы без ассемблерных фрагментов не подвергаются
обработке транслятором с ассемблера, а только компилятором с
языка Си. Программа с ассемблерными фрагментами должна
компилироваться обязательно командным компилятором tcc, поскольку
компилятор tc интегрированной среды не допускает использования
ассемблерных фрагментов. Фирма Воrland планирует убрать это
ограничение в последующих реализациях.
Несколько ключевых слов используется в качестве
псевдопеременных для осуществления непосредственного доступа к
регистрам ЭВМ. Если вам известно, что содержат регистры и как это
можно использовать, то вы можете оптимизировать выполнение
некоторых операций. Будьте внимательны при использовании этого
средства. Наилучшим подходом является трансляция исходной
программы на Си в модуль на ассемблере (что достигается
использованием ключа -S в командной строке компилятора tcc), а
затем внесение изменений в полученный модуль. При переходе на
следующие версии Турбо Си вы должны проверить возможность
использования каждого из описанных средств. Нет уверенности в
том, что Borland не изменит способ доступа к регистрам, и это
изменение может сделать ваши программы неработоспособными.
Начальная установка
-----------------------------------------------------------------
Самым слабым местом документации по Турбо Си является раздел
по начальной установке. Есть ряд фактов, которые обязательно
необходимо знать, а структура руководства такова, что не
позволяет легко найти нужную информацию.
И командный компилятор, и интегрированная среда используют
специальные файлы, в которых пользователь описывает требуемую ему
конфигурацию. Каждому из компиляторов соответствует свой файл.
Руководство дает подробные инструкции по подготовке файла
конфигурации TURBOC.CFG для командного компилятора и явно
недостаточную информацию по файлу конфигурации TCCONFIG.TC для
интегрированной среды. После того, как вы произвели начальные
установки в интегрированной среде, вы должны запустить процесс
инсталляции еще раз и установить значения параметров по
умолчанию, включая путь доступа, по которому компилятор tc
будет искать библиотеки, стартовую программу, включаемые файлы и
себя самого.
Эти параметры могут быть установлены также из
интегрированной среды путем выбора меню Options (Параметров).
После установки всех параметров такими, как вы хотели, выберите в
меню строку "Запомнить", завершая тем самым создание файла
TCCONFIG.TC.
Модели памяти
-----------------------------------------------------------------
Турбо Си поддерживает шесть моделей памяти: крошечную,
малую, среднюю, компактную, большую и огромную. Руководство
пользователя содержит раздел, посвященный моделям памяти и
разъясняющий сегментную организацию памяти для процессора 8086 и
ее проявления в различных моделях памяти. Советуем вам прочитать
и осмыслить этот раздел, поскольку понимание архитектуры
процессора 8086 позволит использовать и модифицировать
резидентные в памяти программные утилиты, представленные в данной
книге.
Библиотека исходных модулей
-----------------------------------------------------------------
Турбо Си поставляется без исходных текстов программ
библиотеки функций исполняющей системы. Но каждый пользователь
Турбо Си может купить лицензию на использование исходных текстов,
которые в этом случае поставляются ему фирмой Borland.
Заключение
-----------------------------------------------------------------
Раздел 4 начинает описание библиотеки функций Турбо Си.
После прочтения этого и последующих разделов вы получите в свое
распоряжение инструментальные программные средства, необходимые
для создания вашими программами всплывающих окон и последующего
преобразования этих программ в резидентные утилиты. На настоящий
момент нет другого компилятора, который поддерживал бы эти
средства на таком же уровне, что и Турбо Си.
ГЛАВА 4
-------
Функции общего назначения
-----------------------------------------------------------------
Эта книга посвящена программному обеспечению, и следующие
разделы содержат набор инструментальных программных средств,
которые могут быть использованы при создании прикладных систем.
Эти инструментальные средства написаны на языке Си,
транслируются с помощью компилятора Турбо Си и готовы к тому,
чтобы быть включенными в ваши программы. Эти функции могут
рассматриваться как расширения языка Си, если будут присоединены
к и без того достаточно обширной библиотеке стандартных
расширений Турбо Си. Настоящий раздел описывает функции первого
уровня, которые необходимо включать в программы, использующие
эти библиотеки. Представленные в данном разделе функции являются
функциями общего назначения, выполняющими операции низкого
уровня (специфическими для аппаратуры IBM PC) по управлению
дисплеем и клавиатурой.
Вы можете посчитать недостаточно обоснованным применение
некоторых из этих функций в своих программах. Назначением этих
функций является поддержка функций библиотеки высокого уровня,
также рассматриваемых в этой книге. В этом смысле они полезны, и
вы можете найти для них применение. Кроме того, глубина вашего
понимания функций, представленных в книге, зависит от осмысления
вами всех функций, в том числе и тех, которые вы не будете
использовать в своих программах.
При чтении описаний этих функций обращайтесь к листингу 4.1
программы ibmpc.c, который приводится после описаний.
void clear_screen()
-------------------
Эта функция очищает экран и устанавливает курсор в левый
верхний угол. Экран заполняется символами пробела, и атрибуты
символов извлекаются из символьной переменной attrib, описанной в
программе ibmpc.c. Значение этого атрибута соответствует байту
атрибута в видеопамяти, сопутствующему каждому байту ASCII-кода
при записи символа в видеопамять. Более подробная информация по
этому вопросу содержится в разделе 5. Переменная attrib принимает
значение, которое соответствует установке черного цвета для фона
символа и белого цвета для самого символа. Если вы желаете другое
значение атрибута, то должны изменить значение этой переменной
перед вызовом функции clear_screen.
int vmode()
-----------
Эта функция возвращает код текущего режима системы
формирования изображения. Она прeжде всего предназначена для
определения того, как программе интерпретировать содержимое
видеопамяти: как содержимое видеопамяти в монохромном режиме или
в алфавитно-цифровом режиме для Цветного Графического Адаптера
(CGA) и для Усовершенствованного Графического Адаптера (EGA). Эти
устройства более подробно рассматриваются в разделе 5. Функция
vmode возвращает код 7, если IBM PC работает в монохромном
режиме. Любое другое значение обозначает алфавитно-цифровой режим
для контроллеров CGA и EGA.
void cursor(int x,int y)
------------------------
Эта функция устанавливает курсор в позицию на экране,
определяемую координатами X и Y. Координаты (0,0) соответствуют
левому верхнему углу экрана. Значение координаты X изменяется в
диапазоне от 0 до 79, а координаты Y - в диапазоне от 0 до 24.
void curr_cursor(int *x, int *y)
--------------------------------
Эта функция считывает текущее положение курсора и записывает
значения координат X и Y в адреса памяти, определяемые с помощью
указателей перед обращением к функции.
int set_cursor_type(int t)
--------------------------
Эта функция устанавливает текущий размер курсора,
интерпретируемый программами из этой книги как тип курсора.
Размер обозначается целочисленной переменной, которая содержит в
старшем байте номер начальной растровой линии курсора и в младшем
байте - номер конечной растровой линии курсора.
Редактор текстов и программа ввода данных, представленные в
следующих разделах, используют возможность изменения размера
курсора для обозначения того, какой из режимов установлен:
Вставки или Замены. Курсор прямоугольной формы обозначает, что
установлен режим Вставки, он определяется значением
переменной, равным 0x0106. При этом курсор занимает
растровые линии с 1 по 6 того знакоместа, в котором он находится.
Курсор в виде знака подчеркивания обозначает режим Замены и
определяется значением переменной, равным 0x0607. При этом
курсор занимает растровые линии 6 и 7 знакоместа.
int get_char()
--------------
Эта функция является очень важной, так как выполняет
несколько крайне необходимых действий в вызывающих ее программах.
get_char принимает поступающий от клавиатуры символ путем
использования программ ROM-BIOS IBM PC. Ее главное назначение
состоит в приеме одиночного символа от клавиатуры без эха, без
преобразования и без обращения к функциям DOS. Кроме того, она
выполняет следующие дополнительные функции.
В то время, как система ожидает нажатия клавиши, функция get
_char вызывает программные прерывания по вектору 0x28, так
называемые прерывания DOSOK. Это прерывание и его значение для
создания резидентных утилит более подробно рассматриваются в
разделе 11.
Когда вы нажимаете функциональную клавишу, ROM BIOS
возвращает двубайтный код. Первый байт имеет нулевое значение и
обозначает, что следующий за ним код символа соответствует
функциональной клавише. Этот второй байт содержит 7-битный ASCII-
код, который является уникальным для каждой функциональной
клавиши. Eсли не учитывать первый нулевой байт, то реакция на
нажатие функциональных клавиш сходна с реакцией на нажатие
клавиш, соответствующих ASCII-символам, в частности, буквам.
Функция get_char преобразует двубайтную последовательность,
возвращаемую ROM-BIOS в ответ на нажатие функциональной клавиши,
в 8-битный код, позволяющий отличать функциональные клавиши от
нефункциональных. Функция осуществляет это преобразование путем
установки старшего разряда в байте, содержащем АSCII-код символа
и следующем за нулевым байтом. Формируемые коды описыватся в
исходном файле keys.h как глобальные символы (см. листинг 4.2).
Функция get_char oжидает нажатия функциональной клавиши,
обозначенной Help. Код, соответствующий функциональной клавише
Help, присвоен целочисленной глобальной переменной, названной
helpkey. Первоначально этой переменной присваивается нулевое
значение, но программные средства, которые работают с окном
Help, предназначенным для отображения справочной информациии,
будут присваивать этой переменной значение, соответствующее
функциональной клавише. При нажатии функциональной клавиши Help
функция get_char проверяет значение глобального указателя
функций, названного helpfunc. Если указатель имеет ненулевое
значение, то функция get_char вызывает адресуемую с помощью
указателя helpfunc функцию выдачи справочной информации.
void vpoke(unsigned vseg,unsigned adr,unsigned chr) int
vpeek (unsigned vseg,unsigned adr)
--------------------------------------------------------
Эти две функции считывают из видеопамяти коды символов и
атрибуты символов и записывают их в видеопамять. Для того, чтобы
использовать эти функции, вы должны разобраться в организации
видеопамяти IBM PC, а также принципах формирования изображения. В
тело функций vpoke и vpeek включены фрагменты на ассемблере. Для
того, чтобы оттранслировать эти функции, вы должны иметь
программу Macro Assembler (MASM) фирмы Microsoft, поскольку
именно она используется в Турбо Си для трансляции ассемблерных
фрагментов. Включение ассемблерных фрагментов необходимо только в
том случае, если ваши программы работают в системах, использущих
Цветной Графический Адаптер (CGA) или совместимый с ним адаптер.
Смысл этого требования разъясняется в разделе 5. Если у вас нет
контроллера CGA или если вы хотите работать с функциями, не
используя ассемблер, то удалите эти функции из исходного модуля
ibmpc.c и вставьте в файл twindow.h из раздела 6 следующие
операторы:
#define vpoke(vseg,adr,chr) poke(vseg,adr,chr)
#define vpeek(vseg,adr) peek(vseg,adr)
Эти макроопределения заменят функции vpoke и vpeek и избавят
от необходимости использования макроассемблера для функций работы
с окнами из этой книги.
Исходные модули функций общего назначения
-----------------------------------------------------------------
Листинг 4.1 представляет собой исходный текст программы
ibmpc.c, которая содержит функции, описанные в данном разделе.
Вследствие того, что функции включают фрагменты на ассемблере,
программу лучше транслировать с помощью командного компилятора
tcc, а не компилятора tc Интегрированной Среды.
Чтобы оттранслировать файл ibmpc.c, введите следующую
команду (не набирая промптер С>):
С>tcc -c ibmpc
Листинг 4.1: ibmpc.c
/* ibmpc.c */
/* Функции нижнего уровня, обращающиеся к BIOS и аппаратным
средствам РС */
#pragma inline #include static union REGS rg;
/* позиция курсора */
void cursor(int x,int y) {
rg.x.ax = 0x0200;
rg.x.bx = 0;
rg.x.dx = ((y << 8) & 0xff00) + x;
int86( 16, &rg, &rg);
}
/* возвратить позицию курсора */
void curr_cursor( int *x, int *y )
{
rg.x.ax = 0x0300;
rg.x.bx = 0;
int86( 16, &rg, &rg );
*x = rg.h.dl;
*y = rg.h.dh;
}
/* установить тип курсора */
void set_cursor_type( int t )
{
rg.x.ax = 0x0100;
rg.x.bx = 0;
rg.x.cx = t;
int86( 16, &rg, &rg );
}
char attrib = 7;
/* очистить экран */
void clear_screen()
{
cursor(0, 0);
rg.h.al = ' ';
rg.h.ah = 9;
rg.x.bx = attrib;
rg.x.cx = 2000;
int86( 16, &rg, &rg );
}
/* возвратить режим работы видеоконтроллера*/
int vmode()
{
rg.h.ah = 15;
int86( 16, &rg, &rg);
return rg.h.al;
}
/* проверить клавишу Scroll Lock */
int scroll_lock()
{
rg.x.ax = 0x0200;
int86( 0x16, &rg, &rg);
return rg.h.al & 0x10;
}
void (* helpfunc)();
int helpkey = 0;
int helping = 0;
/* принять символ от клавиатуры */
int get_char()
{
int c;
while (1) {
rg.h.ah = 1;
int86(0x16, &rg, &rg);
if (rg.x.flags & 0x40) {
int86(0x28, &rg, &rg);
continue;
}
rg.h.ah = 0;
int86(0x16, &rg, &rg);
if (rg.h.al == 0)
c = rg.h.ah | 128;
else
c = rg.h.al;
if (c == helpkey && helpfunc) {
if (!helping) {
helping = 1;
(*helpfunc)();
helping = 0;
continue;
}
}
break;
}
return c;
}
/* занести код символа и его атрибуты в видеопамять */
void vroke(unsigned vseg, unsigned adr, unsigned chr)
{
if (vseg == 45056) /* монохромный режим */
poke(vseg, adr, chr);
else {
_DI = adr; /* смещение до адреса символа в видеопамяти */
_ES = vseg; /* адрес сегмента видеопамяти */
asm cld;
_BX = chr; /* атрибуты и код символа */
_DX = 986; /* состояние видеопорта */
/* ждать начала обратного хода луча*/
do
asm in al,dx;
while (_AL & 1);
/* ждать завершения обратного хода луча */
do
asm in al,dx;
while (!(_AL & 1));
_AL = _BL;
asm stosb; /* запомнить символ */
/* ждать начала обратного хода луча */
do
asm in al,dx;
while (_AL & 1);
/* ждать завершения обратнога хода луча */
do
asm in al,dx;
while (!(_AL & 1));
_AL = _BL;
asm stosb; /* запомнить атрибуты */
}
}
/* считать код символа и его атрибуты из видеопамяти */
int vpeek(unsigned vseg, unsigned adr)
{
int ch, at;
if (vseg == 45056) /* монохромный режим */
return peek(vseg, adr);
asm push ds;
_DX = 986; /* состояние видеопорта */
_DS = vseg; /* адрес сегмента видеопамяти */
_SI = adr;/* смещение до адреса символа в видеопамяти */
asm cld;
/* ждать начала обратного хода луча */
do
asm in al,dx;
while (_AL & 1);
/* ждать завершения обратного хода луча */
do
asm in al,dx;
while (!(_AL & 1));
asm lodsb; /* считать символ */
_BL = _AL;
/* ждать начала обратного хода луча */
do
asm in al,dx;
while (_AL & 1);
/* ждать завершения обратного хода луча */
do
asm in al,dx;
while (!(_AL & 1));
asm lodsb; /* считать атрибут */
_BH = _AL;
_AX = _BX;
asm pop ds;
return _AX;
}
/* keys.h */
#define HT 9
#define RUBOUT 8
#define BELL 7
#define ESC 27
#define SHIFT_HT 143
#define CTRL_T 20
#define CTRL_B 2
#define CTRL_D 4
#define ALT_D 160
#define F1 187
#define F2 188
#define F3 189
#define F4 190
#define F5 191
#define F6 192
#define F7 193
#define F8 194
#define F9 195
#define F10 196
#define HOME 199
#define UP 200
#define PGUP 201
#define BS 203
#define FWD 205
#define END 207
#define DN 208
#define PGDN 209
#define INS 210
#define DEL 211
#define CTRL_HOME 247
#define CTRL_BS 243
#define CTRL_FWD 244
#define CTRL_END 245
Заключение
-----------------------------------------------------------------
На основе представленных выше функций нижнего уровня в
разделе 5 будет развиваться и объясняться концепция экранных
окон, которая составляет следующий, более высокий, уровень в
многоуровневом наборе функций, описываемых в данной книге.
ГЛАВА 5
-------
Экранные окна
-----------------------------------------------------------------
Этот раздел посвящен вопросу о том, что собой представляют
экранные окна и как с ними работать. Раздел 6 разъясняет, как
можно использовать окна в ваших программах, создаваемых в среде
Турбо Си, а также содержит полную библиотеку функций управления
окнами. Следующие разделы содержат расширенную библиотеку
функций, поддерживающих работу с окнами для специфических целей,
как, например, для контекстно-чувствительного вывода справочной
информации, редактирования текста, ввода данных и создания меню.
После того, как вы прочитали об экранных окнах, способах их
создания и применения, постарайтесь вспомнить программные
системы, которые используют подобные средства. Подумайте
также и о том, какую пользу могли бы принести эти средства для
тех программных проектов, в которых вы принимали участие. Затем
попытайтесь найти в этих функциях недостатки, устранение которых
позволит сделать функции более подходящими для вашей работы. В
любой программе вы почти всегда можете отыскать эти недостатки.
В данном случае вы имеете большое преимущество: исходные тексты
функций предоставлены, вы можете модифицировать их по своему
усмотрению.
Экранное окно
-----------------------------------------------------------------
Окном называется область экрана дисплея, которая
используется для определенных целей. Окно обычно имеет форму
прямоугольника или квадрата, а его границей служат символы из
набора графических символов. Использование окон становится
наиболее популярным способом представления информации,
предназначенной для восприятия пользователем ПЭВМ. Этот способ
позволяет на ограниченном пространстве экрана отображать
информацию, передаваемую множеством задач, выполняющихся
асинхронно.
Поскольку отображаемые на экране изображения формируются в
видеопамяти с прямым доступом процессора (так называемый способ
формирования изображения путем управления содержимым памяти),
то окна на экране создаются мгновенно, возникая как бы из
ничего, и так же мгновенно исчезают. Использование окон в
программах позволяет очень удобно отображать различного рода
информацию. Окна появляются и сменяют друг друга: они
появляются на экране, когда это необходимо, и исчезают после
того, как информация, которую они содержат, становится вам
ненужной. Программа может отображать столько окон, сколько
нужно программисту. Окна могут иметь различные размеры, цвета и
форматы. Вы можете разместить окно в любом месте экрана, где это
необходимо. Если окно становится ненужным, то вы можете удалить
его, и окно исчезнет, а на его месте будет то, что было до его
появления.
Каждый, кто занимался проблемами автоматизации в последние
годы, обязательно сталкивался с окнами. ПЭВМ IBM PC сейчас
встречаются везде, в любой сфере общественной и деловой жизни. И
почти на каждой из них работает вездесущая программа Sidekick,
включающая калькулятор, календарь и средства вызова абонента в
сети. Каждый из этих компонентов отображает свою информацию с
помощью окон, которые по мере необходимости появляются и
исчезают на экране, не разрушая того изображения, которое было до
их появления. Волшебники в области разработки программного
обеспечения и ее сбыта из фирмы Bоrland International (это они
дали Вам Турбо Си) внедрили окна во всеобщую практику. Если вы
никогда не видели окон, то отложите книгу в сторону, найдите
IBM PC и нажмите одновременно клавиши и . Если вы не
увидите после этого красно-бело-зелено-голубого окна на экране
(или зеленого и черного для монохромного экрана), то спросите у
владельца машины, почему у него не работает программа Sidekick.
Если же окно небольшого размера все-таки появится, начните с ним
игру. Сделав выбор в меню, содержащемся в первом окне, вы можете
создать другое окно. Вы можете перемещать окна, нажимая клавишу
со стрелкой. Вы можете удалять окна, нажимая клавишу . Вы
можете вызвать на экран справочную информацию по работе с
программой, нажав клавишу .
Из этого раздела вы узнаете, как работать с окнами, как
накладывать их друг на друга, как перемещать их по экрану и как
их использовать в своих программах, разрабатываемых с помощью
Турбо Си. Окна используются для отображения меню, информационных
и предупреждающих сообщений, текстовых файлов, шаблона для ввода
данных и контекстно-чувствительных информационных сообщений.
Программные системы могут использовать окна для отображения
информации независимо от содержания других окон и размера области
пространства экрана, занимаемой другими окнами. Благодаря этому
можно обозревать на экране несколько окон, даже если каждое из
них занимает больше половины экрана, потому что окна могут как бы
"всплывать" друг над другом.
Свойство всплывания окон часто объясняют путем сравнения
экрана дисплея с поверхностью стола. Допустим, на вашем столе
лежит множество документов, но в каждый момент времени вы можете
работать только с одним из этих документов, возможно, наиболее
важным для вас. То, что можно делать с бумагами, можно выполнять
и на экране дисплея. Если первоначально изображение на экране
содержит только одно окно, как это показано на рис. 5.1, и
программе необходимо создать второе окно, не уничтожая при этом
первого, то в результате получится изображение, как на рис. 5.2.
Если затем второе окно становится ненужным, то оно уничтожается,
и изображение снова будет соответствовать рис. 5.1.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ ¦
¦ ЪДДДДДДДДДДДДДДДДДДДДДДДДДД¬ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ Окно A ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ АДДДДДДДДДДДДДДДДДДДДДДДДДДЩ ¦
¦ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рис. 5.1 Экранное окно
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ЪДДДДДДДДДДДґ ¦ ¦
¦ ¦ ¦ Окно B ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ Окн¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ АДДДДДДДДДДДДДДВДДДДДДДДДДДДДЩ ¦
¦ ¦ ¦ ¦
¦ АДДДДДДДДДДДДДДДДДДДДДДДДДДЩ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рис. 5.2 Наложение окон
Для того, чтобы понять, как можно использовать окна,
необходимо знать, какие действия с окнами можно выполнять.
Помните, что окном является прямоугольная область на экране
дисплея. Окна имеют и другие свойства, но главное, что отличает
их от других типов экранных изображений, - это их способность как
бы всплывать и погружаться относительно остального изображения.
Информация, которая содержалась в соответствующей
прямоугольной области экрана до появления нового окна, должна
сохраняться. Окно накладывается на предшествующее изображение,
как бы всплывает. Когда окно уничтожается (погружается), то
информация, которая была до его появления, должна быть
восстановлена. Та часть изображения, на которую накладывается
новое окно, также может содержать окна. На рис. 5.3 представлено
изображение на экране дисплея, которое содержит несколько окон,
причем каждое последующее окно накладывается на часть
предыдущего.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬ ¦
¦ ¦ ¦ ¦
¦ ¦ ¦ ¦
¦ ¦ ЪДДДДДДДДДБДДДДДД¬ ¦
¦ ¦ ¦ ¦ ¦
¦ ЪДДДДДДДДДДДґ ¦ Окно D ¦ ¦
¦ ¦ ¦ Окно B ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ЪДДДДДДДДДДДДДДґ ГДД¬ ¦
¦ ¦ ¦ ¦ Окно C ¦ ¦ ¦ ¦
¦ ¦ Окн¦ ¦ ЪДДДДДДДДБДДДДДДДДДДДДДДДДБД¬¦ ¦
¦ ¦ ¦ ¦ ¦ ¦¦ ¦
¦ ¦ ¦ ¦ ¦ ¦¦ ¦
¦ ¦ АДДДґ ¦ Окно E ¦¦ ¦
¦ ¦ ¦ ¦ ¦¦ ¦
¦ АДДДДДДДДДДДДДДДґ ¦ ¦¦ ¦
¦ ¦ АДДДДДДДДВДДДДДДДДДДДДДДДДВДЩ¦ ¦
¦ ¦ АДДДДДДДДДДДДДДДДЩ ¦ ¦
¦ АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рис. 5.2 Наложение нескольких окон
Если бы каждая программа, работающая с окнами, должна была
управлять размещением окон и восстанавливать содержимое экрана,
которое было до их появления, то создание и сопровождение таких
программ было бы очень трудным делом. К счастью, в этом нет
необходимости. Поскольку функции и свойства окон являются общими
для разных применений, то можно использовать библиотеку функций
общего назначения для работы с окнами. Настоящая книга содержит
такую библиотеку, ее функции описаны в разделе 6.
Основных операций по работе с окнами, использующихся в
большинстве программ, не так уж много, и они не слишком сложны.
При работе с окнами вам необходимо установить окно, определив его
размеры и местоположение. Вы имеете возможность также
устанавливать его цвета, границу и заголовок. Вы можете вписать
свой текст внутрь окна, а также при необходимости изменять
местоположение окна. Наконец, вы можете удалить окно с экрана,
восстановив при этом изображение, которое было на экране до
появления окна. Основываясь на этих операциях, вы можете
создавать программы, которые используют чарующие многоцветьем
красок окна в качестве пользовательского интерфейса. (Следует
помнить, что использование эффективной библиотеки само по себе не
гарантирует высокое качество пользовательских характеристик
разрабатываемой программы. Программист должен использовать вместе
с окнами также звуковые эффекты и другие инструментальные
программные средства).
Чтобы понять, как можно управлять изображением на экране, вы
должны разобраться в организации видеопамяти. Приводимые ниже
сведения представляют собой введение в архитектуру видеопамяти
IBM PC. Для получения исчерпывающей информации по этому вопросу
обращайтесь к "Руководству Питера Нортона для программистов по
IBM PC" (Питер Нортон, Мicrosoft Press, 1985).
Архитектура видеопамяти
-----------------------------------------------------------------
Система формирования изображения является неотъемлемой
частью ПЭВМ IBM PC. В более ранних моделях персональных ЭВМ
видеотерминалы подключались через последовательные порты
ввода/вывода, но аппаратная архитектура IBM PC включает в себя и
видеосистему.
Предназначенное для вывода на экран изображение создается в
видеопамяти. В IBM PC в качестве видеопамяти используется часть
оперативной памяти. Видеопамять доступна для чтения и записи
процессору и, следовательно, вашим программам. Видеопроцессор,
входящий в состав видеоконтроллера, по содержимому видеопамяти
постоянно формирует изображение на экране дисплея. Поэтому
каждый новый символ, записанный в видеопамять, почти немедленно
появляется на экране. Поскольку видеопамять доступна для
микропроцессора, то скорость формирования изображения
соответствует скорости пересылки содержимого памяти, которая
превышает скорость передачи данных при подключении видеотерминала
через последовательные порты ввода/вывода.
Адреса видеопамяти и ее характеристики являются стандартными
для всех ПЭВМ линии IBM PC, а также совместимых с ними.
ПЭВМ типа IBM PC может иметь одну из трех видеосистем,
использующих различные типы видеомониторов и, следовательно,
различные типы видеоконтроллеров. Видеоконтроллер первого типа
называется Монохромным Адаптером (МА), он обеспечивает работу
только монохромного видеомонитора в символьном режиме и не
поддерживает графического режима. Видеоконтроллер второго типа
называется Цветным Графическим Адаптером (CGA). С помощью
контроллера CGA подключается цветной монитор, который может
работать в двух различных режимах. В символьном режиме имеется
возможность выбирать цвет фона и цвет символа из восьми возможных
цветов, а также один из двух уровней интенсивности цвета для
символа. В графическом режиме низкого разрешения (640x200
растровых точек) можно работать только с одним цветом. Третий
видеоконтроллер называется Усовершенствованным Графическим
Адаптером (EGA), который поддерживает такой же символьный режим,
что и CGA, и многоцветный графический режим более высокого
разрешения.
Приведенные в данной книге программы работают с любым из
этих контроллеров в символьном режиме. Поскольку в этом режиме
контроллеры CGA и EGA функционально эквивалентны, то
нижеследующие рассуждения для CGA относятся к обоим этим
контроллерам.
Видеопамять организована в виде двумерного массива символов,
состоящего из рядов и колонок. Ее можно рассматривать и как набор
следующих друг за другом 16-разрядных слов, по одному на каждый
символ. Каждый ряд содержит 80 следующих друг за другом символов,
все они образуют 25 следующих друг за другом колонок. Слово
содержит информацию об одном символе и состоит из двух
восьмибитных байт: один - для ASCII-кода символа, второй - для
атрибутов символа, определяющих его изображение. Код ASCII
записан в младшем (правом) байте слова.
Видеопамять контроллера МА организована в виде одной
страницы, а контроллера CGA - в виде четырех страниц. Программы
из этой книги используют только первую страницу видеопамяти
контроллера CGA.
Видеопамять расположена в верхних областях доступного
процессору адресного пространства. Разработчики IBM PC,
столкнувшись с ограничением на максимальный объем адресуемого
адресного пространства в 1 мегабайт, решили разместить
видеопамять и ПЗУ Базовой Системы Ввода-Вывода (ROM BIOS) в
верхних областях этого пространства. Для того, чтобы позволить
контроллерам МА и CGA работать совместно на одной ПЭВМ,
разработчики назначили различные адреса сегментов видеопамяти для
разных контроллеров. Память контроллера МА начинается в сегменте
0xB000, а память контроллера CGA - в сегменте 0xB800. Программа
может определить, какой из контроллеров используется, путем
вызова соответствующей функции ROM BIOS, и настроиться таким
образом на соответствующий адрес сегмента видеопамяти. Поскольку
архитектура видеопамяти для того и другого случая в основном
одинакова, то необходимые действия по настройке программы будут
минимальными.
Байт атрибутов символа содержит 2 трехразрядных поля
кодирования цвета (одно для цвета фона символа и одно для цвета
самого символа), разряд для задания уровня интенсивности цвета
символа и разряд для установки режима мерцания символа при его
отображении. На рис. 5.4 представлена конфигурация байта
атрибутов.
ЪДДДДВДДДДВДДДДВДДДДВДДДДВДДДДВДДДДВДДДД¬
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ B ¦ R ¦ G ¦ B ¦ I ¦ R ¦ G ¦ B ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦ ¦
АДВДД†ДДДДБДДДДБДДДД†ДДВД†ДДДДБДДДДБДДДДґ
¦ АДДДДДДДВДДДДДДЩ ¦ АДДДДДДДВДДДДДДЩ
¦ ¦ ¦ ¦
¦ ¦ ¦ АДДДДДДД цвет символа
¦ ¦ ¦
¦ ¦ АДДДДДДДДДДДДДДДДД интенсивность цвета
¦ ¦ символа
¦ АДДДДДДДДДДДДДДДДДДДДДДДДДДД цвет фона
¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД мерцание символа
Рис. 5.4 Байт атрибутов символа
Комбинации красной, зеленой и голубой цветовых составляющих
позволяют получить набор из восьми отчетливо различаемых цветов:
белого, красного, зеленого, голубого, синего, малинового, желтого
и черного. Использование разряда интенсивности для цвета символа
позволяет получить еще восемь дополнительных оттенков цвета.
В случае использования Монохромного Адаптера значение байта
атрибутов немного отличается от описанного выше. Поскольку
возможность использования различных цветов не поддерживается
контроллером МА, то разрешены только комбинации белого символа
на черном фоне или черного символа на белом фоне. Другие коды
цвета дают комбинации черного на черном или белого на белом.
Хотя следует отметить, что комбинация голубого символа и
черного фона формирует изображение подчеркнутого символа, что не
поддерживается уже контроллером CGA. Монохромный Адаптер, как и
CGA, поддерживает возможности задания интенсивности цвета и
режима мерцания символа.
Поскольку вы теперь знаете, где размещена видеопамять и что
в нее может быть записано, то можете использовать программу pokes
из библиотеки Турбо Си для формирования изображения на экране
дисплея. Ниже приводится листинг 5.1 маленькой программы под
названием vidpoke.c, которая записывает строку непосредственно в
видеопамять. Исходя из предположения, что на вашей машине
работает контроллер CGA, программа формирует для каждого символа
байт атрибута со значением 7, что соответствут отображению строки
в виде белых символов на черном фоне. Если вы используете
Монохромный Адаптер, измените в строке # define VSEG значение с
0xB800 на 0xB000.
Листинг 5.1: vidpoke.c
/* vidpoke.c */
#define VSEG 0x6800
char vdata [] = "Что сделал Кан?";
main()
{
char *vp;
int v;
for (v=0, vp = vdata; *vp; v +=2, vp++)
poke ( VSEG, v, 0x700| *vp);
}
"Снег" и обратный ход луча развертки
-----------------------------------------------------------------
Если вы запустите программу vidpoke.exe в цикле или
значительно увеличите длину выводимой строки, то сможете увидеть
на экране так называемый "снег", который появляется при
выполнении программы. Вы будете наблюдать "снег" только в случае
использования контроллера CGA или его аналога. Контроллеры ЕGA и
МА не дают подобного эффекта. Из нижеследующих объяснений вы
узнаете, почему возникает "снег" и как его можно устранить
программнми средствами.
"Снег" возникает при использовании контроллера CGA из-за
особенностей аппаратной архитектуры видеосистемы. Поскольку и
микропроцессор, и видеоконтроллер обращаются к одной видеопамяти,
то в случае одновременного обращения возникает необходимость в
координировании доступа к памяти. Работа видеоконтроллера
синхронизирована по времени с работой схемы развертки. Во время
формирования элемента изображения на экране видеоконтроллер
должен иметь доступ к ячейке памяти, которая содержит бит,
соответствующий этому элементу изображения. Видеопроцессор при
этом не может ждать, поскольку он жестко синхронизирован с
устройством формирования растра. Следовательно, если
видеоконтроллер и микропроцессор пытаются одновременно
обратиться к одной и той же ячейке памяти, то видеоконтроллер
должен иметь при этом более высокий приоритет. В хорошо
разработанной системе микропроцессор будет находиться в состоянии
ожидания, при котором запросы микропроцессора к видеопамяти не
поступают. В контроллерах МА и EGA использован этот принцип, и
вы можете не беспокоиться о конфликтных ситуациях между
микропроцессором и видеоконтроллером при доступе к видеопамяти.
Но в контроллере CGA в символьном режиме этого не выполняется,
поэтому вы должны сами предпринимать некоторые действия.
В случае использования контроллера CGA, если микропроцессору
нужно обратиться к видеопамяти для чтения или записи, то это ему
разрешается, а видеоконтроллеру доступ на это время запрещается.
А поскольку работа видеоконтроллерMZШ Б
Эit$
Глава 6
-------
Библиотека оконных функций
-----------------------------------------------------------------
Программы, описанные в этой и нескольких последующих главах,
представляют библиотеку оконных функций, которая поддерживает
широкий диапазон экранных оконных операций. Функции подразделены
на подсистемы, использование которых позволяет организовать меню,
контекстно-управляемые подсказки, редактирование текста и
форматирование данных в прикладных системах. Эти подсистемы
поддерживаются общецелевой оконной библиотекой, которая может
использоваться в прикладных программах так же, как и подсистемы.
В этой главе описывается общецелевая библиотека оконных функций.
Оконные функции, описанные в данной главе, могут применяться
для организации окон в одной из двух конфигураций: стековой и
слоеной; одна конфигурация является с точки зрения использующей
их программы подмножеством другой. Слоеные окна обладают большими
возможностями, чем стековые, однако стековые более эффективны, то
есть выдача на экран и уничтожение их происходит быстрее.
Программа может быть связана либо со стековыми, либо со слоеными
оконными функциями, но не с теми и другими одновременно.
При компиляции оконных функций, вы должны принять решение о
том, какую именно оконную конфигурацию применить. Для стековых
окон определяется переменная времени компиляции FASTWINDOWS, для
слоеных окон она удаляется. Прикладная программа может быть
связана с любой библиотекой до тех пор, пока она не использует
возможности, поддерживаемые только для слоеных окон. Те
прикладные программы, которые используют возможности
исключительно слоеных окон, должны быть связаны с библиотекой
оконных функций, которая была компилирована без определения
FASTWINDOWS.
Стековые окна
-----------------------------------------------------------------
Конфигурация стековых окон предполагает, что любая
выполняемая вами с окном операция (запись в него текста,
изменение цвета, уничтожение его и т.д.) производится, когда окно
является полностью видимым пользователю. Полная видимость
означает, что ни одна часть окна не накрыта другим окном и что
окно не скрыто функцией hide_window (о которой будет сказано
ниже). Когда установлено стековое окно, оконное программное
обеспечение строит буфер для хранения прежнего содержимого
видеопамяти, которую будет занимать окно. Видеопамять сохраняется
в буфере, а окно записывается в видеопамять. При выполнении любых
операций, модифицирующих окно, все изменения выполняются
непосредственно в видеопамяти, а программное обеспечение
предполагает, что окно является полностью видимым. Когда окно
уничтожается, содержимое хранящего его буфера записывается
обратно в видеопамять, восстанавливая таким образом видеообраз
памяти к состоянию до образования окна.
Вы будете обычно обращаться только к стековому окну,
образованному последним. Если вы сначала создали окно А, а затем
окно В, которое закрывает часть окна А, то при записи текста в
окно А может случиться, что часть текста попадет в часть окна В,
закрывающую окно А. Далее, если вы уничтожите окно A до
уничтожения окна В, то часть буфера сохранения окна А
запишется в начало части окна В.
Большинство коммерческих оконных пакетов поддерживает только
стековые окна, поскольку большинство приложений, использующих
окна, могут успешно функционировать в среде стековых окон.
Обычной практикой в приложениях является использование окна,
открытого последним, и уничтожение окон в порядке, обратном их
созданию. Такие приложения должны использовать стековые окна
из-за преимуществ их функционирования.
Оконные операции, описанные в данной книге, позволяют вам
создавать одно или более окон и затем относить различные операции
к одному из созданных окон. Вы можете обращаться к конкретному
окну или использовать пустую спецификацию для сообщения вызванной
функции о своем намерении выполнить операцию в окне, созданном
последним. Это соглашение используется как для стековых, так и
для слоеных окон, однако пользователи стековых оконных функций
должны быть уверены, что любое окно, к которому они обращаются,
либо создано последним, либо полностью видимо на экране.
Слоеные окна
-----------------------------------------------------------------
Слоеные окна обладают гораздо большей гибкостью, чем
стековые, к тому же они предоставляют пользователю гораздо больше
возможностей по созданию различных оконных интерфейсов. Когда
слоеное окно создано, любая оконная операция может быть
адресована ему, независимо от его видимости или близости к другим
окнам. В дополнение к обычному набору оконных операций слоеные
окна могут перемещаться в двумерной плоскости экрана и могут
выдвигаться на передний или убираться на задний планы в слоях
созданных окон.
Когда создано слоеное окно, распределяется буфер сохранения
видеосодержимого, но окно не отображается. Буфер сохранения
инициализируется видеозначениями, которые окно могло бы
содержать, если бы оно было видимым. Любые последующие операции,
производимые в этом окне, пока оно невидимо, производятся в
буфере сохранения.
Когда слоеное окно отображается, видеопамять и буфер
сохранения обмениваются содержимым. Затем, до тех пор пока окно
является полностью видимым (не закрытым полностью или частично
другим окном), любые операции производятся с видеопамятью, а не с
буфером сохранения. Когда одно и более других окон покрывают
адресуемое окно полностью или частично, определение области
изменения является более сложным. Для тех частей окна, которые
являются видимыми, изменение производится в видеопамяти. Однако
для областей, закрытых другими окнами, изменение производится в
буфере сохранения покрывающего окна. Поскольку окно может иметь
различные части, закрытые несколькими другими окнами, то алгоритм
определения места, где должно быть сделано изменение, обязан
сначала просмотреть все окна, созданные позже данного, с целью
установления факта выполнения изменения в области, покрытой
следующим окном последовательности. Если такое окно найдено, то
изменение записывается в его буфер сохранения. Если ни одно окно
не закрывает модифицируемый участок, то изменение производится в
видеопамяти.
Рассмотрим рисунок 6.1. Три окна расположены так, что часть
окна А видима, часть закрыта окном В и часть - окном С. В буферах
сохранения каждого окна вы можете видеть границы частей других
окон, которые закрыты.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ .ЪДДДДДДДДДДД¬ ¦
¦ . ¦ ¦ ¦
¦ . ГДДДДДДДДДДДґ Буфер ¦
¦ . ¦ ¦ сохранения В ¦
¦ . ¦ ¦ ¦
¦ . ¦ ¦ ¦
¦ . АДДДДДДДДДДДЩ ¦
¦ . . ¦
¦ . ¦
¦ . . ¦
¦ ЪДДДДДДДДДД¬ ¦
¦ ЪДДДДґ B ГДДДД¬ . ¦
¦ ¦ A ¦ ¦ ¦ . ¦
¦ ¦ ¦ ЪДДДДБДДДДБДДД¬. . ¦
¦ ¦ ¦ ¦ C ¦. . ¦
¦ ¦ АДДДДД¦ ¦ . ¦
¦ АДДДДДДДДДД¦ ¦ . ¦
¦ ¦ ¦ . ¦
¦ ¦ ¦ ЪДДДДДДВДДДДДДДДДДВДДД¬ ¦
¦ АДДДДДДДДДДДДДЩ ¦ ¦ ¦ ¦ ¦
¦ . ¦ ¦ ¦ ¦ ¦
¦ . ¦ ¦ ¦ ¦ ¦
¦ . ГДДДДДДЩ ¦ ¦ ¦
¦ . ГДДДДДДДДДДДДДДДДДЩ ¦ ¦
¦ . АДДДДДДДДДДДДДДДДДДДДДЩ ¦
¦ Буфер сохранения С ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 6.1. Три слоеных перекрывающихся окна.
Если вы запишите текстовую строку "now is the time" в окно
А, текст будет направлен в три различных места. Результат показан
на рисунке 6.2. Так как часть окна А, где записано "now",
является видимой, то слово записывается непосредственно в
видеопамять и может быть прочитано пользователем. Слова "is the"
являются частью окна А, которая закрыта окном В, поэтому эти
слова записываются в буфер сохранения окна В. Слово "time"
оказывается в той части окна А, которая покрыта окном С,
следовательно "time" записывается в буфер сохранения окна С.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ ¦
¦ ¦
¦ ¦
¦ . ЪДДДДДДДДД¬ ¦
¦ . ГДДДДДДДДДґ ¦
¦ . ¦ is the ¦ Буфер сохранения В ¦
¦ . ¦ ¦ ¦
¦ ЪДДДДДДД¬ АДДДДДДДДДЩ ¦
¦ ЪДДДДДДДґ B ГДДДДДД¬ . ¦
¦ ¦ A ¦ ЪДДБДДДДДДБД¬. . ¦
¦ ¦ now ¦ ¦ C ¦. . ¦
¦ ¦ АДДДДґ ¦ . ¦
¦ АДДДДДДДДДДДДґ ¦ ЪДДДДДВДДДДДДДДВДД¬ ¦
¦ АДДДДДДДДДДДЩ ¦ ¦ time ¦ ¦ ¦
¦ . ГДДДДДЩ ¦ ¦ ¦
¦ . ГДДДДДДДДДДДДДДЩ ¦ ¦
¦ . АДДДДДДДДДДДДДДДДДЩ ¦
¦ Буфер сохранения С ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 6.2. Слоеные окна с текстом.
Рисунок 6.3. показывает, что происходит при уничтожении окна
В. Часть окна А из его буфера сохранения поступает в
видеопамять, и слово "is" может теперь быть прочитано
пользователем. Однако, поскольку часть окна В была покрыта окном
С, то часть буфера сохранения окна В копируется в буфер
сохранения окна С, следовательно, буфер сохранения окна С теперь
содержит слова "the time".
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ ¦
¦ ¦
¦ ¦
¦ ЪДДДДДДДДДДДДДДДДДДДДДД¬ ¦
¦ ¦ A ЪДДДДДДДДДБДДДДДД¬ . ¦
¦ ¦ ¦ C ¦ . ¦
¦ ¦ now is ¦ ¦ . ¦
¦ ¦ ¦ ¦ . ¦
¦ АДДДДДДДДДДДДґ ¦ ЪДДДДДДДДДДДДДДДДДДДВДД¬ ¦
¦ АДДДДДДДДДДДДДДДДЩ ¦ the time ¦ ¦ ¦
¦ . ¦ ¦ ¦ ¦
¦ . ¦ ¦ ¦ ¦
¦ . ГДДДДДДДДДДДДДДДДДДДЩ ¦ ¦
¦ . АДДДДДДДДДДДДДДДДДДДДДДЩ ¦
¦ Буфер сохранения С ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 6.3. Уничтожение слоеного окна.
Независимо от использования стековых или слоеных окон,
работа их будет зависеть от требований вашей системы. Каждый
подход имеет свои преимущества и недостатки. Если вы начали со
стековых окон и обнаружили позже, что вам нужны дополнительные
свойства слоеной оконной архитектуры, вы можете выполнить
изменение путем перекомпиляции оконных функций без определения
FASTWINDOWS и перередактировать связи ваших программ.
Оконные функции
-----------------------------------------------------------------
Эти функции включены в оконную библиотеку. Для каждой
функции описаны ее назначение и способ применения. Далее
приводятся примеры использования этих функций.
WINDOW *establish_window(x,y,h,w)
---------------------------------
Эта функция создает окно, но не отображает его. (Для показа
окна воспользуйтесь функцией display_window). Параметры x и y
являются координатами верхнего левого угла окна. Эти параметры
выражаются в символьных позициях экрана, где координаты левого
верхнего угла самого экрана равны (0,0). Параметры h и w являются
высотой и шириной окна в символьных позициях. Эта функция не
вызовет никакого изменения экрана. Если вы создадите окно,
позиция и размеры которого не позволяют ему быть полностью
видимым, программное обеспечение установит позицию, при которой
оно полностью поместится на экране. Если ширина больше 80 или
высота больше 25, функция преобразует соответствующий размер до
максимально допустимого значения.
Окно создается с умалчиваемыми атрибутами. Его рамка
образуется одинарными линиями, цвет содержимого - ярко-белый на
черном фоне, а заголовок отсутствует. Эти атрибуты могут быть
изменены соответствующими вызовами других функций, описанных
ниже.
Окна располагаются в обратном порядке по отношению к их
созданию. Окно, созданное самым последним, является верхним окном
экрана и будет (при выдаче) закрывать окна, созданные ранее. Эта
иерархия зависит от порядка, в котором окна образуются, а не от
порядка их показа.
Функция establish_window возвращает указатель на структуру
WINDOW, которая определена во включаемом файле twindow.h. Этот
указатель используется при последующих вызовах оконных функций
для идентификации окна, к которому относится вызов. Передавайте
указатель NULL другим оконным функциям, если хотите работать с
окном, образованным последним.
void set_border(WINDOW *wnd, int btype)
---------------------------------------
Эта функция устанавливает тип оконной рамки. Целочисленный
параметр btype должен принимать одно из следующих значений:
. 0 - одинарные линии (по умолчанию);
. 1 - двойные линии;
. 2 - одинарные верх и низ, двойные боковые;
. 3 - двойные верх и низ, одинарные боковые;
. 4 - специальное проталкиваемое вниз окно меню с одинарными
линиями и t-блоком от верхнего левого до верхнего
правого угла.
void set_colors(WINDOW *wnd, int area, int bg, int fg, int inten)
-----------------------------------------------------------------
Эта функция устанавливает цвета для окна. Параметр area
может принимать следующие значения:
. ALL
. BORDER
. TITLE
. ACCENT
. NORMAL
Этот параметр определяет части окна, на которые
распространяется действие. ALL соответствует всем частям. BORDER
устанавливает цвета рамки окна, которая занимает односимвольный
ряд позиций вокруг него. TITLE устанавливает цвета заголовка,
размещаемого на верхней границе окна. ACCENT - это область,
используемая для блоков меню и другого выделенного текста,
который появляется в качестве временно подсвечиваемых частей
области NORMAL, где отображается весь текст. Целые числа bg и
fg задают цвета для фона и переднего плана различных частей
окна. Допускаются следующие цвета:
. RED - алый;
. GREEN - зеленый;
. BLUE - голубой;
. WHITE - белый;
. YELLOW - желтый;
. AQUA - аквамариновый;
. MAGENTA - красный;
. BLACK - черный.
Целое число inten определяет интенсивность символов
переднего плана и может принимать два значения:
. BRIGHT - яркая
. DIM - обычная
Все значения определены в исходном файле twindow.h, который
рассматривается позже в этой главе.
void set_title(WINDOW *wnd, char *title)
----------------------------------------
Эта функция устанавливает значение заголовка окна.
Передаваемая вами строка должна сохраняться в течение всего
существования окна, поэтому используйте литеральную константу или
внешний массив символов.
void set_intensity(WINDOW *wnd, int inten)
------------------------------------------
Эта функция устанавливает интенсивность фона для всех частей
окна. Значениями inten могут быть BRIGHT или DIM.
void display_window(WINDOW *wnd)
--------------------------------
Эта функция отображает окно, которое было ранее создано.
Чтобы избежать неудачного отображения, вызывайте эту функцию
после установки всех атрибутов и, если возможно, после того, как
окно заполнено текстом, который оно должно выдавать.
void delete_window(WINDOW *wnd)
-------------------------------
Эта функция уничтожает созданное ранее окно. Если окно
является видимым (выдано с помощью display_window), то оно
очищается, и экран восстанавливается к его предыдущему образу.
void clear_window(WINDOW *wnd)
------------------------------
Эта функция заполняет текстовую область окна пробелами.
void hide_window(WINDOW *wnd)
-----------------------------
Эта функция очищает выданное окно, восстанавливая на экране
его предыдущее содержимое. Окно остается существующим и может
модифицироваться любым способом. Последующий вызов display_window
восстановит его на экране в соответствующем расположении
относительно других окон.
void wcursor(WINDOW *wnd, int x, int y)
---------------------------------------
Каждое окно обладает логической позицией курсора, которая
изменяется от 0,0 (верхний левый угол текстовой области окна) до
граничных размеров окна. Эта функция переставляет курсор в окне.
Функции wputehar и wprintf выдают текст относительно данной
позиции курсора. Они изменяют позицию курсора точно также, как
это происходило бы, если бы управление курсором осуществлялось с
помощью клавиатуры дисплея. Символ новой строки (\n)
устанавливает курсор в нулевой столбец следующей строки,
перемещая текст в окне вверх на одну строку, если курсор уже
находится в нижней строке окна. Символ табуляции (\t) перемещает
курсор к следующей позиции табуляции в окне. Табуляции
располагаются с интервалом в четыре символа.
void wprintf(WINDOW *wnd, char *fmt, ...)
-----------------------------------------
Эта функция является оконной версией стандартной функции
printf языка Си. Она использует стандартную функцию sprintf для
построения строки, выдаваемой в окно. Убедитесь, что
результирующая строка не длиннее 100 символов, или измените длину
в массиве dlin функции wprintf в исходном файле twindow.c.
void wputchar(WINDOW *wnd, int ch)
----------------------------------
Эта функция является оконной версией putchar. Она записывает
символ из ch в окно в текущую позицию курсора. Курсор при этом
продвигается дальше. Если символ является символом новой строки
(\n), курсор переставляется в нулевую позицию следующей строки.
Если символ является табуляцией (\t), курсор продвигается к
следующей позиции табуляции окна. Оконные позиции табуляции
располагаются в каждой четвертой позиции окна.
void reverse_video(WINDOW *wnd)
-------------------------------
После обращения к этой функции все вызовы wprintf и wputchar
будут при отображении использовать цвета ACCENT вместо NORMAL.
void normal_video(WINDOW *wnd)
------------------------------
После обращения к этой функции все вызовы wprintf и wputchar
будут при отображении использовать цвета NORMAL. Эта функция
используется для возврата к нормальному отображению после вызова
reverse_video.
void close_all()
----------------
Эта функция уничтожает все образованные окна.
void move_windom(WINDOW *wnd, int x, int y)
-------------------------------------------
Эта функция перемещает окно таким образом, что его верхний
левый угол устанавливается в символьных координатах, заданных x и
у. Эта функция может быть использована только для слоеных окон.
void rmove_window(WINDOW *wnd, int x, int y)
--------------------------------------------
Эта функция перемещает окно путем добавления к значениям x и
y текущих координат левого верхнего угла окна. Используйте эту
функцию только для слоеных окон.
void forefront(WINDOW *wnd)
---------------------------
Эта функция перемещает окно в самое переднее положение
относительно других окон. Окно, если оно видимо, отображается
поверх остальных. Используйте эту функцию только для слоеных
окон.
void rear_window(WINDOW *wnd)
-----------------------------
Эта функция перемещает окно в самое заднее положение
относительно других окон. Окно, если оно видимо, отображается под
всеми остальными. Используйте эту функцию только для слоеных
окон.
int get_selection(WINDOW *wnd, int sel, char *keys)
---------------------------------------------------
Эта функция позволяет использовать окно в качестве меню. Вы
должны создать окно и записать в него несколько строк текста,
скажем, функцией wprintf. Вы можете использовать set_colors для
установки значений цветов ACCENT в окне (по умолчанию - черные
буквы на белом фоне). Затем вы вызываете get_selection. Функция
использует окно в качестве меню, каждая строка которого
представляет альтернативу выбора. Альтернативы подсвечиваются
цветом ACCENT. Целочисленное значение sel используется для
первоначального размещения яркого блока курсора меню на одной из
альтернатив. Значение 1 соответствует первой альтернативе, 2 -
второй и т.д.
Пользователь может перемещать блок меню вверх и вниз
клавишами управления курсором и производить выбор в меню клавишей
<Ввод>. Клавиша <Ключ> применяется для выхода из этого процесса.
Cсылка keys указывает на строку значений клавиш, которые
могут использоваться для выбора из меню. Некоторые системы меню
разрешают пользователю применять нажатия клавиш наряду с
перемещением блока курсора. Для выключения этой возможности
передайте параметру keys ссылку NULL.
Эта функция возвращает целочисленное значение, равное номеру
выбранной в меню альтернативы. Значение обычно равняется единице
и более, однако, если пользователь нажимает клавишу <Ключ>,
функция возвращает нуль. Функция будет также возвращать значения
FWD или BS, если пользователь нажимает клавиши управления
курсором вправо или влево. Эти значения определены в keys.h.
void error_message(char *s)
---------------------------
Эта функция выдает сообщение об ошибке, указываемое ссылкой
s, и включает звуковой сигнал. Сообщение отображается в
окне в нижнем правом квадранте экрана. Сообщение остается на
экране после завершения функции.
void clear_message()
--------------------
Эта функция удаляет сообщение об ошибке, если оно было
выдано функцией error_message.
Листинги оконных функций
-----------------------------------------------------------------
Данные листинги содержат исходные файлы функций поддержки
окна. Приводятся два листинга: twindow.h и twindow.c. Каждый
листинг сопровождается описанием его содержания.
Исходный листинг: twindow.h
---------------------------
Листинг 6.1, twindow.h, определяет оконные структуры и
содержит прототипы для функций. Вы должны включать их в любую
исходную программу, которая использует оконные функции.
Листинг 6.1: twindow.h
/* twindow.h */
/* Выделите это определение из комментария для стековых окон,
* но не для слоеных окон.
*
* #define FASTWINDOWS
*
*/
/* window colors */
#define RED 4
#define GREEN 2
#define BLUE 1
#define WHITE (RED+GREEN+BLUE)
#define YELLOW (RED+GREEN)
#define AQUA (GREEN+BLUE)
#define MAGENTA (RED+BLUE)
#define BLACK 0
#define BRIGHT 8
#define DIM 0
#define BORDER 0
#define TITLE 1
#define ACCENT 2
#define NORMAL 3
#define ALL 4
#define TRUE 1
#define FALSE 0
#define ERROR -1
#define OK 0
/* оконные управляющие структуры */
typedef struct field { /* описание поля ввода данных */
char *fmask; /* маска поля ввода данных */
int fprot; /* защита поля */
char *fbuff; /* буфер поля */
int ftype; /* тип поля */
int from; /* строка поля */
int fcol; /* столбец поля */
void (*fhelp)(); /* функция подсказки поля */
char *fhwin; /* функция подсказки окна */
int flx, fly; /* расположение подсказки окна */
int (*fvalid)(); /* функция заполнения поля */
struct field *fnxt; /* следующее поле выдачи */
struct field *fprv; /* предыдущее поле выдачи */
} FIELD;
typedef struct _wnd {
int _wv; /* истина, если окно видимо */
int _hd; /* истина, если окно скрыто */
char *_ws; /* указывает на блок сохранения окна */
char *_tl; /* указывает на заголовок окна */
int _wx; /* nv x координата */
int _wy; /* nv y координата */
int _ww; /* ширина окна */
int _wh; /* высота окна */
int _wsp; /* указатель прокрутки */
int _sp; /* указатель выбора */
int _cr; /* позиция х курсора */
int btype; /* тип рамки */
int wcolor[4]; /* цвета окна */
int _pn; /* предыдущий нормальный цвет */
struct _wnd *_nx; /* указывает на следующее окно */
struct _wnd *_pv; /* указывает на предыдущее окно */
FIELD *_fh; /* указывает на 1-е поле ввода данных */
FIELD *_ft; /* указывает на последнее поле ввода данных */
} WINDOW;
typedef struct w_menu {
char *mname;
char **mselcs;
void (**func)();
} MENU;
#define SAV (wnd->_ws)
#define WTITLE (wnd->_tl)
#define COL (wnd->_wx)
#define ROW (wnd->_wy)
#define WIDTH (wnd->_ww)
#define HEIGHT (wnd->_wh)
#define SCROLL (wnd->_wsp)
#define SELECT (wnd->_sp)
#define WCURS (wnd->_cr)
#define WBORDER (wnd->wcolor[BORDER])
#define WTITLEC (wnd->wcolor[TITLE])
#define WACCENT (wnd->wcolor[ACCENT])
#define WNORMAL (wnd->wcolor[NORMAL])
#define PNORMAL (wnd->_pn)
#define BTYPE (wnd->btype)
#define NEXT (wnd->_nx)
#define PREV (wnd->_pv)
#define WCOLOR (wnd->wcolor)
#define VISIBLE (wnd->_wv)
#define HIDDEN (wnd->_hd)
#define FHEAD (wnd->_fh)
#define FTAIL (wnd->_ft)
#define NW (wcs[wnd->btype].nw)
#define NE (wcs[wnd->btype].ne)
#define SE (wcs[wnd->btype].se)
#define SW (wcs[wnd->btype].sw)
#define SIDE (wcs[wnd->btype].side)
#define LINE (wcs[wnd->btype].line)
/* ПРОТОТИПЫ ФУНКЦИЙ И МАКРОСЫ */
/* общецелевые функции и макросы */
void clear_screen(void);
int vmode(void);
void cursor(int, int);
void curr_cursor(int *, int *);
int cursor_type(void);
void set_cursor_type(int);
int get_char(void);
int scroll_lock(void);
void vpoke(unsigned, unsigned, unsigned);
int vpeek(unsigned, unsigned);
/* оконные функции и макросы */
WINDOW *establish_window(int, int, int, int);
void set_border(WINDOW *, int);
void set_colors(WINDOW *, int, int, int, int);
void set_intensity(WINDOW *, int);
void set_title(WINDOW *, char *);
void display_window(WINDOW *);
void delete_window(WINDOW *);
void clear_window(WINDOW *);
void hide_window(WINDOW *);
void wprintf(WINDOW *, char *, ...);
void wputchar(WINDOW *, int);
void close_all(void);
void wcursor(WINDOW *, int x, int y);
void error_message(char *);
void clear_message(void);
int get_selection(WINDOW *, int, char *);
#define reverse_video(wnd) wnd->wcolor[3]=wnd->wcolor[2]
#define normal_video(wnd) wnd->wcolor[3]=wnd->_pn
#define rmove_window(wnd,x,y) repos_wnd(wnd, x, y, 0)
#define move_window(wnd,x,y) repos_wnd(wnd, COL-x, ROW-y, 0)
#define forefront(wnd) repos_wnd(wnd, 0, 0, 1)
#define rear_window(wnd) repos_wnd(wnd, 0, 0, -1)
/* внутренние для оконных процессов */
void accent(WINDOW *);
void deaccent(WINDOW *);
void scroll(WINDOW *, int);
void repos_wnd(WINDOW *, int, int, int);
void acline(WINDOW *, int);
#define accent(wnd) acline(wnd, WACCENT)
#define deaccent(wnd) acline(wnd, WNORMAL)
#define clr(bg,fg,in) ((fg)|(bg<<4)|(in))
#define vad(x,y) ((y)*160+(x)*2)
#ifdef FASTWINDOWS
#define cht(ch,at) (((ch)&255)|((at)<<8))
#define displ(w,x,y,c,a) vpoke(VSG,vad(x+COL,y+ROW),cht(c,a))
#define dget(w,x,y) vpeek(VSG,vad(x+COL,y+ROW))
#define verify_wnd(w) (*(w)=listtail)!=0
#else
void displ(WINDOW *wnd, int x, int y, int ch, int at);
#endif
/* функция редактора */
void text_editor(WINDOW *, char *, unsigned);
/* функция меню */
void menu_select(char *name, MENU *mn);
/* функция подсказки */
void load_help(char *);
void set_help(char *, int, int);
/* функция ввода данных */
void init_template(WINDOW *);
FIELD *establish_field(WINDOW *, int, int, char *, char *, int);
void clear_template(WINDOW *);
void field_tally(WINDOW *);
int data_entry(WINDOW *);
void wprompt(WINDOW *, int, int, char *);
void error_message(char *);
void clear_notice(void);
void field_window(FIELD *, char *, int, int);
#define field_protect(f,s) f->fprot=s
#define field_help(f,h) f->fhelp=h
#define field_validate(f,v) f->fvalid=v
Описание программы: twindow.h
-----------------------------------------------------------------
Глобальная переменная FASTWINDOWS определена внутри
комментария в представленной программе. Включение переменной
рассчитано на применение стековой оконной конфигурации. Без
изменений при компиляции будет принята слоеная оконная
конфигурация. Для компиляции стековой оконной системы необходимо
выделить оператор #define из комментария.
Структура FIELD используется для определения полей ввода
данных внутри области данных в окнах. Этот процесс описан в
Главе 8.
Структура WINDOW описывает окно для системы . Каждому окну
назначается одна структура этого типа.
Структура MENU используется программным обеспечением
оконных меню в Главе 10. Должен быть массив структур MENU с
одним элементом для каждого проталкиваемого вниз меню.
Список операторов #define используется для придания
операторам в twindow.c лучшей читаемости. Мнемонические имена
соответствуют элементам структуры WINDOW, указанной ссылкой
wnd. Все функции в twindow.c используют имя этой ссылки по
соглашению.
twindow.h содержит прототипы для всех оконных функций,
которые будут вызываться прикладными программами.
Исходный листинг: twindow.c.
----------------------------
Листинг 6.2 - это twindow.c. Он содержит все описанные
ранее в этой главе функции. Вы должны откомпилировать его и
связывать его объектный модуль с любой программой, которая
использует окна. Поскольку он вызывает функции из ibmpc.c, его
объектный модуль должен быть также включен в редактирование
связей.
Листинг 6-2: twindow.c
/* twindow.c */
#include
#include
#include
#include
#include
#include
#include
#include "twindow.h"
#include "keys.h"
#define TABS 4
#define SCRNHT 25
#define SCRNWIDTH 80
#define ON 1
#define OFF 0
#define ERROR -1
/* локальные прототипы */
redraw(WINDOW *wnd);
wframe(WINDOW *wnd);
dtitle(WINDOW *wnd);
int *waddr(WINDOW *wnd, int x, int y);
vswap(WINDOW *wnd);
vsave(WINDOW *wnd);
vrstr(WINDOW *wnd);
add_list(WINDOW *wnd);
beg_list(WINDOW *wnd);
remove_list(WINDOW *wnd);
insert_list(WINDOW *wl, WINDOW *w2);
#ifndef FASTWINDOWS
int dget(WINDOW *wnd, int x, int y);
verify_wnd(WINDOW **wl);
#endif
/* массив наборов символов рамки */
struct {
int nw, ne, se, sw, side, line;
} wcs[] = {
{218,191,217,192,179,196}, /* одинарная линия */
{201,187,188,200,186,205}, /* двойная линия */
{214,183,189,211,186,196}, /* одинарный верх, двойные бока */
{213,184,190,212,179,205}, /* двойной верх, одинарные бока */
{194,194,217,192,179,196}, /* выталкиваемое вниз меню */
};
/* голова и хвост связанного списка оконных структур */
WINDOW *listhead = NULL;
WINDOW *listtail = NULL;
int VSG; /* адрес видеосегмента */
/* создание нового окна */
WINDOW *establish_window(x, y, h, w)
{
WINDOW *wnd;
VSG = (vmode() == 7 ? 0xb000 : 0xb800);
if ((wnd = (WINDOW *) malloc(sizeof (WINDOW))) == NULL)
return NULL;
/* параметры ограничений */
WTITLE = "";
HEIGHT = min(h, SCRNHT);
WIDTH = min(w, SCRNWIDTH);
COL = max(0, min(x, SCRNWIDTH-WIDTH));
ROW = max(0, min(y, SCRNHT-HEIGHT));
WCURS = 0;
SCROLL = 0;
SELECT = 1;
BTYPE = 0;
VISIBLE = HIDDEN = 0;
PREV = NEXT = NULL;
FHEAD = FTAIL = NULL;
WBORDER=WNORMAL=PNORMAL=WTITLEC =
clr(BLACK, WHITE, BRIGHT);
WACCENT = clr(WHITE, BLACK, DIM);
if ((SAV = malloc(WIDTH * HEIGHT * 2)) == (char *) 0)
return NULL;
add_list(wnd);
#ifndef FASTWINDOWS
clear_window(wnd);
wframe(wnd);
#endif
return wnd;
}
/* установить рамку окна */
void set_border(WINDOW *wnd, int btype)
{
if (verify_wnd(&wnd)) {
BTYPE = btype;
redraw(wnd);
}
}
/* установить цвета */
void set_colors(WINDOW *wnd,int area, int bg, int fg, int inten)
{
if (vmode() == 7) {
if (bg != WHITE && bg != BLACK)
return;
if (fg != WHITE && fg != BLACK)
return;
}
if (verify_wnd(&wnd)) {
if (area == ALL)
while (area)
WCOLOR [--area] = clr(bg, fg, inten);
else
WCOLOR [area] = clr(bg, fg, inten);
redraw(wnd);
}
}
/* установить яркость окна */
void set_intensity(WINDOW *wnd, int inten)
{
int area = ALL;
if (verify_wnd(&wnd)) {
while (area) {
WCOLOR [--area] &= ~BRIGHT;
WCOLOR [area] |= inten;
}
redraw(wnd);
}
}
/* установить заголовок */
void set_title(WINDOW *wnd, char *title)
{
if (verify_wnd(&wnd)) {
WTITLE = title;
redraw(wnd);
}
}
/* перевыдать окно при изменении атрибута */
static redraw(WINDOW *wnd)
{
#ifndef FASTWINDOWS
int x, y, chat, atr;
for (y = 1; y < HEIGHT-1; y++)
for (x = 1; x < WIDTH-1; x++) {
chat = dget(wnd, x, y);
atr = (((chat>>8)&255) ==
PNORMAL ? WNORMAL : WACCENT);
displ(wnd, x, y, chat&255, atr);
}
wframe(wnd);
#endif
PNORMAL = WNORMAL;
}
/* выдать созданное окно */
void display_window(WINDOW *wnd)
{
if (verify_wnd(&wnd) && !VISIBLE) {
VISIBLE = 1;
#ifdef FASTWINDOWS
if (HIDDEN) {
HIDDEN = 0;
vrstr(wnd);
}
else {
vsave(wnd);
clear_window(wnd);
wframe(wnd);
}
#else
vswap(wnd);
#endif
}
}
/* закрыть все окна */
void close_all()
{
WINDOW *sav, *wnd = listtail;
while (wnd) {
sav = PREV;
delete_window(wnd);
wnd = sav;
}
}
/* удалить окно */
void delete_window(WINDOW *wnd)
{
if (verify_wnd(&wnd)) {
hide_window(wnd);
free(SAV);
remove_list(wnd); /* удалить окно из списка */
free(wnd);
}
}
/* скрыть окно */
void hide_window(WINDOW *wnd)
{
if (verify_wnd(&wnd) && VISIBLE) {
#ifndef FASTWINDOWS
vswap(wnd);
#else
vrstr(wnd);
#endif
HIDDEN = 1;
VISIBLE = 0;
}
}
#ifndef FASTWINDOWS
/* перемещение окна в его 3-х мерном плане */
void repos_wnd(WINDOW *wnd, int x, int y, int z)
{
WINDOW *twnd;
int x1, y1, chat;
if (!verify_wnd(&wnd))
return;
twnd = establish_window(x+COL, y+ROW, HEIGHT, WIDTH);
twnd -> _tl = WTITLE;
twnd -> btype = BTYPE;
twnd -> wcolor[BORDER] = WBORDER;
twnd -> wcolor[TITLE] = WTITLEC;
twnd -> wcolor[ACCENT] = WACCENT;
twnd -> wcolor[NORMAL] = WNORMAL;
twnd -> _wsp = SCROLL;
twnd -> _cr = WCURS;
if (z != 1) {
remove_list(twnd);
if (z == 0)
insert_list(twnd, wnd);
else
beg_list(twnd);
}
for (y1 = 0; y1 < twnd->_wh; y1++)
for (x1 = 0; x1 < twnd->_ww; x1++) {
chat = dget(wnd, x1, y1);
displ(twnd, x1, y1, chat&255, (chat>>8)&255);
}
twnd->_wv = 1;
vswap(twnd);
hide_window(wnd);
free(SAV);
remove_list(wnd);
*wnd = *twnd;
insert_list(wnd, twnd);
remove_list(twnd);
free(twnd);
}
#endif
/* очистить область окна */
void clear_window(WINDOW *wnd)
{
register int x1, y1;
if (verify_wnd(&wnd))
for (y1 = 1; y1 < HEIGHT-1; y1++)
for (x1 = 1; x1 < WIDTH-1; x1++)
displ(wnd,x1, y1, ' ', WNORMAL);
}
/* изобразить окно */
static wframe(WINDOW *wnd)
{
register int x1, y1;
if (!verify_wnd(&wnd))
return;
/* заголовок окна */
displ(wnd,0, 0, NW, WBORDER);
dtitle(wnd);
displ(wnd,WIDTH-1, 0, NE, WBORDER);
/* боковые стороны окна */
for (y1 = 1; y1 < HEIGHT-1; y1++) {
displ(wnd,0, y1, SIDE, WBORDER);
displ(wnd,WIDTH-1, y1, SIDE, WBORDER);
}
/* низ окна */
displ(wnd,0, y1, SW, WBORDER);
for (x1 = 1; x1 < WIDTH-1; x1++)
displ(wnd,x1, y1, LINE, WBORDER);
displ(wnd,x1, y1, SE, WBORDER);
}
/* выдать заголовок окна */
static dtitle(WINDOW *wnd)
{
int x1 = 1, i, ln;
char *s = WTITLE;
if (!verify_wnd(&wnd))
return;
if (s) {
ln = strlen(s);
if (ln > WIDTH-2)
i = 0;
else
i = ((WIDTH-2-ln) / 2);
if (i > 0)
while (i--)
displ(wnd, x1++, 0, LINE, WBORDER);
while (*s && x1 < WIDTH-1)
displ(wnd, x1++, 0, *s++, WTITLEC);
}
while (x1 < WIDTH-1)
displ(wnd, x1++, 0, LINE, WBORDER);
}
/* оконно-ориентированная printf */
void wprintf(WINDOW *wnd, char *ln, ...)
{
char dlin [100], *dl = dlin;
if (verify_wnd(&wnd)) {
va_list ap;
va_start(ap, ln);
vsprintf(dlin, ln, ap);
va_end(ap);
while (*dl)
wputchar(wnd, *dl++);
}
}
/* записать символ в окно */
void wputchar(WINDOW *wnd, int c)
{
if (!verify_wnd(&wnd))
return;
switch (c) {
case '\n':
if (SCROLL == HEIGHT-3)
scroll(wnd, UP);
else
SCROLL++;
WCURS = 0;
break;
case '\t':
do displ(wnd,(WCURS++)+3,SCROLL+1,'',WNORMAL);
while ((WCURS%TABS) && (WCURS+1) < WIDTH-1);
break;
default: ;
if ((WCURS+1) < WIDTH-1) {
displ(wnd, WCURS+1, SCROLL+1, c, WNORMAL);
WCURS++;
}
break;
}
}
/* установить курсор окна */
void wcursor(WINDOW *wnd, int x, int y)
{
if (verify_wnd(&wnd) && x < WIDTH-1 && y < HEIGHT-1) {
WCURS = x;
SCROLL = y;
cursor(COL+x+1, ROW+y+1);
}
}
/* позволяет пользователю произвести оконный выбор */
int get_selections(WINDOW *wnd, int s, char *keys)
{
int c = 0, ky;
if (!verify_wnd(&wnd))
return 0;
SELECT = s;
while (c != ESC && c != '\r' && c != BS && c != FWD) {
accent(wnd);
c = get_char();
deaccent(wnd);
switch (c) {
case UP: if (SELECT > 1)
SELECT--;
else
SELECT = SCROLL+1;
break;
case DN: if (SELECT < SCROLL+1)
SELECT++;
else
SELECT = 1;
break;
case '\r':
case ESC:
case FWD:
case BS: break;
default: if (keys) {
ky = 0;
while (*(keys + ky)) {
if (*(keys+ky)==toupper(c) ||
*(keys+ky)==tolower(c))
return ky + 1;
ky++;
}
}
break;
}
}
return c == '\r' ? SELECT : c == ESC ? 0 : c;
}
union REGS rg;
/* прокручивает содержимое окна вверх или вниз */
void scroll(WINDOW *wnd, int dir)
{
int row = HEIGHT-1, col, chat;
if (!verify_wnd(&wnd))
return;
if (NEXT == NULL && HEIGHT > 3 && VISIBLE) {
rg.h.ah = dir == UP ? 6 : 7;
rg.h.al = 1;
rg.h.bh = WNORMAL;
rg.h.cl = COL + 1;
rg.h.ch = ROW + 1;
rg.h.dl = COL + WIDTH - 2;
rg.h.dh = ROW + HEIGHT - 2;
int86(16, &rg, &rg);
return;
}
if (dir == UP) {
for (row = 2; row < HEIGHT-1; row++)
for (col = 1; col < WIDTH-1; col++) {
chat = dget(wnd, col, row);
displ(wnd,col,row-1,chat&255,(chat>>8)&255);
}
for (col = 1; col < WIDTH-1; col++)
displ(wnd, col, row-1, ' ', WNORMAL);
}
else {
for (row = HEIGHT-2; row > 1; --row)
for (col = 1; col < WIDTH-1; col++) {
chat = dget(wnd, col, row-1);
displ(wnd,col,row,chat&255,(chat>>8)&255);
}
for (col = 1; col < WIDTH-1; col++)
displ(wnd, col, row, '', WNORMAL);
}
}
#ifndef FASTWINDOWS
/* вычисляет абрис отображаемого символа окна */
static int *waddr(WINDOW *wnd, int x, int y)
{
WINDOW *nxt = NEXT;
int *vp;
if (!VISIBLE)
return (int *) (SAV+y*(WIDTH*2)+x*2);
x += COL;
y += ROW;
while (nxt) {
if (nxt->_wv)
if (x >= nxt->_wx && x <= nxt->_wx + nxt->_ww-1)
if (y >= nxt->_wy &&
y <= nxt->_wy + nxt->_wh-1) {
x -= nxt->_wx;
y -= nxt->_wy;
vp = (int *)
((nxt->_ws) +y*(nxt->_ww*2)+x*2);
return vp;
}
nxt = nxt->_nx;
}
return NULL;
}
/* выдать символ в окно */
void displ(WINDOW *wnd, int x, int y, int ch, int at)
{
int *vp;
int vch = (ch&255)|(at<<8);
if ((vp = waddr(wnd, x, y)) != NULL)
*vp = vch;
else
vpoke(VSG,vad(x+COL,y+ROW),vch);
}
/* получить отображенный символ из окна */
static int dget(WINDOW *wnd, int x, int y)
{
int *vp;
if ((vp = waddr(wnd, x, y)) != NULL)
return *vp;
return vpeek(VSG,vad(x+COL,y+ROW));
}
/* видеофункции низкого уровня */
/* обменивает содержимое видеообраза и буфера сохранения */
static vswap(WINDOW *wnd)
{
int x, y, chat;
int *bf = (int *) SAV;
for (y = 0; y < HEIGHT; y++)
for (x = 0; x < WIDTH; x++) {
chat = *bf;
*bf++ = dget(wnd, x, y);
displ(wnd, x, y, chat&255, (chat>>8)&255);
}
}
#else
/* сохраняет видеопамять в буфере сохранения */
static vsave(WINDOW *wnd)
{
int x, y;
int *bf = (int *) SAV;
for (y = 0; y < HEIGHT; y++)
for (x = 0; x < WIDTH; x++)
*bf++ = vpeek(VSG, vad(x+COL, y+ROW));
}
/* восстанавливает видеопамять из буфера сохранения */
static vrstr(WINDOW *wnd)
{
int x, y;
int *bf = (int *) SAV;
for (y = 0; y < HEIGHT; y++)
for (x = 0; x < WIDTH; x++)
vpoke(VSG,vad(x+COL, y+ROW), *bf++);
}
#endif
/* заменяет яркость строки, указываемой SELECT */
void acline(WINDOW *wnd, int set)
{
int x, ch;
if (!verify_wnd(&wnd))
return;
for (x = 1; x < WIDTH - 1; x++) {
ch = dget(wnd, x, SELECT) & 255;
displ(wnd, x, SELECT, ch, set);
}
}
/* ФУНКЦИИ ОБРАБОТКИ СПИСКА */
/* добавляет окно в конец списка */
static add_list(WINDOW *wnd)
{
if (listtail) {
PREV = listtail;
listtail->_nx = wnd;
}
listtail = wnd;
if (!listhead)
listhead = wnd;
}
/* добавляет окно в начало списка */
static beg_list(WINDOW *wnd)
{
if (listhead) {
NEXT = listhead;
listhead->_pv = wnd;
}
listhead = wnd;
if (!listtail)
listtail = wnd;
}
/* удаляет окно из списка */
static remove_list(WINDOW *wnd)
{
if (NEXT)
NEXT->_pv = PREV;
if (PREV)
PREV->_nx = NEXT;
if (listhead == wnd)
listhead = NEXT;
if (listtail == wnd)
listtail = PREV;
NEXT = PREV = NULL;
}
/* вставляет w 1 после w 2 */
static insert_list(WINDOW *w1, WINDOW *w2)
{
w1->_pv = w2;
w1->_nx = w2->_nx;
w2->_nx = w1;
if (w1->_nx == NULL)
listtail = w1;
else
w1->_nx->_pv = w1;
}
#ifndef FASTWINDOWS
/* проверяет наличие окна в списке */
static verify_wnd(WINDOW **w1)
{
WINDOW *wnd;
wnd = listhead;
if (*w1 == NULL)
*w1 = listtail;
else {
while (wnd != NULL) {
if (*w1 == wnd)
break;
wnd = NEXT;
}
}
return wnd != NULL;
}
#endif
WINDOW *ewnd = NULL;
/* сообщение об ошибках */
void error_message(char *s)
{
ewnd = establish_window(50, 22, 3, max(10, strlen(s)+2));
set_colors(ewnd, ALL, RED, YELLOW, BRIGHT);
set_title(ewnd, " ERROR! ");
display_window(ewnd);
wprintf(ewnd, s);
putchar(BELL);
}
void clear_message()
{
if (ewnd)
delete_window(ewnd);
ewnd = NULL;
}
Описание программы: twindow.c
-----------------------------
Далее описывается исходная программа twindow.c. Для каждой
функции описывается, что она делает и как работает. Программист
может использовать эти описания для понимания текста программы.
Объявления внешних данных в twindow.c включают прототипы для
каждой функции, локальные в исходном файле, массив структур для
определения пяти типов рамки окна, головной и хвостовой указатели
для списка структур WINDOW.
Рамка окна управляется элементом структуры WINDOW, которая
устанавливает окно. Этот элемент является целочисленным смещением
в таблице типов рамки. Вход, на который указывает смещение,
содержит шесть значений, каждое из которых представляет одну из
сторон или узлов окна. Первое значение определяет верхний левый
или северо-западный угол. Имя переменной (nw, ne, se, sw)
сообщает вам, какой угол определяется. Целое число side
относится к вертикальным сторонам рамки; целое число line
соответствует верхней и нижней горизонтальным линиям рамки.
Значения относятся к символам из набора графических символов
ПЭВМ.
Две WINDOW-ссылки listhead и listtail являются головным и
хвостовым указателями для списка окон. Когда создаются окна, они
добавляются к этому списку. Первоначально эти два указателя равны
NULL. Когда создается первое окно, выделяется память для
структуры WINDOW, и ее адрес копируется в оба указателя. У списка
имеется голова, указывающая на первое окно списка, и хвост,
указывающий на последнее. Когда создается второе окно, его адрес
копируется в хвостовой указатель. Кроме того, адрес второго окна
записывается в указатель _nx в первой структуре WINDOW, а адрес
первой записывается в указатель _pv второй. Голова списка
указывает на первое окно, которое указывает на второе и т.д.
Хвост списка указывает на последнее окно. Каждое окно также
указывает на своего предшественника в цепи, следовательно, список
является двунаправленной структурой данных, называемой двусвязным
списком. (Для знакомства со списковыми структурами данных см.:
Brady. С Development Tools for the IBM PC. - 1986.).
Функция establish_window инициализирует переменную VSG
адресом сегмента видеопамяти. Функция распределяет память для
структуры WINDOW и инициализирует эту структуру оконными
характеристиками, принимаемыми по умолчанию, а также размером и
координатами, заданными при вызове функции. Она выделяет память
для буфера сохранения видеопамяти и записывает адрес буфера в
структуру WINDOW. После инициализации структуры функция вызывает
add_list для добавления структуры к списку окон. Текстовая
область окна очищается, и образ окна выделяется, если
обрабатываются слоеные окна. Эти функции оперируют в буфере
сохранения, поэтому окно пока не изображается. Функция establish_
window возвращает адрес структуры WINDOW в точку вызова.
Функции set_border, set_colors, set_intensity и set_title
модифицируют характеристики созданного окна. Сначала они вызывают
verify_wnd для проверки того, что при вызове передан адрес
созданного окна. Затем они модифицируют заданный атрибут. В конце
они вызывают функцию redraw для записи изменений на экран.
Функция redraw перевыдает окно, если обрабатываются слоеные
окна.
Функция display_window оперирует по-разному для стековых и
слоеных окон. В любом случае она ничего не делает, если окно
является видимым для пользователя. Если окно невидимо, то путем
вызова функции vswap display_window замещает видеопамять
буфером сохранения, если действуют слоеные окна. Для стековых
окон делается проверка, не скрыто ли окно. Если окно скрыто, то
оконный буфер сохранения записывается в видеопамять вызовом
vrstr. Если окно не скрыто, то оно никогда не выдается, поэтому
вызывается vsave для сохранения текущего содержимого
видеопамяти, a clear_window и wframe вызываются для выдачи
пустого окна.
Функция close_all уничтожает все окна путем прохода по
списку структур WINDOW и вызова delete_window.
Функция delete_window удаляет окно из системы путем его
скрытия, освобождения памяти, занятой буфером сохранения,
удаления структуры WINDOW из списка и освобождения памяти,
содержащей структуру WINDOW.
Функция hide_window вызывает vswap для замены буфера
сохранения видеопамятью для слоеного окна и вызова vrstr для
восстановления видеопамяти для стекового окна.
Функция repos_window имеется только для слоеных окон. Она
вызывается одним из макросов move_window, rmove_window,
rear_window и forefront. Она изменяет положение окна путем
создания временного окна, помещая временное окно в список в
соответствии с информацией, полученной из макроса, записывая
оригинальное содержимое окна в буфер сохранения временного окна,
выдавая временное окно и скрывая оригинал.
Crear_window записывает пробелы в область данных окна, а
wframe и dtitle изображают окно с заголовком наверху. Эти функции
используют функцию displ для записи значений в окно.
Функция wprintf является примером нового предполагаемого
стандарта ANSI для функций с переменным числом параметров. В
прошлом большинство компиляторов обрабатывали printf на
ассемблере для просмотра переменного числа параметров из стека.
Предполагаемый стандарт использует многоточие (...) в списке
параметров функции для указания присутствия переменного числа
параметров с различными типами данных. Специальный тип массива
va_list используется для объявления списка, а va_start
устанавливает начало и конец списка. Функция vsprintf является
версией sprintf, которая допускает параметр va_list. В данном
случае параметры, передаваемые в wprintf,
перерабатываются vsprintf в строку с именем dlin. Затем строка
выдается в окно по одному символу за раз путем вызова wputchar.
Если у вас получится вызов wprintf, который образует строку
более 100 символов, придется увеличить длину массива dlin.
Функция wputchar выдает символ в окно в текущей позиции
курсора. Расположение оконного курсора является функцией двух
элементов структуры WINDOW, которые указываются макросами WCURS
(столбец) и SCROLL (строка). Функция реагирует на символы
новой строки (\n) и табуляции (\t) следующим образом. Для новой
строки, если переменная SCROLL соответствует низу окна,
содержимое окна проворачивается вверх на одну строку; иначе
значение переменной SCROLL увеличивается. В любом случае
переменная WCURS устанавливается на столбец 0. Если в
wputchar послан символ табуляции, переменная WCURS
продвигается к следующей позиции табуляции в окне. Остальные
символы отображаются в окне, а переменная WCURS возрастает.
Строки, длина которых превышает ширину окна, не переносятся;
они обрезаются.
Функция wcursor устанавливает переменные WCURS и SCROLL на
координаты, заданные при вызове. Она также устанавливает
видеокурсор на экранную позицию, соответствующую оконному
курсору.
Функция get_selection создает блок курсора в окне и
позволяет пользователю перемещать блок вверх и вниз, а также
производить выбор нажатием клавиши <Ввод>. Макроопределение
SELECT ссылается на переменную в структуре WINDOW и используется
для перемещения блока курсора в окне. Функции accent и deaccent
используются для включения и выключения блока курсора путем
изменения видеоатрибута строки на ACCENT и NORMAL. При нажатии
верхней и нижней клавиш со стрелками функция изменяет значение
переменной SELECT. При вызове можно также передать адрес массива
символов, содержащего список клавиш, используемых для выбора из
окна. Если пользователь нажимает одну из этих клавиш,
производится соответствующий выбор так же, как если бы блок
курсора находился в соответствующей строке и была нажата клавиша
<Ввод>.
Функция scroll проворачивает порцию текста в окне вверх или
вниз на одну строку. Если окно является последним и видимым,
функция прокрутки ROM-BIOS применяется для ускорения по сравнению
с программной прокруткой. Функция ROM-BIOS не применяется, если
окно имеет только одну строку текста из-за ошибки в IBM PC и
некоторых моделях АТ. Эта ошибка вызывает появление неверных
видеорезультатов при попытке провернуть единственную строку.
Ошибка была устранена IBM в АТ BIOS, но в некоторых моделях она
осталась. Если окно не является последним или если оно имеет одну
строку текста, текстовая область проворачивается программно с
помощью функций dget и displ для чтения и записи символов текста
из окна и в окно.
Функция waddr оперирует только со слоеными окнами. Она
возвращает целочисленный адрес позиции в окне, где расположены
символ и атрибут. Если окно не видимо, функция возвращает адрес в
буфере сохранения, вычисленный по координатам x и y. Если окно
видимо, сканируется список для поиска окон, более поздних, чем
адресуемое окно. Если более позднее окно закрывает позицию
адресуемого символа, возвращается адрес, соответствующий
сохраненному адресу этого окна. Если ни одно более позднее окно
не закрывает позицию символа, возвращается указатель NULL для
сообщения в точку вызова об использовании видеопамяти.
Функция displ и функция dget вызываются для выдачи и приема
видеосимвола и атрибута в и из слоеного окна. Эти функции
вызывают waddr для проверки необходимости чтения или записи в
область сохранения. Если нет, адресуется видеопамять.
Функция wsvap меняет местами содержимое буфера сохранения
слоеного окна и видеопамяти или, возможно, буферов сохранения
более поздних окон, которые закрывают адресуемое окно. Эта
функция использует displ и dget для выполнения изменения.
Функции vsave и vrstr работают со стековыми окнами. Vsave
копирует содержимое видеопамяти в буфер сохранения, а vrstr
копирует буфер сохранения в видеопамять.
Функция acline вызывается макросами accent и deaccent для
изменения выбранной строки окна на цветовую конфигурацию ACCENT
или NORMAL.
Функция add_list добавляет структуру WINDOW в конец списка.
Функция beg_list добавляет структуру WINDOW в начало списка.
Функция remove_list удаляет структуру WINDOW из списка.
Функция iusert_list вставляет структуру WINDOW в список
после другой заданной структуры WINDOW.
Функция verify_wnd ищет в списке заданный адрес структуры
WINDOW. Она возвращает истину или ложь в зависимости от наличия
или отсутствия структуры WINDOW в списке. Если заданный указатель
WINDOW равен NULL, функция возвращает адрес последней структуры
WINDOW в списке.
Функция error_message создает окно для выдачи специального
сообщения об ошибке. Сообщение записывается в окно вызовом
wprintf, и включается звуковой сигнал.
Функция clear_message очищает последнее сообщение об ошибке.
Примеры окон
-----------------------------------------------------------------
Далее рассматриваются возможности оконной библиотеки.
Приводятся примеры программ, каждая из которых иллюстрирует
рассматриваемые возможности. Примеры состоят из небольшой
управляющей программы с главной функцией, которая вызывает
функцию примера для демонстрируемой возможности. Функция примера
содержит вызовы ранее рассмотренных библиотечных функций и служит
иллюстрацией их использования. Каждый пример программы включает
проектный (.prj) файл, используемый Турбо Си для построения
выполняемой программы.
Затем эти же самые примеры функций будут объединены в один
выполняемый модуль, который демонстрирует оконные меню. Поэтому
они написаны без собственных main-функций.
Перемещение окна
-----------------
При использовании слоеных окон вам доступны функции,
позволяющие перемещать окно в абсолютную или относительную
позицию на экране. Заметим, что эти функции - move_window и rmove
_window - недоступны для стековых окон.
Программа, иллюстрирующая перемещение окна, приведена на
листингах 6.3, 6.4 и 6.5. Листинг 6.3 является маленькой
управляющей программой, а листинг 6.5 представляет проектный make
-файл. Обращайтесь к листингу 6.4, testmove.c, при чтении данного
описания.
Для запуска примера введите следующую команду:
c>move
(В этом и последующих примерах предполагается, что С
является вашим системным дисководом. Не вводите подсказку с>.).
Помимо иллюстрации движения окна, testmove.c показывает
также процесс создания окон, установку их цветов, выдачу на экран
и запись текста в них. Программа создает три окна, присваивает
каждому из них собственные цвета, выдает их и записывает цитату
во второе из трех окон. Этот пример иллюстрирует возможность
записи текста в окно, которое частично закрыто другим окном.
После запуска программы вы увидите на дисплее то, что показано на
рисунке 6.4.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ C> ¦
¦ ¦
¦ ЪДДДДДДДДДДДДДДДДДДДДДД¬ ¦
¦ ЪДДДДДД¦ I wouldn't care who ¦ ¦
¦ ¦ ¦ wrote the laws if I ¦ ¦
¦ ¦ ¦ could write the ¦ ¦
¦ ¦ ¦ b ЪДДДДДДДДД¬ ¦ ¦
¦ ¦ ¦ ¦ ¦ferson ¦ ¦
¦ ¦ АДДД¦ ГДДДДДДДДЩ ¦
¦ ¦ ¦ ¦ ¦
¦ АДДДДДДДДДДґ ¦ ¦
¦ АДДДДДДДДДЩ ¦
¦ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 6.4. Перемещение слоеных окон.
Теперь программа ожидает нажатия клавиши. Программа
специально ждет нажатия одной из клавиш управления курсором или
клавиши <Ключ>. Каждое нажатие клавиши со стрелкой вызывает
перемещение окна на одну символьную позицию в направлении
стрелки. Функция rmove_window используется для перемещения окна.
Обратите внимание на то, как центральное окно перемещается между
двумя остальными.
Когда вы нажимаете клавишу <Ключ>, открытое первым окно
уничтожается. Еще одно нажатие вызывает уничтожение верхнего
окна. Заключительное нажатие уничтожает среднее окно с цитатой и
завершает программу.
Перемещение окна иллюстрирует использование оконных буферов
сохранения, запрограммированное в библиотеке. Поскольку требуется
определенная обработка для анализа каждого буфера при записи
символа в окно, работа функций усложняется по мере увеличения
размера окон и их количества. При перемещении большого слоеного
окна на старых медленных моделях ПЭВМ это можно наблюдать
визуально.
Листинг 6.3: move.c
/* move.c */
void testmove(void);
main()
{
testmove();
}
Листинг 6.4: testmove.c
/* testmove.c */
#include "twindow.h "
#include "keys.h"
void testmove()
{
WINDOW *wndA, *wndB, *wndC;
int c;
wndA = establish_window(5, 5, 9, 19);
wndB = establish_window(10, 3, 9, 23);
wndC = establish_window(13, 8, 9, 12);
set_colors(wndA, ALL, RED, YELLOW, BRIGHT);
set_colors(wndB, ALL, AQUA, YELLOW, BRIGHT);
set_colors(wndC, ALL, WHITE, YELLOW, BRIGHT);
display_window(wndA);
displey_window(wndB);
display_window(wndC);
wprintf(wndB, "\n I wouldn't care who");
wprintf(wndB, "\n wrote the laws if I");
wprintf(wndB, "\n could write the");
wprintf(wndB, "\n ballads.");
wprintf(wndB, "\n\n Thomas Jefferson");
do {
int x = 0, y = 0;
c = get_char();
switch (c) {
case FWD: x++;
break;
case BS: --x;
break;
case UP: --y;
break;
case DN: y++;
default: break;
}
if (x || y)
rmove_window(wndB, x, y);
} while (c != ESC);
delete_window(wndA);
get_char();
delete_window(wndC);
get_char();
delete_window(wndB);
}
Листинг 6.5: move.prj
move
testmove (twindow.h, keys.h)
twindow (twindow.h, keys.h)
ibmpc.obj
Подъем и опускание окон
-----------------------
С помощью функций forefront и rear_window вы можете поднять
окно в последнюю позицию, помещая его поверх всех остальных, а
также опустить окно в первую позицию, помещая его под всеми
остальными. Эта возможность применима в программах с несколькими
окнами, где пользователь выбирает одно из них для некоторой цели
путем временного перевода остальных на фон. Эта возможность
полезна в любых приложениях, в которых пользователь часто
переходит от окна к окну.
Программа, иллюстрирующая подъемы и опускания окон,
приведена в листингах 6.6, 6.7 и 6.8. Листинг 6.6 является
маленькой управляющей программой, а листинг 6.8 - проектным make-
файлом. Обращайтесь к листингу 6.7, promote.c, при чтении данных
разъяснений.
Для запуска примера введите следующую команду:
c>prom
Программа promote.c использует те же образцы трех окон, что
и программа testmove.c. Теперь все три окна включают записанный в
них текст, каждый из которых содержит имя окна: window A, window
B и window C. На рисунке 6.5 показан первоначально выданный
экран.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ C> ¦
¦ ¦
¦ ЪДДДДДДДДДДДДДДДДДДД¬ ¦
¦ ¦ window B ¦ ¦
¦ ЪДДДДДґ ¦ ¦
¦ ¦ ¦ ЪДДДДДДДДДД¬ ¦ ¦
¦ ¦ win ¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ АДДДДґ window C ГДДДЩ ¦
¦ ¦ ¦ ¦ ¦
¦ АДДДДДДДДДДґ ¦ ¦
¦ АДДДДДДДДДДЩ ¦
¦ ¦
¦ ¦
¦ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 6.5. Подъем слоеных окон.
Для подъема и опускания окон используется клавиатура.
Используйте нажатия клавиш с маленькими буквами а, b и с для
подъема окон, названных этими буквами. Используйте клавиши с
большими буквами для их опускания. Этот процесс продолжается до
тех пор, пока вы не нажмете клавишу <Ключ> для уничтожения одного
из окон. Еще два нажатия вызовут уничтожение остальных двух окон,
и программа завершится.
Листинг 6.6: prom.c
/* prom.c */
void promote(void);
main()
{
promote();
}
Листинг 6.7: promote.c
/* promote.c */
#include "twindow.h"
#include "keys.h"
void promote()
{
WINDOW *wndA, *wndB, *wndC;
int c;
wndA = establish_window(5, 5, 9, 19);
wndB = establish_window(10, 3, 9, 20);
wndC = establish_window(13, 8, 9, 12);
set_colors(wndA, ALL, RED, YELLOW, BRIGHT);
set_colors(wndB, ALL, AQUA, YELLOW, BRIGHT);
set_colors(wndC, ALL, WHITE, YELLOW, BRIGHT);
display_window(wndA);
display_window(wndB);
display_window(wndC);
wprintf(wndA, "\n\n Window A");
wprintf(wndB, "\n\n Window B");
wprintf(wndC, "\n\n Window C");
do {
c = get_char();
switch (c) {
case 'a': forefront(wndA);
break;
case 'b': forefront(wndB);
break;
case 'c': forefront(wndC);
break;
case 'A': rear_window(wndA);
break;
case 'B': rear_window(wndB);
break;
case 'C': rear_window(wndC);
break;
default: break;
}
} while (c != ESC);
delete_window(wndA);
get_char();
delete_window(wndC);
get_char();
delete_window(wndB);
}
Листинг 6.8: prom.prj
prom
promote (twindow.h, keys.h)
twindow (twindow.h, keys.h)
ibmpc.obj
Назначение заголовков и изменение цветов окна
---------------------------------------------
Когда окно создано, вы можете назначить ему заголовок, а
также установить цвета переднего плана и фона.
Программа, иллюстрирующая заголовки и цвета окон, приведена
в листингах 6.9, 6.10 и 6.11. Листинг 6.9 является маленькой
управляющей программой, а листинг 6.11 - проектным make-файлом.
Обращайтесь к листингу 6.10, ccolor.c, при чтении данных
разъяснений.
Для запуска примера вводите следующую команду
c>color
Снова выдается три окна. Каждое из них имеет свой цвет (если
у вас цветной монитор), но ни у одного из них нет заголовка. Окна
расположены в тех же местах и имеют те же размеры, что и в
предыдущих примерах. Программа ожидает нажатия клавиши с одной из
букв: r, g или b. Она будет использовать эти буквы для изменения
заголовка среднего окна на "RED", "GREEN" или "BLUE", а также
изменит цвет среднего окна на соответствующий новому заголовку.
Рисунок 6.6 показывает экран после выбора алого (red) окна.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ C> ¦
¦ ¦
¦ ¦
¦ ¦
¦ ЪДДДДДДДREDДДДДДДД¬ ¦
¦ ¦ ¦ ¦
¦ ЪДДДДґ ¦ ¦
¦ ¦ ¦ ¦ ¦
¦ ¦ ¦ ЪДДДДДДДДД¬ ¦ ¦
¦ ¦ ¦ ¦ ¦ ¦ ¦
¦ ¦ АДДДґ ГДДДЩ ¦
¦ ¦ ¦ ¦ ¦
¦ АДДДДДДДДґ ¦ ¦
¦ АДДДДДДДДДЩ ¦
¦ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 6.6. Изменение цветов и заголовков.
Нажмите клавишу <Ключ> для выхода и уничтожения окна, а
также любые две другие клавиши для уничтожения двух остальных
окон и завершения программы.
Листинг 6.9: color.c
/* color.c */
void ccolor(void);
main()
{
ccolor();
}
Листинг 6.10: сcolor.c
/* ccolor.c */
#include "twindow.h"
#include "keys.h"
void ccolor()
{
WINDOW *wndA, *wndB, *wndC;
int c;
wndA = establish_window(8, 8, 9, 19);
wndB = establish_window(13, 6, 9, 20);
wndC = establish_window(16, 11, 9, 12);
set_colors(wndA, ALL, RED, YELLOW, BRIGHT);
set_colors(wndB, ALL, AQUA, YELLOW, BRIGHT);
set_colors(wndC, ALL, WHITE, YELLOW, BRIGHT);
display_window(wndA);
display_window(wndB);
display_window(wndC);
do {
c = get_char();
switch (c) {
case 'r':
set_title(wndB, " RED ");
set_colors(wndB, ALL, RED, WHITE, BRIGHT);
break;
case 'b':
set_title(wndB, " BLUE ");
set_colors(wndB, ALL, BLUE, WHITE, BRIGHT);
break;
case 'g':
set_title(wndB, " GREEN ");
set_colors(wndB, ALL, GREEN, WHITE, BRIGHT);
break;
default:
break;
}
} while (c != ESC);
delete_window(wndA);
get_char();
delete_window(wndC);
get_char();
delete_window(wndB);
}
Листинг 6.11: color.prj
color
ccolor (twindow.h, keys.h)
twindow (twindow.h, keys.h)
ibmpc.obj
Сравнение стековых и слоеных окон
---------------------------------
Рассматриваемая здесь программа дает возможность сравнить
представления стековых и слоеных окон. Вы можете откомпилировать
ее с оконной библиотекой, которая была компилирована для обоих
типов окон.
Программа, иллюстрирующая различия между стековыми и
слоеными окнами, показана на листингах 6.12, 6.13 и 6.14. Листинг
6.12 является маленькой управляющей программой, а листинг 6.14 -
проектным make-файлом. Обращайтесь к листингу 6.13, fast.c, при
чтении данных разъяснений.
Для запуска примера введите следующую команду:
c>fast
Помните, что программа сама по себе не показывает сравнение.
Вам необходимо построить две версии ее, одну - со стековой
оконной библиотекой, а вторую - со слоеной оконной библиотекой, и
сравнить представления обеих программ.
После запуска программы она создаст и выдаст пятнадцать окон
в последовательности, показанной на рисунке 6.7. Когда вы
нажимаете любую клавишу, программа уничтожает каждое окно в
обратном порядке. Вы можете сравнить соответствующие скорости
работы программ, компилированных в двух средах.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ЪДДДДДДДДДДДДДД¬ ¦
¦ ¦ ЪДДДДДДДДДДДДДДД¬ ¦
¦ ¦ ¦ ЪДДДДДДДДДДДДДДД¬ ¦
¦ ¦ ¦ ¦ ЪДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ЪДДДДДДДДДДДДДДДДДДД¬ ¦
¦ АД¦ ¦ ¦ ¦ ¦
¦ АД¦ ¦ Hello, Dolly # 14 ¦ ¦
¦ АД¦ ¦ ¦
¦ АДДДДДДДДДДДДДДДДДДДЩ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 6.7. Сравнение стековых и слоеных окон.
Листинг 6.12: fast.c
/* fast.c */
void fasttest(void);
main()
{
fasttest();
}
Листинг 6.13: fasttest.c
/* fasttest.c */
#include
#include "twindow.h"
void fasttest()
{
int row, col;
for (row = 0, col = 0; col < 15; row += 3, col++) {
establish_window(row, col, 10, 30);
set_colors(NULL, ALL, RED, YELLOW, BRIGHT);
display_window(NULL);
wprintf(NULL, "\n\n\n Hello, Dolly # %d", col);
}
get_char();
while (col--)
delete_window(NULL);
}
Листинг 6.14: fast.prj
fast
fasttest (twindow.h)
twindow (twindow.h, keys.h)
ibm.obj
Перемещение, подъем, скрытие окон, меню, изменение интенсивности
----------------------------------------------------------------
В следующем примере комбинируется несколько уже показанных
возможностей и даются примеры еще двух возможностей:
использование get_selection для обработки простого меню и
использование set_intensity для изменения яркости переднего плана
окон.
Программа, иллюстрирующая эти возможности, представлена на
листингах 6.15, 6.16 и 6.17. Листинг 6.15 является маленькой
управляющей программой, а листинг 6.17 - проектным make-файлом.
Обращайтесь к листингу 6.16, poems.c, при чтении данных
разъяснений.
Для запуска примера введите следующую команду:
c>poetry
Эта программа выдает пять различных стихотворений на экран и
позволяет вам выбрать одно из них, перемещать его, поднять его на
передний план, опустить его назад и уничтожить. Программа
начинает работу с выдачи оконного меню, которое перечисляет все
стихотворения. Вы можете переместить курсор вверх или вниз и
выбрать одно стихотворение нажатием клавиши <Ввод>. Вы также
можете нажать одну из цифр от 1 до 5, выбирая номер
стихотворения. В результате будет показано выбранное
стихотворение. На рисунке 6.8 показано меню стихотворений.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ ЪДДДДДДДДДДДДДДДДДД Select A PoemДДДДДДДДДДДД¬ ¦
¦ ¦ ¦ ¦
¦ ¦ 1: TELL ALL THE TRUTH BUT TELL IT SLANT ¦ ¦
¦ ¦ 2: AFTER LONG SILENSE ¦ ¦
¦ ¦ 3: A MAN SAID TO THE UNIVERSE ¦ ¦
¦ ¦ 4: FLYING CROOKED ¦ ¦
¦ ¦ 5: THE IDLE LIFE I LEAD ¦ ¦
¦ ¦ ¦ ¦
¦ АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ ¦
¦ ¦
¦ ¦
¦ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рисунок 6.8. Меню стихотворений.
После выбора стихотворения вы можете передвинуть его
клавишами со стрелками, возвращаясь к меню нажатием клавиши
<Ключ> или выбирая другое стихотворение нажатием соответствующей
цифровой клавиши. Выбранное текущее стихотворение выдается с
повышенной яркостью, а все остальные - с обычной. Если вы
выбираете стихотворение, выданное с обычной яркостью, то оно
становится ярким, а остальные - обычными. Для перевода
стихотворения на передний план нажмите клавишу <Плюс> (+); для
посылки его на фон нажмите клавишу <Минус> (-). Для уничтожения
текущего стихотворения нажмите клавишу <Удл>. Вы можете
восстановить его нажатием клавиши с номером. После этого нажмите
клавишу <Ключ> для возврата к меню, а затем снова клавишу <Ключ>
для выхода из программы.
На рисунке 6.9 показаны стихотворения, разбросанные по
экрану в различных местах.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД...
¦ Ъ 1: TELL ALL THE TRUTH BUT TELL IT...
¦ C> ¦
¦ ¦ Tell all the truth but tell it sl...
¦ ¦
¦ ЪДДДДДДДДД 2: AFTER LONG SILENCE ДДДДДДДДД¬t lies
¦ ¦ ¦r infirm Delight
¦ ¦Speesh after long silense; it is right, ¦b surprise
¦ ¦All other lovers being estranged or dead ¦ ЪДДД 5: THE IDLE...
¦ ¦Unfriendly lamplight hid under its shade,¦h¦
¦ ¦The curtaiЪДДДДДДДД 4: FLYING CROOKED ДДДД ¦The idle life I...
¦ ¦That we de¦ ¦Is like a pleas...
¦ ¦Upon the s¦The butterfly, a cabbare-white, ¦Wherein I rest ...
¦ ¦Bodily dec¦(His honest idiocy of flight) ¦The dreams that...
¦ ¦We loved e¦Will never now, it is too late, ¦
¦ ¦ ¦Master the art of flying straigh¦And still of al...
¦ ¦ ¦Yet has - who knows so well as I¦In turt so swif...
¦ АДЪД3: A MAN SAID TO THE UNIVERSEДДД¬ o fly:¦Each in its fan...
¦ ¦ ¦ by gue¦A nobler than t...
¦ ¦A man said to the universe: ¦lessnes¦
¦ ¦"Sur, I exist!" ¦ ¦And every eve I...
¦ ¦"However," replied the uviverse, ¦d gift ¦Noting my step ...
¦ ¦"The fast has not created in me ¦es ¦That I have kvo...
¦ ¦A sense of obligation." ¦ ¦In all my life ...
¦ ¦ Stephen Crane ГДДДДДДДґ Robert...
¦ ¦ ¦ ¦
¦ АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ АДДДДДДДДДДДДДДД...
¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Рисунок 6.9. Стихотворения.
Листинг 6.15: poetry.c
/* poetry.c */
#include "twindow.h"
void poems(void);
main()
{
load_help("tcprogs.hlp");
poems();
}
Листинг 6.16: poems.c
/* poems.c */
#include
#include
#include
#include "twindow.h"
#include "keys.h"
/* локальные прототипы */
void get_poem(int s);
int ht (char **tb);
int wd (char **tb);
char *titles [] = {
" 1: TELL ALL THE TRUTH BUT TELL IT SLANT ",
" 2: AFTER LONG SILENSE ",
" 3: A MAN SAID TO THE UNIVERSE ",
" 4: FLYING CROOKED ",
" 5: THE IDLE LIFE I LEAD ",0 };
WINDOW *pno [] = {0, 0, 0, 0, 0};
static int x [] = {20, 15, 29, 10, 17};
static int y [] = {5,10, 13, 18, 6};
static int wcl [] [2] = { {BLUE, WHITE},
{MAGENTA, WHITE},
{RED, WHITE},
{GREEN, WHITE},
{AQUA, WHITE} };
char *poem1 [] = {
"Tell all the truth but tell it slant -",
"Success in Circuit lies",
"Too bright for our infirm Delight",
"The Truth's superb surprise",
"",
"As Lightning to the Children eased",
"With explanation kind",
"The Truth must dazzle gradually",
"Or every man be blind -",
" Emily Dickenson",0 };
char *poem2 [] = {
"Speech after long silence; it is right,",
"All other lovers being estranged or dead,",
"Unfriendly lamplight hid under its shade,",
"The curtains drawn upon unfriendly night,",
"That we descant and yet again descant",
"Upon the supreme theme of Art and Song:",
"Bodily decrepitude is wisdom; young",
"We loved each other and were ignorant.",
" William Butler Yeats",0 };
char *poem3 [] = {
"A man said to the universe:",
"\"Sir, I exist!\"",
"\"However,\" replied the universe,",
"\"The fast has not created in me",
"A sense of obligation.\"",
" Stephen Crane",0 };
char *poem4 [] = {
"The butterfly, a cabbage-white,",
"(His honest idiocy of flight)",
"Will never now, it is too late,",
"Master the art of flying straight,",
"Yet has - who knows so well as I? -",
"A just sense of how not to fly:",
"He lurches here and there by guess",
"And God and hope and hopelessness.",
"Even the aerobatic swift",
"Has not his flyihg-crooked gift.",
" Robert Graves",0 };
char *poem5 [] = {
"The idle life I lead",
"Is like a pleasant sleep,",
"Wherein I rest and heed",
"The dreams that by me sweep.",
"",
"And still of all my dreams",
"In turn so swiftly past,",
"Each in its fancy seems,",
"A nobler than the last.",
"",
"And every eve I say,",
"Noting my step in bliss,",
"That I have known no day",
"In all my life like this.",
" Robert Bridges",0 };
char **poem [] = { poem1, poem2, poem3, poem4, poem5, 0 };
void poems()
{
int s = 0, i, c;
WINDOW *mn;
char **cp;
cursor(0, 25);
mn = establish_window(0, 0, 7, 45);
set_title(mn, " Select A Poem ");
set_colors(mn, ALL, BLUE, GREEN, BRIGHT);
set_colors(mn, ACCENT, GREEN, WHITE, BRIGHT);
display_window(mn);
cp = titles;
while (*cp)
wprintf(mn, "\n%s", *cp++);
while (1) {
set_help("poemmenu", 40, 10);
s = get_selection(mn, s+1, "12345");
if (s == 0)
break;
if (s == FWD || s == BS) {
s = 0;
continue;
}
hide_window(mn);
get_poem(--s);
c = 0;
set_help("poems ", 5, 15);
while (c != ESC) {
c = get_char();
switch (c) {
case FWD: rmove_window(pno[s], 1, 0);
break;
case BS: rmove_window(pno[s], -1,0);
break;
case UP: rmove_window(pno[s], 0, -1);
break;
case DN: rmove_window(pno[s], 0, 1);
break;
case DEL: delete_window(pno[s]);
pno[s] = NULL;
break;
case '+': forefront(pno[s]);
break;
case '-': rear_window(pno[s]);
default: break;
}
if (c > '0' && c < '6')
get_poem(s = c - '1');
}
forefront(mn);
display_window(mn);
}
close_all();
for (i = 0; i < 5; i++)
pno[i] = NULL;
}
/* активизирует стихотворение по номеру */
static void get_poem(int s)
{
char **cp;
static int lastp = -1;
if (lastp != -1)
set_intensity(pno[lastp], DIM);
lastp = s;
if (pno [s])
set_intensity(pno[s], BRIGHT);
else {
pno [s] = establish_window
(x[s], y[s], ht(poem[s]), wd(poem[s]));
set_title(pno[s], titles[s]);
set_colors(pno[s],ALL,wcl[s][0],wcl[s][1], BRIGHT);
set_border(pno[s], 1);
display_window(pno[s]);
cp = poem[s];
while (*cp)
wprintf(pno[s], "\n %s", *cp++);
}
}
/* вычисляет высоту показываемой таблицы окна */
static int ht(char **tb)
{
int h = 0;
while (*(tb + h++)) ;
return h + 3;
}
/* вычисляет ширину показываемой таблицы окна */
static int wd(char **tb)
{
int w = 0;
while (*tb) {
w = max(w, strlen(*tb));
tb++;
}
return w + 4;
}
Листинг 6.17: poetry.prj
poetry
poems (twindow.h, keys.h)
thelp (twindow.h, keys.h)
twindow (twindow.h, keys.h)
ibmpc.obj
В программе poetry клавиша используется в качестве
функциональеной клавиши контекстно-управляемой подсказки. Когда
вы нажимаете , появляется окно с подсказывающим сообщением,
относящимся к тому, что вы сейчас делаете. В Главе 7 объясняется,
как включается эта возможность.
Резюме
-----------------------------------------------------------------
Теперь у вас имеется основа для создания оконного
программного инструментария. С помощью этих функций вы можете
добавить окна к своему программному обеспечению, а также
отображать в них текст. Однако приложение окон может быть в
дальнейшем развито до возможностей более высокого уровня, которые
являются общими во многих прикладных системах. Несколько
последующих глав добавляют эти возможности к вашей оконной
библиотеке. Глава 7 вводит использование окон для добавления
контекстно-управляемой пользовательской подсказки в ваши
программы.
ГЛАВА 7
-------
Контекстно-управляемые окна подсказки
-----------------------------------------------------------------
Первой проблемой, обычно возникающей при запуске новой
программы, является незнакомство программы с языком своего
пользователя. Какая клавиша должна быть нажата? Какое действие
будет следующим? Что выполняет данный элемент меню? Независимо от
того, сколько усилий вложено в разработку самообъясняемого
пользовательского языка, у пользователей всегда возникают
вопросы, поскольку язык новой системы для него всегда
иностранный. Дело не только в знакомстве пользователей с языком,
часто они даже не знают, что система может, а чего не может
делать. По традиции эта проблема решается обращением к печатным
руководствам пользователя и, возможно, автоматизированным
справочникам. Недостатком этих решений является необходимость
переключения внимания пользователя от работы с системой к чтению
руководства или запуску справочника.
Если бы экраны были достаточно большими, система могла бы
поддерживать постоянный показ руководства пользователя. Как
только пользователю потребовалась бы информация, руководство было
бы доступно. Это решение не продержалось бы долго; когда
пользователь уже знает систему, информация подсказки не нужна и
нежелательна. Значимая для новичка информация - это мусор для
ветерана.
Большинство интерактивных систем имеют общее свойство: когда
пользователю необходима информация, он смотрит на экран и гадает,
какую клавишу нажать. Представляется вполне естественным, что в
число нажатий клавиш, которые система будет распознавать в любое
время, нужно включить функциональную клавишу (Подсказка).
Нажмите требуемую прикладную клавишу, и программа выполнит ваш
запрос; нажмите функциональную клавишу подсказки, и программа
сообщит вам кое-что о том, какие клавиши она ожидает и что
случится при их нажатии.
Поскольку интерактивные системы используют экран для общения
с пользователем и показа данных, сообщение подсказки - это
материал для выталкиваемого вверх окна. Такое построение окна
позволяет получить подсказку без нарушения прикладного
использования экрана. Такое окно называется контекстно-
управляемым окном подсказки, окном, которое выскакивает для
подсказки пользователю при нажатии назначенной функциональной
клавиши подсказки. (Программная промышленность признала
стандартом для функциональной клавиши подсказки, однако многие
программные пакеты используют другие клавиши). Окно подсказки
содержит текст, который объясняет некоторую часть программы.
Когда пользователи работают с программой и переходят от
возможности к возможности, содержимое и расположение окон
подсказки изменяются для отражения текущего контекста. Эти
изменения не видны, поскольку они происходят внутри программ.
Когда пользователь нажимает функциональную клавишу подсказки,
соответствующее сообщение-подсказка выдается в выскакивающем
окне.
Поскольку сообщение-подсказка зависит от текущего положения
в программе, окно подсказки называется контекстно-управляемым.
Опытные пользователи могут игнорировать возможности подсказки
программы; новички могут нажимать функциональную клавишу
подсказки при каждом изменении состояния программы и получать
советы, напоминания или детальные инструкции.
Разработчик системы решает, как много и какого вида
подсказки система будет предусматривать для пользователя.
Некоторые системы обладают несколькими уровнями подсказки в
зависимости от опыта пользователя. Программа обработки слов Word
Star применяла эту технологию на протяжении многих лет. Уровни
подсказки изменяются от простых сообщений типа "нажмите <Ключ>
для возврата к..." до полных пользовательских руководств. Многие
разработчики программ предпочитают передавать руководство
пользователя таким способом, а не в виде больших громоздких
документов. Эта процедура имеет два последствия: пользователи
связывают экстравагантные руководства с качеством программ, а
большие книги способствуют борьбе с "программными пиратами".
Как бы там ни было, пользователи также пришли к ожиданию
систем, требующих минимального использования пользовательских
руководств. Пользователи хотят интерактивную подсказку.
Программирование окон подсказки
-----------------------------------------------------------------
Функции подсказки в этой книге поддерживают концепции
контекстно-управляемой подсказки с помощью использования оконных
функций, текстового файла и функциональных вызовов из прикладной
программы. Каждая прикладная программа сообщает функциям
подсказки, какой файл подсказки использовать и какое окно
подсказки является текущим. Файл подсказки содержит текст для
каждого окна подсказки. Функции подсказки отслеживают сигналы от
клавиатуры и выдают текущее окно подсказки при нажатии
функциональной клавиши .
Программа, использующая программное обеспечение подсказки,
должна предусматривать следующие интерфейсы с функциями
подсказки:
- функциональный вызов для задания имени текстового файла
подсказки;
- значение функциональной клавиши подсказки;
- функциональные вызовы для идентификации текущего окна
подсказки;
- использование функции клавиатурного ввода get_char для
любого ввода с клавиатуры (get_char описана в Главе 4).
Последнее из этих четырех требований может потребовать
некоторых разъяснений. Во время работы программы программное
обеспечение подсказки перехватывает каждое прерывание от
клавиатуры для того, чтобы убедиться, не нажата ли функциональная
клавиша подсказки. Если это так, то управление передается
оконному процессу подсказки. Если нет, значение клавиши
пересылается в ожидающую его программную функцию. Программное
обеспечение подсказки может управлять этим перехватом только в
том случае, если вы используете функцию get_char для ввода с
клавиатуры. Как разъяснено выше, get-char следит за нажатием на
клавиатуре функциональной клавиши подсказки. Если вы используете
другие способы чтения символов с клавиатуры, то переключение
функциональной клавишей подсказки не будет сделано.
Имеются и другие методы наблюдения за клавиатурой с
ожиданием нажатия функциональной клавиши подсказки. Некоторые из
этих методов привлекают присоединяющее перехватывающее
программное обеспечение к вектору клавиатурного прерывания или
вектору клавиатурной BIOS. По различным причинам было решено, что
эти методы не будут использоваться. Во-первых, программное
обеспечение, описываемое в данной книге, предполагает
пользовательскую среду, включающую окна для меню, ввода данных и
текста. Весь клавиатурный ввод, необходимый программе, может
управляться одной из этих возможностей, причем все они используют
функцию get_char. Вам никогда не понадобится снова
программировать клавиатурный ввод. Во-вторых, если программа
присоединяет себя к вектору прерывания, то при ненормальном
завершении программы происходят странные вещи; обычно ПЭВМ
приходится перезапускать. В-третьих, разрабатываемые вами
программы могут быть резидентными в памяти. Резидентные в памяти
программы часто присоединяются к клавиатуре для других целей.
Вы узнаете больше о резидентных в памяти программах,
векторах прерываний и о том, как присоединять одно окно к
другому, в Главе 11. Допустим, что ваше программное обеспечение
читает клавиатуру с помощью функции get_char. Тогда вектора
клавиатуры остаются одни, и система хорошо себя ведет независимо
от резидентных программ, аварийных завершений программ при
тестировании или неожиданных завершений программы в
производственной системе.
Требуемое использование get_char лишает вас трех стандартных
возможностей Си:
- вы не сможете использовать стандартные библиотечные функции
scanf или getchar, поскольку ни одна из этих функций не
использует функцию get_char;
- вы не сможете воспользоваться стандартной функцией ввода с
консоли (getch), предусмотренной в Турбо Си;
- вы не сможете использовать логическое устройство stdin для
клавиатурного ввода, что означает для вашей программы
невозможность назначать файлы или программные каналы вместо
клавиатуры.
Потеря функций scanf и getchar - это небольшая потеря для
программирования диалога. Эти функции бесполезны в интерактивной
среде. Они являются функциями буферизованного ввода, требующими
нажатия клавиши <Ввод> для завершения ввода символа или строки;
кроме того, они нечувствительны к расположению курсора и длине
поля. Эти функции дублируют свой ввод в стандартный вывод,
соблюдая соглашения командной строки ДОС, и они будут
дублировать изображения двойных символов при вводе управляющего
символа. Если введена клавишная комбинация Упр/С, функции могут
аварийно завершить программу. Функции плохо работают с
функциональными или курсорными клавишами. Турбо Си включает их
для поддержки совместимости с UNIX. Эти функции происходят из
компьютерных систем с телетайпными терминалами.
Может показаться, что вы теряете ряд возможностей, когда
лишаетесь устройства stdin, однако это устройство предназначено
для программ, которые могут принимать входные данные из файлов и
выходов других программ так же, как и с клавиатуры. Эти программы
(или, по крайней мере, stdin используется для ввода данных в них)
обычно не предназначены для использования в интерактивной среде.
Текстовый файл окна подсказки
-----------------------------------------------------------------
Окна подсказки появляются, когда пользователь нажимает
функциональную клавишу подсказки, описанную в текстовом файле.
Текстовый файл подсказки - это ASCII-файл, который вы строите
текстовым редактором Турбо Си или любым другим редактором,
который может создавать ASCII-файлы.
Окна подсказки описываются мнемоническим идентификатором,
который используется программным обеспечением подсказки для
идентификации и расположения текста. Строки, следующие за
идентификатором, содержат текст, количество строк и длина строки
которого определяют высоту и ширину окна подсказки. На рисунке
7.1 показан небольшой файл подсказки, описывающий два окна
подсказки.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ ¦
¦ Enter the name of the ¦
¦ employee as last name, ¦
¦ comma, first name, viddle ¦
¦ initial. Example: ¦
¦ Hart, William S ¦
¦ ¦
¦ Enter the employee number ¦
¦ with from I to life digits. ¦
¦ Example: 12345 ¦
¦ ¦
¦ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рис. 7.1. Пример файла подсказки
Идентификатор окна подсказки состоит ровно из восьми
символов и окружается угловыми скобками, как показано на рисунке
7.1. Каждый идентификатор появляется в своей собственной строке,
последней строкой файла должен быть лексический вход. На рисунке
7.2 приведены два окна подсказки, которые будут отображены в
результате этих описаний.
ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬
¦ ¦
¦ ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬ ¦
¦ ¦ Enter the name of the ¦ ¦
¦ ¦ employee as last name, ¦ ¦
¦ ¦ comma, first name, middle, ¦ ¦
¦ ¦ initial. Example: ¦ ¦
¦ ¦ Hart, William S ¦ ¦
¦ ¦ [Help] to return ¦ ¦
¦ АДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ ¦
¦ ¦
¦ ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДД¬ ¦
¦ ¦ Enter the employee number ¦ ¦
¦ ¦ with from 1 to five digits. ¦ ¦
¦ ¦ Example: 12345 ¦ ¦
¦ ¦ [Help] to return ¦ ¦
¦ АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ ¦
АДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
Рис. 7.2. Примеры окон подсказки
Все примеры программ, которые приводятся в этой и
последующих главах, используют программное обеспечение подсказки.
Далее рассматривается простой пример для иллюстрации самой
подсказки, а затем - использование подсказки по прямому
назначению - для предоставления пользователю интерактивной
контекстно-управляемой подсказки. С этой целью один и тот же файл
подсказки предусматривается для всех программ. Он называется
tcprogs.hlp и показан на листинге 7.1.
Листинг 7.1: tcprogs.hlp
Press 1, 2, or 3
for a pithy maxim.