Практика программирования (Бейсик, Си, Паскаль)

       

Красивые окна в текстовом режиме


В ЭТОМ разделе мы познакомим вас с небольшим пакетом программ на Си, разработанным одним из авторов этой книги 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)

Вывод символа в заданную позицию

<


Для удобства общения между функциями пакета определены следующие глобальные переменные:

  • PAGE — байт с номером активной страницы (начальное значение — 0);

  • 1 _ATTR — байт текущих цветовых атрибутов выводимого текста;

  • _COLR__S — байт с номером цвета выводимых символов;

  • _COLOR_F — байт с номером цвета фона;

  • __INTENS — байт с признаком обычной (0) или повышенной (1) яркости;

  • _BLINK — байт с признаком мерцания (0 — мерцания нет);

  • _ROW_CUR — байт с номером текущей строки;

  • _COL_CUR — байт с номером текущего столбца.

    С целью сокращения обозначений и приближения их к идентификаторам регистров на Ассемблере введены следующие подстановки:

  • union REGS reg;


  • #define АН 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.dl

  • #define INT10h int86 ( 0x10 , &reg, &reg)

    Программа 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,&reg,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 поместить полученные объектные модули в библиотеку. Эта библиотека может быть подключена к проекту вашего приложения.


    Содержание раздела