Add Inter-Process Communication
This commit is contained in:
parent
46cdaf4e63
commit
19bf4b5288
4 changed files with 423 additions and 1 deletions
145
cimp.bas
145
cimp.bas
|
|
@ -3,6 +3,11 @@ declare library"terminkey"
|
||||||
sub echooff()
|
sub echooff()
|
||||||
sub echoon()
|
sub echoon()
|
||||||
function termwidth()
|
function termwidth()
|
||||||
|
function ipc_init&()
|
||||||
|
function ipc_check_message%(byval buf as _offset, byval max_len as long)
|
||||||
|
sub ipc_send_message(buf as string)
|
||||||
|
sub ipc_cleanup()
|
||||||
|
sub get_absolute_path(rel_path as string, byval abs_path as _offset, byval max_len as long)
|
||||||
end declare
|
end declare
|
||||||
|
|
||||||
$console:only
|
$console:only
|
||||||
|
|
@ -22,6 +27,65 @@ if command$=""then
|
||||||
goto quit
|
goto quit
|
||||||
end if
|
end if
|
||||||
|
|
||||||
|
dim ipc_status as long
|
||||||
|
ipc_status = ipc_init
|
||||||
|
|
||||||
|
if ipc_status = 0 then
|
||||||
|
' --- CLIENT MODE ---
|
||||||
|
dim cmd_msg as string
|
||||||
|
|
||||||
|
if command$(1) = "--next" then
|
||||||
|
cmd_msg = "NEXT"
|
||||||
|
elseif command$(1) = "--prev" then
|
||||||
|
cmd_msg = "PREV"
|
||||||
|
elseif command$(1) = "-v" or command$(1) = "--volume" then
|
||||||
|
cmd_msg = "VOL:" + command$(2)
|
||||||
|
elseif command$(1) = "--add" then
|
||||||
|
cmd_msg = "ADD:" + get_abs_path(command$(2))
|
||||||
|
elseif command$(1) = "--playlist" then
|
||||||
|
cmd_msg = "GET_PLAYLIST"
|
||||||
|
else
|
||||||
|
' Default behavior: Resolve target file/playlist and replace active queue
|
||||||
|
cmd_msg = "PLAY:" + get_abs_path(command$(1))
|
||||||
|
end if
|
||||||
|
|
||||||
|
' Send instruction to the main player instance
|
||||||
|
ipc_send_message cmd_msg
|
||||||
|
|
||||||
|
' --- Two-way Client feedback for --playlist ---
|
||||||
|
if cmd_msg = "GET_PLAYLIST" then
|
||||||
|
ipc_cleanup
|
||||||
|
_delay 0.1
|
||||||
|
|
||||||
|
dim client_listen as long
|
||||||
|
client_listen = ipc_init
|
||||||
|
if client_listen = 1 then
|
||||||
|
dim reply_buf as string
|
||||||
|
reply_buf = space$(4096) + chr$(0)
|
||||||
|
dim start_wait as double
|
||||||
|
start_wait = timer
|
||||||
|
|
||||||
|
do while timer - start_wait < 2
|
||||||
|
dim reply_len as long
|
||||||
|
reply_len = ipc_check_message(_offset(reply_buf), 4096)
|
||||||
|
if reply_len > 0 then
|
||||||
|
print left$(reply_buf, reply_len)
|
||||||
|
exit do
|
||||||
|
end if
|
||||||
|
_limit 30
|
||||||
|
loop
|
||||||
|
ipc_cleanup
|
||||||
|
end if
|
||||||
|
end if
|
||||||
|
|
||||||
|
system ' Exit client instance completely
|
||||||
|
elseif ipc_status = -1 then
|
||||||
|
print "Error initializing Inter-Process Communication."
|
||||||
|
goto quit
|
||||||
|
end if
|
||||||
|
|
||||||
|
' --- SERVER MODE (Main Player Execution continues below) ---
|
||||||
|
|
||||||
echooff
|
echooff
|
||||||
cursoroff
|
cursoroff
|
||||||
chdir _startdir$
|
chdir _startdir$
|
||||||
|
|
@ -258,10 +322,76 @@ while keyin<>27
|
||||||
|
|
||||||
_limit 30
|
_limit 30
|
||||||
if _exit then goto quit
|
if _exit then goto quit
|
||||||
wend
|
|
||||||
|
' --- Poll Client Messages ---
|
||||||
|
dim incoming_buf as string
|
||||||
|
incoming_buf = space$(512) + chr$(0)
|
||||||
|
dim msg_len as long
|
||||||
|
msg_len = ipc_check_message(_offset(incoming_buf), 512)
|
||||||
|
|
||||||
|
if msg_len > 0 then
|
||||||
|
dim client_cmd as string
|
||||||
|
client_cmd = left$(incoming_buf, msg_len)
|
||||||
|
|
||||||
|
if client_cmd = "NEXT" then
|
||||||
|
playnext = 1
|
||||||
|
elseif client_cmd = "PREV" then
|
||||||
|
playnext = -1
|
||||||
|
elseif left$(client_cmd, 4) = "VOL:" then
|
||||||
|
volume = val(mid$(client_cmd, 5)) / 100
|
||||||
|
if volume > 1 then volume = 1
|
||||||
|
if volume < 0 then volume = 0
|
||||||
|
_sndvol musichandle, volume
|
||||||
|
elseif left$(client_cmd, 4) = "ADD:" then
|
||||||
|
dim new_file as string
|
||||||
|
new_file = mid$(client_cmd, 5)
|
||||||
|
if _fileexists(new_file) then
|
||||||
|
if lcase$(right$(new_file, 4)) = ".m3u" then
|
||||||
|
parsem3u new_file, file()
|
||||||
|
else
|
||||||
|
redim _preserve file(ubound(file) + 1) as string
|
||||||
|
file(ubound(file)) = new_file
|
||||||
|
end if
|
||||||
|
end if
|
||||||
|
elseif left$(client_cmd, 5) = "PLAY:" then
|
||||||
|
dim replace_file as string
|
||||||
|
replace_file = mid$(client_cmd, 6)
|
||||||
|
if _fileexists(replace_file) then
|
||||||
|
redim file(0) as string
|
||||||
|
if lcase$(right$(replace_file, 4)) = ".m3u" then
|
||||||
|
parsem3u replace_file, file()
|
||||||
|
else
|
||||||
|
file(0) = replace_file
|
||||||
|
end if
|
||||||
|
i = 0
|
||||||
|
playnext = 1
|
||||||
|
end if
|
||||||
|
elseif client_cmd = "GET_PLAYLIST" then
|
||||||
|
dim p_idx as long
|
||||||
|
dim playlist_payload as string
|
||||||
|
playlist_payload = "=== Current Playlist ===" + chr$(10)
|
||||||
|
for p_idx = lbound(file) to ubound(file)
|
||||||
|
if p_idx = i then
|
||||||
|
playlist_payload = playlist_payload + "-> " + file(p_idx) + chr$(10)
|
||||||
|
else
|
||||||
|
playlist_payload = playlist_payload + " " + file(p_idx) + chr$(10)
|
||||||
|
end if
|
||||||
|
next p_idx
|
||||||
|
|
||||||
|
ipc_cleanup
|
||||||
|
_delay 0.1
|
||||||
|
ipc_send_message playlist_payload
|
||||||
|
_delay 0.1
|
||||||
|
dim reinit as long
|
||||||
|
reinit = ipc_init
|
||||||
|
end if
|
||||||
|
end if
|
||||||
|
' --- End Poll Client Messages ---
|
||||||
|
wend
|
||||||
|
|
||||||
quit:
|
quit:
|
||||||
_sndclose musichandle
|
_sndclose musichandle
|
||||||
|
ipc_cleanup
|
||||||
print clearrest
|
print clearrest
|
||||||
print clearrest;
|
print clearrest;
|
||||||
print cursorback;
|
print cursorback;
|
||||||
|
|
@ -496,3 +626,16 @@ function getcodepoint& (utf8char$)
|
||||||
getcodepoint&=(b1 and &h07)*262144+(b2 and &h3f)*4096+(b3 and &h3f)*64+(b4 and &h3f)
|
getcodepoint&=(b1 and &h07)*262144+(b2 and &h3f)*4096+(b3 and &h3f)*64+(b4 and &h3f)
|
||||||
end select
|
end select
|
||||||
end function
|
end function
|
||||||
|
|
||||||
|
function get_abs_path$ (relative_path as string)
|
||||||
|
dim abs_buf as string
|
||||||
|
abs_buf = space$(512) + chr$(0) ' Allocate buffer space for the C function
|
||||||
|
get_absolute_path relative_path, _offset(abs_buf), 512
|
||||||
|
dim null_pos as long
|
||||||
|
null_pos = instr(abs_buf, chr$(0))
|
||||||
|
if null_pos > 0 then
|
||||||
|
get_abs_path$ = _trim$(left$(abs_buf, null_pos - 1))
|
||||||
|
else
|
||||||
|
get_abs_path$ = _trim$(abs_buf)
|
||||||
|
end if
|
||||||
|
end function
|
||||||
|
|
|
||||||
223
terminkey.h
223
terminkey.h
|
|
@ -3,11 +3,23 @@
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
#else
|
#else
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.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
|
#endif
|
||||||
|
|
||||||
// Special key codes (unified across platforms)
|
// Special key codes (unified across platforms)
|
||||||
|
|
@ -21,8 +33,16 @@ void echooff();
|
||||||
void echoon();
|
void echoon();
|
||||||
int termwidth();
|
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
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
static HANDLE hServerPipe = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
int terminkey() {
|
int terminkey() {
|
||||||
if (_kbhit()) {
|
if (_kbhit()) {
|
||||||
int ch = _getch();
|
int ch = _getch();
|
||||||
|
|
@ -65,8 +85,91 @@ int termwidth() {
|
||||||
return -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;
|
||||||
|
BOOL connected = ConnectNamedPipe(hServerPipe, NULL) ?
|
||||||
|
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
|
||||||
|
|
||||||
|
if (connected) {
|
||||||
|
BOOL success = ReadFile(hServerPipe, buffer, max_len - 1, &bytesRead, NULL);
|
||||||
|
if (success && bytesRead > 0) {
|
||||||
|
buffer[bytesRead] = '\0';
|
||||||
|
DisconnectNamedPipe(hServerPipe);
|
||||||
|
ConnectNamedPipe(hServerPipe, NULL);
|
||||||
|
return (int)bytesRead;
|
||||||
|
}
|
||||||
|
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
|
#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() {
|
int terminkey() {
|
||||||
struct termios oldt, newt;
|
struct termios oldt, newt;
|
||||||
int ch;
|
int ch;
|
||||||
|
|
@ -126,4 +229,124 @@ int termwidth() {
|
||||||
return -1;
|
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
|
#endif
|
||||||
|
|
|
||||||
44
test_ipc.bas
Normal file
44
test_ipc.bas
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
$console:only
|
||||||
|
declare library "terminkey"
|
||||||
|
function ipc_init&()
|
||||||
|
function ipc_check_message%(byval buf as _offset, byval max_len as long)
|
||||||
|
sub ipc_send_message(buf as string)
|
||||||
|
sub ipc_cleanup()
|
||||||
|
sub get_absolute_path(rel_path as string, byval abs_path as _offset, byval max_len as long)
|
||||||
|
end declare
|
||||||
|
|
||||||
|
dim r as long
|
||||||
|
r = ipc_init
|
||||||
|
print "ipc_init returned:"; r
|
||||||
|
|
||||||
|
if r = 1 then
|
||||||
|
print "We are the server! Waiting for a message..."
|
||||||
|
dim msg as string
|
||||||
|
msg = space$(100) + chr$(0)
|
||||||
|
dim start_time as double
|
||||||
|
start_time = timer
|
||||||
|
do while timer - start_time < 3
|
||||||
|
dim n as long
|
||||||
|
n = ipc_check_message(_offset(msg), 100)
|
||||||
|
if n > 0 then
|
||||||
|
print "Received: "; left$(msg, n)
|
||||||
|
exit do
|
||||||
|
end if
|
||||||
|
_limit 10
|
||||||
|
loop
|
||||||
|
ipc_cleanup
|
||||||
|
elseif r = 0 then
|
||||||
|
print "We are the client! Sending a message..."
|
||||||
|
ipc_send_message "Hello from client!"
|
||||||
|
else
|
||||||
|
print "Error initializing IPC"
|
||||||
|
end if
|
||||||
|
|
||||||
|
dim rel as string
|
||||||
|
rel = "readme.md"
|
||||||
|
dim abs_path as string
|
||||||
|
abs_path = space$(512) + chr$(0)
|
||||||
|
get_absolute_path rel, _offset(abs_path), 512
|
||||||
|
print "Absolute path of '"; rel; "' is: '"; _trim$(abs_path); "'"
|
||||||
|
|
||||||
|
system
|
||||||
12
todo.md
Normal file
12
todo.md
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# todo
|
||||||
|
## ipc
|
||||||
|
add ipc functionality so it's possible while the program runs to:
|
||||||
|
- play next and previous track with `cimp --next` and `cimp --prev`
|
||||||
|
- change volume with `cimp --volume xx`
|
||||||
|
- add file to playlist with `cimp --add filename.ext`
|
||||||
|
- if started without `--add` it should clear the playlist and load and play the given file or playlist.
|
||||||
|
- if given the flag `--playlist` it should output the song array as a playlist.
|
||||||
|
|
||||||
|
the c functions are ready in terminkey.h and an example of how it works is found in test_ipc.bas
|
||||||
|
|
||||||
|
## recurcive add all music in folder
|
||||||
Loading…
Add table
Add a link
Reference in a new issue