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 напишите функции для:
  • очистки экрана.
  • позиционирования курсора.
  • включения/выключения режима выделения текста инверсией.

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

Используются технологии uCoz