cimp/terminkey.h
2026-06-23 12:22:51 +02:00

365 lines
9.6 KiB
C

// #include <stdio.h>
#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#else
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#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>
#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();
int ipc_init();
int ipc_check_message(uintptr_t buffer_ptr, int max_len);
void ipc_send_message(const char* message);
void ipc_cleanup();
void get_absolute_path(const char* rel_path, uintptr_t abs_path_ptr, int max_len);
#ifdef _WIN32
static HANDLE hServerPipe = INVALID_HANDLE_VALUE;
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;
}
int ipc_init() {
hServerPipe = CreateNamedPipe(
"\\\\.\\pipe\\cimp_ipc",
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT,
1,
4096,
4096,
0,
NULL
);
if (hServerPipe == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
if (err == ERROR_ACCESS_DENIED || err == ERROR_PIPE_BUSY) {
return 0; // Already running
}
return -1; // Other error
}
ConnectNamedPipe(hServerPipe, NULL);
return 1;
}
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;
// Check if a client is connected (or already connected)
BOOL connected = ConnectNamedPipe(hServerPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (connected) {
DWORD bytesAvail = 0;
// Sneak peek to see if data has safely arrived in the buffer yet
if (PeekNamedPipe(hServerPipe, NULL, 0, NULL, &bytesAvail, NULL)) {
if (bytesAvail > 0) {
// Data is ready, perform the read safely
BOOL success = ReadFile(hServerPipe, buffer, max_len - 1, &bytesRead, NULL);
if (success && bytesRead > 0) {
buffer[bytesRead] = '\0';
DisconnectNamedPipe(hServerPipe);
ConnectNamedPipe(hServerPipe, NULL); // Re-arm for next connection
return (int)bytesRead;
}
}
} else {
// If Peek fails because the client dropped out without sending anything
if (GetLastError() == ERROR_BROKEN_PIPE) {
DisconnectNamedPipe(hServerPipe);
ConnectNamedPipe(hServerPipe, NULL);
}
}
}
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) {
CloseHandle(hServerPipe);
hServerPipe = INVALID_HANDLE_VALUE;
}
}
void get_absolute_path(const char* rel_path, uintptr_t abs_path_ptr, int max_len) {
char* abs_path = (char*)abs_path_ptr;
_fullpath(abs_path, rel_path, max_len);
}
#else
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);
}
}
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;
}
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);
}
void get_absolute_path(const char* rel_path, uintptr_t abs_path_ptr, int max_len) {
char* abs_path = (char*)abs_path_ptr;
char resolved[PATH_MAX];
if (realpath(rel_path, resolved) != NULL) {
strncpy(abs_path, resolved, max_len);
abs_path[max_len - 1] = '\0';
} else {
if (rel_path[0] == '/') {
strncpy(abs_path, rel_path, max_len);
abs_path[max_len - 1] = '\0';
} else {
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
snprintf(abs_path, max_len, "%s/%s", cwd, rel_path);
} else {
strncpy(abs_path, rel_path, max_len);
abs_path[max_len - 1] = '\0';
}
}
}
}
#endif