2 * ion/ioncore/ioncore.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * See the included file LICENSE for details.
16 #include <sys/types.h>
26 #include <libtu/util.h>
27 #include <libtu/optparser.h>
28 #include <libextl/readconfig.h>
29 #include <libextl/extl.h>
30 #include <libmainloop/select.h>
31 #include <libmainloop/signal.h>
32 #include <libmainloop/hooks.h>
33 #include <libmainloop/exec.h>
60 #include "screen-notify.h"
64 #include "../version.h"
73 static const char *progname="ion";
75 static const char ioncore_copy[]=
76 "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2007.";
78 static const char ioncore_license[]=DUMMY_TR(
79 "This software is essentially licensed under the GNU Lesser General\n"
80 "Public License (LGPL), version 2.1, unless otherwise indicated in\n"
81 "components taken from elsewhere. Additional terms apply to the use\n"
82 "of the name of the project, Ion(tm). For details, see the file\n"
83 "LICENSE that you should have received with this software.\n"
85 "This program is distributed in the hope that it will be useful,\n"
86 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
87 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
89 static const char *ioncore_about=NULL;
91 WHook *ioncore_post_layout_setup_hook=NULL;
92 WHook *ioncore_snapshot_hook=NULL;
93 WHook *ioncore_deinit_hook=NULL;
102 void ioncore_warn_nolog(const char *str)
104 fprintf(stderr, "%s: %s\n", libtu_progname(), str);
117 static bool check_encoding()
122 const char *langi, *ctype, *a, *b;
123 bool enc_check_ok=FALSE;
125 langi=nl_langinfo(CODESET);
126 ctype=setlocale(LC_CTYPE, NULL);
128 if(langi==NULL || ctype==NULL)
131 if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
134 /* Compare encodings case-insensitively, ignoring dashes (-) */
136 b=strchr(ctype, '.');
148 if(*b=='\0' || *b=='@'){
149 enc_check_ok=(*a=='\0');
152 if(*a=='\0' || tolower(*a)!=tolower(*b))
160 ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
163 if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
164 ioncore_g.enc_sb=FALSE;
165 ioncore_g.enc_utf8=TRUE;
166 ioncore_g.use_mb=TRUE;
170 for(i=0; i<256; i++){
172 if(mbtowc(&wc, chs, 8)==-1){
173 /* Doesn't look like a single-byte encoding. */
180 /* Seems like a single-byte encoding... */
181 ioncore_g.use_mb=TRUE;
185 if(mbtowc(NULL, NULL, 0)!=0){
186 warn("Statefull encodings are unsupported.");
190 ioncore_g.enc_sb=FALSE;
191 ioncore_g.use_mb=TRUE;
196 warn("Cannot verify locale encoding setting integrity "
197 "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
198 "The LC_CTYPE environment variable should be of the form "
199 "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
200 "should match the nl_langinfo value above.", ctype, langi);
205 static bool init_locale()
209 p=setlocale(LC_ALL, "");
212 warn("setlocale() call failed.");
216 /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
219 if(!XSupportsLocale()){
220 warn("XSupportsLocale() failed.");
226 warn("Reverting locale settings to \"C\".");
228 if(setlocale(LC_ALL, "C")==NULL)
229 warn("setlocale() call failed.");
236 #ifndef CF_NO_GETTEXT
238 #define TEXTDOMAIN "ion3"
240 static bool init_messages(const char *localedir)
242 if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
243 warn_err_obj("bindtextdomain");
245 }else if(textdomain(TEXTDOMAIN)==NULL){
246 warn_err_obj("textdomain");
259 /*{{{ ioncore_init */
262 #define INIT_HOOK_(NM) \
263 NM=mainloop_register_hook(#NM, create_hook()); \
264 if(NM==NULL) return FALSE
266 #define ADD_HOOK_(NM, FN) \
267 if(!hook_add(NM, (void (*)())FN)) return FALSE
269 #define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT)
271 static bool init_hooks()
273 INIT_HOOK_(ioncore_post_layout_setup_hook);
274 INIT_HOOK_(ioncore_snapshot_hook);
275 INIT_HOOK_(ioncore_deinit_hook);
276 INIT_HOOK_(screen_managed_changed_hook);
277 INIT_HOOK_(frame_managed_changed_hook);
278 INIT_HOOK_(clientwin_mapped_hook);
279 INIT_HOOK_(clientwin_unmapped_hook);
280 INIT_HOOK_(clientwin_property_change_hook);
281 INIT_HOOK_(ioncore_submap_ungrab_hook);
283 INIT_HOOK_(region_notify_hook);
284 ADD_HOOK_(region_notify_hook, ioncore_frame_quasiactivation_notify);
285 ADD_HOOK_(region_notify_hook, ioncore_screen_activity_notify);
287 INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
288 INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
289 INIT_HOOK(region_do_warp_alt, region_do_warp_default);
290 INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ);
292 mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
294 mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
301 static bool register_classes()
305 fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin),
306 (WRegionLoadCreateFn*)clientwin_load);
307 fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex),
308 (WRegionLoadCreateFn*)mplex_load);
309 fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame),
310 (WRegionLoadCreateFn*)frame_load);
311 fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin),
312 (WRegionLoadCreateFn*)infowin_load);
313 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW),
314 (WRegionLoadCreateFn*)groupcw_load);
315 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS),
316 (WRegionLoadCreateFn*)groupws_load);
322 #define INITSTR(NM) \
323 ioncore_g.notifies.NM=stringstore_alloc(#NM); \
324 if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
326 static bool init_global()
328 /* argc, argv must be set be the program */
330 ioncore_g.display=NULL;
332 ioncore_g.sm_client_id=NULL;
333 ioncore_g.rootwins=NULL;
334 ioncore_g.screens=NULL;
335 ioncore_g.focus_next=NULL;
336 ioncore_g.warp_next=FALSE;
337 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
339 ioncore_g.focus_current=NULL;
341 ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
342 ioncore_g.opmode=IONCORE_OPMODE_INIT;
343 ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
344 ioncore_g.opaque_resize=0;
345 ioncore_g.warp_enabled=TRUE;
346 ioncore_g.switchto_new=TRUE;
347 ioncore_g.no_mousefocus=FALSE;
348 ioncore_g.unsqueeze_enabled=TRUE;
349 ioncore_g.autoraise=TRUE;
351 ioncore_g.enc_utf8=FALSE;
352 ioncore_g.enc_sb=TRUE;
353 ioncore_g.use_mb=FALSE;
355 ioncore_g.screen_notify=TRUE;
357 ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
359 ioncore_g.framed_transients=TRUE;
362 INITSTR(inactivated);
364 INITSTR(sub_activity);
366 INITSTR(unset_manager);
367 INITSTR(set_manager);
368 INITSTR(unset_return);
370 INITSTR(pseudoactivated);
371 INITSTR(pseudoinactivated);
381 bool ioncore_init(const char *prog, int argc, char *argv[],
382 const char *localedir)
394 #ifndef CF_NO_GETTEXT
395 init_messages(localedir);
398 ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
400 if(!ioncore_init_bindmaps())
403 if(!register_classes())
409 if(!ioncore_init_module_support())
419 /*{{{ ioncore_startup */
422 static void ioncore_init_session(const char *display)
424 const char *dpyend=NULL;
425 char *tmp=NULL, *colon=NULL;
426 const char *sm=getenv("SESSION_MANAGER");
429 ioncore_load_module("mod_sm");
431 if(extl_sessiondir()!=NULL)
434 /* Not running under SM; use display-specific directory */
435 dpyend=strchr(display, ':');
437 dpyend=strchr(dpyend, '.');
439 libtu_asprintf(&tmp, "default-session-%s", display);
441 libtu_asprintf(&tmp, "default-session-%.*s",
442 (int)(dpyend-display), display);
450 colon=strchr(colon, ':');
456 extl_set_sessiondir(tmp);
461 static bool ioncore_init_x(const char *display, int stflags)
465 static bool called=FALSE;
467 /* Sorry, this function can not be re-entered due to laziness
468 * towards implementing checking of things already initialized.
469 * Nobody would call this twice anyway.
474 /* Open the display. */
475 dpy=XOpenDisplay(display);
478 warn(TR("Could not connect to X display '%s'"),
479 XDisplayName(display));
483 if(stflags&IONCORE_STARTUP_ONEROOT){
484 drw=DefaultScreen(dpy);
488 nrw=ScreenCount(dpy);
493 ioncore_g.display=scopy(display);
494 if(ioncore_g.display==NULL){
501 ioncore_g.win_context=XUniqueContext();
502 ioncore_g.conn=ConnectionNumber(dpy);
504 cloexec_braindamage_fix(ioncore_g.conn);
506 ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
507 ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
508 ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
509 ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
510 ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
511 ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
512 ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
513 ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
514 ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
515 ioncore_g.atom_dockapp_hack=XInternAtom(dpy, "_ION_DOCKAPP_HACK", False);
516 ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
519 ioncore_init_bindings();
520 ioncore_init_cursors();
524 ioncore_init_session(XDisplayName(display));
526 for(i=drw; i<nrw; i++)
529 if(ioncore_g.rootwins==NULL){
531 warn(TR("Could not find a screen to manage."));
535 if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
536 ioncore_x_connection_handler)){
544 static void set_initial_focus()
546 Window root=None, win=None;
552 XQueryPointer(ioncore_g.dpy, None, &root, &win,
553 &x, &y, &wx, &wy, &mask);
555 FOR_ALL_SCREENS(scr){
556 Window scrroot=region_root_of((WRegion*)scr);
557 if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){
563 scr=ioncore_g.screens;
565 region_focuslist_push((WRegion*)scr);
566 region_do_set_focus((WRegion*)scr, FALSE);
570 bool ioncore_startup(const char *display, const char *cfgfile,
576 /* Don't trap termination signals just yet. */
577 sigemptyset(&inittrap);
578 sigaddset(&inittrap, SIGALRM);
579 sigaddset(&inittrap, SIGCHLD);
580 sigaddset(&inittrap, SIGPIPE);
581 sigaddset(&inittrap, SIGUSR2);
582 mainloop_trap_signals(&inittrap);
587 ioncore_register_exports();
589 if(!ioncore_init_x(display, stflags))
594 if(!extl_read_config("ioncore_ext", NULL, TRUE))
597 ioncore_read_main_config(cfgfile);
599 if(!ioncore_init_layout())
602 hook_call_v(ioncore_post_layout_setup_hook);
604 FOR_ALL_ROOTWINS(rootwin)
605 rootwin_manage_initial_windows(rootwin);
616 /*{{{ ioncore_deinit */
619 void ioncore_deinit()
624 ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
626 if(ioncore_g.dpy==NULL)
629 hook_call_v(ioncore_deinit_hook);
631 while(ioncore_g.screens!=NULL)
632 destroy_obj((Obj*)ioncore_g.screens);
634 /*ioncore_unload_modules();*/
636 while(ioncore_g.rootwins!=NULL)
637 destroy_obj((Obj*)ioncore_g.rootwins);
639 ioncore_deinit_bindmaps();
641 mainloop_unregister_input_fd(ioncore_g.conn);
656 /*{{{ Miscellaneous */
660 * Is Ion supporting locale-specifically multibyte-encoded strings?
664 bool ioncore_is_i18n()
666 return ioncore_g.use_mb;
671 * Returns Ioncore version string.
675 const char *ioncore_version()
681 * Returns the name of program using Ioncore.
685 const char *ioncore_progname()
692 * Returns an about message (version, author, copyright notice).
696 const char *ioncore_aboutmsg()
698 return ioncore_about;