2 * ion/ioncore/ioncore.c
4 * Copyright (c) Tuomo Valkonen 1999-2008.
6 * See the included file LICENSE for details.
16 #include <sys/types.h>
27 #include <libtu/util.h>
28 #include <libtu/optparser.h>
29 #include <libextl/readconfig.h>
30 #include <libextl/extl.h>
31 #include <libmainloop/select.h>
32 #include <libmainloop/signal.h>
33 #include <libmainloop/hooks.h>
34 #include <libmainloop/exec.h>
61 #include "screen-notify.h"
65 #include "../version.h"
74 static const char *progname="ion";
76 static const char ioncore_copy[]=
77 "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2008.";
79 static const char ioncore_license[]=DUMMY_TR(
80 "This software is licensed under the GNU Lesser General Public License\n"
81 "(LGPL), version 2.1, extended with terms applying to the use of the name\n"
82 "of the project, Ion(tm), unless otherwise indicated in components taken\n"
83 "from elsewhere. For details, see the file LICENSE that you should have\n"
84 "received with this software.\n"
86 "This program is distributed in the hope that it will be useful,\n"
87 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
88 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
90 static const char *ioncore_about=NULL;
92 WHook *ioncore_post_layout_setup_hook=NULL;
93 WHook *ioncore_snapshot_hook=NULL;
94 WHook *ioncore_deinit_hook=NULL;
103 void ioncore_warn_nolog(const char *str, ...)
108 fprintf(stderr, "%s: ", libtu_progname());
109 vfprintf(stderr, str, args);
110 fprintf(stderr, "\n");
123 static bool check_encoding()
128 const char *langi, *ctype, *a, *b;
129 bool enc_check_ok=FALSE;
131 langi=nl_langinfo(CODESET);
132 ctype=setlocale(LC_CTYPE, NULL);
134 if(langi==NULL || ctype==NULL)
137 if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
140 /* Compare encodings case-insensitively, ignoring dashes (-) */
142 b=strchr(ctype, '.');
154 if(*b=='\0' || *b=='@'){
155 enc_check_ok=(*a=='\0');
158 if(*a=='\0' || tolower(*a)!=tolower(*b))
166 ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
169 if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
170 ioncore_g.enc_sb=FALSE;
171 ioncore_g.enc_utf8=TRUE;
172 ioncore_g.use_mb=TRUE;
176 for(i=0; i<256; i++){
178 if(mbtowc(&wc, chs, 8)==-1){
179 /* Doesn't look like a single-byte encoding. */
186 /* Seems like a single-byte encoding... */
187 ioncore_g.use_mb=TRUE;
191 if(mbtowc(NULL, NULL, 0)!=0){
192 warn("Statefull encodings are unsupported.");
196 ioncore_g.enc_sb=FALSE;
197 ioncore_g.use_mb=TRUE;
202 warn("Cannot verify locale encoding setting integrity "
203 "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
204 "The LC_CTYPE environment variable should be of the form "
205 "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
206 "should match the nl_langinfo value above.", ctype, langi);
211 static bool init_locale()
215 p=setlocale(LC_ALL, "");
218 warn("setlocale() call failed.");
222 /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
225 if(!XSupportsLocale()){
226 warn("XSupportsLocale() failed.");
232 warn("Reverting locale settings to \"C\".");
234 if(setlocale(LC_ALL, "C")==NULL)
235 warn("setlocale() call failed.");
242 #ifndef CF_NO_GETTEXT
244 #define TEXTDOMAIN "ion3"
246 static bool init_messages(const char *localedir)
248 if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
249 warn_err_obj("bindtextdomain");
251 }else if(textdomain(TEXTDOMAIN)==NULL){
252 warn_err_obj("textdomain");
265 /*{{{ ioncore_init */
268 #define INIT_HOOK_(NM) \
269 NM=mainloop_register_hook(#NM, create_hook()); \
270 if(NM==NULL) return FALSE
272 #define ADD_HOOK_(NM, FN) \
273 if(!hook_add(NM, (void (*)())FN)) return FALSE
275 #define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT)
277 static bool init_hooks()
279 INIT_HOOK_(ioncore_post_layout_setup_hook);
280 INIT_HOOK_(ioncore_snapshot_hook);
281 INIT_HOOK_(ioncore_deinit_hook);
282 INIT_HOOK_(screen_managed_changed_hook);
283 INIT_HOOK_(frame_managed_changed_hook);
284 INIT_HOOK_(clientwin_mapped_hook);
285 INIT_HOOK_(clientwin_unmapped_hook);
286 INIT_HOOK_(clientwin_property_change_hook);
287 INIT_HOOK_(ioncore_submap_ungrab_hook);
289 INIT_HOOK_(region_notify_hook);
290 ADD_HOOK_(region_notify_hook, ioncore_screen_activity_notify);
292 INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
293 INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
294 INIT_HOOK(region_do_warp_alt, region_do_warp_default);
295 INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ);
297 mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
299 mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
306 static bool register_classes()
310 fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin),
311 (WRegionLoadCreateFn*)clientwin_load);
312 fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex),
313 (WRegionLoadCreateFn*)mplex_load);
314 fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame),
315 (WRegionLoadCreateFn*)frame_load);
316 fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin),
317 (WRegionLoadCreateFn*)infowin_load);
318 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW),
319 (WRegionLoadCreateFn*)groupcw_load);
320 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS),
321 (WRegionLoadCreateFn*)groupws_load);
327 #define INITSTR(NM) \
328 ioncore_g.notifies.NM=stringstore_alloc(#NM); \
329 if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
331 static bool init_global()
333 /* argc, argv must be set be the program */
335 ioncore_g.display=NULL;
337 ioncore_g.sm_client_id=NULL;
338 ioncore_g.rootwins=NULL;
339 ioncore_g.screens=NULL;
340 ioncore_g.focus_next=NULL;
341 ioncore_g.warp_next=FALSE;
342 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
344 ioncore_g.focus_current=NULL;
346 ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
347 ioncore_g.opmode=IONCORE_OPMODE_INIT;
348 ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
349 ioncore_g.opaque_resize=0;
350 ioncore_g.warp_enabled=TRUE;
351 ioncore_g.switchto_new=TRUE;
352 ioncore_g.no_mousefocus=FALSE;
353 ioncore_g.unsqueeze_enabled=TRUE;
354 ioncore_g.autoraise=TRUE;
356 ioncore_g.enc_utf8=FALSE;
357 ioncore_g.enc_sb=TRUE;
358 ioncore_g.use_mb=FALSE;
360 ioncore_g.screen_notify=TRUE;
362 ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
364 ioncore_g.framed_transients=TRUE;
367 INITSTR(inactivated);
369 INITSTR(sub_activity);
371 INITSTR(unset_manager);
372 INITSTR(set_manager);
373 INITSTR(unset_return);
375 INITSTR(pseudoactivated);
376 INITSTR(pseudoinactivated);
386 bool ioncore_init(const char *prog, int argc, char *argv[],
387 const char *localedir)
399 #ifndef CF_NO_GETTEXT
400 init_messages(localedir);
403 ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
405 if(!ioncore_init_bindmaps())
408 if(!register_classes())
414 if(!ioncore_init_module_support())
424 /*{{{ ioncore_startup */
427 static void ioncore_init_session(const char *display)
429 const char *dpyend=NULL;
430 char *tmp=NULL, *colon=NULL;
431 const char *sm=getenv("SESSION_MANAGER");
434 ioncore_load_module("mod_sm");
436 if(extl_sessiondir()!=NULL)
439 /* Not running under SM; use display-specific directory */
440 dpyend=strchr(display, ':');
442 dpyend=strchr(dpyend, '.');
444 libtu_asprintf(&tmp, "default-session-%s", display);
446 libtu_asprintf(&tmp, "default-session-%.*s",
447 (int)(dpyend-display), display);
455 colon=strchr(colon, ':');
461 extl_set_sessiondir(tmp);
466 static bool ioncore_init_x(const char *display, int stflags)
470 static bool called=FALSE;
472 /* Sorry, this function can not be re-entered due to laziness
473 * towards implementing checking of things already initialized.
474 * Nobody would call this twice anyway.
479 /* Open the display. */
480 dpy=XOpenDisplay(display);
483 warn(TR("Could not connect to X display '%s'"),
484 XDisplayName(display));
488 if(stflags&IONCORE_STARTUP_ONEROOT){
489 drw=DefaultScreen(dpy);
493 nrw=ScreenCount(dpy);
498 ioncore_g.display=scopy(display);
499 if(ioncore_g.display==NULL){
506 ioncore_g.win_context=XUniqueContext();
507 ioncore_g.conn=ConnectionNumber(dpy);
509 cloexec_braindamage_fix(ioncore_g.conn);
511 ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
512 ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
513 ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
514 ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
515 ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
516 ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
517 ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
518 ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
519 ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
520 ioncore_g.atom_dockapp_hack=XInternAtom(dpy, "_ION_DOCKAPP_HACK", False);
521 ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
524 ioncore_init_bindings();
525 ioncore_init_cursors();
529 ioncore_init_session(XDisplayName(display));
531 for(i=drw; i<nrw; i++)
534 if(ioncore_g.rootwins==NULL){
536 warn(TR("Could not find a screen to manage."));
540 if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
541 ioncore_x_connection_handler)){
549 static void set_initial_focus()
551 Window root=None, win=None;
557 XQueryPointer(ioncore_g.dpy, None, &root, &win,
558 &x, &y, &wx, &wy, &mask);
560 FOR_ALL_SCREENS(scr){
561 Window scrroot=region_root_of((WRegion*)scr);
562 if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){
568 scr=ioncore_g.screens;
570 region_focuslist_push((WRegion*)scr);
571 region_do_set_focus((WRegion*)scr, FALSE);
575 bool ioncore_startup(const char *display, const char *cfgfile,
581 /* Don't trap termination signals just yet. */
582 sigemptyset(&inittrap);
583 sigaddset(&inittrap, SIGALRM);
584 sigaddset(&inittrap, SIGCHLD);
585 sigaddset(&inittrap, SIGPIPE);
586 sigaddset(&inittrap, SIGUSR2);
587 mainloop_trap_signals(&inittrap);
592 ioncore_register_exports();
594 if(!ioncore_init_x(display, stflags))
599 if(!extl_read_config("ioncore_ext", NULL, TRUE))
602 ioncore_read_main_config(cfgfile);
604 if(!ioncore_init_layout())
607 hook_call_v(ioncore_post_layout_setup_hook);
609 FOR_ALL_ROOTWINS(rootwin)
610 rootwin_manage_initial_windows(rootwin);
621 /*{{{ ioncore_deinit */
624 void ioncore_deinit()
629 ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
631 if(ioncore_g.dpy==NULL)
634 hook_call_v(ioncore_deinit_hook);
636 while(ioncore_g.screens!=NULL)
637 destroy_obj((Obj*)ioncore_g.screens);
639 /*ioncore_unload_modules();*/
641 while(ioncore_g.rootwins!=NULL)
642 destroy_obj((Obj*)ioncore_g.rootwins);
644 ioncore_deinit_bindmaps();
646 mainloop_unregister_input_fd(ioncore_g.conn);
661 /*{{{ Miscellaneous */
665 * Is Ion supporting locale-specifically multibyte-encoded strings?
669 bool ioncore_is_i18n()
671 return ioncore_g.use_mb;
676 * Returns Ioncore version string.
680 const char *ioncore_version()
686 * Returns the name of program using Ioncore.
690 const char *ioncore_progname()
697 * Returns an about message (version, author, copyright notice).
701 const char *ioncore_aboutmsg()
703 return ioncore_about;