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