// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------
// Zbiór procedur obsługi konsoli znakowej w Windows
//          (C)2006 mgr Jerzy Wałaszek
//--------------------------------------------------

#include <iostream>
#include <windows.h>
#include <string>
#include "newconio.h"

using namespace std;

__declspec(dllimport) BOOL WINAPI SetConsoleDisplayMode(HANDLE,DWORD,PCOORD);

static WORD   _txattr;
static HANDLE _hcout,_hcin;
static int    _cextend = -1;

//----------------------------------------------------------------------
// Poniższa tablica odwzorowuje wirtualne kody klawiszy Windows na kody
// BIOS dla klawiatury IBM PC. Dla każdego wirtualnego kodu klawisza
// dostępne są cztery wartości BIOS: zwykła, z shift, z Ctrl i z Alt.
// Kombinacje klawiszy nie posiadające odpowiednika BIOS mają wartość
// -1 i są ignorowane. Rozszerzone klawisze (nie ze zbioru ASCII)
// mają ustawiony na 1 bit 8 przy pomocy makra EXT.
//----------------------------------------------------------------------

#define EXT(key)    ((key)+0x100)
#define ISEXT(val)  ((val)&0x100)
#define EXTVAL(val) ((val)&0xff)

struct kbd
{
    short keycode; // kod wirtualny
    short normal;  // normalny kod BIOS
    short shift;   // kod BIOS z shift
    short ctrl;    // kod BIOS z Ctrl
    short alt;     // kod BIOS z Alt
} kbdtab [] =
{
//  wirtualny     normalny   shift       ctrl         alt

  { VK_BACK,      0x08,       0x08,       0x7f,       EXT(14)  },
  { VK_TAB,       0x09,       EXT(15),    EXT(148),   EXT(165) },
  { VK_RETURN,    0x0d,       0x0d,       0x0a,       EXT(166) },
  { VK_ESCAPE,    0x1b,       0x1b,       0x1b,       EXT(1)   },
  { VK_SPACE,     0x20,       0x20,       EXT(3),     0x20,    },
  { '0',          '0',        ')',        -1,         EXT(129) },
  { '1',          '1',        '!',        -1,         EXT(120) },
  { '2',          '2',        '@',        EXT(3),     EXT(121) },
  { '3',          '3',        '#',        -1,         EXT(122) },
  { '4',          '4',        '$',        -1,         EXT(123) },
  { '5',          '5',        '%',        -1,         EXT(124) },
  { '6',          '6',        '^',        0x1e,       EXT(125) },
  { '7',          '7',        '&',        -1,         EXT(126) },
  { '8',          '8',        '*',        -1,         EXT(127) },
  { '9',          '9',        '(',        -1,         EXT(128) },

  { 'A',          'a',        'A',        0x01,       EXT(30)  },
  { 'B',          'b',        'B',        0x02,       EXT(48)  },
  { 'C',          'c',        'C',        0x03,       EXT(46)  },
  { 'D',          'd',        'D',        0x04,       EXT(32)  },
  { 'E',          'e',        'E',        0x05,       EXT(18)  },
  { 'F',          'f',        'F',        0x06,       EXT(33)  },
  { 'G',          'g',        'G',        0x07,       EXT(34)  },
  { 'H',          'h',        'H',        0x08,       EXT(35)  },
  { 'I',          'i',        'I',        0x09,       EXT(23)  },
  { 'J',          'j',        'J',        0x0a,       EXT(36)  },
  { 'K',          'k',        'K',        0x0b,       EXT(37)  },
  { 'L',          'l',        'L',        0x0c,       EXT(38)  },
  { 'M',          'm',        'M',        0x0d,       EXT(50)  },
  { 'N',          'n',        'N',        0x0e,       EXT(49)  },
  { 'O',          'o',        'O',        0x0f,       EXT(24)  },
  { 'P',          'p',        'P',        0x10,       EXT(25)  },
  { 'Q',          'q',        'Q',        0x11,       EXT(16)  },
  { 'R',          'r',        'R',        0x12,       EXT(19)  },
  { 'S',          's',        'S',        0x13,       EXT(31)  },
  { 'T',          't',        'T',        0x14,       EXT(20)  },
  { 'U',          'u',        'U',        0x15,       EXT(22)  },
  { 'V',          'v',        'V',        0x16,       EXT(47)  },
  { 'W',          'w',        'W',        0x17,       EXT(17)  },
  { 'X',          'x',        'X',        0x18,       EXT(45)  },
  { 'Y',          'y',        'Y',        0x19,       EXT(21)  },
  { 'Z',          'z',        'Z',        0x1a,       EXT(44)  },

  { VK_PRIOR,     EXT(73),    EXT(73),    EXT(132),   EXT(153) },
  { VK_NEXT,      EXT(81),    EXT(81),    EXT(118),   EXT(161) },
  { VK_END,       EXT(79),    EXT(79),    EXT(117),   EXT(159) },
  { VK_HOME,      EXT(71),    EXT(71),    EXT(119),   EXT(151) },
  { VK_LEFT,      EXT(75),    EXT(75),    EXT(115),   EXT(155) },
  { VK_UP,        EXT(72),    EXT(72),    EXT(141),   EXT(152) },
  { VK_RIGHT,     EXT(77),    EXT(77),    EXT(116),   EXT(157) },
  { VK_DOWN,      EXT(80),    EXT(80),    EXT(145),   EXT(160) },
  { VK_INSERT,    EXT(82),    EXT(82),    EXT(146),   EXT(162) },
  { VK_DELETE,    EXT(83),    EXT(83),    EXT(147),   EXT(163) },
  { VK_NUMPAD0,   '0',        EXT(82),    EXT(146),   -1       },
  { VK_NUMPAD1,   '1',        EXT(79),    EXT(117),   -1       },
  { VK_NUMPAD2,   '2',        EXT(80),    EXT(145),   -1       },
  { VK_NUMPAD3,   '3',        EXT(81),    EXT(118),   -1       },
  { VK_NUMPAD4,   '4',        EXT(75),    EXT(115),   -1       },
  { VK_NUMPAD5,   '5',        EXT(76),    EXT(143),   -1       },
  { VK_NUMPAD6,   '6',        EXT(77),    EXT(116),   -1       },
  { VK_NUMPAD7,   '7',        EXT(71),    EXT(119),   -1       },
  { VK_NUMPAD8,   '8',        EXT(72),    EXT(141),   -1       },
  { VK_NUMPAD9,   '9',        EXT(73),    EXT(132),   -1       },
  { VK_MULTIPLY,  '*',        '*',        EXT(150),   EXT(55)  },
  { VK_ADD,       '+',        '+',        EXT(144),   EXT(78)  },
  { VK_SUBTRACT,  '-',        '-',        EXT(142),   EXT(74)  },
  { VK_DECIMAL,   '.',        '.',        EXT(83),    EXT(147) },
  { VK_DIVIDE,    '/',        '/',        EXT(149),   EXT(164) },
  { VK_F1,        EXT(59),    EXT(84),    EXT(94),    EXT(104) },
  { VK_F2,        EXT(60),    EXT(85),    EXT(95),    EXT(105) },
  { VK_F3,        EXT(61),    EXT(86),    EXT(96),    EXT(106) },
  { VK_F4,        EXT(62),    EXT(87),    EXT(97),    EXT(107) },
  { VK_F5,        EXT(63),    EXT(88),    EXT(98),    EXT(108) },
  { VK_F6,        EXT(64),    EXT(89),    EXT(99),    EXT(109) },
  { VK_F7,        EXT(65),    EXT(90),    EXT(100),   EXT(110) },
  { VK_F8,        EXT(66),    EXT(91),    EXT(101),   EXT(111) },
  { VK_F9,        EXT(67),    EXT(92),    EXT(102),   EXT(112) },
  { VK_F10,       EXT(68),    EXT(93),    EXT(103),   EXT(113) },
  { VK_F11,       EXT(133),   EXT(135),   EXT(137),   EXT(139) },
  { VK_F12,       EXT(134),   EXT(136),   EXT(138),   EXT(140) },
  { 0xdc,         '\\',       '|',        0x1c,       EXT(43)  },
  { 0xbf,         '/',        '?',        -1,         EXT(53)  },
  { 0xbd,         '-',        '_',        0x1f,       EXT(130) },
  { 0xbb,         '=',        '+',        -1,         EXT(131) },
  { 0xdb,         '[',        '{',        0x1b,       EXT(26)  },
  { 0xdd,         ']',        '}',        0x1d,       EXT(27)  },
  { 0xba,         ';',        ':',        -1,         EXT(39)  },
  { 0xde,         '\'',       '"',        -1,         EXT(40)  },
  { 0xbc,         ',',        '<',        -1,         EXT(51)  },
  { 0xbe,         '.',        '>',        -1,         EXT(52)  },
  { 0xc0,         '`',        '~',        -1,         EXT(41)  },

  { -1,           -1,         -1,         -1,         -1       }  // KONIEC
};

