mirror of
https://git.shylie.info/shylie/terml.git
synced 2024-11-21 15:40:07 +00:00
Initial commit.
This commit is contained in:
commit
226adeaf72
26
CMakeLists.txt
Normal file
26
CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
|
project(terml CXX)
|
||||||
|
|
||||||
|
add_library(terml STATIC
|
||||||
|
include/terml.h
|
||||||
|
|
||||||
|
source/terml_private.h
|
||||||
|
source/terml_windows.h
|
||||||
|
source/terml_linux.h
|
||||||
|
source/terml_windows.cpp
|
||||||
|
source/terml_linux.cpp
|
||||||
|
source/terml.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(terml
|
||||||
|
PROPERTIES
|
||||||
|
CXX_STANDARD 11
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(terml
|
||||||
|
PUBLIC
|
||||||
|
include
|
||||||
|
PRIVATE
|
||||||
|
source
|
||||||
|
)
|
36
include/terml.h
Normal file
36
include/terml.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef TERML_TERML_H
|
||||||
|
#define TERML_TERML_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef void (*terml_main_callback)();
|
||||||
|
typedef int (*terml_quit_callback)();
|
||||||
|
typedef void (*terml_key_callback) (char code);
|
||||||
|
|
||||||
|
int terml_init();
|
||||||
|
int terml_deinit();
|
||||||
|
|
||||||
|
unsigned int terml_get_width();
|
||||||
|
unsigned int terml_get_height();
|
||||||
|
|
||||||
|
char terml_get(unsigned int x, unsigned int y, int* foreground_color, int* background_color);
|
||||||
|
void terml_set(unsigned int x, unsigned int y, char c, int foreground_color, int background_color);
|
||||||
|
void terml_flush();
|
||||||
|
|
||||||
|
void terml_set_main_callback(terml_main_callback);
|
||||||
|
void terml_set_quit_callback(terml_quit_callback);
|
||||||
|
void terml_set_key_callback(terml_key_callback);
|
||||||
|
|
||||||
|
void terml_start();
|
||||||
|
void terml_stop();
|
||||||
|
|
||||||
|
const char* terml_get_error();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif//TERML_TERML_H
|
347
source/terml.cpp
Normal file
347
source/terml.cpp
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
#include "terml_private.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "terml_windows.h"
|
||||||
|
#else
|
||||||
|
#include "terml_linux.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
terml::terml() :
|
||||||
|
main(nullptr),
|
||||||
|
quit(nullptr),
|
||||||
|
key(nullptr),
|
||||||
|
buffer(nullptr),
|
||||||
|
width(0),
|
||||||
|
height(0)
|
||||||
|
{
|
||||||
|
printf(ALT_BUF() HIDE_CURSOR());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
terml::~terml()
|
||||||
|
{
|
||||||
|
printf(REG_BUF() SHOW_CURSOR());
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char terml::get(unsigned int x, unsigned int y, int* fg, int* bg) const
|
||||||
|
{
|
||||||
|
const unsigned int offset = x + y * width;
|
||||||
|
|
||||||
|
if (fg)
|
||||||
|
{
|
||||||
|
const unsigned char r =
|
||||||
|
(buffer[offset * CELL_SIZE + 9] - '0') +
|
||||||
|
(buffer[offset * CELL_SIZE + 8] - '0') * 10 +
|
||||||
|
(buffer[offset * CELL_SIZE + 7] - '0') * 100;
|
||||||
|
|
||||||
|
const unsigned char g =
|
||||||
|
(buffer[offset * CELL_SIZE + 13] - '0') +
|
||||||
|
(buffer[offset * CELL_SIZE + 12] - '0') * 10 +
|
||||||
|
(buffer[offset * CELL_SIZE + 11] - '0') * 100;
|
||||||
|
|
||||||
|
const unsigned char b =
|
||||||
|
(buffer[offset * CELL_SIZE + 17] - '0') +
|
||||||
|
(buffer[offset * CELL_SIZE + 16] - '0') * 10 +
|
||||||
|
(buffer[offset * CELL_SIZE + 15] - '0') * 100;
|
||||||
|
|
||||||
|
*fg = (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bg)
|
||||||
|
{
|
||||||
|
const unsigned char r =
|
||||||
|
(buffer[offset * CELL_SIZE + 26] - '0') +
|
||||||
|
(buffer[offset * CELL_SIZE + 27] - '0') * 10 +
|
||||||
|
(buffer[offset * CELL_SIZE + 28] - '0') * 100;
|
||||||
|
|
||||||
|
const unsigned char g =
|
||||||
|
(buffer[offset * CELL_SIZE + 30] - '0') +
|
||||||
|
(buffer[offset * CELL_SIZE + 31] - '0') * 10 +
|
||||||
|
(buffer[offset * CELL_SIZE + 32] - '0') * 100;
|
||||||
|
|
||||||
|
const unsigned char b =
|
||||||
|
(buffer[offset * CELL_SIZE + 34] - '0') +
|
||||||
|
(buffer[offset * CELL_SIZE + 35] - '0') * 10 +
|
||||||
|
(buffer[offset * CELL_SIZE + 36] - '0') * 100;
|
||||||
|
|
||||||
|
*bg = (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer[offset * CELL_SIZE + CELL_SIZE - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::set(unsigned int x, unsigned int y, char c, int fg, int bg)
|
||||||
|
{
|
||||||
|
const unsigned int offset = x + y * width;
|
||||||
|
|
||||||
|
unsigned char fgr = (fg & 0xFF0000) >> 16;
|
||||||
|
unsigned char fgg = (fg & 0x00FF00) >> 8;
|
||||||
|
unsigned char fgb = (fg & 0x0000FF);
|
||||||
|
|
||||||
|
unsigned char bgr = (bg & 0xFF0000) >> 16;
|
||||||
|
unsigned char bgg = (bg & 0x00FF00) >> 8;
|
||||||
|
unsigned char bgb = (bg & 0x0000FF);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
buffer[offset * CELL_SIZE + (9 - i)] = (fgr % 10) + '0';
|
||||||
|
fgr /= 10;
|
||||||
|
|
||||||
|
buffer[offset * CELL_SIZE + (13 - i)] = (fgg % 10) + '0';
|
||||||
|
fgg /= 10;
|
||||||
|
|
||||||
|
buffer[offset * CELL_SIZE + (17 - i)] = (fgb % 10) + '0';
|
||||||
|
fgb /= 10;
|
||||||
|
|
||||||
|
buffer[offset * CELL_SIZE + (28 - i)] = (bgr % 10) + '0';
|
||||||
|
bgr /= 10;
|
||||||
|
|
||||||
|
buffer[offset * CELL_SIZE + (32 - i)] = (bgg % 10) + '0';
|
||||||
|
bgg /= 10;
|
||||||
|
|
||||||
|
buffer[offset * CELL_SIZE + (36 - i)] = (bgb % 10) + '0';
|
||||||
|
bgb /= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[offset * CELL_SIZE + CELL_SIZE - 1] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::flush() const
|
||||||
|
{
|
||||||
|
printf(CUP(1, 1));
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
printf("%s", buffer);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::set_main_callback(terml_main_callback callback)
|
||||||
|
{
|
||||||
|
main = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::set_quit_callback(terml_quit_callback callback)
|
||||||
|
{
|
||||||
|
quit = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::set_key_callback(terml_key_callback callback)
|
||||||
|
{
|
||||||
|
key = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::key_event(char code)
|
||||||
|
{
|
||||||
|
if (key)
|
||||||
|
{
|
||||||
|
key(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::mainloop()
|
||||||
|
{
|
||||||
|
should_quit = false;
|
||||||
|
really_should_quit = false;
|
||||||
|
|
||||||
|
const unsigned long long wait_time = timer_frequency() / 1000;
|
||||||
|
unsigned long long last_time = timer();
|
||||||
|
|
||||||
|
while (!really_should_quit)
|
||||||
|
{
|
||||||
|
unsigned long long current_time = timer();
|
||||||
|
|
||||||
|
while (current_time >= last_time + wait_time)
|
||||||
|
{
|
||||||
|
main();
|
||||||
|
|
||||||
|
process_events();
|
||||||
|
|
||||||
|
last_time += wait_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_quit)
|
||||||
|
{
|
||||||
|
if (!quit || quit())
|
||||||
|
{
|
||||||
|
really_should_quit = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
should_quit = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::stop()
|
||||||
|
{
|
||||||
|
should_quit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int terml::get_width() const
|
||||||
|
{
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int terml::get_height() const
|
||||||
|
{
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml::setup_buffer()
|
||||||
|
{
|
||||||
|
printf(CUP(999, 999) REPORT_CUSROR_POSITION());
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
constexpr const unsigned int STDIN_BUFFER_SIZE = 16;
|
||||||
|
char stdin_buffer[STDIN_BUFFER_SIZE + 1];
|
||||||
|
read_stdin(stdin_buffer, STDIN_BUFFER_SIZE);
|
||||||
|
|
||||||
|
stdin_buffer[STDIN_BUFFER_SIZE] = '\0';
|
||||||
|
|
||||||
|
unsigned int new_width = width;
|
||||||
|
unsigned int new_height = height;
|
||||||
|
const int scanned = sscanf(stdin_buffer, CURSOR_POSITION_FORMAT(), &new_height, &new_width);
|
||||||
|
if (scanned != 2)
|
||||||
|
{
|
||||||
|
throw "Failed to determine screen size.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width != new_width || height != new_height)
|
||||||
|
{
|
||||||
|
width = new_width;
|
||||||
|
height = new_height;
|
||||||
|
|
||||||
|
if (buffer) { delete[] buffer; }
|
||||||
|
buffer = new char[CELL_SIZE * width * height + 1];
|
||||||
|
|
||||||
|
constexpr char BLANK_CELL[] = FG(255, 255, 255) CSI "48;2;000;000;000m" " ";
|
||||||
|
|
||||||
|
for (int i = 0; i < width * height; i++)
|
||||||
|
{
|
||||||
|
memcpy(buffer + i * CELL_SIZE, BLANK_CELL, CELL_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[CELL_SIZE * width * height] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static terml* TERML_G;
|
||||||
|
static const char* LAST_ERROR = nullptr;
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
int terml_init()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!TERML_G)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
TERML_G = new terml_windows;
|
||||||
|
#else
|
||||||
|
TERML_G = new terml_linux;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TERML_G->set_console_settings();
|
||||||
|
TERML_G->setup_buffer();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (const char* c)
|
||||||
|
{
|
||||||
|
LAST_ERROR = c;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int terml_deinit()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (TERML_G)
|
||||||
|
{
|
||||||
|
TERML_G->reset_console_settings();
|
||||||
|
delete TERML_G;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
catch (const char* c)
|
||||||
|
{
|
||||||
|
LAST_ERROR = c;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int terml_get_width()
|
||||||
|
{
|
||||||
|
return TERML_G->get_width();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int terml_get_height()
|
||||||
|
{
|
||||||
|
return TERML_G->get_height();
|
||||||
|
}
|
||||||
|
|
||||||
|
char terml_get(unsigned int x, unsigned int y, int* fg, int* bg)
|
||||||
|
{
|
||||||
|
return TERML_G->get(x, y, fg, bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_set(unsigned int x, unsigned int y, char c, int fg, int bg)
|
||||||
|
{
|
||||||
|
TERML_G->set(x, y, c, fg, bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_flush()
|
||||||
|
{
|
||||||
|
TERML_G->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_set_main_callback(terml_main_callback callback)
|
||||||
|
{
|
||||||
|
TERML_G->set_main_callback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_set_quit_callback(terml_quit_callback callback)
|
||||||
|
{
|
||||||
|
TERML_G->set_quit_callback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_set_key_callback(terml_key_callback callback)
|
||||||
|
{
|
||||||
|
TERML_G->set_key_callback(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_start()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TERML_G->mainloop();
|
||||||
|
}
|
||||||
|
catch (const char* c)
|
||||||
|
{
|
||||||
|
LAST_ERROR = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_stop()
|
||||||
|
{
|
||||||
|
TERML_G->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* terml_get_error()
|
||||||
|
{
|
||||||
|
const char* err = LAST_ERROR;
|
||||||
|
LAST_ERROR = nullptr;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
85
source/terml_linux.cpp
Normal file
85
source/terml_linux.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include "terml_linux.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
|
void terml_linux::set_console_settings()
|
||||||
|
{
|
||||||
|
tcgetattr(STDIN_FILENO, &old_input_settings);
|
||||||
|
tcgetattr(STDOUT_FILENO, &old_output_settings);
|
||||||
|
|
||||||
|
termios t = old_input_settings;
|
||||||
|
t.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &t);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_linux::reset_console_settings()
|
||||||
|
{
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, &old_input_settings);
|
||||||
|
tcsetattr(STDOUT_FILENO, TCSANOW, &old_output_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_linux::read_stdin(char* buffer, unsigned int buffer_size)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < buffer_size;)
|
||||||
|
{
|
||||||
|
char ch = getchar();
|
||||||
|
if (i == 0 && ch != '\x1B')
|
||||||
|
{
|
||||||
|
key_event(ch);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer[i++] = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 'R') { return; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long terml_linux::timer()
|
||||||
|
{
|
||||||
|
timespec spec;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &spec);
|
||||||
|
|
||||||
|
return timer_frequency() * spec.tv_sec + spec.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long terml_linux::timer_frequency()
|
||||||
|
{
|
||||||
|
return 1000000000; // nanoseconds / second
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_linux::process_events()
|
||||||
|
{
|
||||||
|
constexpr int BUF_SIZE = 128;
|
||||||
|
char buf[BUF_SIZE];
|
||||||
|
int num_read;
|
||||||
|
|
||||||
|
pollfd pfd;
|
||||||
|
pfd.fd = STDIN_FILENO;
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
pfd.revents = 0;
|
||||||
|
|
||||||
|
poll(&pfd, 1, 0);
|
||||||
|
|
||||||
|
while (pfd.revents & POLLIN)
|
||||||
|
{
|
||||||
|
num_read = read(STDIN_FILENO, buf, BUF_SIZE);
|
||||||
|
for (int i = 0; i < num_read; i++)
|
||||||
|
{
|
||||||
|
key_event(buf[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
poll(&pfd, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup_buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
28
source/terml_linux.h
Normal file
28
source/terml_linux.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef TERML_TERML_LINUX_H
|
||||||
|
#define TERML_TERML_LINUX_H
|
||||||
|
|
||||||
|
#include "terml_private.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
class terml_linux : public terml
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void set_console_settings() override;
|
||||||
|
virtual void reset_console_settings() override;
|
||||||
|
virtual void read_stdin(char* buffer, unsigned int buffer_size) override;
|
||||||
|
virtual unsigned long long timer() override;
|
||||||
|
virtual unsigned long long timer_frequency() override;
|
||||||
|
virtual void process_events() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
termios old_input_settings;
|
||||||
|
termios old_output_settings;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif//_WIN32
|
||||||
|
|
||||||
|
#endif//TERML_TERML_LINUX_H
|
80
source/terml_private.h
Normal file
80
source/terml_private.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#ifndef TERML_TERML_PRIVATE_H
|
||||||
|
#define TERML_TERML_PRIVATE_H
|
||||||
|
|
||||||
|
#include "terml.h"
|
||||||
|
|
||||||
|
#define STRINGIFY2(x) #x
|
||||||
|
#define STRINGIFY(x) STRINGIFY2(x)
|
||||||
|
|
||||||
|
#define ESC "\x1B"
|
||||||
|
#define CSI "\x1B["
|
||||||
|
|
||||||
|
#define ALT_BUF() CSI "?1049h"
|
||||||
|
#define REG_BUF() CSI "?1049l"
|
||||||
|
|
||||||
|
#define CLEAR_SCREEN() CSI "2J"
|
||||||
|
|
||||||
|
#define HIDE_CURSOR() CSI "?25l"
|
||||||
|
#define SHOW_CURSOR() CSI "?25h"
|
||||||
|
|
||||||
|
#define REPORT_CUSROR_POSITION() CSI "6n"
|
||||||
|
#define CURSOR_POSITION_FORMAT() "%*1s[%u;%u"
|
||||||
|
|
||||||
|
#define CUP(x, y) CSI STRINGIFY(y) ";" STRINGIFY(x) "H"
|
||||||
|
|
||||||
|
#define FG(r, g, b) CSI "38;2;" STRINGIFY(r) ";" STRINGIFY(g) ";" STRINGIFY(b) "m"
|
||||||
|
#define BG(r, g, b) CSI "48;2;" STRINGIFY(r) ";" STRINGIFY(g) ";" STRINGIFY(b) "m"
|
||||||
|
|
||||||
|
class terml
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
terml();
|
||||||
|
virtual ~terml();
|
||||||
|
|
||||||
|
terml(const terml&) = delete;
|
||||||
|
terml(terml&&) = delete;
|
||||||
|
|
||||||
|
terml& operator=(const terml&) = delete;
|
||||||
|
terml& operator=(terml&&) = delete;
|
||||||
|
|
||||||
|
char get(unsigned int x, unsigned int y, int* fg, int* bg) const;
|
||||||
|
void set(unsigned int x, unsigned int y, char c, int fg, int bg);
|
||||||
|
void flush() const;
|
||||||
|
|
||||||
|
void set_main_callback(terml_main_callback);
|
||||||
|
void set_quit_callback(terml_quit_callback);
|
||||||
|
void set_key_callback(terml_key_callback);
|
||||||
|
|
||||||
|
void mainloop();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
unsigned int get_width() const;
|
||||||
|
unsigned int get_height() const;
|
||||||
|
|
||||||
|
void setup_buffer();
|
||||||
|
|
||||||
|
virtual void set_console_settings() = 0;
|
||||||
|
virtual void reset_console_settings() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void read_stdin(char* buffer, unsigned int buffer_size) = 0;
|
||||||
|
virtual unsigned long long timer() = 0;
|
||||||
|
virtual unsigned long long timer_frequency() = 0;
|
||||||
|
virtual void process_events() = 0;
|
||||||
|
|
||||||
|
void key_event(char code);
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* buffer;
|
||||||
|
unsigned int width;
|
||||||
|
unsigned int height;
|
||||||
|
terml_main_callback main;
|
||||||
|
terml_quit_callback quit;
|
||||||
|
terml_key_callback key;
|
||||||
|
bool should_quit;
|
||||||
|
bool really_should_quit;
|
||||||
|
|
||||||
|
static const unsigned int CELL_SIZE = sizeof(FG(255, 255, 255) BG(255, 255, 255) " ") - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif//TERML_TERML_PRIVATE_H
|
100
source/terml_windows.cpp
Normal file
100
source/terml_windows.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include "terml_windows.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
void terml_windows::set_console_settings()
|
||||||
|
{
|
||||||
|
handle_stdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
GetConsoleMode(handle_stdin, &previous_input_mode);
|
||||||
|
DWORD new_mode = previous_input_mode;
|
||||||
|
new_mode &= ~ENABLE_ECHO_INPUT;
|
||||||
|
new_mode &= ~ENABLE_LINE_INPUT;
|
||||||
|
new_mode |= ENABLE_WINDOW_INPUT;
|
||||||
|
new_mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||||
|
if (!SetConsoleMode(handle_stdin, new_mode))
|
||||||
|
{
|
||||||
|
throw "Failed to set stdin mode";
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
GetConsoleMode(handle, &previous_output_mode);
|
||||||
|
new_mode = previous_output_mode;
|
||||||
|
new_mode |= ENABLE_PROCESSED_OUTPUT;
|
||||||
|
new_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||||
|
if (!SetConsoleMode(handle, new_mode))
|
||||||
|
{
|
||||||
|
throw "Failed to set stdout mode";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_windows::reset_console_settings()
|
||||||
|
{
|
||||||
|
if (!SetConsoleMode(handle_stdin, previous_input_mode))
|
||||||
|
{
|
||||||
|
throw "Failed to reset stdin mode";
|
||||||
|
}
|
||||||
|
if (!SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), previous_output_mode))
|
||||||
|
{
|
||||||
|
throw "Failed to reset stdout mode";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_windows::read_stdin(char* buffer, unsigned int buffer_size)
|
||||||
|
{
|
||||||
|
unsigned long unused;
|
||||||
|
ReadConsole(handle_stdin, buffer, buffer_size, &unused, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long terml_windows::timer()
|
||||||
|
{
|
||||||
|
LARGE_INTEGER time;
|
||||||
|
QueryPerformanceCounter(&time);
|
||||||
|
return time.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long terml_windows::timer_frequency()
|
||||||
|
{
|
||||||
|
LARGE_INTEGER frequency;
|
||||||
|
QueryPerformanceFrequency(&frequency);
|
||||||
|
return frequency.QuadPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terml_windows::process_events()
|
||||||
|
{
|
||||||
|
DWORD num_events_available = 0;
|
||||||
|
GetNumberOfConsoleInputEvents(handle_stdin, &num_events_available);
|
||||||
|
|
||||||
|
DWORD num_events_read = 0;
|
||||||
|
INPUT_RECORD input_record_buffer[128];
|
||||||
|
while (num_events_available > 0)
|
||||||
|
{
|
||||||
|
ReadConsoleInput(handle_stdin, input_record_buffer, sizeof(input_record_buffer) / sizeof(*input_record_buffer), &num_events_read);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_events_read; i++)
|
||||||
|
{
|
||||||
|
switch (input_record_buffer[i].EventType)
|
||||||
|
{
|
||||||
|
case KEY_EVENT:
|
||||||
|
{
|
||||||
|
KEY_EVENT_RECORD* record = &input_record_buffer[i].Event.KeyEvent;
|
||||||
|
if (record->bKeyDown)
|
||||||
|
{
|
||||||
|
for (int repeat = 0; repeat < record->wRepeatCount; repeat++)
|
||||||
|
{
|
||||||
|
key_event(record->uChar.AsciiChar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WINDOW_BUFFER_SIZE_EVENT:
|
||||||
|
setup_buffer();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
num_events_available -= num_events_read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
29
source/terml_windows.h
Normal file
29
source/terml_windows.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef TERML_TERML_WINDOWS_H
|
||||||
|
#define TERML_TERML_WINDOWS_H
|
||||||
|
|
||||||
|
#include "terml_private.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
class terml_windows : public terml
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void set_console_settings() override;
|
||||||
|
virtual void reset_console_settings() override;
|
||||||
|
virtual void read_stdin(char* buffer, unsigned int buffer_size) override;
|
||||||
|
virtual unsigned long long timer() override;
|
||||||
|
virtual unsigned long long timer_frequency() override;
|
||||||
|
virtual void process_events() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DWORD previous_input_mode;
|
||||||
|
DWORD previous_output_mode;
|
||||||
|
HANDLE handle_stdin;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif//_WIN32
|
||||||
|
|
||||||
|
#endif//TERML_TERML_WINDOWS_H
|
Loading…
Reference in New Issue
Block a user