171 lines
6.5 KiB
C
Executable File
171 lines
6.5 KiB
C
Executable File
#include "tim.h"
|
|
|
|
#ifdef TIM_WINDOWS
|
|
|
|
i64 tim_time_usec(void) {
|
|
LARGE_INTEGER ticks = {0};
|
|
LARGE_INTEGER freq = {0};
|
|
QueryPerformanceCounter(&ticks);
|
|
QueryPerformanceFrequency(&freq);
|
|
return 1000000 * ticks.QuadPart / freq.QuadPart;
|
|
}
|
|
|
|
void tim_write_str(cstr s, i32 size) {
|
|
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
WriteFile(h, s, size, NULL, NULL);
|
|
FlushFileBuffers(h);
|
|
}
|
|
|
|
void tim_update_screen_size(void) {
|
|
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi = {0};
|
|
if (GetConsoleScreenBufferInfo(hout, &csbi) == 0) {
|
|
printf("ERROR: can't get console buffer size\n");
|
|
exit(1);
|
|
}
|
|
i32 w = csbi.srWindow.Right - csbi.srWindow.Left + 1;
|
|
i32 h = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
|
|
tim.resized = (u32)(w * h) <= TIM_MAX_CELLS && (w != tim.w || h != tim.h);
|
|
if (tim.resized) {
|
|
tim.w = tim.scopes[0].w = w;
|
|
tim.h = tim.scopes[0].h = h;
|
|
tim.window = csbi.srWindow;
|
|
}
|
|
}
|
|
|
|
void tim_init_terminal(void) {
|
|
DWORD mode = 0;
|
|
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
|
|
GetConsoleMode(hin, &tim.mode_in); // get current input mode
|
|
mode = tim.mode_in; //
|
|
mode &= ~ENABLE_ECHO_INPUT; // disable echo
|
|
mode &= ~ENABLE_LINE_INPUT; // disable line buffer
|
|
// TODO: enable ctrl-c again
|
|
mode &= ~ENABLE_PROCESSED_INPUT; // disable ctrl-c
|
|
mode |= ENABLE_WINDOW_INPUT; // enable resize event
|
|
mode |= ENABLE_MOUSE_INPUT; // enable mouse event
|
|
mode |= ENABLE_EXTENDED_FLAGS; // for ENABLE_QUICK_EDIT
|
|
mode &= ~ENABLE_QUICK_EDIT_MODE; // disable select mode
|
|
SetConsoleMode(hin, mode); // set input mode
|
|
//
|
|
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); //
|
|
GetConsoleMode(hout, &tim.mode_out); // get current output mode
|
|
mode = tim.mode_out; //
|
|
mode |= ENABLE_PROCESSED_OUTPUT; // enable ascii sequences
|
|
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; // enable vt sequences
|
|
SetConsoleMode(hout, mode); // set output mode
|
|
//
|
|
tim.cp_in = GetConsoleCP(); // get current code page
|
|
tim.cp_out = GetConsoleOutputCP(); //
|
|
SetConsoleCP(CP_UTF8); // set utf8 in/out code page
|
|
SetConsoleOutputCP(CP_UTF8); //
|
|
tim_write_str(S("\33[?1049h")); // use alternate buffer
|
|
tim_update_screen_size(); //
|
|
}
|
|
|
|
void tim_reset_terminal(void) {
|
|
tim_write_str(S("\33[m")); // reset colors
|
|
tim_write_str(S("\33[?25h")); // show cursor
|
|
tim_write_str(S("\33[?1049l")); // exit alternate buffer
|
|
HANDLE hin = GetStdHandle(STD_INPUT_HANDLE); //
|
|
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE); //
|
|
SetConsoleMode(hin, tim.mode_in); // set original mode
|
|
SetConsoleMode(hout, tim.mode_out); //
|
|
SetConsoleCP(tim.cp_in); // set original code page
|
|
SetConsoleOutputCP(tim.cp_out); //
|
|
}
|
|
|
|
void tim_read_event(i32 timeout_ms) {
|
|
TimEvent* e = &tim.event;
|
|
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
const i8 key_table[256] = {
|
|
[VK_BACK] = TimKey_Backspace,
|
|
[VK_TAB] = TimKey_Tab,
|
|
[VK_RETURN] = TimKey_Enter,
|
|
[VK_ESCAPE] = TimKey_Escape,
|
|
[VK_PRIOR] = TimKey_PageUp,
|
|
[VK_NEXT] = TimKey_PageDown,
|
|
[VK_END] = TimKey_End,
|
|
[VK_HOME] = TimKey_Home,
|
|
[VK_LEFT] = TimKey_Left,
|
|
[VK_UP] = TimKey_Up,
|
|
[VK_RIGHT] = TimKey_Right,
|
|
[VK_DOWN] = TimKey_Down,
|
|
[VK_INSERT] = TimKey_Insert,
|
|
[VK_DELETE] = TimKey_Delete,
|
|
};
|
|
|
|
while (true) {
|
|
memset(e, 0, sizeof(*e));
|
|
// In cmd.exe the cursor somtimes reappears. This reliably hides it.
|
|
tim_write_str(S("\33[?25l"));
|
|
|
|
DWORD r = WaitForSingleObject(h, timeout_ms);
|
|
if (r == WAIT_TIMEOUT) {
|
|
e->type = TimEvent_Draw;
|
|
tim_update_screen_size(); // workaround, see WINDOW_BUFFER_SIZE_EVENT
|
|
return;
|
|
} else if (r != WAIT_OBJECT_0) {
|
|
continue;
|
|
}
|
|
|
|
// received input
|
|
INPUT_RECORD rec = {0};
|
|
DWORD n = 0;
|
|
ReadConsoleInput(h, &rec, 1, &n);
|
|
|
|
switch (rec.EventType) {
|
|
case KEY_EVENT: {
|
|
if (!rec.Event.KeyEvent.bKeyDown) {
|
|
// only interested in key press
|
|
continue;
|
|
}
|
|
i32 key = key_table[(u8)rec.Event.KeyEvent.wVirtualKeyCode];
|
|
WCHAR chr = rec.Event.KeyEvent.uChar.UnicodeChar;
|
|
if (!key && chr < ' ') {
|
|
// non printable key
|
|
continue;
|
|
}
|
|
tim_update_screen_size(); // workaround, see WINDOW_BUFFER_SIZE_EVENT
|
|
e->type = TimEvent_Key;
|
|
if (key) {
|
|
e->key = key;
|
|
return;
|
|
}
|
|
e->key = chr;
|
|
WideCharToMultiByte(CP_UTF8, 0, &chr, 1, e->s, sizeof(e->s),
|
|
NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
case MOUSE_EVENT: {
|
|
bool move = rec.Event.MouseEvent.dwEventFlags & ~DOUBLE_CLICK;
|
|
bool left = rec.Event.MouseEvent.dwButtonState &
|
|
FROM_LEFT_1ST_BUTTON_PRESSED;
|
|
if (move || !left) {
|
|
// ignore move events and buttons other than left
|
|
continue;
|
|
}
|
|
tim_update_screen_size(); // workaround, see WINDOW_BUFFER_SIZE_EVENT
|
|
e->type = TimEvent_Mouse;
|
|
e->key = TimKey_MouseButtonLeft;
|
|
e->x = rec.Event.MouseEvent.dwMousePosition.X - tim.window.Left;
|
|
e->y = rec.Event.MouseEvent.dwMousePosition.Y - tim.window.Top;
|
|
return;
|
|
}
|
|
|
|
case WINDOW_BUFFER_SIZE_EVENT:
|
|
e->type = TimEvent_Draw;
|
|
// cmd.exe screen buffer and window size are separate, making this
|
|
// event a bit unreliable. Effectively it is only emitted when the
|
|
// terminal width changes and not for the height. As a workaround
|
|
// the screen size is updated every time an event is emitted.
|
|
tim_update_screen_size();
|
|
return;
|
|
}
|
|
} // while
|
|
}
|
|
|
|
#endif // TIM_WINDOWS
|