// Funkcja inicjuje parametry konsoli - należy
// ją wywołać na samym początku programu
//--------------------------------------------

void _cinit()
{
  _hcout = GetStdHandle(STD_OUTPUT_HANDLE);
  _hcin  = GetStdHandle(STD_INPUT_HANDLE);
  _txattr = 7; // Czarne tło i białe znaki
  clrscr();
}

// Funkcja konwertuje tekst ze standardu Windows 1250
// na standard konsoli znakowej Latin II.
//---------------------------------------------------
// s - tekst kodowany wg Windows 1250
//---------------------------------------------------
string _pl(string s)
{
    for(unsigned i = 0; i < s.length(); i++)
        switch(s[i])
        {
            case 'ą' : s[i] = 165; break;
            case 'ć' : s[i] = 134; break;
            case 'ę' : s[i] = 169; break;
            case 'ł' : s[i] = 136; break;
            case 'ń' : s[i] = 228; break;
            case 'ó' : s[i] = 162; break;
            case 'ś' : s[i] = 152; break;
            case 'ż' : s[i] = 190; break;
            case 'ź' : s[i] = 171; break;
            case 'Ą' : s[i] = 164; break;
            case 'Ć' : s[i] = 143; break;
            case 'Ę' : s[i] = 168; break;
            case 'Ł' : s[i] = 157; break;
            case 'Ń' : s[i] = 227; break;
            case 'Ó' : s[i] = 224; break;
            case 'Ś' : s[i] = 151; break;
            case 'Ż' : s[i] = 189; break;
            case 'Ź' : s[i] = 141; break;
        }
    return s;
}

