1 /*      $Id$
2 
3         This program is free software; you can redistribute it and/or modify
4         it under the terms of the GNU General Public License as published by
5         the Free Software Foundation; either version 2, or (at your option)
6         any later version.
7 
8         This program is distributed in the hope that it will be useful,
9         but WITHOUT ANY WARRANTY; without even the implied warranty of
10         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11         GNU General Public License for more details.
12 
13         You should have received a copy of the GNU General Public License
14         along with this program; if not, write to the Free Software
15         Foundation, Inc., Inc., 51 Franklin Street, Fifth Floor, Boston,
16         MA 02110-1301, USA.
17 
18 
19         oroborus - (c) 2001 Ken Lynch
20         xfwm4    - (c) 2002-2011 Olivier Fourdan
21 
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <X11/X.h>
29 #include <X11/Xlib.h>
30 #include <glib.h>
31 #include <gdk/gdk.h>
32 #include <gdk/gdkx.h>
33 #include <gtk/gtk.h>
34 #include <libxfce4util/libxfce4util.h>
35 #include <libxfce4ui/libxfce4ui.h>
36 
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <sys/wait.h>
41 #include <time.h>
42 #include <unistd.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <signal.h>
48 #include <string.h>
49 
50 #include "display.h"
51 #include "screen.h"
52 #include "events.h"
53 #include "event_filter.h"
54 #include "frame.h"
55 #include "settings.h"
56 #include "client.h"
57 #include "menu.h"
58 #include "focus.h"
59 #include "keyboard.h"
60 #include "workspaces.h"
61 #include "mywindow.h"
62 #include "session.h"
63 #include "startup_notification.h"
64 #include "compositor.h"
65 #include "spinning_cursor.h"
66 
67 #define BASE_EVENT_MASK \
68     SubstructureNotifyMask|\
69     StructureNotifyMask|\
70     SubstructureRedirectMask|\
71     ButtonPressMask|\
72     ButtonReleaseMask|\
73     KeyPressMask|\
74     KeyReleaseMask|\
75     FocusChangeMask|\
76     PropertyChangeMask|\
77     ColormapChangeMask
78 
79 #ifdef HAVE_COMPOSITOR
80 #define MAIN_EVENT_MASK BASE_EVENT_MASK|ExposureMask
81 #else /* HAVE_COMPOSITOR */
82 #define MAIN_EVENT_MASK BASE_EVENT_MASK
83 #endif /* HAVE_COMPOSITOR */
84 
85 #ifdef HAVE_COMPOSITOR
86 static gboolean compositor = TRUE;
87 static vblankMode vblank_mode = VBLANK_AUTO;
88 #define XFWM4_ERROR      (xfwm4_error_quark ())
89 
90 static GQuark
xfwm4_error_quark(void)91 xfwm4_error_quark (void)
92 {
93   return g_quark_from_static_string ("xfwm4-error-quark");
94 }
95 #endif /* HAVE_COMPOSITOR */
96 
97 #ifdef DEBUG
98 static gboolean
setupLog(gboolean debug)99 setupLog (gboolean debug)
100 {
101     const gchar *str;
102     gchar *logfile;
103     int fd;
104 
105     if (debug)
106     {
107         str = g_getenv ("XFWM4_LOG_FILE");
108         if (str)
109         {
110             logfile = g_strdup (str);
111         }
112         else
113         {
114             logfile = g_strdup_printf ("xfwm4-debug-%d.log", (int) getpid ());
115         }
116     }
117     else
118     {
119         logfile = "/dev/null";
120     }
121 
122     fd = dup(fileno(stderr));
123     if (fd == -1)
124     {
125         g_warning ("Fail to open %s: %s", logfile, g_strerror (errno));
126         g_free (logfile);
127         return FALSE;
128     }
129 
130     if (!freopen (logfile, "w", stderr))
131     {
132         g_warning ("Fail to redirect stderr: %s", g_strerror (errno));
133         g_free (logfile);
134         close (fd);
135         return FALSE;
136     }
137 
138     if (debug)
139     {
140         g_print ("Logging to %s\n", logfile);
141         g_free (logfile);
142     }
143 
144     return TRUE;
145 }
146 #endif /* DEBUG */
147 
148 static void
handleSignal(int sig)149 handleSignal (int sig)
150 {
151     DisplayInfo *display_info;
152 
153     display_info = myDisplayGetDefault ();
154     if (display_info)
155     {
156         switch (sig)
157         {
158             case SIGINT:
159                 /* Walk thru */
160             case SIGTERM:
161                 gtk_main_quit ();
162                 display_info->quit = TRUE;
163                 break;
164             case SIGHUP:
165                 /* Walk thru */
166             case SIGUSR1:
167                 display_info->reload = TRUE;
168                 break;
169             default:
170                 break;
171         }
172     }
173 }
174 
175 static void
setupHandler(gboolean install)176 setupHandler (gboolean install)
177 {
178     struct sigaction act;
179 
180     if (install)
181         act.sa_handler = handleSignal;
182     else
183         act.sa_handler = SIG_DFL;
184 
185     sigemptyset (&act.sa_mask);
186     act.sa_flags = 0;
187     sigaction (SIGINT,  &act, NULL);
188     sigaction (SIGTERM, &act, NULL);
189     sigaction (SIGHUP,  &act, NULL);
190     sigaction (SIGUSR1, &act, NULL);
191 }
192 
193 static void
cleanUp(void)194 cleanUp (void)
195 {
196     GSList *screens;
197     DisplayInfo *display_info;
198 
199     TRACE ("entering");
200 
201     setupHandler (FALSE);
202 
203     display_info = myDisplayGetDefault ();
204     g_return_if_fail (display_info);
205 
206     eventFilterClose (display_info->xfilter);
207     for (screens = display_info->screens; screens; screens = g_slist_next (screens))
208     {
209         ScreenInfo *screen_info_n = (ScreenInfo *) screens->data;
210         myScreenClose (screen_info_n);
211         g_free (screen_info_n);
212     }
213     sn_close_display ();
214     sessionFreeWindowStates ();
215 
216     myDisplayClose (display_info);
217     g_free (display_info);
218 
219     xfconf_shutdown();
220 }
221 
222 static void
ensure_basedir_spec(void)223 ensure_basedir_spec (void)
224 {
225     char *new, *old, path[PATH_MAX];
226     GError *error;
227     GDir *gdir;
228     const char *name;
229 
230     /* test if new directory is there */
231 
232     new = xfce_resource_save_location (XFCE_RESOURCE_CONFIG,
233                                        "xfce4" G_DIR_SEPARATOR_S "xfwm4",
234                                        FALSE);
235 
236     if (g_file_test (new, G_FILE_TEST_IS_DIR))
237     {
238         g_free (new);
239         return;
240     }
241 
242     error = NULL;
243     if (!xfce_mkdirhier(new, 0700, &error))
244     {
245         g_warning("Unable to create config dir %s: %s", new, error->message);
246         g_error_free (error);
247         g_free (new);
248         return;
249     }
250 
251     g_free (new);
252 
253     /* copy xfwm4rc */
254 
255     old = xfce_get_userfile ("xfwm4rc", NULL);
256 
257     if (g_file_test (old, G_FILE_TEST_EXISTS))
258     {
259         FILE *r, *w;
260 
261         g_strlcpy (path, "xfce4/xfwm4/xfwm4rc", PATH_MAX);
262         new = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, path, FALSE);
263 
264         r = fopen (old, "r");
265         w = fopen (new, "w");
266 
267         g_free (new);
268 
269         if (w && r)
270         {
271             int c;
272 
273             while ((c = getc (r)) != EOF)
274             {
275                 putc (c, w);
276             }
277         }
278 
279         if (r)
280         {
281             fclose (r);
282         }
283 
284         if (w)
285         {
286             fclose (w);
287         }
288     }
289 
290     g_free (old);
291 
292     /* copy saved session data */
293 
294     new = xfce_resource_save_location (XFCE_RESOURCE_CACHE, "sessions", FALSE);
295     if (!xfce_mkdirhier(new, 0700, &error))
296     {
297         g_warning("Unable to create session dir %s: %s", new, error->message);
298         g_error_free (error);
299         g_free (new);
300         return;
301     }
302 
303     old = xfce_get_userfile ("sessions", NULL);
304     gdir = g_dir_open (old, 0, NULL);
305 
306     if (gdir)
307     {
308         while ((name = g_dir_read_name (gdir)) != NULL)
309         {
310             FILE *r, *w;
311 
312             g_snprintf (path, PATH_MAX, "%s/%s", old, name);
313             r = fopen (path, "r");
314 
315             g_snprintf (path, PATH_MAX, "%s/%s", new, name);
316             w = fopen (path, "w");
317 
318             if (w && r)
319             {
320                 int c;
321 
322                 while ((c = getc (r)) != EOF)
323                     putc (c, w);
324             }
325 
326             if (r)
327                 fclose (r);
328             if (w)
329                 fclose (w);
330         }
331 
332         g_dir_close (gdir);
333     }
334 
335     g_free (old);
336     g_free (new);
337 }
338 
339 static void
print_version(void)340 print_version (void)
341 {
342     g_print ("\tThis is %s version %s (revision %s) for Xfce %s\n",
343                     PACKAGE, VERSION, REVISION, xfce_version_string());
344     g_print ("\tReleased under the terms of the GNU General Public License.\n");
345     g_print ("\tCompiled against GTK+-%d.%d.%d, ",
346                     GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
347     g_print ("using GTK+-%d.%d.%d.\n",
348                     gtk_major_version, gtk_minor_version, gtk_micro_version);
349     g_print ("\n");
350     g_print ("\tBuild configuration and supported features:\n");
351 
352     g_print ("\t- Startup notification support:                 ");
353 #ifdef HAVE_LIBSTARTUP_NOTIFICATION
354     g_print ("Yes\n");
355 #else
356     g_print ("No\n");
357 #endif
358 
359     g_print ("\t- XSync support:                                ");
360 #ifdef HAVE_XSYNC
361     g_print ("Yes\n");
362 #else
363     g_print ("No\n");
364 #endif
365 
366     g_print ("\t- Render support:                               ");
367 #ifdef HAVE_RENDER
368     g_print ("Yes\n");
369 #else
370     g_print ("No\n");
371 #endif
372 
373     g_print ("\t- Xrandr support:                               ");
374 #ifdef HAVE_RANDR
375     g_print ("Yes\n");
376 #else
377     g_print ("No\n");
378 #endif
379 
380     g_print ("\t- Xpresent support:                             ");
381 #ifdef HAVE_PRESENT_EXTENSION
382     g_print ("Yes\n");
383 #else
384     g_print ("No\n");
385 #endif
386 
387     g_print ("\t- X Input 2 support:                            ");
388 #ifdef HAVE_XI2
389     g_print ("Yes\n");
390 #else
391     g_print ("No\n");
392 #endif
393 
394     g_print ("\t- Embedded compositor:                          ");
395 #ifdef HAVE_COMPOSITOR
396     g_print ("Yes\n");
397 #else
398     g_print ("No\n");
399 #endif
400 
401 #ifdef HAVE_COMPOSITOR
402     g_print ("\t- Epoxy support:                                ");
403 #ifdef HAVE_EPOXY
404     g_print ("Yes\n");
405 #else
406     g_print ("No\n");
407 #endif /* HAVE_EPOXY */
408 #endif /* HAVE_COMPOSITOR */
409 
410     g_print ("\t- KDE systray proxy (deprecated):               ");
411 #ifdef ENABLE_KDE_SYSTRAY_PROXY
412     g_print ("Yes\n");
413 #else
414     g_print ("No\n");
415 #endif
416 }
417 
418 #ifdef HAVE_COMPOSITOR
419 static gboolean
compositor_callback(const gchar * name,const gchar * value,gpointer user_data,GError ** error)420 compositor_callback (const gchar  *name,
421                      const gchar  *value,
422                      gpointer      user_data,
423                      GError      **error)
424 {
425     gboolean succeed = TRUE;
426 
427     g_return_val_if_fail (value != NULL, FALSE);
428 
429     if (strcmp (value, "off") == 0)
430     {
431         compositor = FALSE;
432     }
433     else if (strcmp (value, "on") == 0)
434     {
435         compositor = TRUE;
436     }
437     else
438     {
439         g_set_error (error, XFWM4_ERROR, 0, "Unrecognized compositor option \"%s\"", value);
440         succeed = FALSE;
441     }
442 
443     return succeed;
444 }
445 
446 static gboolean
vblank_callback(const gchar * name,const gchar * value,gpointer user_data,GError ** error)447 vblank_callback (const gchar  *name,
448                  const gchar  *value,
449                  gpointer      user_data,
450                  GError      **error)
451 {
452     gboolean succeed = TRUE;
453 
454     g_return_val_if_fail (value != NULL, FALSE);
455 
456 #ifdef HAVE_PRESENT_EXTENSION
457     if (strcmp (value, "xpresent") == 0)
458     {
459         vblank_mode = VBLANK_XPRESENT;
460     }
461     else
462 #endif /* HAVE_PRESENT_EXTENSION */
463 #ifdef HAVE_EPOXY
464     if (strcmp (value, "glx") == 0)
465     {
466         vblank_mode = VBLANK_GLX;
467     }
468     else
469 #endif /* HAVE_EPOXY */
470     if (strcmp (value, "off") == 0)
471     {
472         vblank_mode = VBLANK_OFF;
473     }
474     else
475     {
476         g_set_error (error, XFWM4_ERROR, 0, "Unrecognized compositor option \"%s\"", value);
477         succeed = FALSE;
478     }
479 
480     return succeed;
481 }
482 
483 static void
init_compositor_screen(ScreenInfo * screen_info)484 init_compositor_screen (ScreenInfo *screen_info)
485 {
486     DisplayInfo *display_info;
487 
488     display_info = screen_info->display_info;
489     if (vblank_mode != VBLANK_AUTO)
490     {
491         compositorSetVblankMode (screen_info, vblank_mode);
492     }
493 
494     if (display_info->enable_compositor)
495     {
496         gboolean xfwm4_compositor;
497 
498         xfwm4_compositor = TRUE;
499         if (screen_info->params->use_compositing)
500         {
501             /* Enable compositor if "use compositing" is enabled */
502             xfwm4_compositor = compositorManageScreen (screen_info);
503         }
504         /*
505            The user may want to use the manual compositing, but the installed
506            system may not support it, so we need to double check, to see if
507            initialization of the compositor was successful.
508           */
509         if (xfwm4_compositor)
510         {
511             /*
512                Acquire selection on XFWM4_COMPOSITING_MANAGER to advertise our own
513                compositing manager (used by WM tweaks to determine whether or not
514                show the "compositor" tab.
515              */
516             setAtomIdManagerOwner (display_info, XFWM4_COMPOSITING_MANAGER,
517                                    screen_info->xroot, screen_info->xfwm4_win);
518         }
519     }
520 }
521 #endif /* HAVE_COMPOSITOR */
522 
523 static int
initialize(gboolean replace_wm)524 initialize (gboolean replace_wm)
525 {
526     DisplayInfo *display_info;
527     gint i, nscreens, default_screen;
528 
529     DBG ("xfwm4 starting, using GTK+-%d.%d.%d", gtk_major_version,
530          gtk_minor_version, gtk_micro_version);
531 
532     ensure_basedir_spec ();
533 
534     initMenuEventWin ();
535     clientClearFocus (NULL);
536     display_info = myDisplayInit (gdk_display_get_default ());
537 
538 #ifdef HAVE_COMPOSITOR
539     display_info->enable_compositor = compositor;
540 #else
541     display_info->enable_compositor = FALSE;
542 #endif /* HAVE_COMPOSITOR */
543 
544     initModifiers (display_info->dpy);
545 
546     setupHandler (TRUE);
547 
548     nscreens = ScreenCount (display_info->dpy);
549     default_screen = DefaultScreen (display_info->dpy);
550     for(i = 0; i < nscreens; i++)
551     {
552         ScreenInfo *screen_info;
553         GdkScreen *gscr;
554         Window temp_xwindow;
555         GdkWindow *screen_window;
556 
557         if (i == default_screen)
558         {
559             gscr = gdk_display_get_default_screen (display_info->gdisplay);
560         }
561         else
562         {
563             /* create temp 1x1 child window on this screen */
564             temp_xwindow = XCreateSimpleWindow (display_info->dpy,
565                                                 RootWindow (display_info->dpy, i),
566                                                 0, 0, 1, 1, 0, 0, 0);
567             /* allocate new GdkWindow with GdkScreen for this window */
568             screen_window =
569                 gdk_x11_window_foreign_new_for_display (display_info->gdisplay,
570                                                         temp_xwindow);
571             XDestroyWindow (display_info->dpy, temp_xwindow);
572 
573             if (screen_window == NULL)
574             {
575                 g_warning ("Cannot create GdkScreen for screen %i", i);
576                 continue;
577             }
578 
579             gscr = gdk_window_get_screen (screen_window);
580 
581             /* foreign windows have 2 references */
582             g_object_unref (screen_window);
583             g_object_unref (screen_window);
584         }
585         screen_info = myScreenInit (display_info, gscr, MAIN_EVENT_MASK, replace_wm);
586 
587         if (!screen_info)
588         {
589             continue;
590         }
591 
592         if (!initSettings (screen_info))
593         {
594             return -2;
595         }
596 #ifdef HAVE_COMPOSITOR
597         if (display_info->enable_compositor)
598         {
599             init_compositor_screen (screen_info);
600         }
601 #endif /* HAVE_COMPOSITOR */
602         sn_init_display (screen_info);
603         myDisplayAddScreen (display_info, screen_info);
604         screen_info->current_ws = getNetCurrentDesktop (display_info, screen_info->xroot);
605         setUTF8StringHint (display_info, screen_info->xfwm4_win, NET_WM_NAME, "Xfwm4");
606         setNetSupportedHint (display_info, screen_info->xroot, screen_info->xfwm4_win);
607         setNetDesktopInfo (display_info, screen_info->xroot, screen_info->current_ws,
608                                    screen_info->width,
609                                    screen_info->height);
610         workspaceUpdateArea (screen_info);
611         XSetInputFocus (display_info->dpy, screen_info->xfwm4_win, RevertToPointerRoot, CurrentTime);
612 
613         clientFrameAll (screen_info);
614 
615         initPerScreenCallbacks (screen_info);
616 
617         XDefineCursor (display_info->dpy, screen_info->xroot, myDisplayGetCursorRoot(display_info));
618     }
619 
620     /* No screen to manage, give up */
621     if (!display_info->nb_screens)
622     {
623         return -1;
624     }
625     display_info->xfilter = eventFilterInit (display_info->devices, (gpointer) display_info);
626     eventFilterPush (display_info->xfilter, xfwm4_event_filter, (gpointer) display_info);
627     initPerDisplayCallbacks (display_info);
628 
629     return sessionStart (display_info);
630 }
631 
632 static void
init_pango_cache(void)633 init_pango_cache (void)
634 {
635     GtkWidget *tmp_win;
636     PangoLayout *layout;
637 
638     /*
639      * The first time the first Gtk application on a display uses pango,
640      * pango grabs the XServer while it creates the font cache window.
641      * Therefore, force the cache window to be created now instead of
642      * trying to do it while we have another grab and deadlocking the server.
643      */
644     tmp_win = gtk_window_new (GTK_WINDOW_POPUP);
645     layout = gtk_widget_create_pango_layout (tmp_win, "-");
646     pango_layout_get_pixel_extents (layout, NULL, NULL);
647     g_object_unref (G_OBJECT (layout));
648     gtk_widget_destroy (GTK_WIDGET (tmp_win));
649 }
650 
651 int
main(int argc,char ** argv)652 main (int argc, char **argv)
653 {
654     gboolean version = FALSE;
655     gboolean replace_wm = FALSE;
656     int status;
657     GOptionContext *context;
658     GError *error = NULL;
659 #ifdef DEBUG
660     gboolean debug = FALSE;
661 #endif /* DEBUG */
662     GOptionEntry option_entries[] =
663     {
664 #ifdef HAVE_COMPOSITOR
665         { "compositor", 'c', 0, G_OPTION_ARG_CALLBACK,
666           compositor_callback, N_("Set the compositor mode"), "on|off" },
667         { "vblank", 'b', 0, G_OPTION_ARG_CALLBACK,
668           vblank_callback, N_("Set the vblank mode"), "off"
669 #ifdef HAVE_PRESENT_EXTENSION
670           "|xpresent"
671 #endif /* HAVE_PRESENT_EXTENSION */
672 #ifdef HAVE_EPOXY
673           "|glx"
674 #endif /* HAVE_EPOXY */
675         },
676 #endif /* HAVE_COMPOSITOR */
677         { "replace", 'r', 0, G_OPTION_ARG_NONE,
678           &replace_wm, N_("Replace the existing window manager"), NULL },
679         { "version", 'V', 0, G_OPTION_ARG_NONE,
680           &version, N_("Print version information and exit"), NULL },
681 #ifdef DEBUG
682         { "debug", 'd', 0, G_OPTION_ARG_NONE,
683           &debug, N_("Enable debug logging"), NULL },
684 #endif /* DEBUG */
685         { NULL }
686     };
687 
688 #ifdef HAVE_EPOXY
689     /* NVIDIA proprietary/closed source driver queues up to 2 frames by
690      * default before blocking in glXSwapBuffers(), whereas our compositor
691      * expects `glXSwapBuffers()` to block until the next vblank.
692      *
693      * To avoid that, our compositor was issuing a `glXWaitGL()` immediately
694      * after the call to `glXSwapBuffers()` but that translates as a busy
695      * wait, hence dramatically increasing CPU usage of xfwm4 with the
696      * NVIDIA proprietary/closed source driver.
697      *
698      * Instruct the NVIDIA proprietary/closed source driver to allow only
699      * 1 frame using the environment variable “__GL_MaxFramesAllowed” so
700      * that it matches our expectations.
701      *
702      * This must be set before libGL is loaded, hence before gtk_init().
703      *
704      * Taken from similar patch posted by NVIDIA developer for kwin:
705      * https://phabricator.kde.org/D19867
706      */
707     g_setenv("__GL_MaxFramesAllowed", "1", TRUE);
708 #endif /* HAVE_EPOXY */
709 
710     xfce_textdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
711 
712     /* xfwm4 is an X11 window manager, no point in trying to connect to
713      * any other display server (like when running nested within a
714      * Wayland compositor).
715      */
716     gdk_set_allowed_backends ("x11");
717 
718 #ifndef HAVE_XI2
719     /* Disable XI2 in GDK */
720     gdk_disable_multidevice ();
721 #endif
722 
723     context = g_option_context_new (_("[ARGUMENTS...]"));
724     g_option_context_add_main_entries (context, option_entries, GETTEXT_PACKAGE);
725     g_option_context_add_group (context, gtk_get_option_group (FALSE));
726     g_option_context_add_group (context, xfce_sm_client_get_option_group (argc, argv));
727     if (!g_option_context_parse (context, &argc, &argv, &error))
728     {
729           g_print ("%s: %s.\n", PACKAGE_NAME, error->message);
730           g_print (_("Type \"%s --help\" for usage."), G_LOG_DOMAIN);
731           g_print ("\n");
732           g_error_free (error);
733 
734           return EXIT_FAILURE;
735     }
736     g_option_context_free (context);
737 
738 #ifdef DEBUG
739     setupLog (debug);
740 #endif /* DEBUG */
741     DBG ("xfwm4 starting");
742 
743     gtk_init (&argc, &argv);
744 
745     if (G_UNLIKELY (version))
746     {
747          print_version ();
748          return EXIT_SUCCESS;
749     }
750     init_pango_cache ();
751 
752     status = initialize (replace_wm);
753     /*
754        status  < 0   =>   Error, cancel execution
755        status == 0   =>   Run w/out session manager
756        status == 1   =>   Connected to session manager
757      */
758     switch (status)
759     {
760         case -1:
761             g_warning ("Could not find a screen to manage, exiting");
762             exit (1);
763             break;
764         case -2:
765             g_warning ("Missing data from default files");
766             exit (1);
767             break;
768         case 0:
769         case 1:
770             /* enter GTK main loop */
771             gtk_main ();
772             break;
773         default:
774             g_warning ("Unknown error occurred");
775             exit (1);
776             break;
777     }
778     cleanUp ();
779     DBG ("xfwm4 terminated");
780     return 0;
781 }
782