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 <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <X11/Xlib.h>
32 #include <X11/keysym.h>
33 #include "alert.h"
34 #include "lang.h"
35 #include "session.h"
36 #include "util.h"
37 #if USE_COMPOSITE
38 #include <X11/extensions/Xcomposite.h>
39 #endif
40
41 #define ExTextExtents XmbTextExtents
42 #define ExDrawString XmbDrawString
43
44 #define ExSetColor(pxc, r, g, b) \
45 do { \
46 (pxc)->red = (r << 8) | r; (pxc)->green = (g << 8) | g; (pxc)->blue = (b << 8) | b; \
47 } while (0)
48
49 static XFontSet xfs = NULL;
50
51 #define DRAW_BOX_OUT(mdd, mgc, mwin, mx, my, mw, mh) \
52 AlertDrawBox(mdd, mgc, mwin, mx, my, mw, mh, \
53 colorful, cols[0], cols[2], cols[3])
54 #define DRAW_BOX_IN(mdd, mgc, mwin, mx, my, mw, mh) \
55 AlertDrawBox(mdd, mgc, mwin, mx, my, mw, mh, \
56 colorful, cols[2], cols[0], cols[3])
57 static void
AlertDrawBox(Display * mdd,GC mgc,Window mwin,int mx,int my,int mw,int mh,int colorful,unsigned int c1,unsigned int c2,unsigned int cb)58 AlertDrawBox(Display * mdd, GC mgc, Window mwin, int mx, int my, int mw, int mh,
59 int colorful, unsigned int c1, unsigned int c2, unsigned int cb)
60 {
61 if (colorful)
62 {
63 XSetForeground(mdd, mgc, cb);
64 XDrawRectangle(mdd, mwin, mgc, mx, my, mw - 1, mh - 1);
65 XSetForeground(mdd, mgc, c1);
66 XDrawLine(mdd, mwin, mgc, mx + 1, my + 1, mx + mw - 3, my + 1);
67 XDrawLine(mdd, mwin, mgc, mx + 1, my + 1, mx + 1, my + mh - 3);
68 XSetForeground(mdd, mgc, c2);
69 XDrawLine(mdd, mwin, mgc, mx + 2, my + mh - 2, mx + mw - 2,
70 my + mh - 2);
71 XDrawLine(mdd, mwin, mgc, mx + mw - 2, my + 2, mx + mw - 2,
72 my + mh - 2);
73 }
74 else
75 {
76 XDrawRectangle(mdd, mwin, mgc, mx, my, mw - 1, mh - 1);
77 }
78 }
79
80 #define DRAW_THIN_BOX_IN(mdd, mgc, mwin, mx, my, mw, mh) \
81 AlertDrawThinBoxIn(mdd, mgc, mwin, mx, my, mw, mh, \
82 colorful, cols[2], cols[0])
83 static void
AlertDrawThinBoxIn(Display * mdd,GC mgc,Window mwin,int mx,int my,int mw,int mh,int colorful,unsigned int c1,unsigned int c2)84 AlertDrawThinBoxIn(Display * mdd, GC mgc, Window mwin, int mx, int my, int mw,
85 int mh, int colorful, unsigned int c1, unsigned int c2)
86 {
87 if (colorful)
88 {
89 XSetForeground(mdd, mgc, c1);
90 XDrawLine(mdd, mwin, mgc, mx + 1, my + 1, mx + mw - 3, my + 1);
91 XDrawLine(mdd, mwin, mgc, mx + 1, my + 1, mx + 1, my + mh - 3);
92 XSetForeground(mdd, mgc, c2);
93 XDrawLine(mdd, mwin, mgc, mx + 2, my + mh - 2, mx + mw - 2,
94 my + mh - 2);
95 XDrawLine(mdd, mwin, mgc, mx + mw - 2, my + 2, mx + mw - 2,
96 my + mh - 2);
97 }
98 }
99
100 #define DRAW_HEADER(mdd, mgc, mwin, mx, my, mstr) \
101 AlertDrawHeader(mdd, mgc, mwin, mx, my, mstr, \
102 colorful, cols[2], cols[3], cols[4])
103 static void
AlertDrawHeader(Display * mdd,GC mgc,Window mwin,int mx,int my,const char * mstr,int colorful,unsigned int cb,unsigned int ct1,unsigned int ct2)104 AlertDrawHeader(Display * mdd, GC mgc, Window mwin, int mx, int my,
105 const char *mstr, int colorful, unsigned int cb,
106 unsigned int ct1, unsigned int ct2)
107 {
108 int len = strlen(mstr);
109
110 if (colorful)
111 {
112 XSetForeground(mdd, mgc, cb);
113 ExDrawString(mdd, mwin, xfs, mgc, mx + 1, my + 1, mstr, len);
114 ExDrawString(mdd, mwin, xfs, mgc, mx + 2, my + 1, mstr, len);
115 ExDrawString(mdd, mwin, xfs, mgc, mx + 2, my + 2, mstr, len);
116 ExDrawString(mdd, mwin, xfs, mgc, mx + 1, my + 2, mstr, len);
117 XSetForeground(mdd, mgc, ct1);
118 ExDrawString(mdd, mwin, xfs, mgc, mx - 1, my, mstr, len);
119 ExDrawString(mdd, mwin, xfs, mgc, mx, my - 1, mstr, len);
120 ExDrawString(mdd, mwin, xfs, mgc, mx + 1, my, mstr, len);
121 ExDrawString(mdd, mwin, xfs, mgc, mx, my + 1, mstr, len);
122 XSetForeground(mdd, mgc, ct2);
123 ExDrawString(mdd, mwin, xfs, mgc, mx, my, mstr, len);
124 }
125 else
126 {
127 ExDrawString(mdd, mwin, xfs, mgc, mx, my, mstr, len);
128 }
129 }
130
131 #define DRAW_STRING(mdd, mgc, mwin, mx, my, mstr, len) \
132 AlertDrawString(mdd, mgc, mwin, mx, my, mstr, len, colorful, cols[3])
133 static void
AlertDrawString(Display * mdd,GC mgc,Window mwin,int mx,int my,const char * mstr,int len,int colorful,unsigned int ct1)134 AlertDrawString(Display * mdd, GC mgc, Window mwin, int mx, int my,
135 const char *mstr, int len, int colorful, unsigned int ct1)
136 {
137 if (colorful)
138 {
139 XSetForeground(mdd, mgc, ct1);
140 ExDrawString(mdd, mwin, xfs, mgc, mx, my, mstr, len);
141 }
142 else
143 {
144 ExDrawString(mdd, mwin, xfs, mgc, mx, my, mstr, len);
145 }
146 }
147
148 static char *
AlertButtonText(int btn,char * buf,unsigned int len,const char * text)149 AlertButtonText(int btn, char *buf, unsigned int len, const char *text)
150 {
151 if (!text)
152 return NULL;
153
154 Esnprintf(buf, len, "(F%d) %s", btn, text);
155
156 return buf;
157 }
158
159 static void
ShowAlert(const char * title,const char * ignore,const char * restart,const char * quit,const char * fmt,va_list args)160 ShowAlert(const char *title,
161 const char *ignore, const char *restart, const char *quit,
162 const char *fmt, va_list args)
163 {
164 char text[4096], buf1[64], buf2[64], buf3[64];
165 Window win, b1 = 0, b2 = 0, b3 = 0, root;
166 Display *dd;
167 int wid, hih, w, h, i, k, mask;
168 XGCValues gcv;
169 GC gc;
170 unsigned int len;
171 XEvent ev;
172 XSetWindowAttributes att;
173 XRectangle rect1, rect2;
174 char colorful;
175 unsigned long cols[5];
176 XColor xcl;
177 Colormap cmap;
178 int cnum, fh, x, y, ww, hh, bw, bh;
179 char *str1, *str2, *str3, *p;
180 int button;
181 char **missing_charset_list_return, *def_string_return;
182 int missing_charset_count_return;
183 XFontStruct **font_struct_list_return;
184 char **font_name_list_return;
185
186 #if 0
187 /* Don't play sound here (maybe if not forked/in signal handler - later) */
188 SoundPlay(SOUND_ALERT);
189 #endif
190
191 if (!fmt)
192 return;
193
194 Evsnprintf(text, sizeof(text), fmt, args);
195
196 /*
197 * We may get here from obscure places like an X-error or signal handler
198 * and things seem to work properly only if we do a new XOpenDisplay().
199 */
200 dd = XOpenDisplay(NULL);
201 if (!dd)
202 {
203 fprintf(stderr, "%s\n", text);
204 fflush(stderr);
205 return;
206 }
207
208 button = 0;
209
210 if (!title)
211 title = _("Enlightenment Error");
212 str1 = AlertButtonText(1, buf1, sizeof(buf1), ignore);
213 str2 = AlertButtonText(2, buf2, sizeof(buf2), restart);
214 str3 = AlertButtonText(3, buf3, sizeof(buf3), quit);
215
216 cnum = 0;
217 colorful = 0;
218 cols[0] = cols[1] = cols[2] = cols[3] = cols[4] = 0;
219 cmap = DefaultColormap(dd, DefaultScreen(dd));
220 if (DefaultDepth(dd, DefaultScreen(dd)) > 4)
221 {
222 ExSetColor(&xcl, 220, 220, 220);
223 if (!XAllocColor(dd, cmap, &xcl))
224 goto CN;
225 cols[cnum++] = xcl.pixel;
226 ExSetColor(&xcl, 160, 160, 160);
227 if (!XAllocColor(dd, cmap, &xcl))
228 goto CN;
229 cols[cnum++] = xcl.pixel;
230 ExSetColor(&xcl, 100, 100, 100);
231 if (!XAllocColor(dd, cmap, &xcl))
232 goto CN;
233 cols[cnum++] = xcl.pixel;
234 ExSetColor(&xcl, 0, 0, 0);
235 if (!XAllocColor(dd, cmap, &xcl))
236 goto CN;
237 cols[cnum++] = xcl.pixel;
238 ExSetColor(&xcl, 255, 255, 255);
239 if (!XAllocColor(dd, cmap, &xcl))
240 goto CN;
241 cols[cnum++] = xcl.pixel;
242 colorful = 1;
243 }
244 CN:
245
246 if (colorful)
247 att.background_pixel = cols[1];
248 else
249 att.background_pixel = BlackPixel(dd, DefaultScreen(dd));
250 if (colorful)
251 att.border_pixel = cols[3];
252 else
253 att.border_pixel = WhitePixel(dd, DefaultScreen(dd));
254 att.backing_store = Always;
255 att.save_under = True;
256 att.override_redirect = True;
257 mask = CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWSaveUnder |
258 CWBackingStore;
259
260 #if HAVE_COMPOSITE_OVERLAY_WINDOW
261 /*
262 * Intended workings:
263 * Composite extension not enabled (or COW not available?)
264 * - fall back to root
265 * Composite extension enabled
266 * - use COW whether or not compositing is enabled, window mode too
267 */
268 root = XCompositeGetOverlayWindow(dd, DefaultRootWindow(dd));
269 if (root == None)
270 #endif
271 {
272 root = DefaultRootWindow(dd);
273 }
274
275 x = y = -100;
276 ww = hh = 1;
277 win = XCreateWindow(dd, root, x, y, ww, hh, 0,
278 CopyFromParent, InputOutput, CopyFromParent, mask, &att);
279
280 gc = XCreateGC(dd, win, 0, &gcv);
281 if (colorful)
282 XSetForeground(dd, gc, cols[3]);
283 else
284 XSetForeground(dd, gc, att.border_pixel);
285
286 xfs = XCreateFontSet(dd, "fixed",
287 &missing_charset_list_return,
288 &missing_charset_count_return, &def_string_return);
289 if (!xfs)
290 goto done;
291
292 if (missing_charset_list_return)
293 XFreeStringList(missing_charset_list_return);
294
295 k = XFontsOfFontSet(xfs, &font_struct_list_return, &font_name_list_return);
296 fh = 0;
297 for (i = 0; i < k; i++)
298 {
299 h = font_struct_list_return[i]->ascent +
300 font_struct_list_return[i]->descent;
301 if (fh < h)
302 fh = h;
303 }
304
305 XSelectInput(dd, win, ExposureMask);
306 XMapWindow(dd, win);
307
308 XGrabServer(dd);
309
310 XGrabPointer(dd, win, False, ButtonPressMask | ButtonReleaseMask,
311 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
312 XGrabKeyboard(dd, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
313 XGrabKey(dd, AnyKey, AnyModifier, win, False, GrabModeAsync, GrabModeAsync);
314
315 XSetInputFocus(dd, win, RevertToPointerRoot, CurrentTime);
316
317 XSync(dd, False);
318
319 wid = DisplayWidth(dd, DefaultScreen(dd));
320 hih = DisplayHeight(dd, DefaultScreen(dd));
321 ww = (wid >= 600) ? 600 : (wid / 40) * 40;
322 hh = (hih >= 440) ? 440 : (hih / 40) * 40;
323
324 for (i = 40; i < ww; i += 40)
325 {
326 w = i;
327 h = (i * hh) / ww;
328 x = (wid - w) >> 1;
329 y = (hih - h) >> 1;
330 XMoveResizeWindow(dd, win, x, y, w, h);
331 DRAW_BOX_OUT(dd, gc, win, 0, 0, w, h);
332 XSync(dd, False);
333 SleepUs(20000);
334 }
335 x = (wid - ww) >> 1;
336 y = (hih - hh) >> 1;
337 XMoveResizeWindow(dd, win, x, y, ww, hh);
338 XSync(dd, False);
339
340 bw = 0;
341 if (str1)
342 {
343 ExTextExtents(xfs, str1, strlen(str1), &rect1, &rect2);
344 bw = (rect2.width > bw) ? rect2.width : bw;
345 }
346 if (str2)
347 {
348 ExTextExtents(xfs, str2, strlen(str2), &rect1, &rect2);
349 bw = (rect2.width > bw) ? rect2.width : bw;
350 }
351 if (str3)
352 {
353 ExTextExtents(xfs, str3, strlen(str3), &rect1, &rect2);
354 bw = (rect2.width > bw) ? rect2.width : bw;
355 }
356 bw += 20;
357 bh = fh + 10;
358
359 #define BX(i) (5 + (((ww - bw - 10) * (i)) / 2))
360 #define BY (hh - bh - 5)
361
362 if (str1)
363 {
364 b1 = XCreateWindow(dd, win, BX(0), BY, bw, bh, 0, CopyFromParent,
365 InputOutput, CopyFromParent, mask, &att);
366 XMapWindow(dd, b1);
367 }
368 if (str2)
369 {
370 b2 = XCreateWindow(dd, win, BX(1), BY, bw, bh, 0, CopyFromParent,
371 InputOutput, CopyFromParent, mask, &att);
372 XMapWindow(dd, b2);
373 }
374 if (str3)
375 {
376 b3 = XCreateWindow(dd, win, BX(2), BY, bw, bh, 0, CopyFromParent,
377 InputOutput, CopyFromParent, mask, &att);
378 XMapWindow(dd, b3);
379 }
380 XSync(dd, False);
381
382 button = 0;
383 for (; button == 0;)
384 {
385 XNextEvent(dd, &ev);
386
387 switch (ev.type)
388 {
389 case KeyPress:
390 if (str1 && ev.xkey.keycode == XKeysymToKeycode(dd, XK_F1))
391 {
392 DRAW_BOX_IN(dd, gc, b1, 0, 0, bw, bh);
393 XSync(dd, False);
394 SleepUs(500000);
395 DRAW_BOX_OUT(dd, gc, b1, 0, 0, bw, bh);
396 button = 1;
397 goto do_sync;
398 }
399 if (str2 && ev.xkey.keycode == XKeysymToKeycode(dd, XK_F2))
400 {
401 DRAW_BOX_IN(dd, gc, b2, 0, 0, bw, bh);
402 XSync(dd, False);
403 SleepUs(500000);
404 DRAW_BOX_OUT(dd, gc, b2, 0, 0, bw, bh);
405 button = 2;
406 goto do_sync;
407 }
408 if (str3 && ev.xkey.keycode == XKeysymToKeycode(dd, XK_F3))
409 {
410 DRAW_BOX_IN(dd, gc, b3, 0, 0, bw, bh);
411 XSync(dd, False);
412 SleepUs(500000);
413 DRAW_BOX_OUT(dd, gc, b3, 0, 0, bw, bh);
414 button = 3;
415 goto do_sync;
416 }
417 break;
418
419 case ButtonPress:
420 if (!(ev.xbutton.y >= BY && ev.xbutton.y < BY + bh))
421 break;
422
423 x = BX(0);
424 if (b1 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
425 {
426 DRAW_BOX_IN(dd, gc, b1, 0, 0, bw, bh);
427 goto do_sync;
428 }
429 x = BX(1);
430 if (b2 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
431 {
432 DRAW_BOX_IN(dd, gc, b2, 0, 0, bw, bh);
433 goto do_sync;
434 }
435 x = BX(2);
436 if (b3 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
437 {
438 DRAW_BOX_IN(dd, gc, b3, 0, 0, bw, bh);
439 goto do_sync;
440 }
441 break;
442
443 case ButtonRelease:
444 if (!(ev.xbutton.y >= BY && ev.xbutton.y < BY + bh))
445 break;
446
447 x = BX(0);
448 if (b1 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
449 {
450 DRAW_BOX_OUT(dd, gc, b1, 0, 0, bw, bh);
451 button = 1;
452 goto do_sync;
453 }
454 x = BX(1);
455 if (b2 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
456 {
457 DRAW_BOX_OUT(dd, gc, b2, 0, 0, bw, bh);
458 button = 2;
459 goto do_sync;
460 }
461 x = BX(2);
462 if (b3 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
463 {
464 DRAW_BOX_OUT(dd, gc, b3, 0, 0, bw, bh);
465 button = 3;
466 goto do_sync;
467 }
468 break;
469
470 case Expose:
471 /* Flush all other Expose events */
472 while (XCheckTypedWindowEvent(dd, ev.xexpose.window, Expose, &ev))
473 ;
474
475 ExTextExtents(xfs, title, strlen(title), &rect1, &rect2);
476 w = rect2.width;
477
478 DRAW_HEADER(dd, gc, win, (ww - w) / 2, 5 - rect2.y, title);
479 DRAW_BOX_OUT(dd, gc, win, 0, 0, ww, bh);
480 DRAW_BOX_OUT(dd, gc, win, 0, bh - 1, ww, hh - fh - fh - 30 + 2);
481 DRAW_BOX_OUT(dd, gc, win, 0, hh - fh - 20, ww, fh + 20);
482 k = bh;
483 for (p = text;; p += len + 1)
484 {
485 len = strcspn(p, "\n");
486 DRAW_STRING(dd, gc, win, 6, 6 + k + fh, p, len);
487 k += fh + 2;
488 if (p[len] == '\0')
489 break;
490 }
491 if (str1)
492 {
493 ExTextExtents(xfs, str1, strlen(str1), &rect1, &rect2);
494 w = rect2.width;
495 DRAW_HEADER(dd, gc, b1, (bw - w) / 2, 5 - rect2.y, str1);
496 DRAW_BOX_OUT(dd, gc, b1, 0, 0, bw, bh);
497 DRAW_THIN_BOX_IN(dd, gc, win,
498 BX(0) - 2, BY - 2, bw + 4, bh + 4);
499 }
500 if (str2)
501 {
502 ExTextExtents(xfs, str2, strlen(str2), &rect1, &rect2);
503 w = rect2.width;
504 DRAW_HEADER(dd, gc, b2, (bw - w) / 2, 5 - rect2.y, str2);
505 DRAW_BOX_OUT(dd, gc, b2, 0, 0, bw, bh);
506 DRAW_THIN_BOX_IN(dd, gc, win,
507 BX(1) - 2, BY - 2, bw + 4, bh + 4);
508 }
509 if (str3)
510 {
511 ExTextExtents(xfs, str3, strlen(str3), &rect1, &rect2);
512 w = rect2.width;
513 DRAW_HEADER(dd, gc, b3, (bw - w) / 2, 5 - rect2.y, str3);
514 DRAW_BOX_OUT(dd, gc, b3, 0, 0, bw, bh);
515 DRAW_THIN_BOX_IN(dd, gc, win,
516 BX(2) - 2, BY - 2, bw + 4, bh + 4);
517 }
518 do_sync:
519 XSync(dd, False);
520 break;
521
522 default:
523 break;
524 }
525 }
526
527 XFreeFontSet(dd, xfs);
528 done:
529 XUngrabServer(dd);
530 #if HAVE_COMPOSITE_OVERLAY_WINDOW
531 /* Force damage on root window where GSOD is/was rendered */
532 if (root != DefaultRootWindow(dd))
533 {
534 XReparentWindow(dd, win, DefaultRootWindow(dd), x, y);
535 XUnmapWindow(dd, win);
536 XSync(dd, False);
537 SleepUs(20000);
538 }
539 #endif
540 XDestroyWindow(dd, win);
541 XFreeGC(dd, gc);
542 if (cnum > 0)
543 XFreeColors(dd, cmap, cols, cnum, 0);
544 XCloseDisplay(dd);
545
546 switch (button)
547 {
548 default:
549 case 1:
550 break;
551 case 2:
552 SessionExit(EEXIT_RESTART, NULL);
553 break;
554 case 3:
555 SessionExit(EEXIT_EXIT, NULL);
556 break;
557 }
558 }
559
560 void
AlertX(const char * title,const char * ignore,const char * restart,const char * quit,const char * fmt,...)561 AlertX(const char *title, const char *ignore,
562 const char *restart, const char *quit, const char *fmt, ...)
563 {
564 va_list args;
565
566 va_start(args, fmt);
567 ShowAlert(title, ignore, restart, quit, fmt, args);
568 va_end(args);
569 }
570
571 void
Alert(const char * fmt,...)572 Alert(const char *fmt, ...)
573 {
574 va_list args;
575
576 va_start(args, fmt);
577 ShowAlert(_("Enlightenment Message Dialog"), _("Ignore this"),
578 _("Restart Enlightenment"), _("Quit Enlightenment"), fmt, args);
579 va_end(args);
580 }
581
582 void
AlertOK(const char * fmt,...)583 AlertOK(const char *fmt, ...)
584 {
585 va_list args;
586
587 va_start(args, fmt);
588 ShowAlert(_("Attention !!!"), _("OK"), NULL, NULL, fmt, args);
589 va_end(args);
590 }
591