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 echoon()
|
||||
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
|
||||
|
||||
$console:only
|
||||
|
|
@ -22,6 +27,65 @@ if command$=""then
|
|||
goto quit
|
||||
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
|
||||
cursoroff
|
||||
chdir _startdir$
|
||||
|
|
@ -258,10 +322,76 @@ while keyin<>27
|
|||
|
||||
_limit 30
|
||||
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:
|
||||
_sndclose musichandle
|
||||
ipc_cleanup
|
||||
print clearrest
|
||||
print clearrest;
|
||||
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)
|
||||
end select
|
||||
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
|
||||
#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)
|
||||
|
|
@ -21,8 +33,16 @@ 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();
|
||||
|
|
@ -65,8 +85,91 @@ int termwidth() {
|
|||
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
|
||||
|
||||
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;
|
||||
|
|
@ -126,4 +229,124 @@ int termwidth() {
|
|||
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
|
||||
|
|
|
|||
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