2024-03-25 05:34:59 +00:00
|
|
|
#include <iso646.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <windows.h>
|
2024-04-01 22:39:21 +00:00
|
|
|
#include <time.h>
|
2024-03-25 05:34:59 +00:00
|
|
|
|
|
|
|
#include "fumotris.h"
|
2024-03-26 20:11:58 +00:00
|
|
|
#include "gametime.h"
|
2024-03-25 05:34:59 +00:00
|
|
|
#include "input.h"
|
2024-04-03 23:31:47 +00:00
|
|
|
#include "term.h"
|
2024-03-25 05:34:59 +00:00
|
|
|
|
|
|
|
struct Windows {
|
|
|
|
HANDLE input_handle;
|
2024-04-02 20:06:14 +00:00
|
|
|
HANDLE draw_handles[2];
|
2024-03-25 05:34:59 +00:00
|
|
|
};
|
|
|
|
static struct Windows windows;
|
|
|
|
|
2024-04-02 20:06:14 +00:00
|
|
|
bool WinInitHandles()
|
2024-03-25 05:34:59 +00:00
|
|
|
{
|
|
|
|
windows.input_handle = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
|
if (windows.input_handle == INVALID_HANDLE_VALUE)
|
|
|
|
return false;
|
|
|
|
|
2024-04-02 20:06:14 +00:00
|
|
|
windows.draw_handles[0] = CreateWaitableTimer(
|
|
|
|
NULL, // Timer attributes
|
|
|
|
TRUE, // Manual reset
|
|
|
|
NULL // Name
|
|
|
|
);
|
|
|
|
if (!windows.draw_handles[0])
|
|
|
|
return false;
|
|
|
|
|
|
|
|
windows.draw_handles[1] = CreateEvent(
|
|
|
|
NULL, // Event attributes
|
|
|
|
FALSE, // Manual reset
|
|
|
|
FALSE, // Initial state
|
|
|
|
NULL // Name
|
|
|
|
);
|
|
|
|
if (!windows.draw_handles[1])
|
2024-03-25 05:34:59 +00:00
|
|
|
return false;
|
2024-04-02 20:06:14 +00:00
|
|
|
|
2024-03-25 05:34:59 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WinInitConsole()
|
|
|
|
{
|
|
|
|
DWORD mode = ENABLE_EXTENDED_FLAGS
|
|
|
|
| ENABLE_PROCESSED_INPUT
|
|
|
|
| ENABLE_MOUSE_INPUT
|
|
|
|
| ENABLE_WINDOW_INPUT
|
|
|
|
| ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
|
|
|
return SetConsoleMode(windows.input_handle, mode);
|
|
|
|
}
|
|
|
|
|
2024-04-03 23:31:47 +00:00
|
|
|
bool WinGetRefreshRate(u32f *out)
|
2024-04-02 20:06:14 +00:00
|
|
|
{
|
|
|
|
LPDEVMODE mode;
|
|
|
|
if(!EnumDisplaySettingsA(
|
|
|
|
NULL, // Device name (null for current)
|
|
|
|
ENUM_CURRENT_SETTINGS, // Mode
|
|
|
|
&mode // Out
|
|
|
|
))
|
|
|
|
return false;
|
|
|
|
|
2024-04-03 23:31:47 +00:00
|
|
|
*out = mode->dmDisplayFrequency;
|
2024-04-02 20:06:14 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-04 13:27:54 +00:00
|
|
|
void set_key_record(struct Record *record, KEY_EVENT_RECORD win_key)
|
2024-03-25 05:34:59 +00:00
|
|
|
{
|
|
|
|
record->type = KEY;
|
|
|
|
record->id = win_key.wVirtualKeyCode;
|
2024-03-26 20:11:58 +00:00
|
|
|
record->data.key.is_down = win_key.bKeyDown;
|
2024-03-25 05:34:59 +00:00
|
|
|
|
|
|
|
if (win_key.wVirtualKeyCode == VK_ESCAPE)
|
|
|
|
record->type = ESCAPE;
|
|
|
|
}
|
|
|
|
|
2024-04-04 13:27:54 +00:00
|
|
|
bool set_mouse_record(struct Record *record, MOUSE_EVENT_RECORD win_mouse)
|
2024-03-25 05:34:59 +00:00
|
|
|
{
|
|
|
|
switch (win_mouse.dwEventFlags) {
|
|
|
|
case MOUSE_WHEELED:
|
|
|
|
record->type = AXIS;
|
|
|
|
record->id = 0;
|
|
|
|
record->data.axis.value = win_mouse.dwButtonState;
|
|
|
|
break;
|
|
|
|
case MOUSE_HWHEELED:
|
|
|
|
record->type = AXIS;
|
|
|
|
record->id = 1;
|
|
|
|
record->data.axis.value = win_mouse.dwButtonState;
|
|
|
|
break;
|
|
|
|
case MOUSE_MOVED:
|
|
|
|
record->type = JOYSTICK;
|
|
|
|
record->id = 0;
|
|
|
|
record->data.joystick.x = win_mouse.dwMousePosition.X;
|
|
|
|
record->data.joystick.y = win_mouse.dwMousePosition.Y;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-04 13:27:54 +00:00
|
|
|
bool dispatch_record(struct Record *record, INPUT_RECORD win_record)
|
2024-03-25 05:34:59 +00:00
|
|
|
{
|
|
|
|
switch (win_record.EventType) {
|
|
|
|
case KEY_EVENT:
|
|
|
|
set_key_record(record, win_record.Event.KeyEvent);
|
2024-04-02 20:06:14 +00:00
|
|
|
break;
|
2024-03-25 05:34:59 +00:00
|
|
|
case MOUSE_EVENT:
|
|
|
|
return set_mouse_record(record, win_record.Event.MouseEvent);
|
|
|
|
case WINDOW_BUFFER_SIZE_EVENT:
|
|
|
|
set_window_record(record, win_record.Event.WindowBufferSizeEvent);
|
2024-04-02 20:06:14 +00:00
|
|
|
break;
|
2024-03-25 05:34:59 +00:00
|
|
|
default:
|
|
|
|
record->type = ESCAPE;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-02 00:55:30 +00:00
|
|
|
bool WinBlockInput(struct RecordBuffer *buf)
|
2024-03-25 05:34:59 +00:00
|
|
|
{
|
2024-03-26 20:11:58 +00:00
|
|
|
size_t win_size = IO_BUF_SIZE - buf->count;
|
|
|
|
INPUT_RECORD win_buf[win_size];
|
2024-03-25 05:34:59 +00:00
|
|
|
DWORD count;
|
|
|
|
|
2024-04-02 20:06:14 +00:00
|
|
|
if (!ReadConsoleInput(
|
|
|
|
windows.input_handle, // Input handle
|
|
|
|
win_buf, // Record buffer
|
|
|
|
win_size, // Record buffer length
|
|
|
|
&count // Out number of records
|
|
|
|
))
|
2024-03-25 05:34:59 +00:00
|
|
|
return false;
|
2024-03-26 20:11:58 +00:00
|
|
|
|
2024-04-01 22:39:21 +00:00
|
|
|
struct timespec now;
|
|
|
|
timespec_get(&now, TIME_UTC);
|
|
|
|
|
2024-03-26 20:11:58 +00:00
|
|
|
pthread_mutex_lock(&buf->mutex);
|
2024-03-25 05:34:59 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
2024-04-04 13:27:54 +00:00
|
|
|
struct Record record;
|
2024-03-26 20:11:58 +00:00
|
|
|
record.timestamp = now;
|
2024-03-25 05:34:59 +00:00
|
|
|
|
2024-03-26 20:11:58 +00:00
|
|
|
bool include = dispatch_record(&record, win_buf[i]);
|
2024-03-25 05:34:59 +00:00
|
|
|
if (!include)
|
2024-03-26 20:11:58 +00:00
|
|
|
continue;
|
2024-03-25 05:34:59 +00:00
|
|
|
|
2024-04-03 23:31:47 +00:00
|
|
|
buf->records[buf->count++] = record;
|
2024-03-25 05:34:59 +00:00
|
|
|
}
|
2024-03-26 20:11:58 +00:00
|
|
|
|
|
|
|
pthread_mutex_unlock(&buf->mutex);
|
2024-03-25 05:34:59 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-03 23:31:47 +00:00
|
|
|
bool WinWait(struct timespec relative)
|
2024-03-25 05:34:59 +00:00
|
|
|
{
|
|
|
|
LARGE_INTEGER duration;
|
2024-04-03 23:31:47 +00:00
|
|
|
duration.QuadPart = -10000000 * relative.tv_sec - relative.tv_nsec / 100;
|
2024-04-02 20:06:14 +00:00
|
|
|
|
|
|
|
if (!SetWaitableTimer(
|
|
|
|
windows.draw_handles[0], // Timer
|
|
|
|
&duration, // Duration
|
|
|
|
0, // Period
|
|
|
|
NULL, // Completion coroutine
|
|
|
|
NULL, // Completion coroutine arg
|
|
|
|
FALSE // Resume
|
|
|
|
))
|
2024-03-25 05:34:59 +00:00
|
|
|
return false;
|
|
|
|
|
2024-04-02 20:06:14 +00:00
|
|
|
DWORD result = WaitForMultipleObjects(
|
|
|
|
2, // Handle count
|
|
|
|
windows.draw_handles, // Handles
|
|
|
|
FALSE, // Wait for all
|
|
|
|
INFINITE // Timeout
|
|
|
|
);
|
2024-03-25 05:34:59 +00:00
|
|
|
if (result != WAIT_OBJECT_0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|