// Funkcja wyświetla zadany parametrem tekst
// na środku podanego wiersza konsoli
//------------------------------------------
void center(int y, string s)
{
  gotoxy((80 - s.length()) / 2, y);
  cout << s;
}

// Funkcja czyści ekran konsoli.
//------------------------------
void clrscr()
{
   COORD cpos = { 0, 0 };
   DWORD cwrt;
   CONSOLE_SCREEN_BUFFER_INFO cinf;
   DWORD csize;

   if(!GetConsoleScreenBufferInfo(_hcout, &cinf)) return;
   csize = cinf.dwSize.X * cinf.dwSize.Y;
   if(!FillConsoleOutputCharacter(_hcout,' ',csize,cpos,&cwrt)) return;
   if(!FillConsoleOutputAttribute(_hcout,_txattr,csize,cpos,&cwrt)) return;
   SetConsoleCursorPosition(_hcout, cpos);
   SetConsoleTextAttribute(_hcout,_txattr);
}

// Funkcja wyłącza kursor
//-----------------------
void cursoroff()
{
  CONSOLE_CURSOR_INFO ccinf;

  GetConsoleCursorInfo(_hcout,&ccinf);
  ccinf.bVisible = false;
  SetConsoleCursorInfo(_hcout,&ccinf);
}

// Funkcja włącza kursor
//-----------------------
void cursoron()
{
  CONSOLE_CURSOR_INFO ccinf;

  GetConsoleCursorInfo(_hcout,&ccinf);
  ccinf.bVisible = true;
  SetConsoleCursorInfo(_hcout,&ccinf);
}

// Funkcja wstrzymuje wykonanie na zadaną
// liczbę milisekund
//---------------------------------------
// msec - liczba milisekund
//---------------------------------------
void delay(int msec)
{
  unsigned t1 = GetTickCount();
  while((GetTickCount() - t1) < (unsigned)msec) ;
}

