add c and jai
This commit is contained in:
130
c/platform/platform.h
Normal file
130
c/platform/platform.h
Normal file
@@ -0,0 +1,130 @@
|
||||
#pragma once
|
||||
// platform.h — GUI windowing, input, and system services
|
||||
//
|
||||
// Provides window creation, keyboard/mouse input, clipboard access,
|
||||
// and process spawning. Implementation: platform_win32.c.
|
||||
|
||||
#include "base/base_core.h"
|
||||
#include "base/base_math.h"
|
||||
|
||||
////////////////////////////////
|
||||
// Key codes (matching Windows VK codes)
|
||||
|
||||
enum {
|
||||
PKEY_BACKSPACE = 0x08,
|
||||
PKEY_TAB = 0x09,
|
||||
PKEY_RETURN = 0x0D,
|
||||
PKEY_ESCAPE = 0x1B,
|
||||
PKEY_PAGEUP = 0x21,
|
||||
PKEY_PAGEDOWN = 0x22,
|
||||
PKEY_END = 0x23,
|
||||
PKEY_HOME = 0x24,
|
||||
PKEY_LEFT = 0x25,
|
||||
PKEY_UP = 0x26,
|
||||
PKEY_RIGHT = 0x27,
|
||||
PKEY_DOWN = 0x28,
|
||||
PKEY_DELETE = 0x2E,
|
||||
PKEY_0 = 0x30,
|
||||
PKEY_1 = 0x31,
|
||||
PKEY_2 = 0x32,
|
||||
PKEY_3 = 0x33,
|
||||
PKEY_A = 0x41,
|
||||
PKEY_B = 0x42,
|
||||
PKEY_C = 0x43,
|
||||
PKEY_E = 0x45,
|
||||
PKEY_F = 0x46,
|
||||
PKEY_G = 0x47,
|
||||
PKEY_H = 0x48,
|
||||
PKEY_J = 0x4A,
|
||||
PKEY_K = 0x4B,
|
||||
PKEY_L = 0x4C,
|
||||
PKEY_N = 0x4E,
|
||||
PKEY_O = 0x4F,
|
||||
PKEY_P = 0x50,
|
||||
PKEY_Q = 0x51,
|
||||
PKEY_S = 0x53,
|
||||
PKEY_V = 0x56,
|
||||
PKEY_W = 0x57,
|
||||
PKEY_X = 0x58,
|
||||
PKEY_Y = 0x59,
|
||||
PKEY_Z = 0x5A,
|
||||
PKEY_F4 = 0x73,
|
||||
PKEY_F8 = 0x77,
|
||||
PKEY_EQUAL = 0xBB,
|
||||
PKEY_MINUS = 0xBD,
|
||||
PKEY_BACKSLASH = 0xDC,
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
// Input accumulated per frame
|
||||
|
||||
#define PLATFORM_MAX_CHARS_PER_FRAME 64
|
||||
#define PLATFORM_MAX_KEYS_PER_FRAME 32
|
||||
|
||||
// Editor-facing input (ASCII chars + key codes + modifiers)
|
||||
typedef struct PlatformInput {
|
||||
char chars[PLATFORM_MAX_CHARS_PER_FRAME];
|
||||
S32 char_count;
|
||||
|
||||
U8 keys[PLATFORM_MAX_KEYS_PER_FRAME];
|
||||
S32 key_count;
|
||||
|
||||
B32 ctrl_held;
|
||||
B32 shift_held;
|
||||
B32 alt_held;
|
||||
} PlatformInput;
|
||||
|
||||
// Raw windowing input (UTF-16 chars + VK codes + mouse)
|
||||
typedef struct PlatformRawInput {
|
||||
U16 chars[PLATFORM_MAX_CHARS_PER_FRAME];
|
||||
S32 char_count;
|
||||
U8 keys[PLATFORM_MAX_KEYS_PER_FRAME];
|
||||
S32 key_count;
|
||||
B32 ctrl_held;
|
||||
B32 shift_held;
|
||||
Vec2F32 mouse_pos;
|
||||
Vec2F32 scroll_delta;
|
||||
B32 mouse_down;
|
||||
B32 was_mouse_down;
|
||||
} PlatformRawInput;
|
||||
|
||||
////////////////////////////////
|
||||
// Window
|
||||
|
||||
typedef struct PlatformWindow PlatformWindow;
|
||||
|
||||
typedef struct PlatformWindowDesc {
|
||||
const char *title;
|
||||
S32 width;
|
||||
S32 height;
|
||||
} PlatformWindowDesc;
|
||||
|
||||
typedef void (*PlatformFrameCallback)(void *user_data);
|
||||
|
||||
////////////////////////////////
|
||||
// Window API
|
||||
|
||||
PlatformWindow *platform_create_window(PlatformWindowDesc *desc);
|
||||
void platform_destroy_window(PlatformWindow *window);
|
||||
B32 platform_poll_events(PlatformWindow *window);
|
||||
void platform_get_size(PlatformWindow *window, S32 *w, S32 *h);
|
||||
void *platform_get_native_handle(PlatformWindow *window);
|
||||
PlatformRawInput platform_get_input(PlatformWindow *window);
|
||||
void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback cb, void *user_data);
|
||||
F32 platform_get_dpi_scale(PlatformWindow *window);
|
||||
|
||||
////////////////////////////////
|
||||
// Input adapter — convert raw windowing input to editor input
|
||||
|
||||
PlatformInput platform_adapt_input(PlatformRawInput *raw);
|
||||
|
||||
////////////////////////////////
|
||||
// Clipboard
|
||||
|
||||
void platform_clipboard_set(const char *text);
|
||||
const char *platform_clipboard_get(void);
|
||||
|
||||
////////////////////////////////
|
||||
// Process spawning
|
||||
|
||||
void platform_spawn_terminal(const char *command, const char *working_dir);
|
||||
297
c/platform/platform_win32.c
Normal file
297
c/platform/platform_win32.c
Normal file
@@ -0,0 +1,297 @@
|
||||
// platform_win32.c — Win32 windowing, input, clipboard, and process spawning
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
////////////////////////////////
|
||||
// Window internals
|
||||
|
||||
struct PlatformWindow {
|
||||
HWND hwnd;
|
||||
B32 should_close;
|
||||
S32 width;
|
||||
S32 height;
|
||||
PlatformFrameCallback frame_callback;
|
||||
void *frame_callback_user_data;
|
||||
PlatformRawInput input;
|
||||
B32 prev_mouse_down;
|
||||
};
|
||||
|
||||
static PlatformWindow *g_main_window = NULL;
|
||||
static B32 g_wndclass_registered = false;
|
||||
|
||||
////////////////////////////////
|
||||
// Window procedure
|
||||
|
||||
// Forward declare ImGui Win32 handler (defined in imgui_impl_win32.cpp)
|
||||
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static LRESULT CALLBACK platform_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
|
||||
// Let ImGui process input first
|
||||
if (ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam))
|
||||
return true;
|
||||
|
||||
PlatformWindow *pw = (PlatformWindow *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
switch (msg) {
|
||||
case WM_SIZE:
|
||||
if (pw && wparam != SIZE_MINIMIZED) {
|
||||
pw->width = (S32)LOWORD(lparam);
|
||||
pw->height = (S32)HIWORD(lparam);
|
||||
if (pw->frame_callback)
|
||||
pw->frame_callback(pw->frame_callback_user_data);
|
||||
}
|
||||
return 0;
|
||||
case WM_CHAR:
|
||||
if (pw && wparam >= 32 && wparam < 0xFFFF) {
|
||||
PlatformRawInput *ev = &pw->input;
|
||||
if (ev->char_count < PLATFORM_MAX_CHARS_PER_FRAME)
|
||||
ev->chars[ev->char_count++] = (U16)wparam;
|
||||
}
|
||||
return 0;
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
if (pw) {
|
||||
PlatformRawInput *ev = &pw->input;
|
||||
if (ev->key_count < PLATFORM_MAX_KEYS_PER_FRAME)
|
||||
ev->keys[ev->key_count++] = (U8)wparam;
|
||||
ev->ctrl_held = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
|
||||
ev->shift_held = (GetKeyState(VK_SHIFT) & 0x8000) != 0;
|
||||
}
|
||||
break;
|
||||
case WM_MOUSEWHEEL:
|
||||
if (pw) {
|
||||
S16 wheel_delta = (S16)HIWORD(wparam);
|
||||
pw->input.scroll_delta.y += (F32)wheel_delta / (F32)WHEEL_DELTA * 6.0f;
|
||||
}
|
||||
return 0;
|
||||
case WM_SETCURSOR:
|
||||
if (LOWORD(lparam) == HTCLIENT) {
|
||||
SetCursor(LoadCursor(NULL, IDC_IBEAM));
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
case WM_DPICHANGED:
|
||||
if (pw) {
|
||||
RECT *suggested = (RECT *)lparam;
|
||||
SetWindowPos(hwnd, NULL, suggested->left, suggested->top,
|
||||
suggested->right - suggested->left, suggested->bottom - suggested->top,
|
||||
SWP_NOZORDER | SWP_NOACTIVATE);
|
||||
}
|
||||
return 0;
|
||||
case WM_CLOSE:
|
||||
if (pw) pw->should_close = true;
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
if (pw == g_main_window)
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
case WM_SYSCOMMAND:
|
||||
if ((wparam & 0xfff0) == SC_KEYMENU)
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Window lifecycle
|
||||
|
||||
PlatformWindow *platform_create_window(PlatformWindowDesc *desc) {
|
||||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
|
||||
if (!g_wndclass_registered) {
|
||||
WNDCLASSEXW wc = {0};
|
||||
wc.cbSize = sizeof(wc);
|
||||
wc.style = CS_CLASSDC;
|
||||
wc.lpfnWndProc = platform_wndproc;
|
||||
wc.hInstance = GetModuleHandleW(NULL);
|
||||
wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(101));
|
||||
wc.hIconSm = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(101));
|
||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wc.lpszClassName = L"codemax_wc";
|
||||
RegisterClassExW(&wc);
|
||||
g_wndclass_registered = true;
|
||||
}
|
||||
|
||||
UINT dpi = GetDpiForSystem();
|
||||
int screen_w = GetSystemMetrics(SM_CXSCREEN);
|
||||
int screen_h = GetSystemMetrics(SM_CYSCREEN);
|
||||
int x = (screen_w - desc->width) / 2;
|
||||
int y = (screen_h - desc->height) / 2;
|
||||
|
||||
DWORD style = WS_OVERLAPPEDWINDOW;
|
||||
RECT rect = { 0, 0, (LONG)desc->width, (LONG)desc->height };
|
||||
AdjustWindowRectExForDpi(&rect, style, FALSE, 0, dpi);
|
||||
|
||||
int wchar_count = MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, NULL, 0);
|
||||
wchar_t *wtitle = (wchar_t *)_malloca(wchar_count * sizeof(wchar_t));
|
||||
MultiByteToWideChar(CP_UTF8, 0, desc->title, -1, wtitle, wchar_count);
|
||||
|
||||
HWND hwnd = CreateWindowExW(
|
||||
0, L"codemax_wc", wtitle,
|
||||
style,
|
||||
x, y,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
NULL, NULL, GetModuleHandleW(NULL), NULL
|
||||
);
|
||||
_freea(wtitle);
|
||||
|
||||
if (!hwnd) return NULL;
|
||||
|
||||
PlatformWindow *window = (PlatformWindow *)calloc(1, sizeof(PlatformWindow));
|
||||
window->hwnd = hwnd;
|
||||
window->should_close = false;
|
||||
window->width = desc->width;
|
||||
window->height = desc->height;
|
||||
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)window);
|
||||
g_main_window = window;
|
||||
|
||||
ShowWindow(hwnd, SW_SHOWDEFAULT);
|
||||
UpdateWindow(hwnd);
|
||||
return window;
|
||||
}
|
||||
|
||||
void platform_destroy_window(PlatformWindow *window) {
|
||||
if (!window) return;
|
||||
if (window->hwnd) DestroyWindow(window->hwnd);
|
||||
if (g_main_window == window) g_main_window = NULL;
|
||||
free(window);
|
||||
}
|
||||
|
||||
B32 platform_poll_events(PlatformWindow *window) {
|
||||
MSG msg;
|
||||
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
if (msg.message == WM_QUIT)
|
||||
window->should_close = true;
|
||||
}
|
||||
return !window->should_close;
|
||||
}
|
||||
|
||||
void platform_get_size(PlatformWindow *window, S32 *w, S32 *h) {
|
||||
if (w) *w = window->width;
|
||||
if (h) *h = window->height;
|
||||
}
|
||||
|
||||
void *platform_get_native_handle(PlatformWindow *window) {
|
||||
return (void *)window->hwnd;
|
||||
}
|
||||
|
||||
PlatformRawInput platform_get_input(PlatformWindow *window) {
|
||||
PlatformRawInput result = window->input;
|
||||
|
||||
POINT cursor;
|
||||
GetCursorPos(&cursor);
|
||||
ScreenToClient(window->hwnd, &cursor);
|
||||
result.mouse_pos = v2f32((F32)cursor.x, (F32)cursor.y);
|
||||
|
||||
result.was_mouse_down = window->prev_mouse_down;
|
||||
result.mouse_down = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
|
||||
window->prev_mouse_down = result.mouse_down;
|
||||
|
||||
memset(&window->input, 0, sizeof(window->input));
|
||||
return result;
|
||||
}
|
||||
|
||||
void platform_set_frame_callback(PlatformWindow *window, PlatformFrameCallback cb, void *user_data) {
|
||||
window->frame_callback = cb;
|
||||
window->frame_callback_user_data = user_data;
|
||||
}
|
||||
|
||||
F32 platform_get_dpi_scale(PlatformWindow *window) {
|
||||
if (!window || !window->hwnd) return 1.0f;
|
||||
return (F32)GetDpiForWindow(window->hwnd) / 96.0f;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Input adapter
|
||||
|
||||
PlatformInput platform_adapt_input(PlatformRawInput *raw) {
|
||||
PlatformInput pi = {0};
|
||||
pi.ctrl_held = raw->ctrl_held;
|
||||
pi.shift_held = raw->shift_held;
|
||||
pi.alt_held = (GetKeyState(VK_MENU) & 0x8000) != 0;
|
||||
|
||||
// Convert UTF-16 chars to ASCII (printable range only)
|
||||
for (S32 i = 0; i < raw->char_count && pi.char_count < PLATFORM_MAX_CHARS_PER_FRAME; i++) {
|
||||
U16 ch = raw->chars[i];
|
||||
if (ch >= 32 && ch < 127)
|
||||
pi.chars[pi.char_count++] = (char)ch;
|
||||
}
|
||||
|
||||
// Key codes are identical (both use Windows VK codes)
|
||||
for (S32 i = 0; i < raw->key_count && pi.key_count < PLATFORM_MAX_KEYS_PER_FRAME; i++)
|
||||
pi.keys[pi.key_count++] = raw->keys[i];
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Clipboard
|
||||
|
||||
void platform_clipboard_set(const char *text) {
|
||||
if (!text) return;
|
||||
int len = (int)strlen(text);
|
||||
if (len == 0) return;
|
||||
int wlen = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0);
|
||||
if (wlen == 0) return;
|
||||
HGLOBAL hmem = GlobalAlloc(GMEM_MOVEABLE, (wlen + 1) * sizeof(wchar_t));
|
||||
if (!hmem) return;
|
||||
wchar_t *wbuf = (wchar_t *)GlobalLock(hmem);
|
||||
MultiByteToWideChar(CP_UTF8, 0, text, len, wbuf, wlen);
|
||||
wbuf[wlen] = L'\0';
|
||||
GlobalUnlock(hmem);
|
||||
HWND hwnd = g_main_window ? g_main_window->hwnd : NULL;
|
||||
if (OpenClipboard(hwnd)) {
|
||||
EmptyClipboard();
|
||||
SetClipboardData(CF_UNICODETEXT, hmem);
|
||||
CloseClipboard();
|
||||
} else {
|
||||
GlobalFree(hmem);
|
||||
}
|
||||
}
|
||||
|
||||
const char *platform_clipboard_get(void) {
|
||||
static char buf[64 * 1024];
|
||||
buf[0] = '\0';
|
||||
HWND hwnd = g_main_window ? g_main_window->hwnd : NULL;
|
||||
if (!OpenClipboard(hwnd)) return NULL;
|
||||
HGLOBAL hmem = GetClipboardData(CF_UNICODETEXT);
|
||||
if (hmem) {
|
||||
wchar_t *wbuf = (wchar_t *)GlobalLock(hmem);
|
||||
if (wbuf) {
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, sizeof(buf) - 1, NULL, NULL);
|
||||
buf[len > 0 ? len - 1 : 0] = '\0';
|
||||
GlobalUnlock(hmem);
|
||||
}
|
||||
}
|
||||
CloseClipboard();
|
||||
return buf[0] ? buf : NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
// Process spawning
|
||||
|
||||
void platform_spawn_terminal(const char *command, const char *working_dir) {
|
||||
char cmd_line[2048];
|
||||
snprintf(cmd_line, sizeof(cmd_line), "cmd.exe /k %s", command);
|
||||
|
||||
STARTUPINFOA si = {0};
|
||||
si.cb = sizeof(si);
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
CreateProcessA(NULL, cmd_line, NULL, NULL, FALSE,
|
||||
CREATE_NEW_CONSOLE, NULL, working_dir, &si, &pi);
|
||||
if (pi.hProcess) CloseHandle(pi.hProcess);
|
||||
if (pi.hThread) CloseHandle(pi.hThread);
|
||||
}
|
||||
Reference in New Issue
Block a user