2 * ion/ioncore/ioncore.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * Ion is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
18 #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 "../version.h"
69 static const char *progname="ion";
71 static const char ioncore_copy[]=
72 "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2007.";
74 static const char ioncore_license[]=DUMMY_TR(
75 "This program is free software; you can redistribute it and/or\n"
76 "modify it under the terms of the GNU Lesser General Public\n"
77 "License as published by the Free Software Foundation; either\n"
78 "version 2.1 of the License, or (at your option) any later version.\n"
80 "This program is distributed in the hope that it will be useful,\n"
81 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
82 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
83 "Lesser General Public License for more details.\n");
85 static const char *ioncore_about=NULL;
87 WHook *ioncore_post_layout_setup_hook=NULL;
88 WHook *ioncore_snapshot_hook=NULL;
89 WHook *ioncore_deinit_hook=NULL;
98 void ioncore_warn_nolog(const char *str)
100 fprintf(stderr, "%s: %s\n", prog_execname(), str);
113 static bool check_encoding()
118 const char *langi, *ctype, *a, *b;
119 bool enc_check_ok=FALSE;
121 langi=nl_langinfo(CODESET);
122 ctype=setlocale(LC_CTYPE, NULL);
124 if(langi==NULL || ctype==NULL)
127 if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
130 /* Compare encodings case-insensitively, ignoring dashes (-) */
132 b=strchr(ctype, '.');
144 if(*b=='\0' || *b=='@'){
145 enc_check_ok=(*a=='\0');
148 if(*a=='\0' || tolower(*a)!=tolower(*b))
156 ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
159 if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
160 ioncore_g.enc_sb=FALSE;
161 ioncore_g.enc_utf8=TRUE;
162 ioncore_g.use_mb=TRUE;
166 for(i=0; i<256; i++){
168 if(mbtowc(&wc, chs, 8)==-1){
169 /* Doesn't look like a single-byte encoding. */
176 /* Seems like a single-byte encoding... */
177 ioncore_g.use_mb=TRUE;
181 if(mbtowc(NULL, NULL, 0)!=0){
182 warn("Statefull encodings are unsupported.");
186 ioncore_g.enc_sb=FALSE;
187 ioncore_g.use_mb=TRUE;
192 warn("Cannot verify locale encoding setting integrity "
193 "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
194 "The LC_CTYPE environment variable should be of the form "
195 "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
196 "should match the nl_langinfo value above.", ctype, langi);
201 static bool init_locale()
205 p=setlocale(LC_ALL, "");
208 warn("setlocale() call failed.");
212 /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
215 if(!XSupportsLocale()){
216 warn("XSupportsLocale() failed.");
222 warn("Reverting locale settings to \"C\".");
224 if(setlocale(LC_ALL, "C")==NULL)
225 warn("setlocale() call failed.");
230 #define TEXTDOMAIN "ion3"
232 static bool init_messages(const char *localedir)
234 if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
235 warn_err_obj("bindtextdomain");
237 }else if(textdomain(TEXTDOMAIN)==NULL){
238 warn_err_obj("textdomain");
251 /*{{{ ioncore_init */
254 #define INIT_HOOK_(NM) \
255 NM=mainloop_register_hook(#NM, create_hook()); \
256 if(NM==NULL) return FALSE;
258 #define INIT_HOOK(NM, DFLT) \
260 if(!hook_add(NM, (void (*)())DFLT)) return FALSE;
263 static bool init_hooks()
265 INIT_HOOK_(ioncore_post_layout_setup_hook);
266 INIT_HOOK_(ioncore_snapshot_hook);
267 INIT_HOOK_(ioncore_deinit_hook);
268 INIT_HOOK_(screen_managed_changed_hook);
269 INIT_HOOK_(frame_managed_changed_hook);
270 INIT_HOOK_(clientwin_mapped_hook);
271 INIT_HOOK_(clientwin_unmapped_hook);
272 INIT_HOOK_(clientwin_property_change_hook);
274 INIT_HOOK(region_notify_hook, ioncore_frame_quasiactivation_notify);
275 INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
276 INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
277 INIT_HOOK(region_do_warp_alt, region_do_warp_default);
279 mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
281 mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
288 static bool register_classes()
292 fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin),
293 (WRegionLoadCreateFn*)clientwin_load);
294 fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex),
295 (WRegionLoadCreateFn*)mplex_load);
296 fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame),
297 (WRegionLoadCreateFn*)frame_load);
298 fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin),
299 (WRegionLoadCreateFn*)infowin_load);
300 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW),
301 (WRegionLoadCreateFn*)groupcw_load);
302 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS),
303 (WRegionLoadCreateFn*)groupws_load);
309 #define INITSTR(NM) \
310 ioncore_g.notifies.NM=stringstore_alloc(#NM); \
311 if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
313 static bool init_global()
315 /* argc, argv must be set be the program */
317 ioncore_g.display=NULL;
319 ioncore_g.sm_client_id=NULL;
320 ioncore_g.rootwins=NULL;
321 ioncore_g.screens=NULL;
322 ioncore_g.focus_next=NULL;
323 ioncore_g.warp_next=FALSE;
325 ioncore_g.focus_current=NULL;
327 ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
328 ioncore_g.opmode=IONCORE_OPMODE_INIT;
329 ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
330 ioncore_g.opaque_resize=0;
331 ioncore_g.warp_enabled=TRUE;
332 ioncore_g.switchto_new=TRUE;
334 ioncore_g.enc_utf8=FALSE;
335 ioncore_g.enc_sb=TRUE;
336 ioncore_g.use_mb=FALSE;
338 ioncore_g.screen_notify=TRUE;
340 ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
342 ioncore_g.framed_transients=TRUE;
345 INITSTR(inactivated);
347 INITSTR(sub_activity);
349 INITSTR(unset_manager);
350 INITSTR(set_manager);
351 INITSTR(unset_return);
353 INITSTR(pseudoactivated);
354 INITSTR(pseudoinactivated);
362 bool ioncore_init(const char *prog, int argc, char *argv[],
363 const char *localedir)
374 init_messages(localedir);
377 ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
379 if(!ioncore_init_bindmaps())
382 if(!register_classes())
388 if(!ioncore_init_module_support())
398 /*{{{ ioncore_startup */
401 static void ioncore_init_session(const char *display)
403 const char *dpyend=NULL;
404 char *tmp=NULL, *colon=NULL;
405 const char *sm=getenv("SESSION_MANAGER");
408 ioncore_load_module("mod_sm");
410 if(extl_sessiondir()!=NULL)
413 /* Not running under SM; use display-specific directory */
414 dpyend=strchr(display, ':');
416 dpyend=strchr(dpyend, '.');
418 libtu_asprintf(&tmp, "default-session-%s", display);
420 libtu_asprintf(&tmp, "default-session-%.*s",
421 (int)(dpyend-display), display);
429 colon=strchr(colon, ':');
435 extl_set_sessiondir(tmp);
440 static bool ioncore_init_x(const char *display, int stflags)
444 static bool called=FALSE;
446 /* Sorry, this function can not be re-entered due to laziness
447 * towards implementing checking of things already initialized.
448 * Nobody would call this twice anyway.
453 /* Open the display. */
454 dpy=XOpenDisplay(display);
457 warn(TR("Could not connect to X display '%s'"),
458 XDisplayName(display));
462 if(stflags&IONCORE_STARTUP_ONEROOT){
463 drw=DefaultScreen(dpy);
467 nrw=ScreenCount(dpy);
472 ioncore_g.display=scopy(display);
473 if(ioncore_g.display==NULL){
480 ioncore_g.win_context=XUniqueContext();
481 ioncore_g.conn=ConnectionNumber(dpy);
483 cloexec_braindamage_fix(ioncore_g.conn);
485 ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
486 ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
487 ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
488 ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
489 ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
490 ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
491 ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
492 ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
493 ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
494 ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
497 ioncore_init_bindings();
498 ioncore_init_cursors();
502 ioncore_init_session(XDisplayName(display));
504 for(i=drw; i<nrw; i++)
507 if(ioncore_g.rootwins==NULL){
509 warn(TR("Could not find a screen to manage."));
513 if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
514 ioncore_x_connection_handler)){
522 static void set_initial_focus()
524 Window root=None, win=None;
530 XQueryPointer(ioncore_g.dpy, None, &root, &win,
531 &x, &y, &wx, &wy, &mask);
533 FOR_ALL_SCREENS(scr){
534 Window scrroot=region_root_of((WRegion*)scr);
535 if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){
541 scr=ioncore_g.screens;
543 region_focuslist_push((WRegion*)scr);
544 region_do_set_focus((WRegion*)scr, FALSE);
548 bool ioncore_startup(const char *display, const char *cfgfile,
554 /* Don't trap termination signals just yet. */
555 sigemptyset(&inittrap);
556 sigaddset(&inittrap, SIGALRM);
557 sigaddset(&inittrap, SIGCHLD);
558 sigaddset(&inittrap, SIGPIPE);
559 sigaddset(&inittrap, SIGUSR2);
560 mainloop_trap_signals(&inittrap);
565 ioncore_register_exports();
567 if(!ioncore_init_x(display, stflags))
572 if(!extl_read_config("ioncore_ext", NULL, TRUE))
575 ioncore_read_main_config(cfgfile);
577 if(!ioncore_init_layout())
580 hook_call_v(ioncore_post_layout_setup_hook);
582 FOR_ALL_ROOTWINS(rootwin)
583 rootwin_manage_initial_windows(rootwin);
594 /*{{{ ioncore_deinit */
597 void ioncore_deinit()
602 ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
604 if(ioncore_g.dpy==NULL)
607 hook_call_v(ioncore_deinit_hook);
609 while(ioncore_g.screens!=NULL)
610 destroy_obj((Obj*)ioncore_g.screens);
612 /*ioncore_unload_modules();*/
614 while(ioncore_g.rootwins!=NULL)
615 destroy_obj((Obj*)ioncore_g.rootwins);
617 ioncore_deinit_bindmaps();
619 mainloop_unregister_input_fd(ioncore_g.conn);
634 /*{{{ Miscellaneous */
638 * Is Ion supporting locale-specifically multibyte-encoded strings?
642 bool ioncore_is_i18n()
644 return ioncore_g.use_mb;
649 * Returns Ioncore version string.
653 const char *ioncore_version()
659 * Returns the name of program using Ioncore.
663 const char *ioncore_progname()
670 * Returns an about message (version, author, copyright notice).
674 const char *ioncore_aboutmsg()
676 return ioncore_about;