Call arbitrary unix programs on events.

Forums:

MOC version: 
2.5.2

I wrote a quick tool that connects to the socket, and does "stuff" when events happen.

I'm playing with using it like this:

mocevents CTIME "echo %ct | osd_cat" STATE "twmnc -t %state"

To display an OSD that shows current time in the song, and to post a notification whenever the music state changes.
You could use it to do other things quite easily.

I created the mocevents.c file in the source tree, and did:

gcc -c mocevents.c&&\ /bin/sh ./libtool --tag=CC --mode=link gcc -pthread -g -O2 -Wall -W \ -I/usr/include/alsa -ldb -lasound -lm -lmagic -lncursesw -lsamplerate -lcurl \ -export-dynamic -o mocevents mocevents.o \ log.o protocol.o server.o common.o playlist.o fifo_buf.o out_buf.o audio.o decoder.o \ interface.o interface_elements.o menu.o files.o options.o player.o playlist_file.o \ themes.o keys.o io.o compat.o audio_conversion.o rbtree.o tags_cache.o utf8.o rcc.o \ softmixer.o lyrics.o lists.o equalizer.o oss.o alsa.o null_out.o md5.o io_curl.o \ -Wl,--export-dynamic -ldb /usr/lib64/libasound.so -lpthread -lrt /usr/lib64/libmagic.so \ -lncursesw /usr/lib64/libsamplerate.so /usr/lib64/libcurl.so /usr/lib64/libidn.so -lssh2 \ /usr/lib64/libldap.so /usr/lib64/liblber.so /usr/lib64/libsasl2.so -lresolv -lssl -lcrypto \ -lz /usr/lib64/libltdl.so -lm -ldl -pthread

To compile it.

I should have probably posted the contents of mocevents.c too:

