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