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

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