// Funkcja wypełnia zadany obszar okna konsoli
// podanym atrybutem. Pozycja kursora oraz znaki
// zawarte w obszarze nie są zmieniane.
//----------------------------------------------
// attr - atrybut do wypełnienia obszaru
// xb,yb - współrzędne lewego górnego narożnika
// xe,ye - współrzędne prawego dolnego narożnika
//----------------------------------------------
void fillrectattr(WORD attr, int xb, int yb, int xe, int ye)
{
  CONSOLE_SCREEN_BUFFER_INFO cinf;
  COORD pos;
  DWORD crd;

  if(!GetConsoleScreenBufferInfo(_hcout, &cinf)) return;
  if((xb >= cinf.dwSize.X) || (yb >= cinf.dwSize.Y) ||
     (xe < xb) || (ye < yb) || (xe < 0) || (ye < 0)) return; // poza oknem
  if(xb < 0) xb = 0;
  if(yb < 0) yb = 0;
  if(xe >= cinf.dwSize.X) xe = cinf.dwSize.X - 1;
  if(ye >= cinf.dwSize.Y) ye = cinf.dwSize.Y - 1;
  DWORD len = xe - xb + 1;
  pos.X = xb;
  for(int i = yb; i <= ye; i++)
  {
    pos.Y = i;
    FillConsoleOutputAttribute(_hcout,attr,len,pos,&crd);
  }
}

// Funkcja wypełnia zadany obszar okna konsoli
// podanym znakiem. Pozycja kursora oraz atrybuty
// zawarte w obszarze nie są zmieniane.
//-----------------------------------------------
// c - znak do wypełnienia obszaru
// xb,yb - współrzędne lewego górnego narożnika
// xe,ye - współrzędne prawego dolnego narożnika
//-----------------------------------------------
void fillrectch(TCHAR c, int xb, int yb, int xe, int ye)
{
  CONSOLE_SCREEN_BUFFER_INFO cinf;
  COORD pos;
  DWORD crd;

  if(!GetConsoleScreenBufferInfo(_hcout, &cinf)) return;
  if((xb >= cinf.dwSize.X) || (yb >= cinf.dwSize.Y) ||
     (xe < xb) || (ye < yb) || (xe < 0) || (ye < 0)) return; // poza oknem
  if(xb < 0) xb = 0;
  if(yb < 0) yb = 0;
  if(xe >= cinf.dwSize.X) xe = cinf.dwSize.X - 1;
  if(ye >= cinf.dwSize.Y) ye = cinf.dwSize.Y - 1;
  DWORD len = xe - xb + 1;
  pos.X = xb;
  for(int i = yb; i <= ye; i++)
  {
    pos.Y = i;
    FillConsoleOutputCharacter(_hcout,c,len,pos,&crd);
  }
}

// Funkcja wypełnia zadany obszar okna konsoli
// podanym znakiem i atrybutem. Pozycja kursora
// nie jest zmieniana.
//----------------------------------------------
// c - znak do wypełnienia
// attr - atrybut do wypełnienia obszaru
// xb,yb - współrzędne lewego górnego narożnika
// xe,ye - współrzędne prawego dolnego narożnika
//----------------------------------------------
void fillrect(TCHAR c, WORD attr, int xb, int yb, int xe, int ye)
{
  CONSOLE_SCREEN_BUFFER_INFO cinf;
  COORD pos;
  DWORD crd;

  if(!GetConsoleScreenBufferInfo(_hcout, &cinf)) return;
  if((xb >= cinf.dwSize.X) || (yb >= cinf.dwSize.Y) ||
     (xe < xb) || (ye < yb) || (xe < 0) || (ye < 0)) return; // poza oknem
  if(xb < 0) xb = 0;
  if(yb < 0) yb = 0;
  if(xe >= cinf.dwSize.X) xe = cinf.dwSize.X - 1;
  if(ye >= cinf.dwSize.Y) ye = cinf.dwSize.Y - 1;
  DWORD len = xe - xb + 1;
  pos.X = xb;
  for(int i = yb; i <= ye; i++)
  {
    pos.Y = i;
    FillConsoleOutputCharacter(_hcout,c,len,pos,&crd);
    FillConsoleOutputAttribute(_hcout,attr,len,pos,&crd);
  }
}

