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 <sys/utsname.h>
27 #include <signal.h>
28 #include <time.h>
29 #include <X11/Xlib.h>
30
31 #include "E.h"
32 #include "comms.h"
33 #include "cursors.h"
34 #include "desktops.h"
35 #include "dialog.h"
36 #include "edbus.h"
37 #include "eimage.h"
38 #include "emodule.h"
39 #include "events.h"
40 #include "ewins.h"
41 #include "file.h"
42 #include "grabs.h"
43 #include "hints.h"
44 #include "session.h"
45 #include "snaps.h"
46 #include "user.h"
47 #include "version.h"
48 #include "xwin.h"
49
50 const char e_wm_name[] = "e16";
51 const char e_wm_version[] = E16_VERSION;
52
53 __EXPORT__ EConf Conf;
54 __EXPORT__ EMode Mode;
55
56 static int EoptGet(int argc, char **argv);
57 static void EoptHelp(void);
58 static void ECheckEprog(const char *name);
59 static void EDirUserConfSet(const char *dir);
60 static void EConfNameSet(const char *dir);
61 static void EDirUserCacheSet(const char *dir);
62 static void EDirsSetup(void);
63 static void ESavePrefixSetup(void);
64 static void RunInitPrograms(void);
65
66 static int eoptind = 0;
67 const char *eoptarg = NULL;
68
69 typedef struct {
70 char sopt;
71 char arg;
72 const char *lopt;
73 const char *oarg;
74 const char *desc;
75 } EOpt;
76
77 static const EOpt Eopts[] = {
78 {'d', 1, "display", "display", "Set display"},
79 {'f', 0, "fast", NULL, "Fast startup"},
80 {'h', 0, "help", NULL, "Show help"},
81 {'m', 1, NULL, NULL, NULL},
82 {'p', 1, "config-prefix", "prefix", "Configuration file name prefix"},
83 {'P', 1, "econfdir", "path", "Set user configuration directory"},
84 {'Q', 1, "ecachedir", "path", "Set user cache directory"},
85 {'s', 1, "single", "screen", "Run only on given screen"},
86 {'S', 1, "sm-client-id", "session id", "Set session manager ID"},
87 {'t', 1, "theme", "name", "Select theme"},
88 {'v', 0, "verbose", NULL, "Show additional info"},
89 {'V', 0, "version", NULL, "Show version"},
90 {'w', 1, "window", "WxH", "Run in window"},
91 {'X', 1, NULL, NULL, NULL},
92 };
93
94 #define Strtoi(s, base) ((s) ? strtol(s, NULL, base) : 0)
95
96 int
main(int argc,char ** argv)97 main(int argc, char **argv)
98 {
99 int ch, i, loop;
100 struct utsname ubuf;
101 const char *str, *dstr;
102 char *theme;
103
104 /* This function runs all the setup for startup, and then
105 * proceeds into the primary event loop at the end.
106 */
107
108 /* Init state variable struct */
109 memset(&Mode, 0, sizeof(EMode));
110
111 Mode.wm.master = 1;
112 Mode.wm.pid = getpid();
113 Mode.wm.exec_name = argv[0];
114 Mode.wm.startup = 1;
115
116 Mode.mode = MODE_NONE;
117
118 EXInit();
119 Dpy.screen = -1;
120
121 str = getenv("EDEBUG");
122 if (str)
123 EDebugInit(str);
124 str = getenv("EDEBUG_COREDUMP");
125 if (str)
126 Mode.wm.coredump = 1;
127 str = getenv("EDEBUG_EXIT");
128 if (str)
129 Mode.debug_exit = atoi(str);
130
131 str = getenv("ECONFNAME");
132 if (str)
133 EConfNameSet(str);
134 str = getenv("ECONFDIR");
135 if (str)
136 EDirUserConfSet(str);
137 str = getenv("ECACHEDIR");
138 if (str)
139 EDirUserCacheSet(str);
140
141 srand((unsigned int)time(NULL));
142
143 if (!uname(&ubuf))
144 Mode.wm.machine_name = Estrdup(ubuf.nodename);
145 if (!Mode.wm.machine_name)
146 Mode.wm.machine_name = Estrdup("localhost");
147
148 /* Now we're going to interpret any of the commandline parameters
149 * that are passed to it -- Well, at least the ones that we
150 * understand.
151 */
152
153 theme = NULL;
154 dstr = NULL;
155
156 for (loop = 1; loop;)
157 {
158 ch = EoptGet(argc, argv);
159 if (ch <= 0)
160 break;
161 #if 0
162 Eprintf("Opt: %c: %d - %s\n", ch, eoptind, eoptarg);
163 #endif
164 switch (ch)
165 {
166 default:
167 case '?':
168 printf("e16: Ignoring: ");
169 for (i = eoptind; i < argc; i++)
170 printf("%s ", argv[i]);
171 printf("\n");
172 loop = 0;
173 break;
174 case 'h':
175 EoptHelp();
176 exit(0);
177 break;
178 case 'd':
179 dstr = eoptarg;
180 break;
181 case 'f':
182 Mode.wm.restart = 1;
183 break;
184 case 'p':
185 EConfNameSet(eoptarg);
186 break;
187 case 'P':
188 EDirUserConfSet(eoptarg);
189 break;
190 case 'Q':
191 EDirUserCacheSet(eoptarg);
192 break;
193 case 's':
194 Mode.wm.single = 1;
195 Dpy.screen = Strtoi(eoptarg, 10);
196 break;
197 case 'S':
198 SetSMID(eoptarg);
199 break;
200 case 't':
201 theme = Estrdup(eoptarg);
202 break;
203 case 'V':
204 printf("%s %s\n", e_wm_name, e_wm_version);
205 exit(0);
206 break;
207 case 'v':
208 EDebugSet(EDBUG_TYPE_VERBOSE, 1);
209 break;
210 case 'w':
211 sscanf(eoptarg, "%dx%d", &Mode.wm.win_w, &Mode.wm.win_h);
212 Mode.wm.window = 1;
213 Mode.wm.single = 1;
214 Mode.wm.master = 0;
215 break;
216 #ifdef USE_EXT_INIT_WIN
217 case 'X':
218 ExtInitWinSet(Strtoi(eoptarg, 0));
219 Mode.wm.restart = 1;
220 break;
221 #endif
222 case 'm':
223 Mode.wm.master = 0;
224 Mode.wm.master_screen = Strtoi(eoptarg, 10);
225 break;
226 }
227 }
228
229 SignalsSetup(); /* Install signal handlers */
230
231 EDirsSetup();
232 ECheckEprog("epp");
233 ECheckEprog("eesh");
234
235 SetupX(dstr); /* This is where the we fork per screen */
236 /* X is now running, and we have forked per screen */
237
238 ESavePrefixSetup();
239
240 /* So far nothing should rely on a selected settings or theme. */
241 ConfigurationLoad(); /* Load settings */
242
243 /* Initialise internationalisation */
244 LangInit();
245
246 /* The theme path must now be available for config file loading. */
247 ThemeFind(theme);
248 Efree(theme);
249
250 /* Set the Environment variables */
251 Esetenv("EVERSION", e_wm_version);
252 Esetenv("EROOT", EDirRoot());
253 Esetenv("EBIN", EDirBin());
254 Esetenv("ECONFDIR", EDirUserConf());
255 Esetenv("ECACHEDIR", EDirUserCache());
256 Esetenv("ETHEME", Mode.theme.path);
257
258 /* Move elsewhere? */
259 EImageInit();
260 HintsInit();
261 CommsInit();
262 SessionInit();
263 SnapshotsLoad();
264
265 #if USE_DBUS
266 DbusInit();
267 #endif
268
269 if (Mode.wm.window)
270 EMapWindow(VROOT);
271
272 ModulesSignal(ESIGNAL_INIT, NULL);
273
274 /* Load the theme */
275 ThemeConfigLoad();
276
277 if (Mode.debug_exit)
278 return 0;
279
280 /* Do initial configuration */
281 ModulesSignal(ESIGNAL_CONFIGURE, NULL);
282
283 /* Set root window cursor */
284 ECsrApply(ECSR_ROOT, WinGetXwin(VROOT));
285
286 #ifdef USE_EXT_INIT_WIN
287 /* Kill the E process owning the "init window" */
288 ExtInitWinKill();
289 #endif
290
291 /* let's make sure we set this up and go to our desk anyways */
292 DeskGoto(DesksGetCurrent());
293 ESync(ESYNC_MAIN);
294
295 #ifdef SIGCONT
296 for (i = 0; i < Mode.wm.child_count; i++)
297 kill(Mode.wm.children[i], SIGCONT);
298 #endif
299
300 ModulesSignal(ESIGNAL_START, NULL);
301 #if ENABLE_DIALOGS
302 DialogsInit();
303 #endif
304 EwinsManage();
305
306 RunInitPrograms();
307 SnapshotsSpawn();
308
309 if (!Mode.wm.restart)
310 StartupWindowsOpen();
311
312 Conf.startup.firsttime = 0;
313 Mode.wm.save_ok = Conf.autosave;
314 Mode.wm.startup = 0;
315 autosave();
316
317 /* The primary event loop */
318 EventsMain();
319
320 SessionExit(EEXIT_QUIT, NULL);
321
322 return 0;
323 }
324
325 void
EExit(int exitcode)326 EExit(int exitcode)
327 {
328 int i;
329
330 if (EDebug(EDBUG_TYPE_SESSION))
331 Eprintf("%s: %d\n", __func__, exitcode);
332
333 if (disp)
334 {
335 EUngrabServer();
336 GrabPointerRelease();
337 XAllowEvents(disp, AsyncBoth, CurrentTime);
338
339 /* XSetInputFocus(disp, NoXID, RevertToParent, CurrentTime); */
340 /* I think this is a better way to release the grabs: (felix) */
341 XSetInputFocus(disp, PointerRoot, RevertToPointerRoot, CurrentTime);
342 ESelectInput(VROOT, 0);
343 EDisplayClose();
344 }
345
346 if (Mode.wm.master)
347 {
348 for (i = 0; i < Mode.wm.child_count; i++)
349 kill(Mode.wm.children[i], SIGINT);
350 }
351 else
352 {
353 exitcode = 0;
354 }
355
356 exit(exitcode);
357 }
358
359 /*
360 * Command line parsing.
361 * Not entirely standard compliant, but close enough.
362 */
363 static int
EoptGet(int argc,char ** argv)364 EoptGet(int argc, char **argv)
365 {
366 const char *s;
367 unsigned int i, len;
368 int lopt;
369 const EOpt *eopt;
370
371 eoptind++;
372 if (eoptind >= argc)
373 return 0;
374
375 s = argv[eoptind];
376 if (*s++ != '-')
377 return 0;
378
379 lopt = 0;
380 if (*s == '-')
381 {
382 lopt = 1;
383 s++;
384 }
385
386 eoptarg = NULL;
387 eopt = NULL;
388 for (i = 0; i < E_ARRAY_SIZE(Eopts); i++)
389 {
390 eopt = &Eopts[i];
391
392 /* Short option */
393 if (!lopt)
394 {
395 if (!eopt->sopt || eopt->sopt != s[0])
396 continue;
397 if (eopt->arg)
398 {
399 if (s[1])
400 {
401 eoptarg = s + 1;
402 goto found;
403 }
404 goto found;
405 }
406 if (s[1])
407 break;
408 goto found;
409 }
410
411 if (!eopt->lopt)
412 continue;
413
414 /* Long option */
415 len = strlen(eopt->lopt);
416 if (strncmp(eopt->lopt, s, len))
417 continue;
418 if (eopt->arg)
419 {
420 if (s[len] == '\0')
421 goto found;
422 if (s[len] != '=')
423 break;
424 eoptarg = s + len + 1;
425 }
426 goto found;
427 }
428 return '?';
429
430 found:
431 if (!eopt->arg || eoptarg)
432 return eopt->sopt;
433
434 if (eoptind >= argc - 1)
435 return '?'; /* Missing param */
436
437 eoptind++;
438 eoptarg = argv[eoptind];
439 return eopt->sopt;
440 }
441
442 static void
EoptHelp(void)443 EoptHelp(void)
444 {
445 unsigned int i;
446 const EOpt *eopt;
447 char buf[256];
448
449 printf("e16 options:\n");
450 for (i = 0; i < E_ARRAY_SIZE(Eopts); i++)
451 {
452 eopt = &Eopts[i];
453 if (!eopt->desc)
454 continue;
455 if (eopt->oarg)
456 Esnprintf(buf, sizeof(buf), "--%s <%s>", eopt->lopt, eopt->oarg);
457 else
458 Esnprintf(buf, sizeof(buf), "--%s", eopt->lopt);
459 printf(" -%c %-30s\t%s\n", eopt->sopt, buf, eopt->desc);
460 }
461 }
462
463 static void
RunDocBrowser(void)464 RunDocBrowser(void)
465 {
466 char buf[FILEPATH_LEN_MAX];
467
468 Esnprintf(buf, sizeof(buf), "%s/edox", EDirBin());
469 if (!canexec(buf))
470 return;
471 Esnprintf(buf, sizeof(buf), "%s/E-docs/MAIN", EDirRoot());
472 if (!canread(buf))
473 return;
474
475 Espawn("%s/edox %s/E-docs", EDirBin(), EDirRoot());
476 }
477
478 static void
RunMenuGen(void)479 RunMenuGen(void)
480 {
481 Espawn("%s/scripts/e_gen_menu", EDirRoot());
482 }
483
484 static void
RunInitPrograms(void)485 RunInitPrograms(void)
486 {
487 if (Mode.wm.session_start)
488 SessionHelper(ESESSION_INIT);
489
490 SessionHelper(ESESSION_START);
491
492 if (Mode.firsttime && Mode.wm.master)
493 {
494 RunMenuGen();
495 RunDocBrowser();
496 }
497 }
498
499 static void
EConfNameSet(const char * name)500 EConfNameSet(const char *name)
501 {
502 EFREE_DUP(Mode.conf.name, name);
503 Esetenv("ECONFNAME", Mode.conf.name);
504 }
505
506 static void
EDirUserConfSet(const char * dir)507 EDirUserConfSet(const char *dir)
508 {
509 EFREE_DUP(Mode.conf.dir, dir);
510 }
511
512 static void
EDirUserCacheSet(const char * dir)513 EDirUserCacheSet(const char *dir)
514 {
515 EFREE_DUP(Mode.conf.cache_dir, dir);
516 }
517
518 void
Etmp(char * s)519 Etmp(char *s)
520 {
521 static unsigned int n_calls = 0;
522
523 Esnprintf(s, 1024, "%s/TMP_%d_%d", EDirUserConf(), getpid(), n_calls++);
524 }
525
526 static void
EDirCheck(const char * dir)527 EDirCheck(const char *dir)
528 {
529 if (file_test(dir, EFILE_DIR | EPERM_RWX))
530 return;
531
532 Alert(_("%s must be a directory in which you have\n"
533 "read, write, and execute permission.\n"), dir);
534 EExit(1);
535 }
536
537 static void
EDirMake(const char * base,const char * name)538 EDirMake(const char *base, const char *name)
539 {
540 char s[1024];
541
542 Esnprintf(s, sizeof(s), "%s/%s", base, name);
543 if (!exists(s))
544 E_md(s);
545 EDirCheck(s);
546 }
547
548 static void
EDirsSetup(void)549 EDirsSetup(void)
550 {
551 const char *home;
552 char s[1024], *cfgdir;
553
554 home = userhome();
555 EDirCheck(home);
556
557 /* Set user config dir if not already set */
558 cfgdir = EDirUserConf();
559 if (!cfgdir)
560 {
561 Esnprintf(s, sizeof(s), "%s/.e16", home);
562 EDirUserConfSet(s);
563 cfgdir = EDirUserConf();
564 }
565
566 if (exists(cfgdir))
567 {
568 if (!isdir(cfgdir))
569 {
570 Esnprintf(s, sizeof(s), "%s.old", cfgdir);
571 E_mv(cfgdir, s);
572 E_md(cfgdir);
573 }
574 else
575 EDirCheck(cfgdir);
576 }
577 else
578 E_md(cfgdir);
579
580 if (!Mode.conf.cache_dir)
581 Mode.conf.cache_dir = cfgdir; /* Beware if ever freed */
582
583 Esnprintf(s, sizeof(s), "%s/menus", EDirUserConf());
584 Mode.firsttime = !exists(s);
585
586 EDirMake(EDirUserConf(), "themes");
587 EDirMake(EDirUserConf(), "backgrounds");
588 EDirMake(EDirUserConf(), "menus");
589
590 EDirMake(EDirUserCache(), "cached");
591 EDirMake(EDirUserCache(), "cached/cfg");
592 EDirMake(EDirUserCache(), "cached/bgsel");
593 EDirMake(EDirUserCache(), "cached/img");
594 EDirMake(EDirUserCache(), "cached/pager");
595 }
596
597 /*
598 * The user control config is called "~/.e16/e_config-$DISPLAY"
599 * The client data appends ".clients" onto this filename and the snapshot data
600 * appends ".snapshots".
601 */
602 static void
ESavePrefixSetup(void)603 ESavePrefixSetup(void)
604 {
605 #define ECFG_DEFAULT "e_config"
606 char *s, buf[1024];
607
608 if (Mode.conf.name)
609 Esnprintf(buf, sizeof(buf), "%s/%s-%d",
610 EDirUserConf(), Mode.conf.name, Dpy.screen);
611 else if (Mode.wm.window)
612 Esnprintf(buf, sizeof(buf), "%s/%s-window", EDirUserConf(), ECFG_DEFAULT);
613 else
614 Esnprintf(buf, sizeof(buf), "%s/%s-%s",
615 EDirUserConf(), ECFG_DEFAULT, Dpy.name);
616
617 Mode.conf.prefix = Estrdup(buf);
618
619 for (s = Mode.conf.prefix; (s = strchr(s, ':')); *s = '-')
620 ;
621 }
622
623 static void
ECheckEprog(const char * name)624 ECheckEprog(const char *name)
625 {
626 char s[1024];
627
628 Esnprintf(s, sizeof(s), "%s/%s", EDirBin(), name);
629
630 if (!exists(s))
631 {
632 Alert(_("!!!!!!!! ERROR ERROR ERROR ERROR !!!!!!!!\n" "\n"
633 "Enlightenment's utility executable cannot be found at:\n"
634 "\n" "%s\n"
635 "This is a fatal error and Enlightenment will cease to run.\n"
636 "Please rectify this situation and ensure it is installed\n"
637 "correctly.\n" "\n"
638 "The reason this could be missing is due to badly created\n"
639 "packages, someone manually deleting that program or perhaps\n"
640 "an error in installing Enlightenment.\n"), s);
641 EExit(1);
642 }
643
644 if (!canexec(s))
645 {
646 Alert(_("!!!!!!!! ERROR ERROR ERROR ERROR !!!!!!!!\n" "\n"
647 "Enlightenment's utility executable is not able to be executed:\n"
648 "\n" "%s\n"
649 "This is a fatal error and Enlightenment will cease to run.\n"
650 "Please rectify this situation and ensure it is installed\n"
651 "correctly.\n"), s);
652 EExit(1);
653 }
654 }
655