/*- * Copyright (c) 1999 Thomas Runge (coto@core.de) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include "radio.h" #include "remote.h" static char lock_data[MAXHOSTNAMELEN+16]; Atom XA_XMRADIO_VERSION = 0; Atom XA_XMRADIO_LOCK = 0; Atom XA_XMRADIO_COMMAND = 0; Atom XA_XMRADIO_RESPONSE = 0; Atom XA_XMRADIO_FREQUENCY = 0; Atom XA_XMRADIO_STATION = 0; void initAtoms() { if(!XA_XMRADIO_VERSION) XA_XMRADIO_VERSION = XInternAtom(dpy, XMRADIO_VERSION_PROP, False); if(!XA_XMRADIO_LOCK) XA_XMRADIO_LOCK = XInternAtom(dpy, XMRADIO_LOCK_PROP, False); if(!XA_XMRADIO_COMMAND) XA_XMRADIO_COMMAND = XInternAtom(dpy, XMRADIO_COMMAND_PROP, False); if(!XA_XMRADIO_RESPONSE) XA_XMRADIO_RESPONSE = XInternAtom(dpy, XMRADIO_RESPONSE_PROP, False); if(!XA_XMRADIO_FREQUENCY) XA_XMRADIO_FREQUENCY = XInternAtom(dpy, XMRADIO_FREQUENCY_PROP, False); if(!XA_XMRADIO_STATION) XA_XMRADIO_STATION = XInternAtom(dpy, XMRADIO_STATION_PROP, False); } static Window findWindow() { int i; Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy)); Window root2, parent, *kids; unsigned int nkids; unsigned char *version = NULL; if(!XQueryTree(dpy, root, &root2, &parent, &kids, &nkids)) { fprintf(stderr, "XQueryTree failed on display %s\n", DisplayString(dpy)); exit(EXIT_FAILURE); } if(!(kids && nkids)) { fprintf(stderr, "root window has no children on display %s\n", DisplayString(dpy)); exit(EXIT_FAILURE); } for(i = nkids-1; i >= 0; i--) { Atom type; int format, status; unsigned long nitems, bytesafter; Window w; w = XmuClientWindow(dpy, kids[i]); status = XGetWindowProperty(dpy, w, XA_XMRADIO_VERSION, 0, 8192, False, XA_STRING, &type, &format, &nitems, &bytesafter, &version); if(!version) continue; if(debug) printf("Found xmradio window(0x%x). Version %s (win %d of %d)\n", (unsigned int)kids[i], version, i, nkids); XFree(version); //return(kids[i]); return(w); } fprintf(stderr, "xmradio not running on display %s\n", DisplayString(dpy)); exit(EXIT_FAILURE); } static void obtainLock(Window win) { int locked = 0; sprintf(lock_data, "pid%d@", getpid ()); if(gethostname(lock_data + strlen(lock_data), MAXHOSTNAMELEN)) { perror("gethostname"); exit(EXIT_FAILURE); } do { int result; Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *prop = NULL; XGrabServer(dpy); result = XGetWindowProperty(dpy, win, XA_XMRADIO_LOCK, 0, 8192, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &prop); if(result != Success || actual_type == None) { if(debug) printf("remote xmradio not locked. lock it now.\n"); XChangeProperty(dpy, win, XA_XMRADIO_LOCK, XA_STRING, 8, PropModeReplace, lock_data, strlen(lock_data)); locked = 1; } XUngrabServer(dpy); XSync(dpy, False); if(!locked) { if(debug) printf("window 0x%x is locked by %s; waiting...\n", (unsigned int) win, prop); while(1) { XEvent event; XNextEvent(dpy, &event); if(event.xany.type == DestroyNotify && event.xdestroywindow.window == win) { fprintf(stderr, "window 0x%x unexpectedly destroyed.\n", (unsigned int) win); exit(EXIT_FAILURE); } if(event.xany.type == PropertyNotify && event.xproperty.state == PropertyDelete && event.xproperty.window == win && event.xproperty.atom == XA_XMRADIO_LOCK) { if(debug) printf("(0x%x unlocked, trying again...)\n", (unsigned int) win); break; } } } XFree(prop); } while(!locked); } static void freeLock(Window win) { int result; Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *prop = NULL; result = XGetWindowProperty(dpy, win, XA_XMRADIO_LOCK, 0, 8192, True, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &prop); if(result != Success) { fprintf(stderr, " unable to read and delete " XMRADIO_LOCK_PROP " property\n"); return; } if(!prop || !*prop) { fprintf(stderr, " invalid data on " XMRADIO_LOCK_PROP " of window 0x%x.\n", (unsigned int) win); } if(strcmp((char*)prop, lock_data)) { fprintf(stderr, XMRADIO_LOCK_PROP " was stolen!" " Expected \"%s\", saw \"%s\"!\n", lock_data, prop); } if(prop) XFree(prop); } /* * return values: * 0 - error * 1 - can't happen * 2 - okay * 3 - win destroyed * 4 - unknown command * 5 - out of range */ static int sendCommand(Window win, char *command) { if(debug) printf("writing prop: %s\n", command); XChangeProperty(dpy, win, XA_XMRADIO_COMMAND, XA_STRING, 8, PropModeReplace, command, strlen(command)); while(1) { XEvent event; XNextEvent(dpy, &event); if(event.xany.type == DestroyNotify && event.xdestroywindow.window == win) { fprintf(stderr, "Failed to send remote command." " window 0x%x was destroyed.\n", (unsigned int) win); return 3; } if(event.xany.type == PropertyNotify && event.xproperty.state == PropertyNewValue && event.xproperty.window == win && event.xproperty.atom == XA_XMRADIO_RESPONSE) { Atom actual_type; int actual_format, result; unsigned long nitems, bytes_after; unsigned char *prop = NULL; result = XGetWindowProperty(dpy, win, XA_XMRADIO_RESPONSE, 0, 8192, True, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &prop); if(result != Success) { fprintf(stderr, "Failed to read " XMRADIO_RESPONSE_PROP " from window 0x%0x.\n", (unsigned int) win); return 3; } if(!prop) { fprintf(stderr, "Invalid data on " XMRADIO_RESPONSE_PROP " property of window 0x%0x.\n", (unsigned int) win); return 0; } if(*prop == '1') { printf("message arrived. working on it...\n"); XFree(prop); } else if(*prop == '2') { if(debug) printf("message arrived. done!\n"); XFree(prop); return 2; } else if(*prop == '4') { fprintf(stderr, "unknown command!\n"); XFree(prop); return 4; } else if(*prop == '5') { fprintf(stderr, "value out of range!\n"); XFree(prop); return 5; } else { fprintf(stderr, "unrecognised " XMRADIO_RESPONSE_PROP " from window 0x%x: %s\n", (unsigned int) win, prop); XFree(prop); } } } } int sendCommands(char **commands) { int status = 0; Window win; win = findWindow(); XSelectInput(dpy, win, (PropertyChangeMask|StructureNotifyMask)); obtainLock(win); while(*commands) { if(debug) printf("sending command: %s\n", *commands); status = sendCommand(win, *commands); if(status != 2) { printf("failed!\n"); break; } else { printf("succeded.\n"); } commands++; } if(status != 3) freeLock(win); return status; }