// Funkcja rysuje prostokątną ramkę
// type - określa typ ramki. Dozwolone typy, to:
//        FRAME_EMPTY,FRAME_SINGLE,FRAME_DOUBLE,
//        FRAME_SOLID, FRAME_SHADED
// attr - określa atrybut koloru dla ramki
// xb,yb - współrzędne lewego górnego wierzchołka
// xe,ye - współrzędne dolnego prawego wierzchołka
//------------------------------------------------
void frame(int type,int attr,int xb,int yb,int xe,int ye)
{
  TCHAR c;

  if((xb >= xe) || (yb >= ye)) return;
  switch(type) // lewy górny róg
  {
    case FRAME_EMPTY : c =  32; break;
    case FRAME_SINGLE: c = 218; break;
    case FRAME_DOUBLE: c = 201; break;
    case FRAME_SOLID : c = 219; break;
    case FRAME_SHADED: c = 177; break;
  }
  fillrect(c,attr,xb,yb,xb,yb);
  switch(type) // lewy dolny róg
  {
    case FRAME_SINGLE: c = 192; break;
    case FRAME_DOUBLE: c = 200; break;
  }
  fillrect(c,attr,xb,ye,xb,ye);
  switch(type) // prawy górny róg
  {
    case FRAME_SINGLE: c = 191; break;
    case FRAME_DOUBLE: c = 187; break;
  }
  fillrect(c,attr,xe,yb,xe,yb);
  switch(type) // prawy dolny róg
  {
    case FRAME_SINGLE: c = 217; break;
    case FRAME_DOUBLE: c = 188; break;
  }
  fillrect(c,attr,xe,ye,xe,ye);
  switch(type) // góra i dół
  {
    case FRAME_SINGLE: c = 196; break;
    case FRAME_DOUBLE: c = 205; break;
  }
  fillrect(c,attr,xb + 1,yb,xe - 1,yb);
  fillrect(c,attr,xb + 1,ye,xe - 1,ye);
  switch(type) // lewy i prawy
  {
    case FRAME_SINGLE: c = 179; break;
    case FRAME_DOUBLE: c = 186; break;
  }
  fillrect(c,attr,xb,yb + 1,xb,ye - 1);
  fillrect(c,attr,xe,yb + 1,xe,ye - 1);
}

// Funkcja rysuje prostokątną ramkę z wypełnieniem
// type - określa typ ramki. Dozwolone typy, to:
//   FRAME_EMPTY  - ramka zbudowana ze spacji
//   FRAME_SINGLE - ramka pojedyncza
//   FRAME_DOUBLE - ramka podwójna
//   FRAME_SOLID  - ramka wypełniona znakiem pełnym
//   FRAME_SHADED - ramka wypełniona znakiem cieniowanym
// attr - określa atrybut koloru dla ramki i tła
// xb,yb - współrzędne lewego górnego wierzchołka
// xe,ye - współrzędne dolnego prawego wierzchołka
//------------------------------------------------
void fillframe(int type,int attr,int xb,int yb,int xe,int ye)
{
  frame(type,attr,xb,yb,xe,ye);
  fillrect(' ',attr,xb+1,yb+1,xe-1,ye-1);
}

// Funkcja przełącza okno konsoli w tryb pełnoekranowy
// lub okienkowy. Pełny ekran ma 80 kolumn x 50 wierszy
// mode = true  - pełny ekran
// mode = false - okno
//-----------------------------------------------------
void fullscreen(bool mode)
{
  COORD dim;
  SetConsoleDisplayMode(_hcout, mode ? 1 : 2, &dim);
}

// Funkcja odczytuje atrybut z pozycji x,y ekranu konsoli
//-------------------------------------------------------
// x - pozycja w poziomie (0..79)
// y - pozycja w pionie   (0..49)
//-------------------------------------------------------
int getattrxy(int x, int y)
{
  COORD pos = {(short)x,(short)y};
  WORD attr;
  DWORD crd;

  ReadConsoleOutputAttribute(_hcout,&attr,1,pos,&crd);
  return attr;
}

