вете функцию refresh() ("обновить"), все изменения происшедшие в окнах будут отобра-
жены на экране дисплея (такое одновременное обновление всех изменившихся частей
экрана позволяет провести ряд оптимизаций). Если вы забудете сделать refresh - экран
останется неизменным. Обычно эту функцию вызывают перед тем, как запросить у пользо-
вателя какой-либо ввод с клавиатуры, чтобы пользователь увидел текущую "свежую" кар-
тинку. Хранение содержимого окон в памяти программы позволяет ей считывать содержи-
мое окон, тогда как большинство обычных терминалов не способны выдать в компьютер
содержимое какой-либо области экрана.
Общение с терминалом через линию связи (или вообще через последовательный прото-
кол) является довольно медленным. На персональных компьютерах существует другой спо-
соб работы с экраном: через прямой доступ в так называемую "видеопамять" - специаль-
ную область памяти компьютера, содержимое которой аппаратно отображается на экране
консоли. Работа с экраном превращается для программиста в работу с этим массивом
байт (запись/чтение). Программы, пользующиеся этим способом, просты и работают очень
быстро (ибо доступ к памяти черезвычайно быстр, и сделанные в ней изменения "проявля-
ются" на экране почти мгновенно). Недостаток таких программ - привязанность к конк-
ретному типу машины. Эти программы немобильны и не могут работать ни на обычных тер-
миналах (подключаемых к линии связи), ни на машинах с другой структурой видеопамяти.
Выбор между "традиционной" работой с экраном и прямым доступом (фактически - между
мобильностью и скоростью) - вопрос принципиальный, тем не менее принятие решения
зависит только от вас. Видеопамять IBM PC в текстовом режиме 80x25 16 цветов имеет
следующую структуру:
struct symbol{ /* IBM PC family */
char chr; /* код символа */
char attr; /* атрибуты символа (цвет) */
} mem[ 25 ] [ 80 ]; /* 25 строк по 80 символов */
Структура байта атрибутов:
-------------------------------------------
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | # бита
------------------|------------------------
|blink| R | G | B | intensity | r | g | b | цвет
------------------|------------------------
background (фон) | foreground (цвет букв)
R - red (красный) G - green (зеленый) B - blue (синий)
blink - мерцание букв (не фона!)
intensity - повышенная яркость
А. Богатырев, 1992-95 - 350 - Си в UNIX
Координатная система на экране: верхний левый угол экрана имеет координаты (0,0), ось
X горизонтальна, ось Y вертикальна и направлена сверху вниз.
Цвет символа получается смешиванием 3х цветов: красного, зеленого и синего
(электронно-лучевая трубка дисплея имеет 3 электронные пушки, отвечающие этим цве-
там). Кроме того, допустимы более яркие цвета. 4 бита задают комбинацию 3х основных
цветов и повышенной яркости. Образуется 2**4=16 цветов:
I R G B номер цвета
BLACK 0 0 0 0 0 черный
BLUE 0 0 0 1 1 синий
GREEN 0 0 1 0 2 зеленый
CYAN 0 0 1 1 3 циановый (серо-голубой)
RED 0 1 0 0 4 красный
MAGENTA 0 1 0 1 5 малиновый
BROWN 0 1 1 0 6 коричневый
LIGHTGRAY 0 1 1 1 7 светло-серый (темно-белый)
DARKGRAY 1 0 0 0 8 темно-серый
LIGHTBLUE 1 0 0 1 9 светло-синий
LIGHTGREEN 1 0 1 0 10 светло-зеленый
LIGHTCYAN 1 0 1 1 11 светло-циановый
LIGHTRED 1 1 0 0 12 ярко-красный
LIGHTMAGENTA 1 1 0 1 13 ярко-малиновый
YELLOW 1 1 1 0 14 желтый
WHITE 1 1 1 1 15 (ярко)-белый
Физический адрес видеопамяти IBM PC в цветном алфавитно-цифровом режиме (80x25,
16 цветов) равен 0xB800:0x0000. В MS DOS указатель на эту память можно получить при
помощи макроса make far pointer: MK_FP (это должен быть far или huge указатель!). В
XENIX[*] указатель получается при помощи системного вызова ioctl, причем система пре-
доставит вам виртуальный адрес, ибо привелегия работы с физическими адресами в UNIX
принадлежит только системе. Работу с экраном в XENIX вы можете увидеть в примере
"осыпающиеся буквы".
8.1.
/*#! /bin/cc fall.c -o fall -lx
* "Осыпающиеся буквы".
* Использование видеопамяти IBM PC в ОС XENIX.
* Данная программа иллюстрирует доступ к экрану
* персонального компьютера как к массиву байт;
* все изменения в массиве немедленно отображаются на экране.
* Функция nap() находится в библиотеке -lx
* Показана также работа с портами IBM PC при помощи ioctl().
*/
#include
#include /* O_RDWR */
#include
#include
#include
#include
#include /* for System V/4 and Interactive UNIX only */
/*#include for XENIX and SCO UNIX only */
#include
____________________
[*] XENIX - (произносится "зиникс") версия UNIX для IBM PC, первоначально разрабо-
танная фирмой Microsoft и поставляемая фирмой Santa Cruz Operation (SCO).
А. Богатырев, 1992-95 - 351 - Си в UNIX
#ifdef M_I386
# define far /* на 32-битной машине far не требуется */
#endif
char far *screen; /* видеопамять как массив байт */
/* far - "длинный" (32-битный) адрес(segment,offset) */
int segm; /* сегмент с видеопамятью */
#define COLS 80 /* число колонок на экране */
#define LINES 25 /* число строк */
#define DELAY 20 /* задержка (миллисекунд) */
int ega; /* дескриптор для доступа к драйверу EGA */
/* структура для обмена с портами */
static struct port_io_struct PORT[ 4 /* не более 4 за раз */] = {
/* операция номер порта данные */
/* .dir .port .data */
/* Переустановить flip/flop:
* заставить порт 0x3C0 ожидать пары адрес/значение
* при последовательной записи байтов в этот порт.
*/
{ IN_ON_PORT, 0x3DA, -1 },
/* IN-чтение */
/* Теперь 3c0 ожидает пары адрес/значение */
{ OUT_ON_PORT, 0x3C0, -1 /* адрес */ },
{ OUT_ON_PORT, 0x3C0, -1 /* значение*/ },
/* OUT-запись */
/* переинициализировать дисплей, установив бит #5 порта 3c0 */
{ OUT_ON_PORT, 0x3C0, 0x20 }
};
void closescr(nsig){ /* конец работы */
setbgcolor(0); /* установить черный фон экрана */
exit(0);
}
/* получение доступа к видеопамяти адаптера VGA/EGA/CGA */
void openscr () {
static struct videodev {
char *dev; int mapmode;
} vd[] = {
{ "/dev/vga", MAPVGA },
{ "/dev/ega", MAPEGA },
{ "/dev/cga", MAPCGA },
{ NULL, -1 }
}, *v; /* устройство для доступа к видеоадаптеру */
for(v=vd; v->dev;v++ )
if((ega = open (v->dev, O_RDWR)) >= 0 ) goto ok;
fprintf( stderr, "Can't open video adapter\n" );
exit(1);
А. Богатырев, 1992-95 - 352 - Си в UNIX
ok:
/* fprintf(stderr, "Adapter:%s\n", v->dev); */
/* получить адрес видеопамяти и доступ к ней */
#ifdef M_I386
screen = (char *) ioctl (ega, v->mapmode, 0);
#else
segm = ioctl (ega, v->mapmode, 0);
screen = sotofar (segm, 0); /* (segment,offset) to far pointer */
#endif
signal( SIGINT, closescr );
}
/* макросы для доступа к байтам "символ" и "атрибуты"
* в координатах (x,y) экрана.
*/
#define GET(x,y) screen[ ((x) + (y) * COLS ) * 2 ]
#define PUT(x,y, c) screen[ ((x) + (y) * COLS ) * 2 ] = (c)
#define GETATTR(x,y) screen[ ((x) + (y) * COLS ) * 2 + 1 ]
#define PUTATTR(x,y, a) screen[ ((x) + (y) * COLS ) * 2 + 1 ] = (a)
/* символ изображается как черный пробел ? */
#define white(c,a) ((isspace(c) || c==0) && (attr & 0160)==0)
/* установить цвет фона экрана */
void setbgcolor( color ){
PORT[1].data = 0; /* регистр номер 0 палитры содержит цвет фона */
/* всего в палитре 16 регистров (0x00...0xFF) */
PORT[2].data = color ;
/* новое значение цвета, составленное как битовая маска
* RGBrgb (r- красный, g- зеленый, b- синий, RGB- дополнительные
* тусклые цвета)
*/
/* выполнить обмены с портами */
if( ioctl( ega, EGAIO, PORT ) < 0 ){
fprintf( stderr, "Can't out port\n" );
perror( "out" );
}
}
А. Богатырев, 1992-95 - 353 - Си в UNIX
void main(ac, av) char **av;{
void fall();
openscr();
if( ac == 1 ){
setbgcolor(020); /* темно-зеленый фон экрана */
fall(); /* осыпание букв */
} else {
if(*av[1] == 'g')
/* Установить режим адаптера graphics 640x350 16-colors */
ioctl( ega, SW_CG640x350, NULL);
/* Если вы хотите получить адрес видеопамяти в графическом режиме,
* вы должны СНАЧАЛА включить этот режим,
* ЗАТЕМ сделать screen=ioctl(ega, v->mapmode, NULL);
* и ЕЩЕ РАЗ сделать включение графического режима.
*/
/* Установить режим адаптера text 80x25 16-colors */
else ioctl( ega, SW_ENHC80x25, NULL);
}
closescr(0);
}
/* осыпать буквы вниз */
void fall(){
register i, j;
int rest;
int nextcol;
int n;
int changed = 1; /* не 0, если еще не все буквы опали */
char mask [ COLS ];
while( changed ){
changed = 0;
for( i = 0 ; i < COLS ; i++ )
mask[ i ] = 0;
for( i = 0 ; i < COLS ; i++ ){
rest = COLS - i; /* осталось осыпать колонок */
nextcol = rand() % rest;
j = 0; /* индекс в mask */
n = 0; /* счетчик */
for(;;){
if( mask[j] == 0 ){
if( n == nextcol ) break;
n++;
} j++;
}
changed += fallColumn( j );
mask[j] = 1;
}
}
}
А. Богатырев, 1992-95 - 354 - Си в UNIX
/* осыпать буквы в одном столбце */
int fallColumn( x ){
register int y;
char ch, attr;
int firstspace = (-1);
int firstnospace = (-1);
Again:
/* find the falled array */
for( y=LINES-1; y >= 0 ; y-- ){
ch = GET( x, y );
attr = GETATTR( x,y );
if( white(ch, attr)){
firstspace = y;
goto FindNoSpace;
}
}
AllNoSpaces:
return 0; /* ничего не изменилось */
FindNoSpace: /* найти не пробел */
for( ; y >= 0 ; y-- ){
ch = GET( x, y );