8.2.

Для работы может оказаться более удобным иметь указатель на видеопамять как на массив структур. Приведем пример для системы MS DOS: #include <dos.h> /* там определено MK_FP */ char far *screen = MK_FP(0xB800 /*сегмент*/, 0x0000 /*смещение*/); struct symb{ char chr; char attr; } far *scr, far *ptr; #define COLS 80 /* число колонок */ #define LINES 25 /* число строк */ #define SCR(x,y) scr[(x) + COLS * (y)] /* x из 0..79, y из 0..24 */ void main(){ int x, y; char c; scr = (struct symb far *) screen; /* или сразу * scr = (struct symb far *) MK_FP(0xB800,0x0000); */ /* переписать строки экрана справа налево */ for(x=0; x < COLS/2; x++ ) for( y=0; y < LINES; y++ ){ c = SCR(x,y).chr; SCR(x,y).chr = SCR(COLS-1-x, y).chr; SCR(COLS-1-x, y).chr = c; } /* сделать цвет экрана: желтым по синему */ for(x=0; x < COLS; x++) for(y=0; y < LINES; y++) SCR(x,y).attr = (0xE | (0x1 << 4)); /* желтый + синий фон */ /* прочесть любую кнопку с клавиатуры (пауза) */ (void) getch(); } И, наконец, еще удобнее работа с видеопамятью как с двумерным массивом структур: #include <dos.h> /* MS DOS */ #define COLS 80 #define LINES 25 struct symb { char chr; char attr; } (far *scr)[ COLS ] = MK_FP(0xB800, 0); void main(void){ register x, y; for(y=0; y < LINES; y++) for(x=0; x < COLS; ++x){ scr[y][x].chr = '?'; scr[y][x].attr = (y << 4) | (x & 0xF); } getch(); }

Учтите, что при работе с экраном через видеопамять, курсор не перемещается! Если в обычной работе с экраном текст выводится в позиции курсора и курсор автоматически продвигается, то здесь курсор будет оставаться на своем прежнем месте. Для перемещения курсора в нужное вам место, вы должны его поставить явным образом по окончании записи в видеопамять (например, обращаясь к портам видеоконтроллера).

Обратите внимание, что спецификатор модели памяти far должен указываться перед КАЖДЫМ указателем (именно для иллюстрации этого в первом примере описан неиспользуемый указатель ptr).

8.3.

Составьте программу сохранения содержимого экрана IBM PC (видеопамяти) в текстовом режиме в файл и обратно (в системе XENIX).

8.4.

Пользуясь прямым доступом в видеопамять, напишите функции для спасения прямоугольной области экрана в массив и обратно. Вот функция для спасения в массив: typedef struct { short xlen, ylen; char *save; } Pict; extern void *malloc(unsigned); Pict *gettext (int x, int y, int xlen, int ylen){ Pict *n = (Pict *) malloc(sizeof *n); register char *s; register i, j; n->xlen = xlen; n->ylen = ylen; s = n->save = (char *) malloc( 2 * xlen * ylen ); for(i=y; i < y+ylen; i++) for(j=x; j < x+xlen; j++){ *s++ = SCR(j,i).chr ; *s++ = SCR(j,i).attr; } return n; }

Добавьте проверки на корректность xlen, ylen (в пределах экрана). Напишите функцию puttext для вывода спасенной области обратно; функцию free(buf) лучше в нее не вставлять. void puttext (Pict *n, int x, int y){ register char *s = n->save; register i, j; for(i=y; i < y + n->ylen; i++) for(j=x; j < x + n->xlen; j++){ SCR(j,i).chr = *s++; SCR(j,i).attr = *s++; } } /* очистка памяти текстового буфера */ void deltext(Pict *n){ free(n->save); free(n); } Приведем еще одну полезную функцию, которая может вам пригодиться - это аналог printf при прямой работе с видеопамятью. #include <stdarg.h> /* текущий цвет: белый по синему */ static char currentColor = 0x1F; int videoprintf (int x, int y, char *fmt, ...){ char buf[512], *s; va_list var; /* clipping (отсечение по границам экрана) */ if( y < 0 || y >= LINES ) return x; va_start(var, fmt); vsprintf(buf, fmt, var); va_end(var); for(s=buf; *s; s++, x++){ /* отсечение */ if(x < 0 ) continue; if(x >= COLS) break; SCR(x,y).chr = *s; SCR(x,y).attr = currentColor; } return x; } void setcolor (int col){ currentColor = col; }

8.5.