// Funkcja odczytuje kod klawisza z bufora konsoli. Jeśli bufor jest pusty, czeka na klawisz.
// Odczytany znak nie pojawia się na ekranie. Jeśli naciśnięto klawisz sterujący, to pierwsze
// wywołanie funkcji zawsze zwraca kod 0. Drugie wywołanie zwraca kod klawiaturowy danego
// klawisza sterującego.
//-------------------------------------------------------------------------------------------
int getch()
{
  INPUT_RECORD inp;
  DWORD nread;
  DWORD kbdmode;
  struct kbd *k;
  int keycode, state, c;

// Jeśli poprzednie wywołanie zwróciło rozszerzony kod 0,
// zwracamy kod klawiaturowy tego klawisza.

  if(_cextend != -1)
  {
    c = _cextend;
    _cextend = -1; // Resetujemy znacznik
    return c;      // Zwracamy rozszerzony kod klawiaturowy
  }

// Pobieramy tryb pracy konsoli, a następnie ustawiamy go w tryb binarny.
// Robimy to, aby zapobiec przetwarzaniu przez Windows klawiszy sterujących
// np. Ctrl-C lub Ctrl-S.

  if(!GetConsoleMode(_hcin,&kbdmode)) return -1;
  if(!SetConsoleMode(_hcin,0))        return -1;

// Pobieramy zdarzenia klawiatury, aż napotkamy właściwe

  for (;;)
  {
    if(!ReadConsoleInput(_hcin, &inp, 1, &nread))
    {
      c = -1; break;
    }
    else if((inp.EventType == KEY_EVENT) && inp.Event.KeyEvent.bKeyDown)
    {
      keycode = inp.Event.KeyEvent.wVirtualKeyCode;
      state   = inp.Event.KeyEvent.dwControlKeyState;
      if((state & NUMLOCK_ON) && (keycode == VK_DELETE)) keycode = VK_DECIMAL;

// Poszukujemy wirtualnego kodu klawisza w tablicy. Ignorujemy
// nierozpoznane klawisze

      for(k = &kbdtab[0]; keycode != k->keycode && k->keycode != -1; k++) ;
      if(k->keycode == -1) continue; // wartość nieobecna w tablicy

// Sprawdzamy stan klawiszy sterujących. ALT posiada najwyższy priorytet
// następnie Ctrl i Shift. W zależności od stanu tych klawiszy
// wybieramy odpowiednią pozycję w tablicy

      if(state & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))        c = k->alt;
      else if(state & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) c = k->ctrl;
      else if(state & SHIFT_PRESSED)                            c = k->shift;
      else
      {

// Jeśli jest to klawisz znakowy, wykorzystujemy kod ASCII dostarczony
// przez Windows i uwzględniamy stan CapsLock.

        if(keycode >= 'A' && keycode <= 'Z')
          c = inp.Event.KeyEvent.uChar.AsciiChar;
        else
          c = k->normal;
      }
      if(c == -1) continue; // brak odpowiednika BIOS

// Jeśli jest to klawisz rozszerzony, zapamiętujemy jego kod
// i zwracamy 0.  Przy następnym wywołaniu zostanie zwrócona
// wartość klawisza.

      if(ISEXT(c))
      {
        _cextend = EXTVAL(c);
        c = 0;
      }
      break;
   }
  }

// Przywracamy normalny tryb pracy konsoli

  if(!SetConsoleMode(_hcin,kbdmode)) return -1;
  return c;
}

// Funkcja odczytuje znak z pozycji x,y ekranu konsoli
//----------------------------------------------------
// x - pozycja w poziomie (0..79)
// y - pozycja w pionie   (0..49)
//----------------------------------------------------
char getchxy(int x, int y)
{
  COORD pos = {(short)x,(short)y};
  TCHAR c;
  DWORD crd;

  ReadConsoleOutputCharacter(_hcout,&c,1,pos,&crd);
  return c;
}

// Funkcja tworzy bufor na treść okna konsoli.
// Następnie umieszcza w tym buforze zawartość
// prostokątnego obszaru konsoli i zwraca
// wskazanie do bufora.
// xb,yb - współrzędne lewego górnego rogu
// xe,ye - współrzędna prawego dolnego rogu
//--------------------------------------------
WORD * getrect(int xb, int yb, int xe, int ye)
{
  CONSOLE_SCREEN_BUFFER_INFO cinf;
  COORD pos;
  DWORD crd;

  if(!GetConsoleScreenBufferInfo(_hcout, &cinf)) return NULL;
  if((xb >= cinf.dwSize.X) || (yb >= cinf.dwSize.Y) ||
     (xe < xb) || (ye < yb) || (xe < 0) || (ye < 0)) return NULL; // poza oknem
  if(xb < 0) xb = 0;
  if(yb < 0) yb = 0;
  if(xe >= cinf.dwSize.X) xe = cinf.dwSize.X - 1;
  if(ye >= cinf.dwSize.Y) ye = cinf.dwSize.Y - 1;
  DWORD len_x = xe - xb + 1;
  WORD * buf = new WORD[4 + 2 * len_x * (ye - yb + 1)];
  if(buf)
  {
    buf[0] = xb; buf[1] = yb; buf[2] = xe; buf[3] = ye; // współrzędne obszaru
    int j = 0;
    for(int i = yb; i <= ye; i++)
    {
      pos.X = xb; pos.Y = i;
      ReadConsoleOutputCharacter(_hcout,(CHAR *)(buf+4+(j++)*len_x),len_x,pos,&crd);
      ReadConsoleOutputAttribute(_hcout,buf+4+(j++)*len_x,len_x,pos,&crd);
    }
  }
  return buf;
}

