1 /*-
2  * Copyright (c) 1999 Thomas Runge (coto@core.de)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <X11/X.h>
34 #include <X11/Xlib.h>
35 #include <X11/Intrinsic.h>
36 #include <X11/Xatom.h>
37 #include <X11/Xmu/WinUtil.h>
38 
39 #include "radio.h"
40 #include "remote.h"
41 
42 static char lock_data[MAXHOSTNAMELEN+16];
43 
44 Atom XA_XMRADIO_VERSION   = 0;
45 Atom XA_XMRADIO_LOCK      = 0;
46 Atom XA_XMRADIO_COMMAND   = 0;
47 Atom XA_XMRADIO_RESPONSE  = 0;
48 Atom XA_XMRADIO_FREQUENCY = 0;
49 Atom XA_XMRADIO_STATION   = 0;
50 
initAtoms()51 void initAtoms()
52 {
53  if(!XA_XMRADIO_VERSION)
54    XA_XMRADIO_VERSION = XInternAtom(dpy, XMRADIO_VERSION_PROP, False);
55  if(!XA_XMRADIO_LOCK)
56    XA_XMRADIO_LOCK = XInternAtom(dpy, XMRADIO_LOCK_PROP, False);
57  if(!XA_XMRADIO_COMMAND)
58    XA_XMRADIO_COMMAND = XInternAtom(dpy, XMRADIO_COMMAND_PROP, False);
59  if(!XA_XMRADIO_RESPONSE)
60    XA_XMRADIO_RESPONSE = XInternAtom(dpy, XMRADIO_RESPONSE_PROP, False);
61  if(!XA_XMRADIO_FREQUENCY)
62    XA_XMRADIO_FREQUENCY = XInternAtom(dpy, XMRADIO_FREQUENCY_PROP, False);
63  if(!XA_XMRADIO_STATION)
64    XA_XMRADIO_STATION = XInternAtom(dpy, XMRADIO_STATION_PROP, False);
65 }
66 
findWindow()67 static Window findWindow()
68 {
69  int i;
70  Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy));
71  Window root2, parent, *kids;
72  unsigned int nkids;
73  unsigned char *version = NULL;
74 
75  if(!XQueryTree(dpy, root, &root2, &parent, &kids, &nkids))
76  {
77   fprintf(stderr, "XQueryTree failed on display %s\n", DisplayString(dpy));
78   exit(EXIT_FAILURE);
79  }
80 
81  if(!(kids && nkids))
82  {
83   fprintf(stderr, "root window has no children on display %s\n",
84                                                DisplayString(dpy));
85   exit(EXIT_FAILURE);
86  }
87 
88  for(i = nkids-1; i >= 0; i--)
89  {
90   Atom type;
91   int format, status;
92   unsigned long nitems, bytesafter;
93   Window w;
94 
95   w = XmuClientWindow(dpy, kids[i]);
96   status = XGetWindowProperty(dpy, w, XA_XMRADIO_VERSION, 0, 8192, False,
97                               XA_STRING, &type, &format, &nitems, &bytesafter,
98                               &version);
99   if(!version)
100    continue;
101 
102   if(debug)
103    printf("Found xmradio window(0x%x). Version %s (win %d of %d)\n",
104                 (unsigned int)kids[i], version, i, nkids);
105 
106   XFree(version);
107 
108   //return(kids[i]);
109   return(w);
110  }
111 
112  fprintf(stderr, "xmradio not running on display %s\n", DisplayString(dpy));
113  exit(EXIT_FAILURE);
114 }
115 
obtainLock(Window win)116 static void obtainLock(Window win)
117 {
118  int locked = 0;
119 
120  sprintf(lock_data, "pid%d@", getpid ());
121  if(gethostname(lock_data + strlen(lock_data), MAXHOSTNAMELEN))
122  {
123   perror("gethostname");
124   exit(EXIT_FAILURE);
125  }
126 
127  do
128  {
129   int result;
130   Atom actual_type;
131   int actual_format;
132   unsigned long nitems, bytes_after;
133   unsigned char *prop = NULL;
134 
135   XGrabServer(dpy);
136 
137   result = XGetWindowProperty(dpy, win, XA_XMRADIO_LOCK, 0, 8192, False,
138                               XA_STRING, &actual_type, &actual_format,
139                               &nitems, &bytes_after, &prop);
140   if(result != Success || actual_type == None)
141   {
142    if(debug)
143     printf("remote xmradio not locked. lock it now.\n");
144 
145    XChangeProperty(dpy, win, XA_XMRADIO_LOCK, XA_STRING, 8, PropModeReplace,
146                    lock_data, strlen(lock_data));
147    locked = 1;
148   }
149 
150   XUngrabServer(dpy);
151   XSync(dpy, False);
152 
153   if(!locked)
154   {
155    if(debug)
156     printf("window 0x%x is locked by %s; waiting...\n", (unsigned int) win,
157                                                         prop);
158 
159    while(1)
160    {
161     XEvent event;
162 
163     XNextEvent(dpy, &event);
164     if(event.xany.type == DestroyNotify && event.xdestroywindow.window == win)
165     {
166      fprintf(stderr, "window 0x%x unexpectedly destroyed.\n",
167                                              (unsigned int) win);
168      exit(EXIT_FAILURE);
169     }
170     if(event.xany.type == PropertyNotify &&
171        event.xproperty.state == PropertyDelete &&
172        event.xproperty.window == win &&
173        event.xproperty.atom == XA_XMRADIO_LOCK)
174     {
175      if(debug)
176       printf("(0x%x unlocked, trying again...)\n", (unsigned int) win);
177      break;
178     }
179    }
180   }
181   XFree(prop);
182  } while(!locked);
183 }
184 
freeLock(Window win)185 static void freeLock(Window win)
186 {
187  int result;
188  Atom actual_type;
189  int actual_format;
190  unsigned long nitems, bytes_after;
191  unsigned char *prop = NULL;
192 
193  result = XGetWindowProperty(dpy, win, XA_XMRADIO_LOCK, 0, 8192, True,
194                              XA_STRING,  &actual_type, &actual_format,
195                               &nitems, &bytes_after, &prop);
196 
197  if(result != Success)
198  {
199   fprintf(stderr, " unable to read and delete " XMRADIO_LOCK_PROP
200                   " property\n");
201   return;
202  }
203  if(!prop || !*prop)
204  {
205   fprintf(stderr, " invalid data on " XMRADIO_LOCK_PROP
206                   " of window 0x%x.\n", (unsigned int) win);
207  }
208  if(strcmp((char*)prop, lock_data))
209  {
210   fprintf(stderr, XMRADIO_LOCK_PROP " was stolen!"
211                  "  Expected \"%s\", saw \"%s\"!\n", lock_data, prop);
212  }
213 
214  if(prop)
215   XFree(prop);
216 }
217 
218 /*
219  * return values:
220  *  0 - error
221  *  1 - can't happen
222  *  2 - okay
223  *  3 - win destroyed
224  *  4 - unknown command
225  *  5 - out of range
226  */
sendCommand(Window win,char * command)227 static int sendCommand(Window win, char *command)
228 {
229  if(debug)
230   printf("writing prop: %s\n", command);
231 
232  XChangeProperty(dpy, win, XA_XMRADIO_COMMAND, XA_STRING, 8,
233                  PropModeReplace, command, strlen(command));
234 
235  while(1)
236  {
237   XEvent event;
238 
239   XNextEvent(dpy, &event);
240   if(event.xany.type == DestroyNotify && event.xdestroywindow.window == win)
241   {
242    fprintf(stderr, "Failed to send remote command."
243                    " window 0x%x was destroyed.\n", (unsigned int) win);
244    return 3;
245   }
246   if(event.xany.type == PropertyNotify &&
247      event.xproperty.state == PropertyNewValue &&
248      event.xproperty.window == win &&
249      event.xproperty.atom == XA_XMRADIO_RESPONSE)
250   {
251    Atom actual_type;
252    int actual_format, result;
253    unsigned long nitems, bytes_after;
254    unsigned char *prop = NULL;
255 
256    result = XGetWindowProperty(dpy, win, XA_XMRADIO_RESPONSE, 0, 8192, True,
257                                XA_STRING, &actual_type, &actual_format,
258                                &nitems, &bytes_after, &prop);
259    if(result != Success)
260    {
261     fprintf(stderr, "Failed to read " XMRADIO_RESPONSE_PROP
262                     " from window 0x%0x.\n", (unsigned int) win);
263     return 3;
264    }
265    if(!prop)
266    {
267     fprintf(stderr, "Invalid data on " XMRADIO_RESPONSE_PROP
268                     " property of window 0x%0x.\n", (unsigned int) win);
269     return 0;
270    }
271    if(*prop == '1')
272    {
273     printf("message arrived. working on it...\n");
274     XFree(prop);
275    }
276    else if(*prop == '2')
277    {
278     if(debug)
279      printf("message arrived. done!\n");
280     XFree(prop);
281     return 2;
282    }
283    else if(*prop == '4')
284    {
285     fprintf(stderr, "unknown command!\n");
286     XFree(prop);
287     return 4;
288    }
289    else if(*prop == '5')
290    {
291     fprintf(stderr, "value out of range!\n");
292     XFree(prop);
293     return 5;
294    }
295    else
296    {
297     fprintf(stderr, "unrecognised " XMRADIO_RESPONSE_PROP
298                     " from window 0x%x: %s\n", (unsigned int) win, prop);
299     XFree(prop);
300    }
301   }
302  }
303 }
304 
sendCommands(char ** commands)305 int sendCommands(char **commands)
306 {
307  int status = 0;
308  Window win;
309 
310  win = findWindow();
311 
312  XSelectInput(dpy, win, (PropertyChangeMask|StructureNotifyMask));
313 
314  obtainLock(win);
315 
316  while(*commands)
317  {
318   if(debug)
319    printf("sending command: %s\n", *commands);
320 
321   status = sendCommand(win, *commands);
322 
323   if(status != 2)
324   {
325    printf("failed!\n");
326    break;
327   }
328   else
329   {
330    printf("succeded.\n");
331   }
332 
333   commands++;
334  }
335 
336  if(status != 3)
337   freeLock(win);
338 
339  return status;
340 }
341 
342