2 * ion/ioncore/ioncore.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
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-2009.";
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, ...)
109 if(ioncore_g.opmode==IONCORE_OPMODE_INIT){
110 fprintf(stderr, "%s: ", libtu_progname());
111 vfprintf(stderr, str, args);
112 fprintf(stderr, "\n");
129 static bool check_encoding()
134 const char *langi, *ctype, *a, *b;
135 bool enc_check_ok=FALSE;
137 langi=nl_langinfo(CODESET);
138 ctype=setlocale(LC_CTYPE, NULL);
140 if(langi==NULL || ctype==NULL)
143 if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
146 /* Compare encodings case-insensitively, ignoring dashes (-) */
148 b=strchr(ctype, '.');
160 if(*b=='\0' || *b=='@'){
161 enc_check_ok=(*a=='\0');
164 if(*a=='\0' || tolower(*a)!=tolower(*b))
172 ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
175 if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
176 ioncore_g.enc_sb=FALSE;
177 ioncore_g.enc_utf8=TRUE;
178 ioncore_g.use_mb=TRUE;
182 for(i=0; i<256; i++){
184 if(mbtowc(&wc, chs, 8)==-1){
185 /* Doesn't look like a single-byte encoding. */
192 /* Seems like a single-byte encoding... */
193 ioncore_g.use_mb=TRUE;
197 if(mbtowc(NULL, NULL, 0)!=0){
198 warn(TR("Statefull encodings are unsupported."));
202 ioncore_g.enc_sb=FALSE;
203 ioncore_g.use_mb=TRUE;
208 warn(TR("Cannot verify locale encoding setting integrity "
209 "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
210 "The LC_CTYPE environment variable should be of the form "
211 "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
212 "should match the nl_langinfo value above."), ctype, langi);
217 static bool init_locale()
221 p=setlocale(LC_ALL, "");
224 warn("setlocale() call failed.");
228 /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
231 if(!XSupportsLocale()){
232 warn("XSupportsLocale() failed.");
238 warn("Reverting locale settings to \"C\".");
240 if(setlocale(LC_ALL, "C")==NULL)
241 warn("setlocale() call failed.");
248 #ifndef CF_NO_GETTEXT
250 #define TEXTDOMAIN "ion3"
252 static bool init_messages(const char *localedir)
254 if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
255 warn_err_obj("bindtextdomain");
257 }else if(textdomain(TEXTDOMAIN)==NULL){
258 warn_err_obj("textdomain");
271 /*{{{ ioncore_init */
274 #define INIT_HOOK_(NM) \
275 NM=mainloop_register_hook(#NM, create_hook()); \
276 if(NM==NULL) return FALSE
278 #define ADD_HOOK_(NM, FN) \
279 if(!hook_add(NM, (void (*)())FN)) return FALSE
281 #define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT)
283 static bool init_hooks()
285 INIT_HOOK_(ioncore_post_layout_setup_hook);
286 INIT_HOOK_(ioncore_snapshot_hook);
287 INIT_HOOK_(ioncore_deinit_hook);
288 INIT_HOOK_(screen_managed_changed_hook);
289 INIT_HOOK_(frame_managed_changed_hook);
290 INIT_HOOK_(clientwin_mapped_hook);
291 INIT_HOOK_(clientwin_unmapped_hook);
292 INIT_HOOK_(clientwin_property_change_hook);
293 INIT_HOOK_(ioncore_submap_ungrab_hook);
295 INIT_HOOK_(region_notify_hook);
296 ADD_HOOK_(region_notify_hook, ioncore_screen_activity_notify);
298 INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
299 INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
300 INIT_HOOK(region_do_warp_alt, region_do_warp_default);
301 INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ);
303 mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
305 mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
312 static bool register_classes()
316 fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin),
317 (WRegionLoadCreateFn*)clientwin_load);
318 fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex),
319 (WRegionLoadCreateFn*)mplex_load);
320 fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame),
321 (WRegionLoadCreateFn*)frame_load);
322 fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin),
323 (WRegionLoadCreateFn*)infowin_load);
324 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW),
325 (WRegionLoadCreateFn*)groupcw_load);
326 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS),
327 (WRegionLoadCreateFn*)groupws_load);
333 #define INITSTR(NM) \
334 ioncore_g.notifies.NM=stringstore_alloc(#NM); \
335 if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
337 static bool init_global()
339 /* argc, argv must be set be the program */
341 ioncore_g.display=NULL;
343 ioncore_g.sm_client_id=NULL;
344 ioncore_g.rootwins=NULL;
345 ioncore_g.screens=NULL;
346 ioncore_g.focus_next=NULL;
347 ioncore_g.warp_next=FALSE;
348 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
350 ioncore_g.focus_current=NULL;
352 ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
353 ioncore_g.opmode=IONCORE_OPMODE_INIT;
354 ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
355 ioncore_g.opaque_resize=0;
356 ioncore_g.warp_enabled=TRUE;
357 ioncore_g.switchto_new=TRUE;
358 ioncore_g.no_mousefocus=FALSE;
359 ioncore_g.unsqueeze_enabled=TRUE;
360 ioncore_g.autoraise=TRUE;
362 ioncore_g.enc_utf8=FALSE;
363 ioncore_g.enc_sb=TRUE;
364 ioncore_g.use_mb=FALSE;
366 ioncore_g.screen_notify=TRUE;
368 ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
370 ioncore_g.framed_transients=TRUE;
373 INITSTR(inactivated);
375 INITSTR(sub_activity);
377 INITSTR(unset_manager);
378 INITSTR(set_manager);
379 INITSTR(unset_return);
381 INITSTR(pseudoactivated);
382 INITSTR(pseudoinactivated);
392 bool ioncore_init(const char *prog, int argc, char *argv[],
393 const char *localedir)
405 #ifndef CF_NO_GETTEXT
406 init_messages(localedir);
409 ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
411 if(!ioncore_init_bindmaps())
414 if(!register_classes())
420 if(!ioncore_init_module_support())
430 /*{{{ ioncore_startup */
433 static void ioncore_init_session(const char *display)
435 const char *dpyend=NULL;
436 char *tmp=NULL, *colon=NULL;
437 const char *sm=getenv("SESSION_MANAGER");
440 ioncore_load_module("mod_sm");
442 if(extl_sessiondir()!=NULL)
445 /* Not running under SM; use display-specific directory */
446 dpyend=strchr(display, ':');
448 dpyend=strchr(dpyend, '.');
450 libtu_asprintf(&tmp, "default-session-%s", display);
452 libtu_asprintf(&tmp, "default-session-%.*s",
453 (int)(dpyend-display), display);
461 colon=strchr(colon, ':');
467 extl_set_sessiondir(tmp);
472 static bool ioncore_init_x(const char *display, int stflags)
476 static bool called=FALSE;
478 /* Sorry, this function can not be re-entered due to laziness
479 * towards implementing checking of things already initialized.
480 * Nobody would call this twice anyway.
485 /* Open the display. */
486 dpy=XOpenDisplay(display);
489 warn(TR("Could not connect to X display '%s'"),
490 XDisplayName(display));
494 if(stflags&IONCORE_STARTUP_ONEROOT){
495 drw=DefaultScreen(dpy);
499 nrw=ScreenCount(dpy);
504 ioncore_g.display=scopy(display);
505 if(ioncore_g.display==NULL){
512 ioncore_g.win_context=XUniqueContext();
513 ioncore_g.conn=ConnectionNumber(dpy);
515 cloexec_braindamage_fix(ioncore_g.conn);
517 ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
518 ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
519 ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
520 ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
521 ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
522 ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
523 ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
524 ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
525 ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
526 ioncore_g.atom_dockapp_hack=XInternAtom(dpy, "_ION_DOCKAPP_HACK", False);
527 ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
530 ioncore_init_bindings();
531 ioncore_init_cursors();
535 ioncore_init_session(XDisplayName(display));
537 for(i=drw; i<nrw; i++)
540 if(ioncore_g.rootwins==NULL){
542 warn(TR("Could not find a screen to manage."));
546 if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
547 ioncore_x_connection_handler)){
555 static void set_initial_focus()
557 Window root=None, win=None;
563 XQueryPointer(ioncore_g.dpy, None, &root, &win,
564 &x, &y, &wx, &wy, &mask);
566 FOR_ALL_SCREENS(scr){
567 Window scrroot=region_root_of((WRegion*)scr);
568 if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){
574 scr=ioncore_g.screens;
576 region_focuslist_push((WRegion*)scr);
577 region_do_set_focus((WRegion*)scr, FALSE);
581 bool ioncore_startup(const char *display, const char *cfgfile,
587 /* Don't trap termination signals just yet. */
588 sigemptyset(&inittrap);
589 sigaddset(&inittrap, SIGALRM);
590 sigaddset(&inittrap, SIGCHLD);
591 sigaddset(&inittrap, SIGPIPE);
592 sigaddset(&inittrap, SIGUSR2);
593 mainloop_trap_signals(&inittrap);
598 ioncore_register_exports();
600 if(!ioncore_init_x(display, stflags))
605 if(!extl_read_config("ioncore_ext", NULL, TRUE))
608 ioncore_read_main_config(cfgfile);
610 if(!ioncore_init_layout())
613 hook_call_v(ioncore_post_layout_setup_hook);
615 FOR_ALL_ROOTWINS(rootwin)
616 rootwin_manage_initial_windows(rootwin);
627 /*{{{ ioncore_deinit */
630 void ioncore_deinit()
635 ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
637 if(ioncore_g.dpy==NULL)
640 hook_call_v(ioncore_deinit_hook);
642 while(ioncore_g.screens!=NULL)
643 destroy_obj((Obj*)ioncore_g.screens);
645 /*ioncore_unload_modules();*/
647 while(ioncore_g.rootwins!=NULL)
648 destroy_obj((Obj*)ioncore_g.rootwins);
650 ioncore_deinit_bindmaps();
652 mainloop_unregister_input_fd(ioncore_g.conn);
667 /*{{{ Miscellaneous */
671 * Is Ion supporting locale-specifically multibyte-encoded strings?
675 bool ioncore_is_i18n()
677 return ioncore_g.use_mb;
682 * Returns Ioncore version string.
686 const char *ioncore_version()
692 * Returns the name of program using Ioncore.
696 const char *ioncore_progname()
703 * Returns an about message (version, author, copyright notice).
707 const char *ioncore_aboutmsg()
709 return ioncore_about;