JWM Source Documentation
main.c
Go to the documentation of this file.
1 
10 #include "jwm.h"
11 #include "main.h"
12 #include "parse.h"
13 #include "help.h"
14 #include "error.h"
15 #include "event.h"
16 
17 #include "border.h"
18 #include "client.h"
19 #include "color.h"
20 #include "command.h"
21 #include "cursor.h"
22 #include "confirm.h"
23 #include "font.h"
24 #include "group.h"
25 #include "key.h"
26 #include "icon.h"
27 #include "taskbar.h"
28 #include "tray.h"
29 #include "traybutton.h"
30 #include "popup.h"
31 #include "pager.h"
32 #include "swallow.h"
33 #include "screen.h"
34 #include "root.h"
35 #include "desktop.h"
36 #include "place.h"
37 #include "clock.h"
38 #include "dock.h"
39 #include "misc.h"
40 #include "background.h"
41 #include "settings.h"
42 #include "timing.h"
43 #include "grab.h"
44 
45 #include <errno.h>
46 
47 Display *display = NULL;
48 Window rootWindow;
51 Colormap rootColormap;
52 Visual *rootVisual;
54 GC rootGC;
58 
59 char shouldExit = 0;
60 char shouldRestart = 0;
61 char isRestarting = 0;
62 char initializing = 0;
63 char shouldReload = 0;
64 
65 unsigned int currentDesktop = 0;
66 
67 char *exitCommand = NULL;
68 
69 XContext clientContext;
70 XContext frameContext;
71 
72 #ifdef USE_SHAPE
73 char haveShape;
75 #endif
76 #ifdef USE_XRENDER
78 #endif
79 
80 static const char CONFIG_FILE[] = "/.jwmrc";
81 
82 static void Initialize(void);
83 static void Startup(void);
84 static void Shutdown(void);
85 static void Destroy(void);
86 
87 static void OpenConnection(void);
88 static void CloseConnection(void);
89 static Bool SelectionReleased(Display *d, XEvent *e, XPointer arg);
90 static void StartupConnection(void);
91 static void ShutdownConnection(void);
92 static void EventLoop(void);
93 static void HandleExit(int sig);
94 static void HandleChild(int sig);
95 static void DoExit(int code);
96 static void SendRestart(void);
97 static void SendExit(void);
98 static void SendReload(void);
99 static void SendJWMMessage(const char *message);
100 
101 static char *displayString = NULL;
102 
103 char *configPath = NULL;
104 
106 #ifndef UNIT_TEST
107 int main(int argc, char *argv[])
108 {
109  char *temp;
110  int x;
111  enum {
112  ACTION_RUN,
113  ACTION_RESTART,
114  ACTION_EXIT,
115  ACTION_RELOAD,
116  ACTION_PARSE
117  } action;
118 
119  StartDebug();
120 
121  /* Get the name of the user's local configuration file. */
122  temp = getenv("HOME");
123  if(temp) {
124  const size_t temp_len = strlen(temp);
125  const size_t config_len = sizeof(CONFIG_FILE);
126  configPath = Allocate(temp_len + config_len);
127  memcpy(configPath, temp, temp_len);
128  memcpy(&configPath[temp_len], CONFIG_FILE, config_len);
129  } else {
131  }
132 
133  /* Parse command line options. */
134  action = ACTION_RUN;
135  for(x = 1; x < argc; x++) {
136  if(!strcmp(argv[x], "-v")) {
137  DisplayAbout();
138  DoExit(0);
139  } else if(!strcmp(argv[x], "-h")) {
140  DisplayHelp();
141  DoExit(0);
142  } else if(!strcmp(argv[x], "-p")) {
143  action = ACTION_PARSE;
144  } else if(!strcmp(argv[x], "-restart")) {
145  action = ACTION_RESTART;
146  } else if(!strcmp(argv[x], "-exit")) {
147  action = ACTION_EXIT;
148  } else if(!strcmp(argv[x], "-reload")) {
149  action = ACTION_RELOAD;
150  } else if(!strcmp(argv[x], "-display") && x + 1 < argc) {
151  displayString = argv[++x];
152  } else if(!strcmp(argv[x], "-f") && x + 1 < argc) {
154  configPath = CopyString(argv[++x]);
155  } else {
156  printf("unrecognized option: %s\n", argv[x]);
157  DisplayHelp();
158  DoExit(1);
159  }
160  }
161 
162  switch(action) {
163  case ACTION_PARSE:
164  Initialize();
166  DoExit(0);
167  case ACTION_RESTART:
168  SendRestart();
169  DoExit(0);
170  case ACTION_EXIT:
171  SendExit();
172  DoExit(0);
173  case ACTION_RELOAD:
174  SendReload();
175  DoExit(0);
176  default:
177  break;
178  }
179 
180 #if defined(HAVE_SETLOCALE) && (defined(ENABLE_NLS) || defined(ENABLE_ICONV))
181  setlocale(LC_ALL, "");
182 #endif
183 #ifdef HAVE_GETTEXT
184  bindtextdomain("jwm", LOCALEDIR);
185  textdomain("jwm");
186 #endif
187 
188  /* The main loop. */
190  do {
191 
193  shouldExit = 0;
194  shouldRestart = 0;
195  shouldReload = 0;
196 
197  /* Prepare JWM components. */
198  Initialize();
199 
200  /* Parse the configuration file. */
202 
203  /* Start up the JWM components. */
204  Startup();
205 
206  /* The main event loop. */
207  EventLoop();
208 
209  /* Shutdown JWM components. */
210  Shutdown();
211 
212  /* Perform any extra cleanup. */
213  Destroy();
214 
215  } while(shouldRestart);
217 
218  /* If we have a command to execute on shutdown, run it now. */
219  if(exitCommand) {
220  execl(SHELL_NAME, SHELL_NAME, "-c", exitCommand, NULL);
221  Warning(_("exec failed: (%s) %s"), SHELL_NAME, exitCommand);
222  DoExit(1);
223  } else {
224  DoExit(0);
225  }
226 
227  /* Control shoud never get here. */
228  return -1;
229 
230 }
231 #endif
232 
234 void DoExit(int code)
235 {
236 
237  Destroy();
238 
239  if(configPath) {
241  configPath = NULL;
242  }
243  if(exitCommand) {
245  exitCommand = NULL;
246  }
247 
248  StopDebug();
249  exit(code);
250 }
251 
253 void EventLoop(void)
254 {
255 
256  XEvent event;
257  TimeType start;
258 
259  /* Loop processing events until it's time to exit. */
260  while(JLIKELY(!shouldExit)) {
261  if(JLIKELY(WaitForEvent(&event))) {
262  ProcessEvent(&event);
263  }
264  }
265 
266  /* Process events one last time. */
267  GetCurrentTime(&start);
268  for(;;) {
269  if(JXPending(display) == 0) {
270  if(!IsSwallowPending()) {
271  break;
272  } else {
273  TimeType now;
274  GetCurrentTime(&now);
275  if(GetTimeDifference(&start, &now) > RESTART_DELAY) {
276  break;
277  }
278  }
279  }
280  if(WaitForEvent(&event)) {
281  ProcessEvent(&event);
282  }
283  }
284 
285 }
286 
288 void OpenConnection(void)
289 {
290 
292  if(JUNLIKELY(!display)) {
293  if(displayString) {
294  printf("error: could not open display %s\n", displayString);
295  } else {
296  printf("error: could not open display\n");
297  }
298  DoExit(1);
299  }
300 
301  rootScreen = DefaultScreen(display);
302  rootWindow = RootWindow(display, rootScreen);
303  rootWidth = DisplayWidth(display, rootScreen);
304  rootHeight = DisplayHeight(display, rootScreen);
305  rootDepth = DefaultDepth(display, rootScreen);
306  rootVisual = DefaultVisual(display, rootScreen);
307  rootColormap = DefaultColormap(display, rootScreen);
308  rootGC = DefaultGC(display, rootScreen);
309  colormapCount = MaxCmapsOfScreen(ScreenOfDisplay(display, rootScreen));
310 
311  XSetGraphicsExposures(display, rootGC, False);
312 }
313 
315 Bool SelectionReleased(Display *d, XEvent *e, XPointer arg)
316 {
317  if(e->type == DestroyNotify) {
318  if(e->xdestroywindow.window == *(Window*)arg) {
319  return True;
320  }
321  }
322  return False;
323 }
324 
327 {
328 
329  XSetWindowAttributes attr;
330 #ifdef USE_SHAPE
331  int shapeError;
332 #endif
333 #ifdef USE_XRENDER
334  int renderEvent;
335  int renderError;
336 #endif
337  struct sigaction sa;
338  char name[32];
339  Window win;
340  XEvent event;
341  int revert;
342 
343  initializing = 1;
344  OpenConnection();
345 
346 #if 0
347  XSynchronize(display, True);
348 #endif
349 
350  /* Create the supporting window used to verify JWM is running. */
352  0, 0, 1, 1, 0, 0, 0);
353 
354  /* Get the atom used for the window manager selection. */
355  snprintf(name, 32, "WM_S%d", rootScreen);
356  managerSelection = JXInternAtom(display, name, False);
357 
358  /* Get the current window manager and take the selection. */
359  GrabServer();
361  if(win != None) {
362  JXSelectInput(display, win, StructureNotifyMask);
363  }
365  supportingWindow, CurrentTime);
366  UngrabServer();
367 
368  /* Wait for the current selection owner to give up the selection. */
369  if(win != None) {
370  /* Note that we need to wait for the current selection owner
371  * to exit before we can expect to select SubstructureRedirectMask. */
372  XIfEvent(display, &event, SelectionReleased, (XPointer)&win);
373  JXSync(display, False);
374  }
375 
376  event.xclient.display = display;
377  event.xclient.type = ClientMessage;
378  event.xclient.window = rootWindow;
379  event.xclient.message_type = JXInternAtom(display, managerProperty, False);
380  event.xclient.format = 32;
381  event.xclient.data.l[0] = CurrentTime;
382  event.xclient.data.l[1] = managerSelection;
383  event.xclient.data.l[2] = supportingWindow;
384  event.xclient.data.l[3] = 2;
385  event.xclient.data.l[4] = 0;
386  JXSendEvent(display, rootWindow, False, StructureNotifyMask, &event);
387  JXSync(display, False);
388 
390 
391  clientContext = XUniqueContext();
392  frameContext = XUniqueContext();
393 
394  /* Set the events we want for the root window.
395  * Note that asking for SubstructureRedirect will fail
396  * if another window manager is already running.
397  */
398  attr.event_mask
399  = SubstructureRedirectMask
400  | SubstructureNotifyMask
401  | StructureNotifyMask
402  | PropertyChangeMask
403  | ColormapChangeMask
404  | ButtonPressMask
405  | ButtonReleaseMask
406  | PointerMotionMask | PointerMotionHintMask;
407  JXChangeWindowAttributes(display, rootWindow, CWEventMask, &attr);
408 
409  memset(&sa, 0, sizeof(sa));
410  sa.sa_flags = 0;
411  sa.sa_handler = HandleExit;
412  sigaction(SIGTERM, &sa, NULL);
413  sigaction(SIGINT, &sa, NULL);
414  sigaction(SIGHUP, &sa, NULL);
415 
416  sa.sa_handler = HandleChild;
417  sigaction(SIGCHLD, &sa, NULL);
418 
419 #ifdef USE_SHAPE
421  if (haveShape) {
422  Debug("shape extension enabled");
423  } else {
424  Debug("shape extension disabled");
425  }
426 #endif
427 
428 #ifdef USE_XRENDER
429  haveRender = JXRenderQueryExtension(display, &renderEvent, &renderError);
430  if(haveRender) {
431  Debug("render extension enabled");
432  } else {
433  Debug("render extension disabled");
434  }
435 #endif
436 
437  /* Make sure we have input focus. */
438  win = None;
439  JXGetInputFocus(display, &win, &revert);
440  if(win == None) {
441  JXSetInputFocus(display, rootWindow, RevertToParent, CurrentTime);
442  }
443 
444  initializing = 0;
445 
446 }
447 
449 void CloseConnection(void)
450 {
451  JXFlush(display);
453 }
454 
457 {
458  CloseConnection();
459 }
460 
462 void HandleExit(int sig)
463 {
464  shouldExit = 1;
465 }
466 
468 void HandleChild(int sig)
469 {
470  const int savedErrno = errno;
471  while(waitpid((pid_t)-1, NULL, WNOHANG) > 0);
472  errno = savedErrno;
473 }
474 
478 void Initialize(void)
479 {
480 
484  InitializeClock();
489 #ifndef DISABLE_CONFIRM
491 #endif
492  InitializeDock();
493  InitializeFonts();
495  InitializeHints();
496  InitializeIcons();
497  InitializeKeys();
498  InitializePager();
500  InitializePopup();
506  InitializeTray();
508 }
509 
513 void Startup(void)
514 {
515 
516  /* This order is important. */
517 
518  /* First we grab the server to prevent clients from changing things
519  * while we're still loading. */
520  GrabServer();
521 
522  StartupSettings();
523  StartupScreens();
524 
525  StartupGroups();
526  StartupColors();
527  StartupFonts();
528  StartupIcons();
530  StartupCursors();
531 
532  StartupPager();
533  StartupClock();
534  StartupTaskBar();
536  StartupDesktops();
537  StartupHints();
538  StartupDock();
539  StartupTray();
540  StartupKeys();
541  StartupBorders();
543  StartupClients();
544 
545 # ifndef DISABLE_CONFIRM
546  StartupDialogs();
547 # endif
548  StartupPopup();
549 
550  StartupRootMenu();
551 
554  JXFlush(display);
555 
556  RequireRestack();
557 
558  /* Allow clients to do their thing. */
559  JXSync(display, True);
560  UngrabServer();
561 
562  StartupSwallow();
563 
564  DrawTray();
565 
566  /* Send expose events. */
568 
569  /* Draw the background (if backgrounds are used). */
571 
572  /* Run any startup commands. */
573  StartupCommands();
574 
575 }
576 
580 void Shutdown(void)
581 {
582 
583  /* This order is important. */
584 
585  ShutdownSwallow();
586 
587 # ifndef DISABLE_CONFIRM
588  ShutdownDialogs();
589 # endif
590  ShutdownPopup();
591  ShutdownKeys();
592  ShutdownPager();
594  ShutdownDock();
595  ShutdownTray();
597  ShutdownTaskBar();
598  ShutdownClock();
599  ShutdownBorders();
600  ShutdownClients();
602  ShutdownIcons();
603  ShutdownCursors();
604  ShutdownFonts();
605  ShutdownColors();
606  ShutdownGroups();
608 
610  ShutdownHints();
611  ShutdownScreens();
613 
615 
616 }
617 
622 void Destroy(void)
623 {
625  DestroyBorders();
626  DestroyClients();
627  DestroyClock();
628  DestroyColors();
629  DestroyCommands();
630  DestroyCursors();
631  DestroyDesktops();
632 #ifndef DISABLE_CONFIRM
633  DestroyDialogs();
634 #endif
635  DestroyDock();
636  DestroyFonts();
637  DestroyGroups();
638  DestroyHints();
639  DestroyIcons();
640  DestroyKeys();
641  DestroyPager();
643  DestroyPopup();
644  DestroyRootMenu();
645  DestroyScreens();
646  DestroySettings();
647  DestroySwallow();
648  DestroyTaskBar();
649  DestroyTray();
651 }
652 
654 void SendRestart(void)
655 {
657 }
658 
660 void SendExit(void)
661 {
663 }
664 
666 void SendReload(void)
667 {
669 }
670 
672 void SendJWMMessage(const char *message)
673 {
674  XEvent event;
675  OpenConnection();
676  memset(&event, 0, sizeof(event));
677  event.xclient.type = ClientMessage;
678  event.xclient.window = rootWindow;
679  event.xclient.message_type = JXInternAtom(display, message, False);
680  event.xclient.format = 32;
681  JXSendEvent(display, rootWindow, False, SubstructureRedirectMask, &event);
682  CloseConnection();
683 }
684 

joewing.net / Projects / JWM