Пользуясь написанными функциями, реализуйте функции для "выскакивающих" окон (pop-up window): Pict *save; save = gettext (x,y,xlen,ylen); // ... рисуем цветными пробелами прямоугольник с // углами (x,y) вверху-слева и (x+xlen-1,y+ylen-1) // внизу-справа... // ...рисуем некие таблицы, меню, текст в этой зоне... // стираем нарисованное окно, восстановив то изображение, // поверх которого оно "всплыло". puttext (save,x,y); deltext (save); Для начала напишите "выскакивающее" окно с сообщением; окно должно исчезать по нажатию любой клавиши. c = message(x, y, text); Размер окна вычисляйте по длине строки text. Код клавиши возвращайте в качестве значения функции.

Теперь сделайте text массивом строк: char *text[]; (последняя строка - NULL).

8.6.

Сделайте так, чтобы "выскакивающие" окна имели тень. Для этого надо сохранить в некоторый буфер атрибуты символов (сами символы не надо!), находящихся на местах $: ########## ##########$ ##########$ $$$$$$$$$$ а затем прописать этим символам на экране атрибут 0x07 (белый по черному). При стирании окна (puttext-ом) следует восстановить спасенные атрибуты этих символов (стереть тень). Если окно имеет размер xlen*ylen, то размер буфера равен xlen+ylen-1 байт.

8.7.

Напишите функцию, рисующую на экране прямоугольную рамку. Используйте ее для рисования рамки окна.

8.8.

Напишите "выскакивающее" окно, которое проявляется на экране как бы расширяясь из точки: ############## ###### ############## ### ###### ############## ###### ############## ############## Вам следует написать функцию box(x,y,width,height), рисующую цветной прямоугольник с верхним левым углом (x,y) и размером (width,height). Пусть конечное окно задается углом (x0,y0) и размером (W,H). Тогда "вырастание" окна описывается таким алгоритмом: void zoom(int x0, int y0, int W, int H){ int x, y, w, h, hprev; /* промежуточное окно */ for(hprev=0, w=1; w < W; w++){ h = H * w; h /= W; /* W/H == w/h */ if(h == hprev) continue; hprev = h; x = x0 + (W - w)/2; /* чтобы центры окон */ y = y0 + (H - h)/2; /* совпадали */ box(x, y, w, h); delay(10); /* задержка 10 миллисек. */ } box(x0, y0, W, H); }

8.9.

Составьте библиотеку функций, аналогичных библиотеке curses, для ЭВМ IBM PC в ОС XENIX. Используйте прямой доступ в видеопамять.

8.10.

Напишите рекурсивное решение задачи "ханойские башни" (перекладывание дисков: есть три стержня, на один из них надеты диски убывающего к вершине диаметра. Требуется переложить их на третий стержень, никогда не кладя диск большего диаметра поверх диска меньшего диаметра). Усложнение - используйте пакет curses для изображения перекладывания дисков на экране терминала. Указание: идея рекурсивного алгоритма: carry(n, from, to, by) = if( n > 0 ){ carry( n-1, from, by, to ); перенесиОдинДиск( from, to ); carry( n-1, by, to, from ); } Вызов: carry( n, 0, 1, 2 ); n - сколько дисков перенести (n > 0). from - откуда (номер стержня). to - куда. by - при помощи (промежуточный стержень). n дисков потребуют (2**n)-1 переносов.

8.11.

Напишите программу, ищущую выход из лабиринта ("червяк в лабиринте"). Лабиринт загружается из файла .maze (не забудьте про расширение табуляций!). Алгоритм имеет рекурсивную природу и выглядит примерно так: #include <setjmp.h> jmp_buf jmp; int found = 0; maze(){ /* Это головная функция */ if( setjmp(jmp) == 0 ){ /* начало */ if( неСтенка(x_входа, y_входа)) GO( x_входа, y_входа); } } GO(x, y){ /* пойти в точку (x, y) */ if( этоВыход(x, y)){ found = 1; /* нашел выход */ пометить(x, y); longjmp(jmp, 1);} пометить(x, y); if( неСтенка(x-1,y)) GO(x-1, y); /* влево */ if( неСтенка(x,y-1)) GO(x, y-1); /* вверх */ if( неСтенка(x+1,y)) GO(x+1, y); /* вправо */ if( неСтенка(x,y+1)) GO(x, y+1); /* вниз */ снятьПометку(x, y); } #define пометить(x, y) лабиринт[y][x] = '*' #define снятьПометку(x, y) лабиринт[y][x] = ' ' #define этоВыход(x, y) (x == x_выхода && y == y_выхода) /* можно искать "золото": (лабиринт[y][x] == '$') */ неСтенка(x, y){ /* стенку изображайте символом @ или # */ if( координатыВнеПоля(x, y)) return 0; /*край лабиринта*/ return (лабиринт[y][x] == ' '); } Отобразите массив лабиринт на видеопамять (или воспользуйтесь curses-ом). Вы увидите червяка, ползающего по лабиринту в своих исканиях.

8.12.

Используя библиотеку termcap напишите функции для:

© Copyright А. Богатырев, 1992-95
Си в UNIX

Назад | Содержание | Вперед