У
практиці програмування необхідність читання вмісту екрана виникає, як правило,
у наступних ситуаціях: 1) при організації віконного інтерфейсу; 2) при побудові
програм переносу резидентной програмою тексту в довільний текстовий редактор;
3) при побудові різних резидентных HELP – систем, що видають “підказку” по слову
в поточній позиції курсору (так називану контекстно
– чуттєву “підказку”).
У даному параграфі розглядаються:
1)засобу BIOSa для
читання інформації з екрана;
2)читання інформації
безпосередньо з відеобуфера;
3)бібліотечні функції
Turbo C для читання інформації з екрана.
Усі розглянуті в данномпараграфе
функції розраховані на роботу адаптера в текстових режимах.
Функція AH=08h
переривання 10h BIOS повертає в регістрі AL ASCII – символу в поточній позиції
курсору. Вона
працює тільки для текстових режимів.
Атрибут прочитаного символу повертається в регістрі AH. Таким чином, позиционируя
курсор по області екрана і виконуючи раз за разом функцію читання, можна одержати
повну копію вмісту області екрана – коди символів і їхні атрибути.
При побудові функцій
читання інформації з екрана “корінний” є функція read_sym(), що повертає
слово, молодший байт якого дорівнює
ASCII – коду символу, а старший – атрибуту символу в поточній позиції курсору.
У приведеному нижче прикладі використовується звертання до функції AH=08h BIOS.
Функція працює з активною відеосторінкою адаптера.
/*L9_12. C*/
int read_sym(void)
{ _AH=0x08;
geninterrupt(0x10);
return(_AX);
}
Читання символу з
екрана засобами BIOSа вимагає збереження, відновлення і позиционирования
курсору, відбувається повільно, не даючи при цьому ніяких переваг. Але в одному
випадку воно усе-таки виправдано – при побудові працюючого й у графічному
режимі варіанта функції “перефарби” рядка.
Часто
виникає проблема зміни тільки кольору рядка символів, що вже знаходиться на
екрані, причому цей рядок може розташовуватися або горизонтально, або
вертикально. Наприклад, при виконанні вибору опції меню після кожного
натискання клавіші зі стрілкою переміщається “высвечивание”. Переміщення “высвечивания”
виконується відновленням кольору попереднього рядка меню в нормальний стан і
зміною кольору наступного рядка. Інше, досить популярне в даний час,
застосування функції “перефарби” рядка – це створення “тіні” для вікон. Тінь
“наводиться” на екран зміною кольору символів по суміжним горизонтальній і
вертикальній гранях вікна. Можливі й інші застосування подібних функцій. Далі приводиться
приклад комбінованої функції hv_light(), що у залежності від значення перемінної
mode “перефарбовує” рядок на екрані, рухаючи або ліворуч праворуч горизонтально
(mode=HORIZONTAL), або знизу нагору вертикально (mode=VERTICAL) від початкової крапки,
заданої координатами рядка s0 і стовпця c0 екрана. Координати відраховуються
від 0. Ідея розглянутої функції складається в послідовному читанні символу з
поточної позиції курсору і його повторного запису на екран, але з атрибутом
atr. У прикладі, що приводиться, функція для доступу до екрана використовує
BIOS, що рятує від необхідності піклуватися про визначення типу відеоадаптера й
інших параметрів дисплея і дозволяє застосовувати функцію в графічних режимах.
Функція використовує активну відеосторінку і контролює допустимість заданих
параметрів. Якщо рядок довжиною в n символів не може бути “перефарбована”,
повертається BAD_PARAM; у противному випадку повертається OK. Символічні
константи визначені у файлі “screen.h”.
/*L9_13.C*/
#include “screen.h”
#include <dos.h>
int hv_light(int s0, int c0, int n, char atr,
int mode)
{ struct cursor_pos old_cursor; /*позиція
курсору*/
register char str, col, max_stolb, max_string, cur_page;
register cur_pos;
/*Контроль допустимості параметрів.*/
_ES=0x40;
max_stolb=*(char_es*)0x4a;
cur_page=*(char_es*)0x62;
max_string=*(char_es*)0x84;
if
(s0>=0 && c0>=0 && s0<=max_string &&
c0<=max_stolb &&
((mode=HORIZONTAL && (c0+n-1)<=max_stolb) |
(mode=VERTICAL && (s0+n-1)<=max_string)))
{
save_cur(&old_cursor); /*збереження позиції курсору*/
cursor (OFF); /* “вимикання” курсору*/
str=s=0; col=c0;
/* Цикл читання, записи і просування курсору.*/
while(n--)
{ /*Установка курсору в потрібну позицію.*/
cur_pos=(str<<8) | col;
*(unsigned_es*)
(0x50+(cur_page<<1)))=cur_pos;
/*Читання символу/атрибута в позиції курсору.*/
_AH=0x08; geninterrupt(0x10);
/*Висновок символу в поточній позиції курсору.*/
_BH=cur_page; _BL=atr; _CX=1; _AH=9;
geninterrupt(0x10);
/*Модифікація координат курсору*/
if (mode= HORIZONTAL) col++;
else
if (mode=VERTICAL) STR++;
else return BAD_PARAM;
}
/*Відновлення
позиції і видимості курсору.*/
rest_cur (&old_cursor); cursor(ON);
return OK;
}
else
return BAD_PARAM;
}
Для одержання
максимальної продуктивності читання інформації варто виконувати безпосередньо з
відеобуфера адаптера. При цьому, як і при записі у відеобуфер, можливе
виникнення “снігу” на екрані (про причини виникнення “снігу” і способі ряборотьби з ним див. у 9.4). Розглянуті далі функції не включають секцію
придушення “снігу”.
Класична задача,у
якій приходиться читати уміст відеопам'яті, - це збереження вікна екрана. У Turbo C для цього призначена функція
gettext(), і якісь інші варіанти на Си напевно будуть працювати повільніше.
#include<conio.h>
int gettext( int left, int top, int right,
int bottom, void*destin)
Записує в буфер
destin символи й атрибути текстового вікна, заданого рядком і стовпцем лівого
верхніх (left, top) і правого нижнього (right, bottom) кутів. Перші два слова
буфера займають ширина і довжина скопійованого вікна. Працює тільки в текстових
режимах відеоадаптера. Координати задаються щодо верхнього лівого кута екрана
(1,1). У випадку успіху повертає ненульове число.
Розглянемо приклад
Сі-функції, “читаючий” словом інформації в поточній позиції курсору. Словом вважається будь-яка послідовність
ASCII-символів, що не включає символи-роздільники. Усі символи-роздільники містяться
в ASCIIZ-рядок, покажчик на початок якої є параметром функції (separators).
Слово на екрані “копіюється” у статичний буфер функції і доповнюється в самому
кінці нуль-термінатором, утворити звичайний ASCIIZ-рядок. Покажчик на її
початок повертається в крапку виклику. У перемінних, на які вказує str і stolb
(нумеруються від 0), передаються рядок і стовпець початку слова на екрані. Якщо
слово не знайдене, str і stolb зберігають поточну позицію курсору. Значення
покажчика, що повертається функцією, NULL говорить про відсутність у поточній
позиції курсору слова у визначеному ранеее змісті. У цьому випадку значення рядка
і стовпця в перемінних, на які посилаються str і stolb, будуть рівні поточної
позиції курсору. Для підвищення продуктивності функція використовує зовнішні
перемінні, співпадаючі за змістом з описаними для функції printhe() (див.
L9_3.C). Алгоритм роботи функції такий. Визначається поточна позиція курсору і
з відеопам'яті зчитується весь рядок символів, у якій розташовується курсор.
Потім починається цикл виділення й аналізу слів у рядку. Для “розбору” рядка на
слова використовується функція Turbo C виділення лексем strtok(). Якщо поточне положення
курсору попадає на слово, робота функції завершується й у крапку виклику
передається покажчик на виділене слово. Якщо в поточній позиції курсору
записаний символ-роздільник, повертається слово, що знаходиться ліворуч від
позиції курсору. Якщо ж ліворуч знаходиться ще один символ-роздільник,
повертається NULL.
/*L9_14. C*/
#include <dos.h>
#include <string.h>
#include “screen.h”
char*read_scr(char*separator, int*str,
int*stolb)
{ extern unsigned vid_memory, start_adr,
max_stolb, max_str;
static char buf[81]; /*буфер для рядка*/
register char index, cur_string, cur_stolb;
register word; char_es*ptr; char*token;
/*Визначення координат курсору з використанням значень у CRTC-регістрах
адаптера – це
підвищує надійність роботи функції в складі резидентных програм.*/
save_cur(unsigned) ((cur_cursor.reg_14<<8 | cur_cursor.reg_15);
cur_string=word | max_stolb; cur_stolb=word % max_stolb;
*str=cur_string; *stolb=cur_stolb-1;
/*Цикл читання символів з екрана; символ '\0' заміняється пробілом.*/
ES=vid_memory;
ptr=(char_es*) (start_adr+((max_stolb*cur_string)<<1));
for
(index=0; index<max_stolb; index++)
{
buf[index]=*ptr;
if(!buf[index]) buf[index]=’’;
ptr+=2;
}
buf[max_stolb]=’\0’;
/*Цикл виділення лексем(див. першу книгу комплексу).*/
token=strtok(buf, separators);
do {
word=FP_OFF(token-buf);
if (word<=cur_stolb &&(word+strlen(token))>=cur_stolb)
{ *stolb=word;return token;}
}
while((token=strtok(NULL, separators))!=NULL);
return NULL;
}
Функцію read_scr()
можна використовувати:
1)у резидентных програмах контекстних
Help-систем; коли програма активізується, вона зчитує слово в поточній
позиції курсору і по ньому видає на екран порцію інформації;
2)у резидентных програмах “обертання” регістра,
на якому набране слово: перетворення всіх прописних букв слова в рядкові, а
рядкових у прописні (обертання верхнього/ нижнього регістра клавіатури), заміна
всіх англійських букв їх русскики клавішними еквівалентами і зворотне
перетворення (“обертання” регістра Русявий/Лат) і т.п. коли програма
активізується, вона зчитує слово в поточній позиції курсору. Потім записує в
буфер клавіатури коди клавіш, що імітують переміщення курсору, стирання символу
і перетворені символи.
Приведемо максимально
продуктивний варіант функції hv_light() “перефарбування” рядка або вертикально,
або горизонтально (варіант функції, що працює через BIOS, див.у L9_13.C). При
роботі на застарілих моделях CGA-адаптерів вона може породжувати “сніг” (спосіб
усунення цього явища див. у 9.4).
/*L9_15.C*/
#include<dos.h>
#include “screen.h”
int hv_light(int s0, int c0, int n, char atr,
int mode)
{ extern unsigned vid_memory, start_adr,
max_stolb, max_str;
register unsigned_es*work;
/*Контроль допустимості параметрів.*/
if(s0>=0 && c0>=0 && s0<=max_str &&
c0<=max_stolb &&
((mode==HORIZONTAL &&(c0+n-1)<=max_stolb) |
(mode==VERTICAL && (s0+n-1)<=max_str)))
{
/*Обчислення покажчика на перший байт у відеобуфері.*/
work=(unsigned_es*)(start_adr+((max_stolb*s0+c0)<<1));
_ES=vidmemory;
/*Цикл читання і запису у відеобуфер.*/
while(n--)
{
_AX=*work; AH=atr; *work=AX;
if(mode==HORIZONTAL)work++;
else
if(mode==VERTICAL)work+=max_stolb;
else return BAD_PARAM;
}
return OK;
}
else
return BAD_PARAM;
}
![]() |
![]() |
![]() |