// Funkcja umieszcza kursor na zadanej pozycji
//--------------------------------------------
// x - pozycja w poziomie (0..79)
// y - pozycja w pionie   (0..49)
//--------------------------------------------
void gotoxy(int x, int y)
{
  COORD cpos = {(short)x,(short)y};
  SetConsoleCursorPosition(_hcout, cpos);
}

// Funkcja rozjaśnia kolor znaków
//-------------------------------
void highvideo()
{
  _txattr = _txattr | 0x8;
  SetConsoleTextAttribute(_hcout,_txattr);
}

// Funkcja sprawdza, czy użytkownik nacisnął jakiś
// klawisz na klawiaturze. Jeśli tak, zwraca true.
//------------------------------------------------
bool kbhit()
{
  DWORD en, enr; // liczba zdarzeń oraz liczba odczytanych zdarzeń

  GetNumberOfConsoleInputEvents(_hcin, &en);
  INPUT_RECORD * ir = new INPUT_RECORD[en];  // przydzielamy pamięć
  PeekConsoleInput(_hcin, ir, en, &enr);     // odczytujemy zdarzenia

  for(DWORD i = 0; i < enr; i++)               // przeglądamy zdarzenia
  {
    if(ir[i].EventType & KEY_EVENT &&
       ir[i].Event.KeyEvent.bKeyDown)        // jeśli zdarzenie klawisza!
    {                                        // ale nie kontrolnego!
      if(ir[i].Event.KeyEvent.wVirtualKeyCode != VK_CONTROL &&
         ir[i].Event.KeyEvent.wVirtualKeyCode != VK_MENU &&
         ir[i].Event.KeyEvent.wVirtualKeyCode != VK_SHIFT)
      {
        delete ir; return true;              // klawisz w buforze konsoli
      }
    }
  }
  delete ir; return false;                   // bufor konsoli jest pusty
}

// Funkcja przyciemnia kolor znaków
//---------------------------------
void lowvideo()
{
  _txattr = _txattr & 0xf7;
  SetConsoleTextAttribute(_hcout,_txattr);
}

// Funkcja zapisuje podany atrybut na pozycji x,y.
// Pozycja kursora i znak na tej pozycji nie są zmieniane
//-------------------------------------------------------
// attr - atrybut koloru
// x - pozycja w poziomie (0..79)
// y - pozycja w pionie   (0..49)
//-------------------------------------------------------
void putattrxy(WORD attr, int x, int y)
{
  COORD pos = {(short)x,(short)y};
  DWORD cwrt;

  WriteConsoleOutputAttribute(_hcout,&attr,1,pos,&cwrt);
}

// Funkcja zapisuje podany znak na pozycji x,y.
// Pozycja kursora i atrybut pozycji nie są zmieniane.
//----------------------------------------------------
// c - znak ASCII do zapisu
// x - pozycja w poziomie (0..79)
// y - pozycja w pionie   (0..49)
//----------------------------------------------------
void putchxy(TCHAR c, int x, int y)
{
  COORD pos = {(short)x,(short)y};
  DWORD cwrt;

  WriteConsoleOutputCharacter(_hcout,&c,1,pos,&cwrt);
}

// Funkcja odtwarza prostokątny obszar ekranu
// zapamiętany w buforze przez getrect(). Bufor
// jest zwalniany do puli pamięci systemu.
// buf - wskaźnik bufora z treścią obszaru
//---------------------------------------------
void putrect(WORD * buf)
{
  COORD pos;
  DWORD crd;

  if(!buf) return; // na wypadek, gdy bufor jest pusty!
  DWORD len_x = buf[2] - buf[0] + 1;
  int j = 0;
  for(int i = buf[1]; i <= buf[3]; i++)
  {
    pos.X = buf[0]; pos.Y = i;
    WriteConsoleOutputCharacter(_hcout,(CHAR *)(buf+4+(j++)*len_x),len_x,pos,&crd);
    WriteConsoleOutputAttribute(_hcout,buf+4+(j++)*len_x,len_x,pos,&crd);
  }
  delete buf;
}

