#include #include #include #include #include #include #include #include /* Maximal socket name. */ #define UNIX_PATH_MAX 108 #define SOCKET_NAME "socket2" #include #include #include #include #include #include #include #include #include struct file_tags { char *title; char *artist; char *album; int track; int time; int filled; /* Which tags are filled: TAGS_COMMENTS, TAGS_TIME. */ }; struct event_queue { struct event *head; struct event *tail; }; struct event { int type; /* type of the event (one of EV_*) */ void *data; /* optional data associated with the event */ struct event *next; }; /* Flags for the info decoder function. */ enum tags_select { TAGS_COMMENTS = 0x01, /* artist, title, etc. */ TAGS_TIME = 0x02 /* time of the file. */ }; /* Definition of events sent by server to the client. */ #define EV_STATE 0x01 /* server has changed the state */ #define EV_CTIME 0x02 /* current time of the song has changed */ #define EV_SRV_ERROR 0x04 /* an error occurred */ #define EV_BUSY 0x05 /* another client is connected to the server */ #define EV_DATA 0x06 /* data in response to a request will arrive */ #define EV_BITRATE 0x07 /* the bitrate has changed */ #define EV_RATE 0x08 /* the rate has changed */ #define EV_CHANNELS 0x09 /* the number of channels has changed */ #define EV_EXIT 0x0a /* the server is about to exit */ #define EV_PONG 0x0b /* response for CMD_PING */ #define EV_OPTIONS 0x0c /* the options has changed */ #define EV_SEND_PLIST 0x0d /* request for sending the playlist */ #define EV_TAGS 0x0e /* tags for the current file have changed */ #define EV_STATUS_MSG 0x0f /* followed by a status message */ #define EV_MIXER_CHANGE 0x10 /* the mixer channel was changed */ #define EV_FILE_TAGS 0x11 /* tags in a response for tags request */ #define EV_AVG_BITRATE 0x12 /* average bitrate has changed (new song) */ #define EV_AUDIO_START 0x13 /* playing of audio has started */ #define EV_AUDIO_STOP 0x14 /* playing of audio has stopped */ /* Events caused by a client that wants to modify the playlist (see * CMD_CLI_PLIST* commands). */ #define EV_PLIST_ADD 0x50 /* add an item, followed by the file name */ #define EV_PLIST_DEL 0x51 /* delete an item, followed by the file name */ #define EV_PLIST_MOVE 0x52 /* move an item, followed by 2 file names */ #define EV_PLIST_CLEAR 0x53 /* clear the playlist */ /* These events, though similar to the four previous are caused by server * which takes care of clients' queue synchronization. */ #define EV_QUEUE_ADD 0x54 #define EV_QUEUE_DEL 0x55 #define EV_QUEUE_MOVE 0x56 #define EV_QUEUE_CLEAR 0x57 /* State of the server. */ #define STATE_PLAY 0x01 #define STATE_STOP 0x02 #define STATE_PAUSE 0x03 /* Definition of server commands. */ #define CMD_PLAY 0x00 /* play the first element on the list */ #define CMD_LIST_CLEAR 0x01 /* clear the list */ #define CMD_LIST_ADD 0x02 /* add an item to the list */ #define CMD_STOP 0x04 /* stop playing */ #define CMD_PAUSE 0x05 /* pause */ #define CMD_UNPAUSE 0x06 /* unpause */ #define CMD_SET_OPTION 0x07 /* set an option */ #define CMD_GET_OPTION 0x08 /* get an option */ #define CMD_GET_CTIME 0x0d /* get the current song time */ #define CMD_GET_SNAME 0x0f /* get the stream file name */ #define CMD_NEXT 0x10 /* start playing next song if available */ #define CMD_QUIT 0x11 /* shutdown the server */ #define CMD_SEEK 0x12 /* seek in the current stream */ #define CMD_GET_STATE 0x13 /* get the state */ #define CMD_DISCONNECT 0x15 /* disconnect from the server */ #define CMD_GET_BITRATE 0x16 /* get the bitrate */ #define CMD_GET_RATE 0x17 /* get the rate */ #define CMD_GET_CHANNELS 0x18 /* get the number of channels */ #define CMD_PING 0x19 /* request for EV_PONG */ #define CMD_GET_MIXER 0x1a /* get the volume level */ #define CMD_SET_MIXER 0x1b /* set the volume level */ #define CMD_DELETE 0x1c /* delete an item from the playlist */ #define CMD_SEND_PLIST_EVENTS 0x1d /* request for playlist events */ #define CMD_PREV 0x20 /* start playing previous song if available */ #define CMD_SEND_PLIST 0x21 /* send the playlist to the requesting client */ #define CMD_GET_PLIST 0x22 /* get the playlist from one of the clients */ #define CMD_CAN_SEND_PLIST 0x23 /* mark the client as able to send playlist */ #define CMD_CLI_PLIST_ADD 0x24 /* add an item to the client's playlist */ #define CMD_CLI_PLIST_DEL 0x25 /* delete an item from the client's playlist */ #define CMD_CLI_PLIST_CLEAR 0x26 /* clear the client's playlist */ #define CMD_GET_SERIAL 0x27 /* get an unique serial number */ #define CMD_PLIST_SET_SERIAL 0x28 /* assign a serial number to the server's playlist */ #define CMD_LOCK 0x29 /* acquire a lock */ #define CMD_UNLOCK 0x2a /* release the lock */ #define CMD_PLIST_GET_SERIAL 0x2b /* get the serial number of the server's playlist */ #define CMD_GET_TAGS 0x2c /* get tags for the currently played file */ #define CMD_TOGGLE_MIXER_CHANNEL 0x2d /* toggle the mixer channel */ #define CMD_GET_MIXER_CHANNEL_NAME 0x2e /* get the mixer channel's name */ #define CMD_GET_FILE_TAGS 0x2f /* get tags for the specified file */ #define CMD_ABORT_TAGS_REQUESTS 0x30 /* abort previous CMD_GET_FILE_TAGS requests up to some file */ #define CMD_CLI_PLIST_MOVE 0x31 /* move an item */ #define CMD_LIST_MOVE 0x32 /* move an item */ #define CMD_GET_AVG_BITRATE 0x33 /* get the average bitrate */ #define CMD_TOGGLE_SOFTMIXER 0x34 /* toggle use of softmixer */ #define CMD_TOGGLE_EQUALIZER 0x35 /* toggle use of equalizer */ #define CMD_EQUALIZER_REFRESH 0x36 /* refresh EQ-presets */ #define CMD_EQUALIZER_PREV 0x37 /* select previous eq-preset */ #define CMD_EQUALIZER_NEXT 0x38 /* select next eq-preset */ #define CMD_TOGGLE_MAKE_MONO 0x39 /* toggle mono mixing */ #define CMD_JUMP_TO 0x3a /* jumps to a some position in the current stream */ #define CMD_QUEUE_ADD 0x3b /* add an item to the queue */ #define CMD_QUEUE_DEL 0x3c /* delete an item from the queue */ #define CMD_QUEUE_MOVE 0x3d /* move an item in the queue */ #define CMD_QUEUE_CLEAR 0x3e /* clear the queue */ #define CMD_GET_QUEUE 0x3f /* request the queue from the server */ #define MAX_SEND_STRING 4096 #ifndef RANGE #define RANGE(min, val, max) ((val) >= (min) && (val) <= (max)) #endif // Functions static int fatal(char *string); static int server_connect (); void *xmalloc (size_t size); // Raw gets char *get_str (int sock); int send_int (int sock, int i); int get_int (int sock, int *i); //wrapper gets static int get_data_int(); static int get_int_from_srv(); static char *get_str_from_srv (); static void wait_for_data (); static void *get_event_data (const int type); static struct file_tags *recv_tags_from_srv(); static struct file_tags *get_data_tags (); struct file_tags *tags_new (); void tags_free (struct file_tags *tags); // Globals. static int srv_sock = -1; char *socket_path = "/home/calvin/.moc/socket2"; static struct event_queue events; static int fatal(char *string) { fprintf(stderr, "%s\n", string); exit(0); } char *sock_path() { return socket_path; }; static int server_connect () { char *path = socket_path; 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, sock_path()); if (connect(sock, (struct sockaddr *)&sock_name, SUN_LEN(&sock_name)) == -1) { close (sock); fprintf(stderr, "%s at (%s)\n", strerror(errno), sock_name); return -1; } return sock; } char *get_str (int sock) { int len; int res, nread = 0; char *str; if (!get_int(sock, &len)) return NULL; if (!RANGE(0, len, MAX_SEND_STRING)) { printf("Bad string length."); return NULL; } str = (char *)malloc (sizeof(char) * (len + 1)); while (nread < len) { res = recv (sock, str + nread, len - nread, 0); if (res == -1) { printf("recv() failed when getting string: %s\n", strerror(errno)); free (str); return NULL; } if (res == 0) { printf("Unexpected EOF when getting string\n"); free (str); return NULL; } nread += res; } str[len] = 0; return str; } int send_str (int sock, const char *str) { int len; len = strlen (str); if (!send_int (sock, len)) return 0; if (send (sock, str, len, 0) != len) return 0; return 1; } int get_int (int sock, int *i) { int res; res = recv (sock, i, sizeof(int), 0); if (res == -1) printf("recv() failed when getting int: %s", strerror(errno)); return res == sizeof(int) ? 1 : 0; } static char *get_data_str () { wait_for_data (); return get_str_from_srv (); } static int get_data_int () { wait_for_data (); return get_int_from_srv (); } static void send_str_to_srv (const char *str) { if (!send_str(srv_sock, str)) fatal ("Can't send() string to the server!"); } struct file_tags *tags_new () { struct file_tags *tags; tags = (struct file_tags *)xmalloc (sizeof(struct file_tags)); tags->title = NULL; tags->artist = NULL; tags->album = NULL; tags->track = -1; tags->time = -1; tags->filled = 0; return tags; } void tags_free (struct file_tags *tags) { assert (tags != NULL); if (tags->title) free (tags->title); if (tags->artist) free (tags->artist); if (tags->album) free (tags->album); free (tags); } struct file_tags *recv_tags (int sock) { struct file_tags *tags = tags_new (); // filename first. get_str(sock); if (!(tags->title = get_str(sock))) { fprintf(stderr, "Error while receiving title\n"); tags_free (tags); return NULL; } if (!(tags->artist = get_str(sock))) { fprintf(stderr, "Error while receiving artist\n"); tags_free (tags); return NULL; } if (!(tags->album = get_str(sock))) { fprintf(stderr, "Error while receiving album\n"); tags_free (tags); return NULL; } if (!get_int(sock, &tags->track)) { fprintf(stderr, "Error while receiving track\n"); tags_free (tags); return NULL; } if (!get_int(sock, &tags->time)) { fprintf(stderr, "Error while receiving time\n"); tags_free (tags); return NULL; } if (!get_int(sock, &tags->filled)) { fprintf(stderr, "Error while receiving filled\n"); tags_free (tags); return NULL; } /* Set NULL instead of empty tags. */ if (!tags->title[0]) { free (tags->title); tags->title = NULL; } if (!tags->artist[0]) { free (tags->artist); tags->artist = NULL; } if (!tags->album[0]) { free (tags->album); tags->album = NULL; } return tags; } /* Send an integer value to the socket, return == 0 on error */ int send_int (int sock, int i) { int res; res = send (sock, &i, sizeof(int), 0); if (res == -1) printf("send() failed: %s", strerror(errno)); return res == sizeof(int) ? 1 : 0; } /* helper that doesn't require a socket */ static void send_int_to_srv (const int num) { if (!send_int(srv_sock, num)) fprintf(stderr, "Can't send() int to the server!\n"); } /* Returned memory is malloc()ed. */ static char *get_str_from_srv () { char *str = get_str (srv_sock); if (!str) fprintf(stderr,"Can't receive string from the server!\n"); return str; } static void *get_event_data (const int type) { switch (type) { case EV_PLIST_ADD: case EV_QUEUE_ADD: break; // return recv_item_from_srv (); case EV_PLIST_DEL: case EV_QUEUE_DEL: case EV_STATUS_MSG: case EV_SRV_ERROR: return get_str_from_srv (); case EV_FILE_TAGS: // return recv_tags_data_from_srv (); break; case EV_PLIST_MOVE: break; } return NULL; } /* Get an integer value from the server that will arrive after EV_DATA. */ static void wait_for_data () { int event; do { event = get_int_from_srv (); if (event == EV_EXIT) fatal("The server exited!"); if (event != EV_DATA) event_push (&events, event, get_event_data(event)); } while (event != EV_DATA); } void *xmalloc (size_t size) { void *p; if ((p = malloc(size)) == NULL) fatal ("Can't allocate memory!"); return p; } void event_push (struct event_queue *q, const int event, void *data) { assert (q != NULL); if (!q->head) { q->head = (struct event *)xmalloc (sizeof(struct event)); q->head->next = NULL; q->head->type = event; q->head->data = data; q->tail = q->head; } else { assert (q->head != NULL); assert (q->tail != NULL); assert (q->tail->next == NULL); q->tail->next = (struct event *)xmalloc ( sizeof(struct event)); q->tail = q->tail->next; q->tail->next = NULL; q->tail->type = event; q->tail->data = data; } } static int get_int_from_srv () { int num; if (!get_int(srv_sock, &num)) fprintf(stderr, "get_int_from_srv: Can't receive value from the server!\n"); return num; } static struct file_tags *recv_tags_from_srv () { struct file_tags *tags = recv_tags (srv_sock); if (!tags) fatal ("recv_tags_from_server: Can't receive tags from the server!"); return tags; } static struct file_tags *get_data_tags () { int got_it = 0; while (!got_it) { int type = get_int_from_srv (); void *data = get_event_data (type); if (type == EV_FILE_TAGS) { got_it = 1; } } return recv_tags_from_srv (); } /* helper functions for getting server info */ static int get_curr_time () { send_int_to_srv (CMD_GET_CTIME); return get_data_int (); } static char *get_curr_file () { send_int_to_srv (CMD_GET_SNAME); return get_data_str (); } static struct file_tags *get_tags(char *file) { send_int_to_srv (CMD_GET_FILE_TAGS); send_str_to_srv (file); send_int_to_srv (TAGS_COMMENTS | TAGS_TIME); return get_data_tags(); } char *time_format(int sec) { int h = (sec/3600); int m = (sec -(3600*h))/60; int s = (sec -(3600*h)-(m*60)); char *str = xmalloc(256); if(h) { sprintf(str, "%d:%2d:%02d",h,m,s); } else { sprintf(str, "%02d:%02d",m,s); } return str; } /* Obtains the next X11 event with specified timeout. */ static Bool XNextEventTimed(Display* dsp, XEvent* event_return, long millis) { if (millis == 0) { XNextEvent(dsp, event_return); return True; } struct timeval tv; tv.tv_sec = millis / 1000; tv.tv_usec = (millis % 1000) * 1000; XFlush(dsp); if (XPending(dsp)) { XNextEvent(dsp, event_return); return True; } else { int fd = ConnectionNumber(dsp); fd_set readset; FD_ZERO(&readset); FD_SET(fd, &readset); if (select(fd+1, &readset, NULL, NULL, &tv) <= 0) { return False; } else { if (XPending(dsp)) { XNextEvent(dsp, event_return); return True; } else { return False; } } } } int main(int argc, char** argv) { Display* dpy = XOpenDisplay(NULL); if (dpy == NULL) { fprintf(stderr, "Cannot open display\n"); exit(1); } srv_sock = server_connect(); if(srv_sock == -1) { fprintf(stderr, "Can't connect to MOCP Server at: %s\n", socket_path); exit(1); } int s = DefaultScreen(dpy); Window win = XCreateSimpleWindow(dpy, RootWindow(dpy, s), 10, 10, 660, 200, 1, BlackPixel(dpy, s), WhitePixel(dpy, s)); XSelectInput(dpy, win, ExposureMask | ButtonPress | KeyPressMask); XMapWindow(dpy, win); XStoreName(dpy, win, "mocicon"); Atom WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(dpy, win, &WM_DELETE_WINDOW, 1); bool uname_ok = false; struct utsname sname; int ret = uname(&sname); if (ret != -1) { uname_ok = true; } while (1) { char *lastfile = NULL; XEvent e; int state; int ev = -1; int cmd = -1; if (!send_int(srv_sock, CMD_GET_STATE)) fatal ("Can't send commands!"); if (!get_int(srv_sock, &ev)) { fatal("Can't get data from the server 1."); } // skip other updates... wait for our data. while(ev != EV_DATA) { send_int(srv_sock, CMD_GET_STATE); if (!get_int(srv_sock, &ev)) { fprintf(stderr, "Can't get data from the server 1.\n"); } } if(!get_int(srv_sock, &state)) { fatal ("Can't get state from the server!"); } if (state == STATE_STOP) { int got_event = XNextEventTimed(dpy, &e, 500); XClearWindow(dpy, win); char *no_song = "No Song is Playing."; XDrawString(dpy, win, DefaultGC(dpy, s), 10, 20, no_song, strlen(no_song)); } else { char *file = get_curr_file(); struct file_tags *tags= get_tags(file); /* printf("current_file: %s\n", file); printf("current_title: %s\n", tags->title); printf("current_artist: %s\n", tags->artist); printf("current_artist: %s\n", tags->album); printf("current_track: %d\n", tags->track); printf("current_time: %s\n", time_format(get_curr_time())); printf("current_length: %s\n", time_format(tags->time)); */ char *curt = time_format(get_curr_time()); char *curtt = time_format(tags->time); char *timestr = xmalloc(256); sprintf(timestr,"%s / %s", curt, curtt); int got_event = XNextEventTimed(dpy, &e, 250); XClearWindow(dpy, win); int y_offset = 20; int x = 10; XDrawString(dpy, win, DefaultGC(dpy, s), x, y_offset, tags->title, strlen(tags->title)); y_offset += 15; XDrawString(dpy, win, DefaultGC(dpy, s), x, y_offset, tags->artist, strlen(tags->artist)); y_offset += 15; XDrawString(dpy, win, DefaultGC(dpy, s), x, y_offset, tags->album, strlen(tags->album)); y_offset += 15; XDrawString(dpy, win, DefaultGC(dpy, s), x, y_offset, timestr, strlen(timestr)); y_offset += 15; tags_free(tags); free(file); free(curt); free(curtt); free(timestr); } if (e.type == ButtonPress) { if (state == STATE_PAUSE) { cmd = CMD_UNPAUSE; } else if (state == STATE_PLAY) { cmd = CMD_PAUSE; } printf("sending pause/play\n"); send_int(srv_sock, cmd); e.type=0; } if (e.type == KeyPress) { char buf[128] = {0}; KeySym keysym; int len = XLookupString(&e.xkey, buf, sizeof buf, &keysym, NULL); if (keysym == XK_Escape) break; } if ((e.type == ClientMessage) && (e.xclient.data.l[0]) == WM_DELETE_WINDOW) { break; } } XDestroyWindow(dpy, win); XCloseDisplay(dpy); return 0; } /* int main2(int argc, char** argv) { Display* dpy = XOpenDisplay(NULL); if (dpy == NULL) { fprintf(stderr, "Cannot open display\n"); exit(1); } srv_sock = server_connect(); if(srv_sock == -1) { fprintf(stderr, "Can't connect to MOCP Server at: %s\n", socket_path); exit(1); } int s = DefaultScreen(dpy); Window win = XCreateSimpleWindow(dpy, RootWindow(dpy, s), 10, 10, 660, 200, 1, BlackPixel(dpy, s), WhitePixel(dpy, s)); XSelectInput(dpy, win, ExposureMask | KeyPressMask); XMapWindow(dpy, win); XStoreName(dpy, win, "Geeks3D.com - X11 window under Linux (Mint 10)"); Atom WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(dpy, win, &WM_DELETE_WINDOW, 1); bool uname_ok = false; struct utsname sname; int ret = uname(&sname); if (ret != -1) { uname_ok = true; } XEvent e; while (1) { XNextEvent(dpy, &e); if (e.type == Expose) { int y_offset = 20; const char* s1 = "X11 test app under Linux"; const char* s2 = "(C)2012 Geeks3D.com"; XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, tags->title, strlen(tags->artist)); y_offset += 20; XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, tags->artist, strlen(tags->artist)); y_offset += 20; if (uname_ok) { char buf[256] = {0}; sprintf(buf, "System information:"); XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf)); y_offset += 15; sprintf(buf, "- System: %s", sname.sysname); XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf)); y_offset += 15; sprintf(buf, "- Release: %s", sname.release); XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf)); y_offset += 15; sprintf(buf, "- Version: %s", sname.version); XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf)); y_offset += 15; sprintf(buf, "- Machine: %s", sname.machine); XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf)); y_offset += 20; } XWindowAttributes wa; XGetWindowAttributes(dpy, win, &wa); int width = wa.width; int height = wa.height; char buf[128]={0}; sprintf(buf, "Current window size: %dx%d", width, height); XDrawString(dpy, win, DefaultGC(dpy, s), 10, y_offset, buf, strlen(buf)); y_offset += 20; } if (e.type == KeyPress) { char buf[128] = {0}; KeySym keysym; int len = XLookupString(&e.xkey, buf, sizeof buf, &keysym, NULL); if (keysym == XK_Escape) break; } if ((e.type == ClientMessage) && (e.xclient.data.l[0]) == WM_DELETE_WINDOW) { break; } } XDestroyWindow(dpy, win); XCloseDisplay(dpy); return 0; } */ /* main loop to publish on MPRIS */ int main1() { srv_sock = server_connect(); if(srv_sock == -1) { fprintf(stderr, "Can't connect to MOCP Server at: %s\n", socket_path); exit(1); } while(1) { int state; int ev = -1; int cmd = -1; if (!send_int(srv_sock, CMD_GET_STATE)) fatal ("Can't send commands!"); if (!get_int(srv_sock, &ev)) { fatal("Can't get data from the server 1."); } // skip other updates... wait for our data. while(ev != EV_DATA) { if (!get_int(srv_sock, &ev)) { fatal("Can't get data from the server 1."); } } if(!get_int(srv_sock, &state)) { fatal ("Can't get state from the server!"); } if (state == STATE_STOP) { fprintf(stderr, "no song playing\n"); exit(0); } //int type = get_int_from_srv (); //void *data = get_event_data (type); char *file = get_curr_file(); printf("current_file: %s\n", file); struct file_tags *tags= get_tags(file); printf("current_title: %s\n", tags->title); printf("current_artist: %s\n", tags->artist); printf("current_artist: %s\n", tags->album); printf("current_track: %d\n", tags->track); printf("current_time: %s\n", time_format(get_curr_time())); printf("current_length: %s\n", time_format(tags->time)); tags_free(tags); free(file); sleep(1); } /* if (state == STATE_PAUSE) { printf("state paused\n"); cmd = CMD_UNPAUSE; } else if (state == STATE_PLAY) { printf("state paused\n"); cmd = CMD_PAUSE; } if (cmd != -1 && !send_int(sock, cmd)) fatal ("Can't send commands!"); if (!send_int(sock, CMD_DISCONNECT)) fatal ("Can't send commands!"); */ } /* * DBusError err; DBusConnection* conn; int ret; // initialise the errors dbus_error_init(&err); // connect to the bus conn = dbus_bus_get(DBUS_BUS_SESSION, &err); if (dbus_error_is_set(&err)) { fprintf(stderr, "Connection Error (%s)\n", err.message); dbus_error_free(&err); } if (NULL == conn) { exit(1); } * */