]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/openbox.c
use ob_debug for any debug printing and only display the output when its a debug...
[mikachu/openbox.git] / openbox / openbox.c
1 #include "debug.h"
2 #include "openbox.h"
3 #include "dock.h"
4 #include "event.h"
5 #include "menu.h"
6 #include "client.h"
7 #include "dispatch.h"
8 #include "xerror.h"
9 #include "prop.h"
10 #include "startup.h"
11 #include "screen.h"
12 #include "focus.h"
13 #include "moveresize.h"
14 #include "frame.h"
15 #include "extensions.h"
16 #include "grab.h"
17 #include "plugin.h"
18 #include "timer.h"
19 #include "group.h"
20 #include "config.h"
21 #include "gettext.h"
22 #include "parser/parse.h"
23 #include "render/render.h"
24 #include "render/theme.h"
25
26 #ifdef HAVE_FCNTL_H
27 #  include <fcntl.h>
28 #endif
29 #ifdef HAVE_SIGNAL_H
30 #define __USE_UNIX98
31 #  include <signal.h>
32 #endif
33 #ifdef HAVE_STDLIB_H
34 #  include <stdlib.h>
35 #endif
36 #ifdef HAVE_LOCALE_H
37 #  include <locale.h>
38 #endif
39 #ifdef HAVE_UNISTD_H
40 #  include <unistd.h>
41 #endif
42 #ifdef HAVE_SYS_STAT_H
43 #  include <sys/stat.h>
44 #  include <sys/types.h>
45 #endif
46
47 #ifdef USE_SM
48 #include <X11/SM/SMlib.h>
49 #endif
50
51 #include <X11/cursorfont.h>
52
53 #ifdef USE_SM
54 gboolean    ob_sm_use = TRUE;
55 SmcConn     ob_sm_conn;
56 gchar      *ob_sm_id;
57 #endif
58
59 RrInstance *ob_rr_inst;
60 RrTheme    *ob_rr_theme;
61 Display    *ob_display;
62 gint        ob_screen;
63 gboolean    ob_replace_wm;
64
65 static ObState   state;
66 static gboolean  xsync;
67 static gboolean  shutdown;
68 static gboolean  restart;
69 static char     *restart_path;
70 static Cursor    cursors[OB_NUM_CURSORS];
71 static KeyCode   keys[OB_NUM_KEYS];
72
73 static void signal_handler(const ObEvent *e, void *data);
74 static void parse_args(int argc, char **argv);
75
76 static void sm_startup(int argc, char **argv);
77 static void sm_shutdown();
78
79 #ifdef USE_SM
80 static void sm_save_yourself(SmcConn conn, SmPointer data, int save_type,
81                              Bool shutdown, int interact_style, Bool fast);
82 static void sm_die(SmcConn conn, SmPointer data);
83 static void sm_save_complete(SmcConn conn, SmPointer data);
84 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data);
85 #endif
86 static void exit_with_error(gchar *msg);
87
88 int main(int argc, char **argv)
89 {
90     struct sigaction action;
91     sigset_t sigset;
92     char *path;
93     xmlDocPtr doc;
94     xmlNodePtr node;
95
96 #ifdef DEBUG
97     ob_debug_show_output(TRUE);
98 #endif
99
100     state = OB_STATE_STARTING;
101
102     /* initialize the locale */
103     if (!setlocale(LC_ALL, ""))
104         g_warning("Couldn't set locale from environment.\n");
105     bindtextdomain(PACKAGE_NAME, LOCALEDIR);
106     bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
107     textdomain(PACKAGE_NAME);
108
109     /* start our event dispatcher and register for signals */
110     dispatch_startup();
111     dispatch_register(Event_Signal, signal_handler, NULL);
112
113     /* set up signal handler */
114     sigemptyset(&sigset);
115     action.sa_handler = dispatch_signal;
116     action.sa_mask = sigset;
117     action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
118     sigaction(SIGUSR1, &action, (struct sigaction *) NULL);
119     sigaction(SIGPIPE, &action, (struct sigaction *) NULL);
120 /*    sigaction(SIGSEGV, &action, (struct sigaction *) NULL);*/
121     sigaction(SIGFPE, &action, (struct sigaction *) NULL);
122     sigaction(SIGTERM, &action, (struct sigaction *) NULL);
123     sigaction(SIGINT, &action, (struct sigaction *) NULL);
124     sigaction(SIGHUP, &action, (struct sigaction *) NULL);
125
126     /* create the ~/.openbox dir */
127     path = g_build_filename(g_get_home_dir(), ".openbox", NULL);
128     mkdir(path, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
129                  S_IROTH | S_IWOTH | S_IXOTH));
130     g_free(path);
131     /* create the ~/.openbox/themes dir */
132     path = g_build_filename(g_get_home_dir(), ".openbox", "themes", NULL);
133     mkdir(path, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP |
134                  S_IROTH | S_IWOTH | S_IXOTH));
135     g_free(path);
136
137     g_set_prgname(argv[0]);
138      
139     /* parse out command line args */
140     parse_args(argc, argv);
141
142     ob_display = XOpenDisplay(NULL);
143     if (ob_display == NULL)
144         exit_with_error("Failed to open the display.");
145     if (fcntl(ConnectionNumber(ob_display), F_SETFD, 1) == -1)
146         exit_with_error("Failed to set display as close-on-exec.");
147
148     sm_startup(argc, argv);
149
150 #ifdef USE_LIBSN
151     ob_sn_display = sn_display_new(ob_display, NULL, NULL);
152 #endif
153
154     ob_screen = DefaultScreen(ob_display);
155
156     ob_rr_inst = RrInstanceNew(ob_display, ob_screen);
157     if (ob_rr_inst == NULL)
158         exit_with_error("Failed to initialize the render library.");
159
160     /* XXX fork self onto other screens */
161      
162     XSynchronize(ob_display, xsync);
163
164     /* check for locale support */
165     if (!XSupportsLocale())
166         g_warning("X server does not support locale.");
167     if (!XSetLocaleModifiers(""))
168         g_warning("Cannot set locale modifiers for the X server.");
169
170     /* set our error handler */
171     XSetErrorHandler(xerror_handler);
172
173     /* set the DISPLAY environment variable for any lauched children, to the
174        display we're using, so they open in the right place. */
175     putenv(g_strdup_printf("DISPLAY=%s", DisplayString(ob_display)));
176
177     /* create available cursors */
178     cursors[OB_CURSOR_POINTER] =
179         XCreateFontCursor(ob_display, XC_left_ptr);
180     cursors[OB_CURSOR_BUSY] =
181         XCreateFontCursor(ob_display, XC_watch);
182     cursors[OB_CURSOR_MOVE] =
183         XCreateFontCursor(ob_display, XC_fleur);
184     cursors[OB_CURSOR_NORTH] =
185         XCreateFontCursor(ob_display, XC_top_side);
186     cursors[OB_CURSOR_NORTHEAST] =
187         XCreateFontCursor(ob_display, XC_top_right_corner);
188     cursors[OB_CURSOR_EAST] =
189         XCreateFontCursor(ob_display, XC_right_side);
190     cursors[OB_CURSOR_SOUTHEAST] =
191         XCreateFontCursor(ob_display, XC_bottom_right_corner);
192     cursors[OB_CURSOR_SOUTH] =
193         XCreateFontCursor(ob_display, XC_bottom_side);
194     cursors[OB_CURSOR_SOUTHWEST] =
195         XCreateFontCursor(ob_display, XC_bottom_left_corner);
196     cursors[OB_CURSOR_WEST] =
197         XCreateFontCursor(ob_display, XC_left_side);
198     cursors[OB_CURSOR_NORTHWEST] =
199         XCreateFontCursor(ob_display, XC_top_left_corner);
200
201     /* create available keycodes */
202     keys[OB_KEY_RETURN] =
203         XKeysymToKeycode(ob_display, XStringToKeysym("Return"));
204     keys[OB_KEY_ESCAPE] =
205         XKeysymToKeycode(ob_display, XStringToKeysym("Escape"));
206     keys[OB_KEY_LEFT] =
207         XKeysymToKeycode(ob_display, XStringToKeysym("Left"));
208     keys[OB_KEY_RIGHT] =
209         XKeysymToKeycode(ob_display, XStringToKeysym("Right"));
210     keys[OB_KEY_UP] =
211         XKeysymToKeycode(ob_display, XStringToKeysym("Up"));
212     keys[OB_KEY_DOWN] =
213         XKeysymToKeycode(ob_display, XStringToKeysym("Down"));
214
215     prop_startup(); /* get atoms values for the display */
216     extensions_query_all(); /* find which extensions are present */
217
218     /* save stuff that we can use to restore state */
219     startup_save();
220
221     if (screen_annex()) { /* it will be ours! */
222         /* startup the parsing so everything can register sections of the rc */
223         parse_startup();
224
225         /* anything that is going to read data from the rc file needs to be 
226            in this group */
227         timer_startup();
228         event_startup();
229         grab_startup();
230         /* focus_backup is used for stacking, so this needs to come before
231            anything that calls stacking_add */
232         focus_startup();
233         window_startup();
234         plugin_startup();
235         /* load the plugins specified in the pluginrc */
236         plugin_loadall();
237
238         /* set up the kernel config shit */
239         config_startup();
240         menu_startup();
241         /* parse/load user options */
242         if (parse_load_rc(&doc, &node))
243             parse_tree(doc, node->xmlChildrenNode, NULL);
244         /* we're done with parsing now, kill it */
245         parse_shutdown();
246
247         /* load the theme specified in the rc file */
248         ob_rr_theme = RrThemeNew(ob_rr_inst, config_theme);
249         if (ob_rr_theme == NULL)
250             exit_with_error("Unable to load a theme.");
251
252         moveresize_startup();
253         screen_startup();
254         group_startup();
255         client_startup();
256         dock_startup();
257
258         /* call startup for all the plugins */
259         plugin_startall();
260
261         /* get all the existing windows */
262         client_manage_all();
263
264         state = OB_STATE_RUNNING;
265         while (!shutdown)
266             event_loop();
267         state = OB_STATE_EXITING;
268
269         dock_remove_all();
270         client_unmanage_all();
271
272         plugin_shutdown(); /* calls all the plugins' shutdown functions */
273         dock_shutdown();
274         client_shutdown();
275         group_shutdown();
276         screen_shutdown();
277         focus_shutdown();
278         moveresize_shutdown();
279         menu_shutdown();
280         window_shutdown();
281         grab_shutdown();
282         event_shutdown();
283         timer_shutdown();
284         config_shutdown();
285     }
286
287     dispatch_shutdown();
288
289     RrThemeFree(ob_rr_theme);
290     RrInstanceFree(ob_rr_inst);
291
292     sm_shutdown();
293
294     XCloseDisplay(ob_display);
295
296     if (restart) {
297         if (restart_path != NULL) {
298             int argcp;
299             char **argvp;
300             GError *err = NULL;
301
302             /* run other shit */
303             if (g_shell_parse_argv(restart_path, &argcp, &argvp, &err)) {
304                 execvp(argvp[0], argvp);
305                 g_strfreev(argvp);
306             } else {
307                 g_warning("failed to execute '%s': %s", restart_path,
308                           err->message);
309             }
310         }
311
312         /* re-run me */
313         execvp(argv[0], argv); /* try how we were run */
314     }
315      
316     return 0;
317 }
318
319 static void sm_startup(int argc, char **argv)
320 {
321 #ifdef USE_SM
322
323 #define SM_ERR_LEN 1024
324
325     SmcCallbacks cb;
326     char sm_err[SM_ERR_LEN];
327
328     cb.save_yourself.callback = sm_save_yourself;
329     cb.save_yourself.client_data = NULL;
330
331     cb.die.callback = sm_die;
332     cb.die.client_data = NULL;
333
334     cb.save_complete.callback = sm_save_complete;
335     cb.save_complete.client_data = NULL;
336
337     cb.shutdown_cancelled.callback = sm_shutdown_cancelled;
338     cb.shutdown_cancelled.client_data = NULL;
339
340     ob_sm_conn = SmcOpenConnection(NULL, NULL, 1, 0,
341                                    SmcSaveYourselfProcMask |
342                                    SmcDieProcMask |
343                                    SmcSaveCompleteProcMask |
344                                    SmcShutdownCancelledProcMask,
345                                    &cb, ob_sm_id, &ob_sm_id,
346                                    SM_ERR_LEN, sm_err);
347     if (ob_sm_conn == NULL)
348         g_warning("Failed to connect to session manager: %s", sm_err);
349     else {
350         SmPropValue val_prog;
351         SmPropValue val_uid;
352         SmPropValue val_hint; 
353         SmPropValue val_pri;
354         SmPropValue val_pid;
355         SmProp prop_cmd = { SmCloneCommand, SmLISTofARRAY8, 1, };
356         SmProp prop_res = { SmRestartCommand, SmLISTofARRAY8, };
357         SmProp prop_prog = { SmProgram, SmARRAY8, 1, };
358         SmProp prop_uid = { SmUserID, SmARRAY8, 1, };
359         SmProp prop_hint = { SmRestartStyleHint, SmCARD8, 1, };
360         SmProp prop_pid = { SmProcessID, SmARRAY8, 1, };
361         SmProp prop_pri = { "_GSM_Priority", SmCARD8, 1, };
362         SmProp *props[7];
363         gulong hint, pri;
364         gchar pid[32];
365         gint i, j;
366         gboolean has_id;
367
368         for (i = 1; i < argc - 1; ++i)
369             if (strcmp(argv[i], "--sm-client-id") == 0)
370                 break;
371         has_id = (i < argc - 1);
372
373         prop_cmd.vals = g_new(SmPropValue, (has_id ? argc-2 : argc));
374         prop_cmd.num_vals = (has_id ? argc-2 : argc);
375         for (i = 0, j = 0; i < argc; ++i, ++j) {
376             if (strcmp (argv[i], "--sm-client-id") == 0) {
377                 ++i, --j; /* skip the next as well, keep j where it is */
378             } else {
379                 prop_cmd.vals[j].value = argv[i];
380                 prop_cmd.vals[j].length = strlen(argv[i]);
381             }
382         }
383
384         prop_res.vals = g_new(SmPropValue, (has_id ? argc : argc+2));
385         prop_res.num_vals = (has_id ? argc : argc+2);
386         for (i = 0, j = 0; i < argc; ++i, ++j) { 
387             if (strcmp (argv[i], "--sm-client-id") == 0) {
388                 ++i, --j; /* skip the next as well, keep j where it is */
389             } else {
390                 prop_res.vals[j].value = argv[i];
391                 prop_res.vals[j].length = strlen(argv[i]);
392             }
393         }
394         prop_res.vals[j].value = "--sm-client-id";
395         prop_res.vals[j++].length = strlen("--sm-client-id");
396         prop_res.vals[j].value = ob_sm_id;
397         prop_res.vals[j++].length = strlen(ob_sm_id);
398
399         val_prog.value = argv[0];
400         val_prog.length = strlen(argv[0]);
401
402         val_uid.value = g_strdup(g_get_user_name());
403         val_uid.length = strlen(val_uid.value);
404
405         hint = SmRestartImmediately;
406         val_hint.value = &hint;
407         val_hint.length = 1;
408
409         sprintf(pid, "%ld", (long)getpid());
410         val_pid.value = pid;
411         val_pid.length = strlen(pid);
412
413         /* priority with gnome-session-manager, low to run before other apps */
414         pri = 20;
415         val_pri.value = &pri;
416         val_pri.length = 1;
417
418         prop_prog.vals = &val_prog;
419         prop_uid.vals = &val_uid;
420         prop_hint.vals = &val_hint;
421         prop_pid.vals = &val_pid;
422         prop_pri.vals = &val_pri;
423
424         props[0] = &prop_prog;
425         props[1] = &prop_cmd;
426         props[2] = &prop_res;
427         props[3] = &prop_uid;
428         props[4] = &prop_hint;
429         props[5] = &prop_pid;
430         props[6] = &prop_pri;
431
432         SmcSetProperties(ob_sm_conn, 7, props);
433
434         g_free(val_uid.value);
435         g_free(prop_cmd.vals);
436         g_free(prop_res.vals);
437
438         ob_debug("Connected to session manager with id %s\n", ob_sm_id);
439     }
440     g_free (ob_sm_id);
441 #endif
442 }
443
444 static void sm_shutdown()
445 {
446 #ifdef USE_SM
447     if (ob_sm_conn) {
448         SmPropValue val_hint;
449         SmProp prop_hint = { SmRestartStyleHint, SmCARD8, 1, };
450         SmProp *props[1];
451         gulong hint;
452
453         /* when we exit, we want to reset this to a more friendly state */
454         hint = SmRestartIfRunning;
455         val_hint.value = &hint;
456         val_hint.length = 1;
457
458         prop_hint.vals = &val_hint;
459
460         props[0] = &prop_hint;
461
462         SmcSetProperties(ob_sm_conn, 1, props);
463
464         SmcCloseConnection(ob_sm_conn, 0, NULL);
465     }
466 #endif
467 }
468
469 static void signal_handler(const ObEvent *e, void *data)
470 {
471     int s;
472
473     s = e->data.s.signal;
474     switch (s) {
475     case SIGUSR1:
476         fprintf(stderr, "Caught SIGUSR1 signal. Restarting.");
477         ob_restart();
478         break;
479
480     case SIGHUP:
481     case SIGINT:
482     case SIGTERM:
483     case SIGPIPE:
484         fprintf(stderr, "Caught signal %d. Exiting.", s);
485         ob_exit();
486         break;
487
488     case SIGFPE:
489     case SIGSEGV:
490         fprintf(stderr, "Caught signal %d. Aborting and dumping core.", s);
491         abort();
492     }
493 }
494
495 static void print_version()
496 {
497     g_print("Openbox %s\n\n", PACKAGE_VERSION);
498     g_print("This program comes with ABSOLUTELY NO WARRANTY.\n");
499     g_print("This is free software, and you are welcome to redistribute it\n");
500     g_print("under certain conditions. See the file COPYING for details.\n\n");
501 }
502
503 static void print_help()
504 {
505     print_version();
506     g_print("Syntax: openbox [options]\n\n");
507     g_print("Options:\n\n");
508 #ifdef USE_SM
509     g_print("  --sm-client-id ID  Specify session management ID\n");
510     g_print("  --sm-disable       Disable connection to session manager\n");
511 #endif
512     g_print("  --replace          Replace the currently running window "
513             "manager\n");
514     g_print("  --help             Display this help and exit\n");
515     g_print("  --version          Display the version and exit\n");
516     g_print("  --sync             Run in synchronous mode (this is slow and\n"
517             "                     meant for debugging X routines)\n");
518     g_print("  --debug            Display debugging output\n");
519     g_print("\nPlease report bugs at %s\n", PACKAGE_BUGREPORT);
520 }
521
522 static void parse_args(int argc, char **argv)
523 {
524     int i;
525
526     for (i = 1; i < argc; ++i) {
527         if (!strcmp(argv[i], "--version")) {
528             print_version();
529             exit(0);
530         } else if (!strcmp(argv[i], "--help")) {
531             print_help();
532             exit(0);
533         } else if (!strcmp(argv[i], "--g-fatal-warnings")) {
534             g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL);
535         } else if (!strcmp(argv[i], "--replace")) {
536             ob_replace_wm = TRUE;
537         } else if (!strcmp(argv[i], "--sync")) {
538             xsync = TRUE;
539         } else if (!strcmp(argv[i], "--debug")) {
540             ob_debug_show_output(TRUE);
541 #ifdef USE_SM
542         } else if (!strcmp(argv[i], "--sm-client-id")) {
543             if (i == argc - 1) /* no args left */
544                 g_printerr(_("--sm-client-id requires an argument\n"));
545             else
546                 ob_sm_id = argv[++i];
547         } else if (!strcmp(argv[i], "--sm-disable")) {
548             ob_sm_use = FALSE;
549 #endif
550         } else {
551             g_printerr("Invalid option: '%s'\n\n", argv[i]);
552             print_help();
553             exit(1);
554         }
555     }
556 }
557
558 #ifdef USE_SM
559 static void sm_save_yourself(SmcConn conn, SmPointer data, int save_type,
560                              Bool shutdown, int interact_style, Bool fast)
561 {
562     ob_debug("got SAVE YOURSELF from session manager\n");
563     SmcSaveYourselfDone(conn, TRUE);
564 }
565
566 static void sm_die(SmcConn conn, SmPointer data)
567 {
568     ob_exit();
569     ob_debug("got DIE from session manager\n");
570 }
571
572 static void sm_save_complete(SmcConn conn, SmPointer data)
573 {
574     ob_debug("got SAVE COMPLETE from session manager\n");
575 }
576
577 static void sm_shutdown_cancelled(SmcConn conn, SmPointer data)
578 {
579     ob_debug("got SHUTDOWN CANCELLED from session manager\n");
580 }
581 #endif
582
583 static void exit_with_error(gchar *msg)
584 {
585     g_critical(msg);
586     sm_shutdown();
587     exit(EXIT_FAILURE);
588 }
589
590 void ob_restart_other(const gchar *path)
591 {
592     restart_path = g_strdup(path);
593     ob_restart();
594 }
595
596 void ob_restart()
597 {
598     restart = TRUE;
599     ob_exit();
600 }
601
602 void ob_exit()
603 {
604     shutdown = TRUE;
605 }
606
607 Cursor ob_cursor(ObCursor cursor)
608 {
609     g_assert(cursor < OB_NUM_CURSORS);
610     return cursors[cursor];
611 }
612
613 KeyCode ob_keycode(ObKey key)
614 {
615     g_assert(key < OB_NUM_KEYS);
616     return keys[key];
617 }
618
619 ObState ob_state()
620 {
621     return state;
622 }