cimp/terminkey.h

366 lines
9.7 KiB
C
Raw Permalink Normal View History

2026-06-01 09:16:39 +02:00
// #include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#include <conio.h>
2026-06-23 10:21:17 +02:00
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
2026-06-01 09:16:39 +02:00
#else
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
2026-06-23 10:21:17 +02:00
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <stdint.h>
2026-06-01 09:16:39 +02:00
#endif
// Special key codes (unified across platforms)
#define KEY_ARROW_UP 1001
#define KEY_ARROW_DOWN 1002
#define KEY_ARROW_RIGHT 1003
#define KEY_ARROW_LEFT 1004
int terminkey();
void echooff();
void echoon();
int termwidth();
2026-06-23 10:21:17 +02:00
int ipc_init();
int ipc_check_message(uintptr_t buffer_ptr, int max_len);
void ipc_send_message(const char* message);
void ipc_cleanup();
2026-06-01 09:16:39 +02:00
#ifdef _WIN32
2026-06-23 10:21:17 +02:00
static HANDLE hServerPipe = INVALID_HANDLE_VALUE;
2026-06-23 12:47:05 +02:00
static OVERLAPPED oOverlap;
static BOOL fPendingIO = FALSE;
2026-06-23 10:21:17 +02:00
2026-06-01 09:16:39 +02:00
int terminkey() {
if (_kbhit()) {
int ch = _getch();
// Handle Windows arrow keys
if (ch == 224) {
int next = _getch();
switch (next) {
case 72: return KEY_ARROW_UP;
case 80: return KEY_ARROW_DOWN;
case 75: return KEY_ARROW_LEFT;
case 77: return KEY_ARROW_RIGHT;
default: return next;
}
}
return ch;
}
return -1;
}
void echooff() {
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode = 0;
GetConsoleMode(hStdin, &mode);
SetConsoleMode(hStdin, mode & ~ENABLE_ECHO_INPUT);
}
void echoon() {
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD mode = 0;
GetConsoleMode(hStdin, &mode);
SetConsoleMode(hStdin, mode | ENABLE_ECHO_INPUT);
}
int termwidth() {
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hStdout, &csbi)) {
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
return -1;
}
2026-06-23 10:21:17 +02:00
int ipc_init() {
2026-06-23 12:47:05 +02:00
// Create a regular blocking pipe, but enable FILE_FLAG_OVERLAPPED for safe async I/O
2026-06-23 10:21:17 +02:00
hServerPipe = CreateNamedPipe(
"\\\\.\\pipe\\cimp_ipc",
2026-06-23 12:47:05 +02:00
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
2026-06-23 10:21:17 +02:00
1,
4096,
4096,
0,
NULL
);
if (hServerPipe == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
if (err == ERROR_ACCESS_DENIED || err == ERROR_PIPE_BUSY) {
2026-06-23 12:33:49 +02:00
return 0; // Already running (Client Mode)
2026-06-23 10:21:17 +02:00
}
return -1; // Other error
}
2026-06-23 12:47:05 +02:00
// Initialize the overlapped structure and create an event
memset(&oOverlap, 0, sizeof(OVERLAPPED));
oOverlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
fPendingIO = FALSE;
2026-06-23 12:33:49 +02:00
return 1; // Server mode successfully started
2026-06-23 10:21:17 +02:00
}
int ipc_check_message(uintptr_t buffer_ptr, int max_len) {
if (hServerPipe == INVALID_HANDLE_VALUE) return 0;
char* buffer = (char*)buffer_ptr;
DWORD bytesRead = 0;
2026-06-23 12:47:05 +02:00
if (!fPendingIO) {
// Start an asynchronous listen operation
if (!ConnectNamedPipe(hServerPipe, &oOverlap)) {
DWORD err = GetLastError();
if (err == ERROR_IO_PENDING) {
fPendingIO = TRUE;
} else if (err == ERROR_PIPE_CONNECTED) {
// Client already connected before we listened!
SetEvent(oOverlap.hEvent);
fPendingIO = TRUE;
2026-06-23 12:40:40 +02:00
}
2026-06-23 12:47:05 +02:00
}
}
if (fPendingIO) {
// Check if the connection event has triggered without waiting (0ms timeout)
if (WaitForSingleObject(oOverlap.hEvent, 0) == WAIT_OBJECT_0) {
DWORD cbRet = 0;
// Connection is ready, let's see if there is data
if (GetOverlappedResult(hServerPipe, &oOverlap, &cbRet, FALSE)) {
DWORD bytesAvail = 0;
if (PeekNamedPipe(hServerPipe, NULL, 0, NULL, &bytesAvail, NULL) && bytesAvail > 0) {
// Data has arrived, read it synchronously
BOOL success = ReadFile(hServerPipe, buffer, max_len - 1, &bytesRead, NULL);
if (success && bytesRead > 0) {
buffer[bytesRead] = '\0';
// Clean up and reset for the next client connection
DisconnectNamedPipe(hServerPipe);
ResetEvent(oOverlap.hEvent);
fPendingIO = FALSE;
return (int)bytesRead;
}
}
} else {
// Client disconnected or pipe broke before sending
2026-06-23 12:22:51 +02:00
DisconnectNamedPipe(hServerPipe);
2026-06-23 12:47:05 +02:00
ResetEvent(oOverlap.hEvent);
fPendingIO = FALSE;
2026-06-23 12:22:51 +02:00
}
2026-06-23 10:21:17 +02:00
}
}
return 0;
}
void ipc_send_message(const char* message) {
HANDLE hPipe = CreateFile(
"\\\\.\\pipe\\cimp_ipc",
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hPipe != INVALID_HANDLE_VALUE) {
DWORD bytesWritten = 0;
WriteFile(hPipe, message, strlen(message), &bytesWritten, NULL);
CloseHandle(hPipe);
}
}
void ipc_cleanup() {
if (hServerPipe != INVALID_HANDLE_VALUE) {
2026-06-23 12:47:05 +02:00
DisconnectNamedPipe(hServerPipe);
2026-06-23 10:21:17 +02:00
CloseHandle(hServerPipe);
hServerPipe = INVALID_HANDLE_VALUE;
}
2026-06-23 12:47:05 +02:00
if (oOverlap.hEvent != NULL) {
CloseHandle(oOverlap.hEvent);
oOverlap.hEvent = NULL;
}
2026-06-23 10:21:17 +02:00
}
2026-06-01 09:16:39 +02:00
#else
2026-06-23 10:21:17 +02:00
static int server_fd = -1;
static char socket_path[256] = "";
static void get_socket_path() {
if (socket_path[0] == '\0') {
uid_t uid = getuid();
snprintf(socket_path, sizeof(socket_path), "/tmp/cimp_ipc_%u.sock", (unsigned int)uid);
}
}
2026-06-01 09:16:39 +02:00
int terminkey() {
struct termios oldt, newt;
int ch;
int oldf;
// Save existing terminal settings
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
// Disable buffering and echoing
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
// Set STDIN to non-blocking
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
// Handle Unix arrow keys (ESC [ A/B/C/D)
if (ch == 27) {
int next1 = getchar();
if (next1 == '[') {
int next2 = getchar();
switch (next2) {
case 'A': ch = KEY_ARROW_UP; break;
case 'B': ch = KEY_ARROW_DOWN; break;
case 'C': ch = KEY_ARROW_RIGHT; break;
case 'D': ch = KEY_ARROW_LEFT; break;
}
}
}
// Restore terminal settings
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
return ch;
}
void echooff() {
struct termios tty;
tcgetattr(STDIN_FILENO, &tty);
tty.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &tty);
}
void echoon() {
struct termios tty;
tcgetattr(STDIN_FILENO, &tty);
tty.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &tty);
}
int termwidth() {
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
return w.ws_col;
}
return -1;
}
2026-06-23 10:21:17 +02:00
int ipc_init() {
get_socket_path();
int test_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (test_fd >= 0) {
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
if (connect(test_fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
close(test_fd);
return 0; // Already running
}
close(test_fd);
}
unlink(socket_path);
server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (server_fd < 0) {
return -1;
}
int flags = fcntl(server_fd, F_GETFL, 0);
fcntl(server_fd, F_SETFL, flags | O_NONBLOCK);
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
close(server_fd);
server_fd = -1;
return -1;
}
if (listen(server_fd, 5) < 0) {
close(server_fd);
server_fd = -1;
unlink(socket_path);
return -1;
}
return 1;
}
int ipc_check_message(uintptr_t buffer_ptr, int max_len) {
if (server_fd < 0) return 0;
char* buffer = (char*)buffer_ptr;
int client_fd = accept(server_fd, NULL, NULL);
if (client_fd < 0) {
return 0;
}
int total_read = 0;
while (total_read < max_len - 1) {
int r = read(client_fd, buffer + total_read, max_len - 1 - total_read);
if (r <= 0) break;
total_read += r;
}
buffer[total_read] = '\0';
close(client_fd);
return total_read;
}
void ipc_send_message(const char* message) {
get_socket_path();
int client_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (client_fd < 0) return;
struct sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
if (connect(client_fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
int total_written = 0;
int len = strlen(message);
while (total_written < len) {
int w = write(client_fd, message + total_written, len - total_written);
if (w <= 0) break;
total_written += w;
}
}
close(client_fd);
}
void ipc_cleanup() {
if (server_fd >= 0) {
close(server_fd);
server_fd = -1;
}
get_socket_path();
unlink(socket_path);
}
2026-06-01 09:16:39 +02:00
#endif