Красивые окна в текстовом режиме
В ЭТОМ разделе мы познакомим вас с небольшим пакетом программ на Си, разработанным одним из авторов этой книги 1990 г., когда большинство программистов были вынуждены изобретать разные средства для управления выводом данных из-за их отсутствия в среде MS-DOS. Аналогичные пакеты с меньшими функциональными возможностями вы можете найти в книгах Р. Уинера "Язык Турбо Си" и В. В. Фаронова "Программирование на персональных ЭВМ в среде Турбо Паскаль".
Пакет с условным названием ТЕХТ_ВОХ предназначен для оформления различных окон на экране дисплея и управления выводом текстовых данных в таких окнах. В его состав входит 21 функция для манипуляции со строками и текстовыми окнами, которые реализованы на базе подфункций прерывания BIOS с номером 0х10. Их список приведен в табл. 10.2. Прерывание Oxio обслуживает видеосистему не только в текстовых, но и в графических режимах, и представленные здесь возможности раскрывают примерно четверть этого арсенала.
Таблица 10.2. Функции манипуляции
Формат вызова функции |
|
Назначение | |||
ask attr (&cs, &cf, sin, sbl) |
Опрос цветовых атрибутов | ||||
set attr (cs, cf , in, bl) |
Установка цветовых атрибутов | ||||
ask_cur (&x, &y) |
Опрос позиции курсора | ||||
set_ cur(x,y) |
Установка курсора в заданную позицию | ||||
move_cur(n) |
Перемещение курсора на n позиций | ||||
box_abs (rowl, coll, row2, col2, bord, shade) |
Оформление окна | ||||
box rel (rowl, coll, rows, cols, bord, shade) |
Оформление окна | ||||
cl_rect (row, col, rows, cols, color) |
Очистка окна | ||||
s_out (ch) |
Вывод символа в текущую позицию | ||||
s_out h (ch, n) |
Вывод n символов по горизонтали | ||||
s_out v(ch,n) |
Вывод n символов по вертикали | ||||
s_box abs (rowl, coll, row2, col2,ch) |
Заполнение окна символом | ||||
s_box rel (rowl, coll, rows, cols, ch) |
Заполнение окна символом | ||||
s_out_с (row, col, nc, str) | Вывод строки по центру | ||||
s_ out_1 (row, col, nc, str) | Вывод строки с прижимом влево | ||||
s_out_ r (row, col, nc, str ) | Вывод строки с прижимом вправо | ||||
ask_page ( ) | Опрос активной страницы | ||||
set_page (n) | Установка активной страницы | ||||
out_err (str) init_txt ( ) | Вывод сообщения об ошибке Инициализация текстового режима | ||||
xy_s_out (row, col, ch) |
Вывод символа в заданную позицию | ||||
Для удобства общения между функциями пакета определены следующие глобальные переменные:
С целью сокращения обозначений и приближения их к идентификаторам регистров на Ассемблере введены следующие подстановки:
Программа ask_attr — опрос цветовых атрибутов
Если вы забыли, как выглядит байт цветовых атрибутов, то загляните в раздел 3.5.5.
int ask_attrtint *cs,int *cf,int *in,int *bl)
// cs - цвет символов, от 0 до 7
// cf - цвет фона, от 0 до 7
//in - яркость, 0 или 1
// b1 - признак мерцания, 0 или 1
{
АН=8;
//Подфункция опроса цветовых атрибутов
BH=_PAGE;
//Номер текущей страницы
INT10h ;
//Чтение текущего символа и атрибутов
_ATTR=AH;
//Байт цветовых атрибутов
__COLOR__S=_ATTR & 0x07;
//Цвет символа
_COLOR_F=(_ATTR & 0x70) >> 4;
//Цвет фона
_INTENS=(_ATTR & 0x08) >> 3;
//Бит интенсивности
_BLINK=(_ATTR & 0x80) >> 7;
//Бит мерцания
*cs=_COLOR__S;
//возврат цвета символов
*cf=_COLOR_F;
//возврат цвета фона
*in= INTENS;
//возврат признака яркости
*b1=_BLINK;
//возврат признака мерцания
return ( ATTR);
//возврат всех атрибутов цвета
}
Программа set_attr — установка цветовых атрибутов
void set_attr(int cs,int cf,int in,int b1) {
//анализ атрибутов цвета на допустимость
if(cs>=0 && cs<8 && cf>=0 && cf<8 &&
in>=0 && in<2 && bl>=0 && bl<2) {
_COLOR__S=cs ;
_COLOR_F=cf;
_INTENS=in;
_BLINK=bl; //объединение атрибутов цвета в одном байте
_ATTR=_BLINK << 7 | _COLOR_F << 4 | _INTENS << 3 | _COLOR__S;
}
else err_out("Ошибка при вызове set_attr"); }
Программа move_cur — перемещение курсора на п позиций вправо
Используя текущие координаты курсора (_ROW_CUR, _COL_CUR), функция вычисляет строку и столбец новой позиции и с помощью функции set_cur перемещает туда курсор. Если курсор выходит за пределы экрана, то его принудительно устанавливают в верхний левый угол.
void move_cur(int n)
{
int pos;
pos=_ROW_CUR* 80+_COL_CUR+n;
_ROW_CUR=pos/80;
_COL_CUR=pos-_ROW_CUR*8 0;
if(_ROW_CUR > 24)
{
_ROW_CUR=0; _COL_CUR=0;
}
set_cur(_ROW_CUR+1,_COL_CUR+1) ;
}
Программа box_abs — построение прямоугольника с рамкой и тенью
Контуры рамки образуются пробелами, одинарными и/или двойными "линиями" с помощью символов псевдографики. Массивы lu, id, ru и rd заполнены, кодами символов, используемыми для отображения левого верхнего (lu), левого нижнего (id), правого верхнего (ru) и правого нижнего (rd) углов рамки. В массивах horiz и vert находятся коды символов, формирующие горизонтальные и вертикальные линии рамки. По индексу bord из них извлекаются знаки соответствующей окантовки и некоторые из них повторяются rpth раз по горизонтали и rptv раз по вертикали.
Тень создается с помощью строки и столбца пробелов, окрашенных в серый цвет и расположенных со сдвигом на одну позицию относительно нижней и левой (shade=-1) или нижней и правой (shade=1) границ рамки. Внутренность окна заполняется пробелами цветом фона, ранее установленного с помощью функции set_attr.
void box_abs(int row1,int coll,int row2,int col2, int bord,int shade)
// rowl,coll - левый верхний угол,
// row2,co12 - правый нижний угол
// bord - номер типа рамки, от 0 до 4
// shade = -1(тень слева), 0(без), 1(тень справа)
{
char lu[5]={ 0x20,0xDA,0хС9,0xD6,0xD5 };
char ld[5]={ 0x20,0xC0,0xC8,0xD3,0xD4 };
char ru[5]={ 0x20,0xBF,0xBB,0xB7,0xB8 };
char rd[5]={ 0x20,0xD9,0xBC,0xBD,0xBE };
char horiz[5]=( 0x20,0xC4,0xCD,0xC4,0xCD };
char vert[5] ={ 0x20,0хВЗ,0хВА,0хВА,0хВЗ };
int rpth,rptv,attr;
rptv=co12-coll-l;
//длина вертикали
if(rptv <= 0) rptv=l;
rpth=row2-rowl;
//длина горизонтали
if{rpth <= 0) rpth=l;
// анализ на допустимость параметров окна
if(shade == 1 && coll+rptv >= 79) goto ml;
if(shade ==-1 && coll==l) goto ml;
if(shade != 0 && rowl+rpth >= 25) goto ml;
if(rowl+rpth > 25 || coll+rptv+1 > 80) goto ml;
xy_s_out(rowl,coll,lu[bord]);
//верхний левый угол
s_out_h(horiz[bord],rptv);
//верхняя горизонталь
s_out_h(ru[bord],1);
//верхний правый угол
set_cur(rowl+1,coll);
//курсор в начало левой вертикали
s_out_v(vert[bord],rpth);
//левая вертикаль
set_cur(rowl+1,co!2);
//курсор в начало правой вертикали
s_out_v(vert[bord],rpth);
//правая вертикаль
//роспись внутренности пробелами
sbox_rel(rowl+1,coll+1,rpth,rptv, 32) ;
xy__s_out(row2,coll,Idfbord]);
//левый нижний угол
s_out_h(horiz[bord],rptv);
//нижняя горизонталь
s_out_h(rd[bord],1);
//правый нижний угол
if(shade == 0) goto m;
//обход, если нет тени
attr=_ATTR;
//запоминание атрибутов цвета
set_attr(7,0,0,0);
//серый цвет для тени,
if(shade == -1)
//если тень слева
{
set_cur(rowl+1,coll-1); //установка курсора левее и ниже
s_out_v{219,rpth+1); //вертикаль тени
s_out_h(219,rptv+1); //горизонталь тени, }
else //если тень справа {
set_cur(rowl+1,col2+l); //курсор правее и ниже
s_out_v(219,rpth+1); //вертикаль тени
set_cur(row2+l,coll+1); //курсор в начало горизонтали
s_out_h(219,rptv+1); //горизонталь тени }
_ATTR=attr; //восстановление атрибутов цвета
m: set_cur (rowl+1, coll+1) ; //курсор в начало окна
return;
ml:err_out("Ошибка при вызове box... "); }
Программа bох_rе1 — построение прямоугольника с рамкой и тенью
Эта программа отличается от предыдущей только способом задания габаритов рамки — вместо второго противоположного угла здесь задается количество строк (rows) и столбцов (cols). Программа определяет координаты противоположного угла и обращается к предыдущей функции.
void box__rel(int rowl,int coll,int rows,int cols,int bord,int shade)
// rows,cols - число строк и столбцов
{
box_abs(rowl,coll,rowl+rows-1,col1+cols-l,bord,shade);
}
Программа cl_rect — очистка прямоугольной области экрана
void cl_rect(int row,int col,int rows,int cols,int color)
// row, col - левый верхний угол,
// rows, cols - число строк и столбцов,
// color - цвет заливки, от 0 до 7
//( RED=4 GREEN=2 BLUE=1 )
{
AL=rows+l; //количество строк
АН=0х06; //номер подфункции
CH=row-l; //номер начальной строки
CL=col-l; //номер начального столбца
DH=row+rows-2; //номер конечной строки
DL=col+cols—2; //номер конечного столбца
ВН=со1ог*1б; //дает фона
INT10h;
set_cur(row,col); //перевод курсора в начало окна }
Программа s_out — вывод символа в текущую позицию
void s_out(char s) {
АН=9; //подфункция вывода символа
AL=s; //код выводимого символа
ВН= PAGE; //номер активной страницы
BL=_ATTR; //текущие цветовые атрибуты
СХ=1; //количество повторяемых символов
INT10h;
move_cur(1); //сдвиг курсора на 1 позицию вправо }
Программа s_out_h — размножение символа с текущей позиции по строке
Функция отличается от предыдущей только установкой счетчика повторений символа. После размножения символа курсор переводится в ближайшую свободную позицию справа.
void s_out_h(char s,int rpt)
// s - размножаемый символ
// rpt - количество повторений {
АН=9; //номер подфункции
AL=s ;
BH=_PAGE ;
BL=_ATTR;
CX=rpt;
INT10h;
move_cur(rpt); }
Программа s_out_v — размножение символа с текущей позиции по столбцу
В цикле организуется перемещение курсора по вертикали вниз и вывод размножаемого символа. По окончании цикла курсор переводится за последний символ.
void s_out_v(char s,int rpt)
//s- размножаемый символ раз
// rpt - количество повторений
{
int row,col,i;
row=_ROW_CUR;
col=_COL_CUR;
BH=_PAGE;
DL=col;
BL=_ATTR;
CX=1;
for(i=row; i<row+rpt; i++)
{
AH=2; //номер подфункции установки курсора
DH=i; //номер строки
INT10h;
АН=9; //номер подфункции вывода символа
AL=s; //код выводимого символа
INT10h;
if(i==25) break;
}
set_cur(i,col+1); //перевод курсора правее колонки }
Программа sbox_abs — заполнение прямоугольной области заданным символом
Организуется цикл по количеству строк, в каждой из которых курсор устанавливается в начальную колонку строки и с помощью функции s_out_h выводится нужное количество символов по горизонтали. После заполнения области курсор переводится в левый верхний угол прямоугольника.
void sbox_abs(int rowl,int coll,int row2,int col2,char s)
// s - символ-заполнитель
// rowl,coll - левый верхний угол
// row2,col2. - правый нижний угол
Программа s_out_c — вывод строки с центровкой в заданной полосе
Функция определяет длину выводимой строки и сравнивает ее с длиной предоставляемой полосы. Если полоса задана с запасом, то вывод текста производится с позиции, отстоящей от начала полосы на половину разницы длин. В противном случае строка размещается с начала полосы.
void st_out__c(int row,int col,int nc,char *string)
// row,col - начало полосы,
//nc - длина полосы,
// string - выводимая строка
(
int ls,i;
ls=strlen(string);
i=(nc-ls)/2;
if(Is <= nc)
st_out_l(row,col+i,nc,string) ;
else
st_out_l(row,col,nc,&string[i]); }
Программа s_out_l — вывод строки в полосу с прижимом влево
Строка выводится посимвольно с начала полосы до тех пор, пока либо не будут исчерпаны все символы строки, либо не будет заполнена последняя позиция полосы. За пределами правой границы полосы вывод не производится.
void st_out_l{int row,int col,int nc,char *string)
// row,col - начало полосы,
// nc - длина полосы,
// string - выводимая строка
{
char s; int i ;
for(i=0; i<nc && string[i] != 0x00; i++)
{
s=string[i]; xy_s_out(row,col+i, s);
}
}
Программа s_out_r — вывод строки в полосу с прижимом вправо
Если ширина полосы превышает длину строки, то вычисляется начальная позиция строки в полосе, при которой последний символ строки попадает в последнюю позицию полосы, и вывод осуществляется по функции st_out_1. Если длина выводимой строки превышает ширину отведенной полосы, то у строки отсекаются лишние символы с начала и остаток строки заполняет полосу целиком.
void st_out_r(int row,int col,int nc, char *string)
// row,col - начало полосы,
//nc - длина полосы,
// string - выводимая строка
{
int ls,i;
ls=strlen(string);
i=nc-ls;
if(Is <= nc)
st_out_l(row,col+i,Is,string);
else
st_out_l(row,col,nc, &string[i] ) ; }
Программа ask__page — опрос активной страницы
Вместо этой функции вызывающая программа может проанализировать значение глобальной переменной _PAGE.
int ask_page(void) {
return (_PAGE); }
Программа set_page — установка текущей страницы
Помимо записи номера активной страницы в глобальную переменную _PAGE, необходимо довести эту информацию и до BIOS с помощью подфункции номер 5.
void set_page(int p) //р - номер, от 0 до 7 {
if(p>=0 && р<8)
{
_PAGE=p;
AH=5;
AL=p;
INT10h; } else err_out("Ошибка при вызове set_page");
}
Программа err_out — вывод сообщения об ошибке
Сообщение об ошибке выдается красными мигающими буквами в самой нижней строке экрана. После вывода сообщения делается выдержка до нажатия любой клавиши.
void err_out (char *string) {
set_attr(7,4,1,1);
st_out_l(25,1,80,string);
getch(); }
Программа init_txt — инициализация пакета text_box
void init_txt (void)
/*********************************************/
/* Инициализация экрана в текстовом режиме : */
/* размер экрана -25x80 */
/* маска символов - 8 х 14 */
/*********************************************/
{
extern union REGS reg;
AH=0;
AL=2 ;
INT10h; /* установка режима */
set_page(0); /* 0-я страница */
set_cur(1,1); /* курсор - в начало */
set_attr(7,0,0,0);/* цветовые атрибуты */
АН=9;
AL=32; /* код пробела */
BL=7; /* белым по черному */
ВН=0; /* 0-я страница */
СХ=2000;
INT10h ; /* очистка экрана */
}
Программа tst_text — проверки пакета text_box
Для проверки работоспособности описанного выше пакета предлагается следующий тест, охватывающий почти все функции пакета:
#include "text.h"
void main() {
init_txt();
set_attr(4,2,1,0); //красный цвет, зеленый фон
box_abs(2,2,10,30,4,1); //прямоугольник с тенью справа
getch();
move_cur(2); //сдвиг курсора вправо
getch();
s_out('А'); //вывод одной буквы
getch();
s_out_h('В',3); //вывод трех букв в строке
getch();
s_out_v('С',4); //вывод четырех букв по вертикали
getch();
cl_rect(3,3,7,27,4) ; //заливка внутренности окна красным
getch();
set_attr(7,1,0,1);
box_rel(13, 40,8,28,4,-1);//прямоугольник с тенью слева
getch();
set_attr(7,1,0,0);
sbox_rel(14,41,6,26,' 7 ') ;
getch();
st_out_l(13,5,16,"1234567890123456"); //линейка в полосе
st_out_l(14,5,16," "); //очистка полосы
getch();
st_out_l(14,5,16,"Привет"); //вывод в полосу с прижимом влево
getch();
st_out_l(15,5,16," "); //очистка полосы
getch();
st__out_c (15, 5,16, "Привет") ; //вывод в полосу по центру getch();
st_out 1(16,5,16," "}; //очистка полосы
getch ();
st_out_r(16,5,16,"Привет"); //вывод в полосу с прижимом вправо
getch () ; }
В состав файла с текстом головной программы можно включить все функции пакета и набрать заголовочный файл text.h:
#include <conio.h>
#include <dos.h>
#include <string.h>
union REGS reg;
#define AH reg.h.ah
#define AL reg.h.al
#define BH reg.h.bh
#define BL reg.h.bl
#define CH reg.h.ch
#define CL reg.h.cl
#define CX reg.x.ex
#define DL reg.h.dl
#define DH reg.h.dh
#define INTlOh
int86(0x10,®,Sreg)
unsigned char
_PAGE,_ATTR,_COLOR_S,_COLOR_F,_INTENS,_BLINK;
unsigned ctear _ROW_CUR,_COL_CUR;
int ask__attr (int *cs,int *cf, int *in,int *bl) ;
void ask_cur (int *r, int *c) ;
int ask_page (void);
void box_abs(int rowl,int coll,int row2,int col2, int bord,int shade);
void box_rel(int rowl,int coll,int rows,int cols, int bord,int shade);
void cl_rect(int rowl, int coll, int rows, int cols, int color) ;
void err_out(char *string);
void init txt (void);
void move_cur (int n) ;
void s_out (char s);
void s_out_h (char s,int rpt) ;
void s_out_v (char s,int rpt);
void sbox_abs (int row1,int coll,int row2,int col2,char s) ;
void sbox rel (int rowl,int coll,int rows,int cols,char s) ;
void set_attr (int cs,int cf,int in,int b1);
void set_cur (int r,int c);
void set_page (int p);
void st_out_c (int row,int col,int nc, char *string);
void st_out_l (int row,int col,int nc, char *string);
void st__out_r (int row, int col, int nc, char *string);
void xy_s_out (int row,int col,char s);
Если этот пакет вам понадобится в будущем, то функции пакета следует протранслировать и с помощью сервисной программы tlib.exe поместить полученные объектные модули в библиотеку. Эта библиотека может быть подключена к проекту вашего приложения.