1 /*
2 * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3 * Copyright (C) 2004-2021 Kim Woelders
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies of the Software, its documentation and marketing & publicity
14 * materials, and acknowledgment shall be given in the documentation, materials
15 * and software packages that this Software was used.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24 #include "config.h"
25
26 #include <X11/Xlib.h>
27
28 #include "E.h"
29 #include "comms.h"
30 #include "hints.h"
31 #include "ipc.h"
32 #include "list.h"
33 #include "xprop.h"
34 #include "xwin.h"
35
36 typedef struct {
37 dlist_t list;
38 char *name;
39 EX_Window xwin;
40 char *msg;
41 char *clientname;
42 char *version;
43 char *info;
44 char replied;
45 } Client;
46
47 static void CommsSend(Client * c, const char *s);
48
49 static LIST_HEAD(client_list);
50
51 static Win comms_win = NULL;
52
53 static Client *
ClientCreate(EX_Window xwin)54 ClientCreate(EX_Window xwin)
55 {
56 Client *c;
57 char st[32];
58
59 c = ECALLOC(Client, 1);
60 if (!c)
61 return NULL;
62
63 Esnprintf(st, sizeof(st), "%8x", (int)xwin);
64 c->name = Estrdup(st);
65 c->xwin = xwin;
66
67 LIST_PREPEND(Client, &client_list, c);
68
69 return c;
70 }
71
72 static void
ClientDestroy(Client * c)73 ClientDestroy(Client * c)
74 {
75 if (!c)
76 return;
77
78 LIST_REMOVE(Client, &client_list, c);
79
80 Efree(c->name);
81 Efree(c->msg);
82 Efree(c->clientname);
83 Efree(c->version);
84 Efree(c->info);
85
86 Efree(c);
87 }
88
89 static int
ClientConfigure(Client * c,const char * str)90 ClientConfigure(Client * c, const char *str)
91 {
92 char param[64];
93 const char *value;
94 int len;
95
96 len = 0;
97 sscanf(str, "%*s %60s %n", param, &len);
98 value = str + len;
99
100 if (!strcmp(param, "clientname"))
101 {
102 EFREE_DUP(c->clientname, value);
103 }
104 else if (!strcmp(param, "version"))
105 {
106 EFREE_DUP(c->version, value);
107 }
108 else if (!strcmp(param, "author"))
109 {
110 }
111 else if (!strcmp(param, "email"))
112 {
113 }
114 else if (!strcmp(param, "web"))
115 {
116 }
117 else if (!strcmp(param, "address"))
118 {
119 }
120 else if (!strcmp(param, "info"))
121 {
122 EFREE_DUP(c->info, value);
123 }
124 else if (!strcmp(param, "pixmap"))
125 {
126 }
127 else
128 {
129 return -1;
130 }
131
132 return 0;
133 }
134
135 static int
ClientMatchWindow(const void * data,const void * match)136 ClientMatchWindow(const void *data, const void *match)
137 {
138 return ((const Client *)data)->xwin != (EX_Window) (long)match;
139 }
140
141 static Client *
ClientFind(EX_Window xwin)142 ClientFind(EX_Window xwin)
143 {
144 return LIST_FIND(Client, &client_list, ClientMatchWindow,
145 (void *)(long)xwin);
146 }
147
148 static char *
ClientCommsGet(Client ** c,XClientMessageEvent * ev)149 ClientCommsGet(Client ** c, XClientMessageEvent * ev)
150 {
151 char s[13], s2[9], *msg;
152 unsigned int i;
153 EX_Window xwin;
154 Client *cl;
155
156 if ((!ev) || (!c))
157 return NULL;
158 if (ev->message_type != ea_m.ENL_MSG)
159 return NULL;
160
161 s[12] = 0;
162 s2[8] = 0;
163 for (i = 0; i < 8; i++)
164 s2[i] = ev->data.b[i];
165 for (i = 0; i < 12; i++)
166 s[i] = ev->data.b[i + 8];
167 xwin = NoXID;
168 sscanf(s2, "%x", &xwin);
169 if (xwin == NoXID)
170 return NULL;
171 cl = ClientFind(xwin);
172 if (!cl)
173 {
174 cl = ClientCreate(xwin);
175 if (!cl)
176 return NULL;
177 }
178
179 /* append text to end of msg */
180 i = (cl->msg) ? strlen(cl->msg) : 0;
181 cl->msg = EREALLOC(char, cl->msg, i + strlen(s) + 1);
182 if (!cl->msg)
183 return NULL;
184 strcpy(cl->msg + i, s);
185
186 msg = NULL;
187 if (strlen(s) < 12)
188 {
189 msg = cl->msg;
190 cl->msg = NULL;
191 *c = cl;
192 }
193
194 return msg;
195 }
196
197 static void
ClientIpcReply(void * data,const char * str)198 ClientIpcReply(void *data, const char *str)
199 {
200 Client *c = (Client *) data;
201
202 if (!c)
203 return;
204
205 if (!str)
206 {
207 /* Don't send empty replies (ack's) if we ever have replied to this
208 * client. Without this hack communication with e.g. epplets fails. */
209 if (c->replied)
210 return;
211 str = "";
212 }
213 CommsSend(c, str);
214 c->replied = 1;
215 }
216
217 static void
ClientHandleComms(XClientMessageEvent * ev)218 ClientHandleComms(XClientMessageEvent * ev)
219 {
220 Client *c;
221 char *s;
222
223 s = ClientCommsGet(&c, ev);
224 if (!s)
225 return;
226
227 if (EDebug(EDBUG_TYPE_IPC))
228 Eprintf("%s: %s\n", __func__, s);
229
230 if (!strncmp(s, "set ", 4))
231 {
232 /* The old Client set command (used by epplets) */
233 if (ClientConfigure(c, s) == 0)
234 goto done;
235 }
236
237 if (!IpcExecReply(s, ClientIpcReply, c))
238 {
239 #if ENABLE_DIALOGS
240 const char *s1, *s2;
241
242 s1 = (c->clientname) ? c->clientname : "UNKNOWN";
243 s2 = (c->version) ? c->version : "UNKNOWN";
244 DialogOK(_("E IPC Error"),
245 _("Received Unknown Client Message.\n"
246 "Client Name: %s\n" "Client Version: %s\n"
247 "Message Contents:\n\n" "%s\n"), s1, s2, s);
248 #endif
249 SoundPlay(SOUND_ERROR_IPC);
250 }
251
252 done:
253 Efree(s);
254 }
255
256 static void
ClientHandleRootEvents(Win win __UNUSED__,XEvent * ev,void * prm __UNUSED__)257 ClientHandleRootEvents(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
258 {
259 Client *c;
260
261 #if 0
262 Eprintf("%s: type=%d win=%#lx\n", __func__, ev->type, ev->xany.window);
263 #endif
264 switch (ev->type)
265 {
266 case DestroyNotify:
267 c = ClientFind(ev->xdestroywindow.window);
268 if (!c)
269 break;
270 ClientDestroy(c);
271 break;
272 }
273 }
274
275 static void
ClientHandleCommsEvents(Win win __UNUSED__,XEvent * ev,void * prm __UNUSED__)276 ClientHandleCommsEvents(Win win __UNUSED__, XEvent * ev, void *prm __UNUSED__)
277 {
278 #if 0
279 Eprintf("%s: type=%d win=%#lx\n", __func__, ev->type, ev->xany.window);
280 #endif
281 switch (ev->type)
282 {
283 case ClientMessage:
284 ClientHandleComms(&(ev->xclient));
285 break;
286 }
287 }
288
289 void
CommsInit(void)290 CommsInit(void)
291 {
292 char s[1024];
293
294 comms_win = ECreateEventWindow(VROOT, -100, -100, 5, 5);
295 ESelectInput(comms_win, StructureNotifyMask | SubstructureNotifyMask);
296 EventCallbackRegister(comms_win, ClientHandleCommsEvents, NULL);
297 EventCallbackRegister(VROOT, ClientHandleRootEvents, NULL);
298
299 Esnprintf(s, sizeof(s), "WINID %8x", WinGetXwin(comms_win));
300 ex_window_prop_string_set(WinGetXwin(comms_win), ea_m.ENLIGHTENMENT_COMMS,
301 s);
302 ex_window_prop_string_set(WinGetXwin(VROOT), ea_m.ENLIGHTENMENT_COMMS, s);
303 }
304
305 static void
CommsDoSend(EX_Window win,const char * s)306 CommsDoSend(EX_Window win, const char *s)
307 {
308 char ss[21];
309 int i, j, k, len;
310 XEvent ev;
311
312 if ((!win) || (!s))
313 return;
314
315 len = strlen(s);
316 ev.xclient.type = ClientMessage;
317 ev.xclient.serial = 0;
318 ev.xclient.send_event = True;
319 ev.xclient.window = win;
320 ev.xclient.message_type = ea_m.ENL_MSG;
321 ev.xclient.format = 8;
322 for (i = 0; i < len + 1; i += 12)
323 {
324 Esnprintf(ss, sizeof(ss), "%8x", WinGetXwin(comms_win));
325 for (j = 0; j < 12; j++)
326 {
327 ss[8 + j] = s[i + j];
328 if (!s[i + j])
329 break;
330 }
331 ss[20] = 0;
332 for (k = 0; k < 20; k++)
333 ev.xclient.data.b[k] = ss[k];
334 EXSendEvent(win, 0, (XEvent *) & ev);
335 }
336 }
337
338 static void
CommsSend(Client * c,const char * s)339 CommsSend(Client * c, const char *s)
340 {
341 if (!c)
342 return;
343
344 CommsDoSend(c->xwin, s);
345 }
346