Часть пятая. Что то с памятью...Оглавление
Что то с памятью моей стало...
В 
прошлой части мы научились выводить в консоли различные сообщения. Теперь настало время не только читать с консольного окна но и принимать различные сообщения от внешних устройств (пока что научимся принимать сообщения от устройств ввода/вывода).
Итак давайте зададимся целью создать игру. Простенькую консольную игрушку вроде арканоида или сокобана.


Для некоторых эти игры вызывают ностальгическое чувство утери прекрасного времени нашей молодости.
Ээээххх не будем о грустном, приступим к теоретическим вопросам.
Во первых графику в консоли мы можем реализовать только текстовыми символами(нет, возможна и GDI графика, но это уже будет не чистая консоль) Вот коды символов консоли для наших целей
Код: "CODE"
 
   0 1 2 3 4 5 6 7 8 9 A B C D E F
B  ░ ▒ ▓ │ ┤ ╡ ╢ ╖ ╕ ╣ ║ ╗ ╝ ╜ ╛ ┐
C  └ ┴ ┬ ├ ─ ┼ ╞ ╟ ╚ ╔ ╩ ╦ ╠ ═ ╬ ╧
D  ╨ ╤ ╥ ╙ ╘ ╒ ╓ ╫ ╪ ┘ ┌ █ ▄ ▌ ▐ ▀
 