/* Pierre Coueffin - August 2019 pcoueffin@gmail.com */ /* Tool to run arbitrary commands when events happen in moc */ /* Lets you do things like: mocevents STATE "twmnc -t %state" */ /* mocevents CTIME "echo %ct | osd_cat" */ /* I'm not sure what the point really is, I just thought it would be cool... */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <stdlib.h> #include <stdbool.h> #include <assert.h> #include "options.h" #include "protocol.h" static void show_usage (const char *prg_name) { printf("Usage:\n%s event1 command1 event2 command2 ... eventn commandn\n\n", prg_name); printf("Where events are one of: \n"); // from protocol.h printf(" STATE \n"); printf(" CTIME \n"); printf(" SRV_ERROR \n"); printf(" BUSY \n"); printf(" DATA \n"); printf(" BITRATE \n"); printf(" RATE \n"); printf(" CHANNELS \n"); printf(" EXIT \n"); printf(" PONG \n"); printf(" OPTIONS \n"); printf(" SEND_PLIST \n"); printf(" TAGS \n"); printf(" STATUS_MSG \n"); printf(" MIXER_CHANGE \n"); printf(" FILE_TAGS \n"); printf(" AVG_BITRATE \n"); printf(" AUDIO_START \n"); printf(" AUDIO_STOP \n"); printf(" QUEUE_ADD \n"); printf(" QUEUE_DEL \n"); printf(" QUEUE_MOVE \n"); printf(" QUEUE_CLEAR \n\n"); printf("And commands can include format strings from mocp's -Q option as arguments\n"); } // From main.c /* Connect to the server, return fd of the socket or -1 on error. */ static int server_connect () { struct sockaddr_un sock_name; int sock; /* Create a socket */ if ((sock = socket (PF_LOCAL, SOCK_STREAM, 0)) == -1) return -1; sock_name.sun_family = AF_LOCAL; strcpy (sock_name.sun_path, socket_name()); if (connect(sock, (struct sockaddr *)&sock_name, SUN_LEN(&sock_name)) == -1) { close (sock); return -1; } return sock; } char cmdBuffer[4096]; char *expand(char *fmtStr) { // I could do this more efficiently by lifting the -Q code from mocp char cmd[4096]; FILE *pipe; sprintf(cmd, "mocp -Q \"%s &\"", fmtStr); assert(pipe=popen(cmd, "r")); fgets(cmdBuffer, 4095, pipe); pclose(pipe); return cmdBuffer; } char *targets[256]; int main (int argc, char *argv[]) { memset(targets, sizeof(char*)*256,0); // We need some args, and always an even number of them... if (argc < 2 || ! (argc % 2)) { show_usage(argv[0]); return 1; } for (int i = 1; i < argc; i+=2) { // from protocol.h again... if (! strcmp(argv[i], "STATE")) targets[EV_STATE] = argv[i+1]; else if (! strcmp(argv[i], "CTIME")) targets[EV_CTIME] = argv[i+1]; else if (! strcmp(argv[i], "SRV_ERROR")) targets[EV_SRV_ERROR] = argv[i+1]; else if (! strcmp(argv[i], "BUSY")) targets[EV_BUSY] = argv[i+1]; else if (! strcmp(argv[i], "DATA")) targets[EV_DATA] = argv[i+1]; else if (! strcmp(argv[i], "BITRATE")) targets[EV_BITRATE] = argv[i+1]; else if (! strcmp(argv[i], "RATE")) targets[EV_RATE] = argv[i+1]; else if (! strcmp(argv[i], "CHANNELS")) targets[EV_CHANNELS] = argv[i+1]; else if (! strcmp(argv[i], "EXIT")) targets[EV_EXIT] = argv[i+1]; else if (! strcmp(argv[i], "PONG")) targets[EV_PONG] = argv[i+1]; else if (! strcmp(argv[i], "OPTIONS")) targets[EV_OPTIONS] = argv[i+1]; else if (! strcmp(argv[i], "SEND_PLIST")) targets[EV_SEND_PLIST] = argv[i+1]; else if (! strcmp(argv[i], "TAGS")) targets[EV_TAGS] = argv[i+1]; else if (! strcmp(argv[i], "STATUS_MSG")) targets[EV_STATUS_MSG] = argv[i+1]; else if (! strcmp(argv[i], "MIXER_CHANGE")) targets[EV_MIXER_CHANGE] = argv[i+1]; else if (! strcmp(argv[i], "FILE_TAGS")) targets[EV_TAGS] = argv[i+1]; else if (! strcmp(argv[i], "AVG_BITRATE")) targets[EV_BITRATE] = argv[i+1]; else if (! strcmp(argv[i], "AUDIO_START")) targets[EV_AUDIO_START] = argv[i+1]; else if (! strcmp(argv[i], "AUDIO_STOP")) targets[EV_AUDIO_STOP] = argv[i+1]; else if (! strcmp(argv[i], "QUEUE_ADD")) targets[EV_QUEUE_ADD] = argv[i+1]; else if (! strcmp(argv[i], "QUEUE_DEL")) targets[EV_QUEUE_DEL] = argv[i+1]; else if (! strcmp(argv[i], "QUEUE_MOVE")) targets[EV_QUEUE_MOVE] = argv[i+1]; else if (! strcmp(argv[i], "QUEUE_CLEAR")) targets[EV_QUEUE_CLEAR] = argv[i+1]; else { printf("Unknown event: %s\n", argv[i]); show_usage(argv[0]); return 1; } } struct event *e; static struct event_queue events; int result; fd_set readset; options_init (); int sock=server_connect(); FD_ZERO(&readset); FD_SET(sock, &readset); do { do { result = select(sock + 1, &readset, NULL, NULL, NULL); } while (result == -1); if (result > 0) { if (FD_ISSET(sock, &readset)) { int evt; get_int_noblock(sock, &evt); printf("Event: 0x%02x\n", evt); if (targets[evt]) { printf("expanding: %s\n", targets[evt]); char *expanded=expand(targets[evt]); printf("Running Command: %s\n", expanded); system(expanded); } } } } while (true); return 0; }

Would you like to have a look at the contributions section of the MOC website and come up with an appropriate description for this? Placing the build intructions as a comment in the code might also be a good idea.

When you're ready, please e-mail it to mocmaint with the source as a compressed attachment.

Will do.