// Funkcja zapisuje podany znak i atrybut na pozycji x,y.
// Pozycja kursora pozycji nie jest zmieniana.
//-------------------------------------------------------
// c - znak ASCII do zapisu
// attr - atrybut koloru
// x - pozycja w poziomie (0..79)
// y - pozycja w pionie   (0..49)
//-------------------------------------------------------
void putxy(TCHAR c, WORD attr, int x, int y)
{
  COORD pos = {(short)x,(short)y};
  DWORD cwrt;

  WriteConsoleOutputCharacter(_hcout,&c,1,pos,&cwrt);
  WriteConsoleOutputAttribute(_hcout,&attr,1,pos,&cwrt);
}

// Funkcja przesuwa zawartość prostokątnego obszaru
// w wyznaczonym kierunku o zadaną liczbę pozycji.
// Powstały pusty obszar wypełniony zostaje spacjami
// w aktualnym kolorze tła.
//------------------------------------------------------------------------
// dir - kierunek przesuwu okna. Dozwolone są następujące kierunki:
//   SCROLL_UP    - przesuw w górę
//   SCROLL_RIGHT - przesuw w prawo
//   SCROLL_DOWN  - przesuw w dół
//   SCROLL_LEFT  - przesuw w lewo
// howfar - określa liczbę kolumn lub wierszy, o które będzie przesunięcie
// xb,yb - współrzędna lewego górnego wierzchołka
// xe,ye - współrzędna prawego dolnego wierzchołka
//------------------------------------------------------------------------
void scrollrect(int dir, unsigned howfar, int xb, int yb, int xe, int ye)
{
  SMALL_RECT r,cr;
  COORD dest;
  CONSOLE_SCREEN_BUFFER_INFO cinf;

  if(!GetConsoleScreenBufferInfo(_hcout, &cinf)) return;
  if((xb >= cinf.dwSize.X) || (yb >= cinf.dwSize.Y) || (dir > 3) || !howfar ||
     (xe < xb) || (ye < yb) || (xe < 0) || (ye < 0)) return; // poza oknem
  if(xb < 0) xb = 0;
  if(yb < 0) yb = 0;
  if(xe >= cinf.dwSize.X) xe = cinf.dwSize.X - 1;
  if(ye >= cinf.dwSize.Y) ye = cinf.dwSize.Y - 1;
  dest.X = r.Left = cr.Left  = xb;
  dest.Y = r.Top  = cr.Top   = yb;
  r.Right  = cr.Right  = xe;
  r.Bottom = cr.Bottom = ye;
  switch(dir)
  {
    case SCROLL_UP    : r.Top    += howfar; break;
    case SCROLL_RIGHT : r.Right  -= howfar; dest.X += howfar; break;
    case SCROLL_DOWN  : r.Bottom -= howfar; dest.Y += howfar; break;
    case SCROLL_LEFT  : r.Left   += howfar; break;
  }
  CHAR_INFO chinf = {{' '},_txattr};
  ScrollConsoleScreenBuffer(_hcout,&r,&cr,dest,&chinf);
}

// Funkcja ustawia atrybut koloru
//-------------------------------
// attr - nowy atrybut
//-------------------------------
void textattr(int attr)
{
  _txattr = attr & 0xff;
  SetConsoleTextAttribute(_hcout,_txattr);
}

// Funkcja ustawia kolor tła tekstu
//---------------------------------
// attr - kod koloru tekstu
//---------------------------------
void textbackground(int attr)
{
  _txattr = (_txattr & 0xf) | ((attr & 0xf) << 4);
  SetConsoleTextAttribute(_hcout,_txattr);
}

// Funkcja ustawia kolor tekstu i tła
//--------------------------------------------
// attr - kod atrybutu dla koloru tekstu i tła
//--------------------------------------------
void textcolor(int attr)
{
  _txattr = (_txattr & 0xf0) | (attr & 0xf);
  SetConsoleTextAttribute(_hcout,_txattr);
}

// Funkcja odczytuje bieżącą pozycję x kursora
//--------------------------------------------
int wherex()
{
  CONSOLE_SCREEN_BUFFER_INFO sinf;

  GetConsoleScreenBufferInfo(_hcout,&sinf);
  return sinf.dwCursorPosition.X;
}

// Funkcja odczytuje bieżącą pozycję y kursora
//--------------------------------------------
int wherey()
{
  CONSOLE_SCREEN_BUFFER_INFO sinf;

  GetConsoleScreenBufferInfo(_hcout,&sinf);
  return sinf.dwCursorPosition.Y;
}