Из этого ясно, что символу ▓ будет соответствовать код 
0B2X, а символу ╟ соответственно 
0С7XВ консольных приложениях есть два пути взаимодействия с пользователем. Высокоуровневый ввод/вывод и низкоуровневый.
С высокоуровневыми конструкциями 
WriteStr() и 
ReadString() мы уже знакомы по 
предыдущим статьям, 
так что давайте рассмотрим низкоуровневые конструкции и особенности Оберон компиляторов обрабатывать данные.
Как положено в таких разработки, для приёма сообщений от операционной системы нам понадобится две вещи.
Первая - это цикл (не вечный) и вторая - это сам приёмник информации (чаще всего это функция).
Выглядеть всё наше хозяйство будет вот так
Код: "OBERON"
- REPEAT 
-   
-     W.GetNumberOfConsoleInputEvents(C.hIn,r); 
-     C.ReadConsoleData(CD); 
-   
- UNTIL (CD.KeyCode=W.VK_ESCAPE) OR Exit; 
Цикл мы предусмотрительно сделали с выходом по условию (забегая вперёд - или по клавише 
Esc или по внутреннему какому то условию)
Я не привожу здесь описаний и код всего взаимодействия, моя цель объяснить особенности наших внутренних 
обёрток для API операционной системы.
Отвлекусь от темы и объясню, что такое так называемая 
обёртка в программировании.
Посмотрим на следующую функцию
Код: "OBERON"
- PROCEDURE WriteStr* (str: String); 
- VAR 
-   n,i,c:Integer; 
- BEGIN 
-   n:=Length(str); 
-   W.WriteFile(hOut, str, n, NIL, NIL); 
- END WriteStr; 
В нутри её мы замечаем 
WinApi функцию 
WriteFile.
Так вот - обёртка это удобное представление некоих действий.
В данном случае, что бы постоянно не вычислять длину строки и не вызывать 
WinApi функцию вывода в консоль
мы просто написали свою и используем для вывода строк функцию 
WriteStr с единственным параметром - 
строкаТак же и библиотеки в языках высокого уровня - это всего лишь 
обёртки для API функций ОС.
Ну суд да дело, а кашу надо есть.
Функция 
WinApi - 
GetNumberOfConsoleInputEvents всего лишь ожидает от операционной системы сообщения (ну зачем нам гонять цикл не по делу) и затем передаёт управление далее программе.
Второй функцией мы видим 
C.ReadConsoleData(CD);.
Так как в секции импорта модуль консоли у нас определена как 
CКод: "OBERON"
- IMPORT  
- 	SYSTEM, C:=Console, W:=Windows; 
То и функция будет в этом модуле.
Открываем наш файл 
Console.Mod и... ничего вменяемого кроме копирайта не находим.
Однако после многочасовых поисков в любом текстовом редакторе натыкаемся на уже известную по имени функцию
ReadConsoleData которая как мы понимаем является обёрткой для какой то функции 
ReadConsoleInputX, таким же макаром исследуем модуль 
Windows.Mod исходя из 
W:=Windows в секции импорта и находим её описание а так же описание похожей функции
Код: "OBERON"
-   
-   ReadConsoleInput-             : PROCEDURE [WINAPI] (hConsoleInput: HANDLE; VAR lpBuffer: INPUT_RECORD;  
-                                                      nLength: LONGINT; VAR lpNumberOfEventsRead: LONGINT): BOOL; 
-   ReadConsoleInputX-            : PROCEDURE [WINAPI] (hConsoleInput: HANDLE; VAR lpBuffer: INPUT_RECORD_X;  
-                                                      nLength: LONGINT; VAR lpNumberOfEventsRead: LONGINT): BOOL; 
Что за дела? Спросите вы. Почему функция 
ReadConsoleInput это WinApi функция которая есть в описании а 
ReadConsoleInputХ это какая то новая функция с изменнённым типом lpBuffer: 
INPUT_RECORD_X.
Сейчас я вам всё объясню.
Посмотрим на апишную структуру 
INPUT_RECORDКод: "OBERON"
- INPUT_RECORD*                             =  RECORD [NOTAG] 
-     EventType*: INTEGER; 
-     Event*: RECORD [union] 
-       KeyEvent*              : KEY_EVENT_RECORD; 
-       MouseEvent*            : MOUSE_EVENT_RECORD; 
-       WindowBufferSizeEvent* : WINDOW_BUFFER_SIZE_RECORD; 
-       MenuEvent*             : MENU_EVENT_RECORD; 
-       FocusEvent*            : FOCUS_EVENT_RECORD; 
-     END; 
-   END; 
Поле 
Event определено как UNION а значит это изменяемое поле и значение его будет зависить от 
EventTypeТо есть если 
EventType равен нулю то запонится поле 
KeyEvent, если двум то заполнится поле 
WindowBufferSizeEvent одно но!!!
ПОЛЯ UNION НЕ ПОДДЕРЖИВАЮТСЯ В ETH Oberon КОМПИЛЯТОРЕ!!!И что же делать? Придётся создать структуру похожую, но с массивом данных, в которые и придут данные из Api функции, а потом уже по этим данным заполнить свою структуру для удобства.
Код: "OBERON"
- INPUT_RECORD_X*                           =  RECORD [NOTAG] 
-     EventType*: INTEGER; 
-     Event*: RECORD [NOTAG] 
-       Data*: ARRAY 32 OF CHAR; 
-     END; 
-   END; 
: 
ARRAY 32 
OF CHAR;
Вот в этот массив имеющий длинну самой большой структуры 
INPUT_RECORD мы и записываем наши данные.
После этого обрабатываем их 
обёрткой ReadConsoleData и заполняем уже нашу структуру 
CONSOLE_DATAКод: "OBERON"
- CONSOLE_DATA*                             =  RECORD 
-     EventType*   : LONGINT; 
-     KeyDown*     : LONGINT; 
-     KeyChar*     : CHAR; 
-     KeyCode*     : INTEGER; 
-     MouseX*      : INTEGER; 
-     MouseY*      : INTEGER; 
-     MouseBState* : SET; 
-     MouseCState* : SET; 
-     MouseEState* : SET; 
-   END; 
Она включает в себя коды нажатых клавиш, координаты мышки а так же статусы сообщений (нажата/отжата кнопка, нажата правая/средняя/левая кнопка мыши)
Ну вот пожалуй и всё. Основные моменты я описал в данной статье, а частные вы можете посмотреть во вложении.
Посмотреть скриншоты игры и саму игру можно скачать тут - 
Скриншоты и описание.
Засим консольный цикл считаю законченым, далее мы перейдем уже к проработке GUI приложений, OpenGL библиотеке и так далее.