1 /*
2  * Copyright (C) 1997-2009, Michael Jennings
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 static const char cvs_ident[] = "$Id: e.c 51650 2010-08-26 01:34:13Z lucas $";
25 
26 #include "config.h"
27 #include "feature.h"
28 
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <X11/cursorfont.h>
34 #include <signal.h>
35 
36 #include "e.h"
37 #include "command.h"
38 #include "startup.h"
39 #include "options.h"
40 #include "pixmap.h"
41 #include "system.h"
42 
43 Window ipc_win = None, my_ipc_win = None;
44 static unsigned char timeout = 0;
45 
46 /* Returns true if running under E, false otherwise */
47 unsigned char
check_for_enlightenment(void)48 check_for_enlightenment(void)
49 {
50     static signed char have_e = -1;
51 
52     if (have_e == -1) {
53         if (props[PROP_ENL_COMMS] != None) {
54             D_ENL(("Enlightenment detected.\n"));
55             have_e = 1;
56         } else {
57             D_ENL(("Enlightenment not detected.\n"));
58             have_e = 0;
59         }
60     }
61     return (have_e);
62 }
63 
64 Window
enl_ipc_get_win(void)65 enl_ipc_get_win(void)
66 {
67     unsigned char *str = NULL;
68     Atom prop;
69     unsigned long num, after;
70     int format;
71     Window dummy_win;
72     int dummy_int;
73     unsigned int dummy_uint;
74 
75     D_ENL(("Searching for IPC window.\n"));
76 
77     if ((props[PROP_ENL_COMMS] == None) || (props[PROP_ENL_VERSION] == None)) {
78         D_ENL((" -> Enlightenment is not running.  You lose!\n"));
79         return None;
80     }
81     XGetWindowProperty(Xdisplay, Xroot, props[PROP_ENL_COMMS], 0, 14, False, AnyPropertyType, &prop, &format, &num, &after, &str);
82     if (str) {
83         sscanf((char *) str, "%*s %x", (unsigned int *) &ipc_win);
84         XFree(str);
85     }
86     if (ipc_win != None) {
87         XGetWindowProperty(Xdisplay, Xroot, props[PROP_ENL_VERSION], 0, 14, False, AnyPropertyType,
88                            &prop, &format, &num, &after, &str);
89         if (str) {
90             char *ver, *tmp;
91 
92             tmp = strchr((char *) str, ' ');
93             if (!tmp) {
94                 tmp = strchr((char *) str, '-');
95             }
96             if (tmp) {
97                 ver = tmp + 1;
98                 tmp = strchr((char *) ver, ' ');
99                 if (! tmp) {
100                     tmp = strchr((char *) ver, '-');
101                 }
102                 if (tmp) {
103                     *tmp = 0;
104                 }
105 
106                 /* Make sure the version string is within the acceptable range. */
107                 if (SPIF_CMP_IS_LESS(spiftool_version_compare((spif_charptr_t) str, (spif_charptr_t) "0.16.4"))
108                     || SPIF_CMP_IS_GREATER(spiftool_version_compare((spif_charptr_t) str, (spif_charptr_t) "0.16.999"))) {
109                     D_ENL((" -> IPC version string \"%s\" out of range.  I'll have to ignore it.\n"));
110                     ipc_win = None;
111                 }
112             }
113             XFree(str);
114         }
115     }
116     if (ipc_win != None) {
117         if (!XGetGeometry
118             (Xdisplay, ipc_win, &dummy_win, &dummy_int, &dummy_int, &dummy_uint, &dummy_uint, &dummy_uint, &dummy_uint)) {
119             D_ENL((" -> IPC Window property is valid, but the window doesn't exist.  I give up!\n"));
120             ipc_win = None;
121         }
122         str = NULL;
123         if (ipc_win != None) {
124             XGetWindowProperty(Xdisplay, ipc_win, props[PROP_ENL_COMMS], 0, 14, False, AnyPropertyType, &prop, &format, &num,
125                                &after, &str);
126             if (str) {
127                 XFree(str);
128             } else {
129                 D_ENL((" -> IPC Window lacks the proper atom.  I can't talk to fake IPC windows....\n"));
130                 ipc_win = None;
131             }
132         }
133     }
134     if (ipc_win != None) {
135         D_ENL((" -> IPC Window found and verified as 0x%08x.  Registering Eterm as an IPC client.\n", (int) ipc_win));
136         XSelectInput(Xdisplay, ipc_win, StructureNotifyMask | SubstructureNotifyMask);
137         enl_ipc_send("set clientname " APL_NAME);
138         enl_ipc_send("set version " VERSION);
139         enl_ipc_send("set email mej@eterm.org");
140         enl_ipc_send("set web http://www.eterm.org/");
141         enl_ipc_send("set info Eterm Enlightened Terminal Emulator");
142     }
143     if (my_ipc_win == None) {
144         my_ipc_win = XCreateSimpleWindow(Xdisplay, Xroot, -2, -2, 1, 1, 0, 0, 0);
145     }
146     return (ipc_win);
147 }
148 
149 void
enl_ipc_send(char * str)150 enl_ipc_send(char *str)
151 {
152     static char *last_msg = NULL;
153     char buff[21];
154     register unsigned short i;
155     register unsigned char j;
156     unsigned short len;
157     XEvent ev;
158 
159     if (!str) {
160         ASSERT(last_msg != NULL);
161         str = last_msg;
162         D_ENL(("Resending last message \"%s\" to Enlightenment.\n", str));
163     } else {
164         if (last_msg) {
165             FREE(last_msg);
166         }
167         last_msg = STRDUP(str);
168         D_ENL(("Sending \"%s\" to Enlightenment.\n", str));
169     }
170 
171     if (ipc_win == None) {
172         if ((ipc_win = enl_ipc_get_win()) == None) {
173             D_ENL(("...or perhaps not, since Enlightenment doesn't seem to be running.  No IPC window, no IPC.  Sorry....\n"));
174             return;
175         }
176     }
177     len = strlen(str);
178     for (; XCheckTypedWindowEvent(Xdisplay, my_ipc_win, ClientMessage, &ev););  /* Discard any out-of-sync messages */
179     ev.xclient.type = ClientMessage;
180     ev.xclient.serial = 0;
181     ev.xclient.send_event = True;
182     ev.xclient.window = ipc_win;
183     ev.xclient.message_type = props[PROP_ENL_MSG];
184     ev.xclient.format = 8;
185 
186     for (i = 0; i < len + 1; i += 12) {
187         sprintf(buff, "%8x", (int) my_ipc_win);
188         for (j = 0; j < 12; j++) {
189             buff[8 + j] = str[i + j];
190             if (!str[i + j]) {
191                 break;
192             }
193         }
194         buff[20] = 0;
195         for (j = 0; j < 20; j++) {
196             ev.xclient.data.b[j] = buff[j];
197         }
198         XSendEvent(Xdisplay, ipc_win, False, 0, (XEvent *) & ev);
199     }
200     D_ENL(("Message sent to IPC window 0x%08x.\n", ipc_win));
201 }
202 
203 static RETSIGTYPE
enl_ipc_timeout(int sig)204 enl_ipc_timeout(int sig)
205 {
206     timeout = 1;
207     SIG_RETURN(sig);
208     sig = 0;
209 }
210 
211 char *
enl_wait_for_reply(void)212 enl_wait_for_reply(void)
213 {
214 
215     XEvent ev;
216     static char msg_buffer[20];
217     register unsigned char i;
218 
219     alarm(3);
220     for (; !XCheckTypedWindowEvent(Xdisplay, my_ipc_win, ClientMessage, &ev) && !timeout;);
221     alarm(0);
222     if (ev.xany.type != ClientMessage) {
223         return (IPC_TIMEOUT);
224     }
225     for (i = 0; i < 20; i++) {
226         msg_buffer[i] = ev.xclient.data.b[i];
227     }
228     return (msg_buffer + 8);
229 }
230 
231 char *
enl_ipc_get(const char * msg_data)232 enl_ipc_get(const char *msg_data)
233 {
234 
235     static char *message = NULL;
236     static unsigned short len = 0;
237     char buff[13], *ret_msg = NULL;
238     register unsigned char i;
239     unsigned char blen;
240 
241     if (msg_data == IPC_TIMEOUT) {
242         return (IPC_TIMEOUT);
243     }
244     for (i = 0; i < 12; i++) {
245         buff[i] = msg_data[i];
246     }
247     buff[12] = 0;
248     blen = strlen(buff);
249     if (message) {
250         len += blen;
251         message = (char *) REALLOC(message, len + 1);
252         strcat(message, buff);
253     } else {
254         len = blen;
255         message = (char *) MALLOC(len + 1);
256         strcpy(message, buff);
257     }
258     if (blen < 12) {
259         ret_msg = message;
260         message = NULL;
261         D_ENL(("Received complete reply:  \"%s\"\n", ret_msg));
262     }
263     return (ret_msg);
264 }
265 
266 char *
enl_send_and_wait(char * msg)267 enl_send_and_wait(char *msg)
268 {
269 
270     char *reply = IPC_TIMEOUT;
271     eterm_sighandler_t old_alrm;
272 
273     if (ipc_win == None) {
274         /* The IPC window is missing.  Wait for it to return or Eterm to be killed. */
275         for (; enl_ipc_get_win() == None;) {
276             sleep(1);
277         }
278     }
279     old_alrm = (eterm_sighandler_t) signal(SIGALRM, (eterm_sighandler_t) enl_ipc_timeout);
280     for (; reply == IPC_TIMEOUT;) {
281         timeout = 0;
282         enl_ipc_send(msg);
283         for (; !(reply = enl_ipc_get(enl_wait_for_reply())););
284         if (reply == IPC_TIMEOUT) {
285             /* We timed out.  The IPC window must be AWOL.  Reset and resend message. */
286             D_ENL(("IPC timed out.  IPC window 0x%08x has gone AWOL.  Clearing ipc_win.\n", ipc_win));
287             XSelectInput(Xdisplay, ipc_win, None);
288             ipc_win = None;
289             (void) check_image_ipc(1);
290         }
291     }
292     signal(SIGALRM, old_alrm);
293     return (reply);
294 }
295