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>
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-2008.";
78 static const char ioncore_license[]=DUMMY_TR(
79 "This software is licensed under the GNU Lesser General Public License\n"
80 "(LGPL), version 2.1, extended with terms applying to the use of the name\n"
81 "of the project, Ion(tm), unless otherwise indicated in components taken\n"
82 "from elsewhere. For details, see the file LICENSE that you should have\n"
83 "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_screen_activity_notify);
286 INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
287 INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
288 INIT_HOOK(region_do_warp_alt, region_do_warp_default);
289 INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ);
291 mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
293 mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
300 static bool register_classes()
304 fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin),
305 (WRegionLoadCreateFn*)clientwin_load);
306 fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex),
307 (WRegionLoadCreateFn*)mplex_load);
308 fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame),
309 (WRegionLoadCreateFn*)frame_load);
310 fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin),
311 (WRegionLoadCreateFn*)infowin_load);
312 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW),
313 (WRegionLoadCreateFn*)groupcw_load);
314 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS),
315 (WRegionLoadCreateFn*)groupws_load);
321 #define INITSTR(NM) \
322 ioncore_g.notifies.NM=stringstore_alloc(#NM); \
323 if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
325 static bool init_global()
327 /* argc, argv must be set be the program */
329 ioncore_g.display=NULL;
331 ioncore_g.sm_client_id=NULL;
332 ioncore_g.rootwins=NULL;
333 ioncore_g.screens=NULL;
334 ioncore_g.focus_next=NULL;
335 ioncore_g.warp_next=FALSE;
336 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
338 ioncore_g.focus_current=NULL;
340 ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
341 ioncore_g.opmode=IONCORE_OPMODE_INIT;
342 ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
343 ioncore_g.opaque_resize=0;
344 ioncore_g.warp_enabled=TRUE;
345 ioncore_g.switchto_new=TRUE;
346 ioncore_g.no_mousefocus=FALSE;
347 ioncore_g.unsqueeze_enabled=TRUE;
348 ioncore_g.autoraise=TRUE;
350 ioncore_g.enc_utf8=FALSE;
351 ioncore_g.enc_sb=TRUE;
352 ioncore_g.use_mb=FALSE;
354 ioncore_g.screen_notify=TRUE;
356 ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
358 ioncore_g.framed_transients=TRUE;
361 INITSTR(inactivated);
363 INITSTR(sub_activity);
365 INITSTR(unset_manager);
366 INITSTR(set_manager);
367 INITSTR(unset_return);
369 INITSTR(pseudoactivated);
370 INITSTR(pseudoinactivated);
380 bool ioncore_init(const char *prog, int argc, char *argv[],
381 const char *localedir)
393 #ifndef CF_NO_GETTEXT
394 init_messages(localedir);
397 ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
399 if(!ioncore_init_bindmaps())
402 if(!register_classes())
408 if(!ioncore_init_module_support())
418 /*{{{ ioncore_startup */
421 static void ioncore_init_session(const char *display)
423 const char *dpyend=NULL;
424 char *tmp=NULL, *colon=NULL;
425 const char *sm=getenv("SESSION_MANAGER");
428 ioncore_load_module("mod_sm");
430 if(extl_sessiondir()!=NULL)
433 /* Not running under SM; use display-specific directory */
434 dpyend=strchr(display, ':');
436 dpyend=strchr(dpyend, '.');
438 libtu_asprintf(&tmp, "default-session-%s", display);
440 libtu_asprintf(&tmp, "default-session-%.*s",
441 (int)(dpyend-display), display);
449 colon=strchr(colon, ':');
455 extl_set_sessiondir(tmp);
460 static bool ioncore_init_x(const char *display, int stflags)
464 static bool called=FALSE;
466 /* Sorry, this function can not be re-entered due to laziness
467 * towards implementing checking of things already initialized.
468 * Nobody would call this twice anyway.
473 /* Open the display. */
474 dpy=XOpenDisplay(display);
477 warn(TR("Could not connect to X display '%s'"),
478 XDisplayName(display));
482 if(stflags&IONCORE_STARTUP_ONEROOT){
483 drw=DefaultScreen(dpy);
487 nrw=ScreenCount(dpy);
492 ioncore_g.display=scopy(display);
493 if(ioncore_g.display==NULL){
500 ioncore_g.win_context=XUniqueContext();
501 ioncore_g.conn=ConnectionNumber(dpy);
503 cloexec_braindamage_fix(ioncore_g.conn);
505 ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
506 ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
507 ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
508 ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
509 ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
510 ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
511 ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
512 ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
513 ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
514 ioncore_g.atom_dockapp_hack=XInternAtom(dpy, "_ION_DOCKAPP_HACK", False);
515 ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
518 ioncore_init_bindings();
519 ioncore_init_cursors();
523 ioncore_init_session(XDisplayName(display));
525 for(i=drw; i<nrw; i++)
528 if(ioncore_g.rootwins==NULL){
530 warn(TR("Could not find a screen to manage."));
534 if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
535 ioncore_x_connection_handler)){
543 static void set_initial_focus()
545 Window root=None, win=None;
551 XQueryPointer(ioncore_g.dpy, None, &root, &win,
552 &x, &y, &wx, &wy, &mask);
554 FOR_ALL_SCREENS(scr){
555 Window scrroot=region_root_of((WRegion*)scr);
556 if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){
562 scr=ioncore_g.screens;
564 region_focuslist_push((WRegion*)scr);
565 region_do_set_focus((WRegion*)scr, FALSE);
569 bool ioncore_startup(const char *display, const char *cfgfile,
575 /* Don't trap termination signals just yet. */
576 sigemptyset(&inittrap);
577 sigaddset(&inittrap, SIGALRM);
578 sigaddset(&inittrap, SIGCHLD);
579 sigaddset(&inittrap, SIGPIPE);
580 sigaddset(&inittrap, SIGUSR2);
581 mainloop_trap_signals(&inittrap);
586 ioncore_register_exports();
588 if(!ioncore_init_x(display, stflags))
593 if(!extl_read_config("ioncore_ext", NULL, TRUE))
596 ioncore_read_main_config(cfgfile);
598 if(!ioncore_init_layout())
601 hook_call_v(ioncore_post_layout_setup_hook);
603 FOR_ALL_ROOTWINS(rootwin)
604 rootwin_manage_initial_windows(rootwin);
615 /*{{{ ioncore_deinit */
618 void ioncore_deinit()
623 ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
625 if(ioncore_g.dpy==NULL)
628 hook_call_v(ioncore_deinit_hook);
630 while(ioncore_g.screens!=NULL)
631 destroy_obj((Obj*)ioncore_g.screens);
633 /*ioncore_unload_modules();*/
635 while(ioncore_g.rootwins!=NULL)
636 destroy_obj((Obj*)ioncore_g.rootwins);
638 ioncore_deinit_bindmaps();
640 mainloop_unregister_input_fd(ioncore_g.conn);
655 /*{{{ Miscellaneous */
659 * Is Ion supporting locale-specifically multibyte-encoded strings?
663 bool ioncore_is_i18n()
665 return ioncore_g.use_mb;
670 * Returns Ioncore version string.
674 const char *ioncore_version()
680 * Returns the name of program using Ioncore.
684 const char *ioncore_progname()
691 * Returns an about message (version, author, copyright notice).
695 const char *ioncore_aboutmsg()
697 return ioncore_about;