2 * ion/ioncore/ioncore.c
4 * Copyright (c) Tuomo Valkonen 1999-2007.
6 * See the included file LICENSE for details.
15 #include <sys/types.h>
23 #include <libtu/util.h>
24 #include <libtu/optparser.h>
25 #include <libextl/readconfig.h>
26 #include <libextl/extl.h>
27 #include <libmainloop/select.h>
28 #include <libmainloop/signal.h>
29 #include <libmainloop/hooks.h>
30 #include <libmainloop/exec.h>
57 #include "screen-notify.h"
61 #include "../version.h"
70 static const char *progname="ion";
72 static const char ioncore_copy[]=
73 "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2007.";
75 static const char ioncore_license[]=DUMMY_TR(
76 "This program is free software; you can redistribute it and/or\n"
77 "modify it under the terms of the GNU Lesser General Public\n"
78 "License as published by the Free Software Foundation; either\n"
79 "version 2.1 of the License, or (at your option) any later version.\n"
81 "This program is distributed in the hope that it will be useful,\n"
82 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
83 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
84 "Lesser General Public License for more details.\n");
86 static const char *ioncore_about=NULL;
88 WHook *ioncore_post_layout_setup_hook=NULL;
89 WHook *ioncore_snapshot_hook=NULL;
90 WHook *ioncore_deinit_hook=NULL;
99 void ioncore_warn_nolog(const char *str)
101 fprintf(stderr, "%s: %s\n", libtu_progname(), str);
114 static bool check_encoding()
119 const char *langi, *ctype, *a, *b;
120 bool enc_check_ok=FALSE;
122 langi=nl_langinfo(CODESET);
123 ctype=setlocale(LC_CTYPE, NULL);
125 if(langi==NULL || ctype==NULL)
128 if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
131 /* Compare encodings case-insensitively, ignoring dashes (-) */
133 b=strchr(ctype, '.');
145 if(*b=='\0' || *b=='@'){
146 enc_check_ok=(*a=='\0');
149 if(*a=='\0' || tolower(*a)!=tolower(*b))
157 ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
160 if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
161 ioncore_g.enc_sb=FALSE;
162 ioncore_g.enc_utf8=TRUE;
163 ioncore_g.use_mb=TRUE;
167 for(i=0; i<256; i++){
169 if(mbtowc(&wc, chs, 8)==-1){
170 /* Doesn't look like a single-byte encoding. */
177 /* Seems like a single-byte encoding... */
178 ioncore_g.use_mb=TRUE;
182 if(mbtowc(NULL, NULL, 0)!=0){
183 warn("Statefull encodings are unsupported.");
187 ioncore_g.enc_sb=FALSE;
188 ioncore_g.use_mb=TRUE;
193 warn("Cannot verify locale encoding setting integrity "
194 "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
195 "The LC_CTYPE environment variable should be of the form "
196 "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
197 "should match the nl_langinfo value above.", ctype, langi);
202 static bool init_locale()
206 p=setlocale(LC_ALL, "");
209 warn("setlocale() call failed.");
213 /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
216 if(!XSupportsLocale()){
217 warn("XSupportsLocale() failed.");
223 warn("Reverting locale settings to \"C\".");
225 if(setlocale(LC_ALL, "C")==NULL)
226 warn("setlocale() call failed.");
231 #define TEXTDOMAIN "ion3"
233 static bool init_messages(const char *localedir)
235 if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
236 warn_err_obj("bindtextdomain");
238 }else if(textdomain(TEXTDOMAIN)==NULL){
239 warn_err_obj("textdomain");
252 /*{{{ ioncore_init */
255 #define INIT_HOOK_(NM) \
256 NM=mainloop_register_hook(#NM, create_hook()); \
257 if(NM==NULL) return FALSE
259 #define ADD_HOOK_(NM, FN) \
260 if(!hook_add(NM, (void (*)())FN)) return FALSE
262 #define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT)
264 static bool init_hooks()
266 INIT_HOOK_(ioncore_post_layout_setup_hook);
267 INIT_HOOK_(ioncore_snapshot_hook);
268 INIT_HOOK_(ioncore_deinit_hook);
269 INIT_HOOK_(screen_managed_changed_hook);
270 INIT_HOOK_(frame_managed_changed_hook);
271 INIT_HOOK_(clientwin_mapped_hook);
272 INIT_HOOK_(clientwin_unmapped_hook);
273 INIT_HOOK_(clientwin_property_change_hook);
274 INIT_HOOK_(ioncore_submap_ungrab_hook);
276 INIT_HOOK_(region_notify_hook);
277 ADD_HOOK_(region_notify_hook, ioncore_frame_quasiactivation_notify);
278 ADD_HOOK_(region_notify_hook, ioncore_screen_activity_notify);
280 INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
281 INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
282 INIT_HOOK(region_do_warp_alt, region_do_warp_default);
283 INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ);
285 mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
287 mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
294 static bool register_classes()
298 fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin),
299 (WRegionLoadCreateFn*)clientwin_load);
300 fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex),
301 (WRegionLoadCreateFn*)mplex_load);
302 fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame),
303 (WRegionLoadCreateFn*)frame_load);
304 fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin),
305 (WRegionLoadCreateFn*)infowin_load);
306 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW),
307 (WRegionLoadCreateFn*)groupcw_load);
308 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS),
309 (WRegionLoadCreateFn*)groupws_load);
315 #define INITSTR(NM) \
316 ioncore_g.notifies.NM=stringstore_alloc(#NM); \
317 if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
319 static bool init_global()
321 /* argc, argv must be set be the program */
323 ioncore_g.display=NULL;
325 ioncore_g.sm_client_id=NULL;
326 ioncore_g.rootwins=NULL;
327 ioncore_g.screens=NULL;
328 ioncore_g.focus_next=NULL;
329 ioncore_g.warp_next=FALSE;
330 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
332 ioncore_g.focus_current=NULL;
334 ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
335 ioncore_g.opmode=IONCORE_OPMODE_INIT;
336 ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
337 ioncore_g.opaque_resize=0;
338 ioncore_g.warp_enabled=TRUE;
339 ioncore_g.switchto_new=TRUE;
340 ioncore_g.no_mousefocus=FALSE;
341 ioncore_g.unsqueeze_enabled=TRUE;
342 ioncore_g.autoraise=TRUE;
344 ioncore_g.enc_utf8=FALSE;
345 ioncore_g.enc_sb=TRUE;
346 ioncore_g.use_mb=FALSE;
348 ioncore_g.screen_notify=TRUE;
350 ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
352 ioncore_g.framed_transients=TRUE;
355 INITSTR(inactivated);
357 INITSTR(sub_activity);
359 INITSTR(unset_manager);
360 INITSTR(set_manager);
361 INITSTR(unset_return);
363 INITSTR(pseudoactivated);
364 INITSTR(pseudoinactivated);
374 bool ioncore_init(const char *prog, int argc, char *argv[],
375 const char *localedir)
386 init_messages(localedir);
389 ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
391 if(!ioncore_init_bindmaps())
394 if(!register_classes())
400 if(!ioncore_init_module_support())
410 /*{{{ ioncore_startup */
413 static void ioncore_init_session(const char *display)
415 const char *dpyend=NULL;
416 char *tmp=NULL, *colon=NULL;
417 const char *sm=getenv("SESSION_MANAGER");
420 ioncore_load_module("mod_sm");
422 if(extl_sessiondir()!=NULL)
425 /* Not running under SM; use display-specific directory */
426 dpyend=strchr(display, ':');
428 dpyend=strchr(dpyend, '.');
430 libtu_asprintf(&tmp, "default-session-%s", display);
432 libtu_asprintf(&tmp, "default-session-%.*s",
433 (int)(dpyend-display), display);
441 colon=strchr(colon, ':');
447 extl_set_sessiondir(tmp);
452 static bool ioncore_init_x(const char *display, int stflags)
456 static bool called=FALSE;
458 /* Sorry, this function can not be re-entered due to laziness
459 * towards implementing checking of things already initialized.
460 * Nobody would call this twice anyway.
465 /* Open the display. */
466 dpy=XOpenDisplay(display);
469 warn(TR("Could not connect to X display '%s'"),
470 XDisplayName(display));
474 if(stflags&IONCORE_STARTUP_ONEROOT){
475 drw=DefaultScreen(dpy);
479 nrw=ScreenCount(dpy);
484 ioncore_g.display=scopy(display);
485 if(ioncore_g.display==NULL){
492 ioncore_g.win_context=XUniqueContext();
493 ioncore_g.conn=ConnectionNumber(dpy);
495 cloexec_braindamage_fix(ioncore_g.conn);
497 ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
498 ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
499 ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
500 ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
501 ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
502 ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
503 ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
504 ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
505 ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
506 ioncore_g.atom_dockapp_hack=XInternAtom(dpy, "_ION_DOCKAPP_HACK", False);
507 ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
510 ioncore_init_bindings();
511 ioncore_init_cursors();
515 ioncore_init_session(XDisplayName(display));
517 for(i=drw; i<nrw; i++)
520 if(ioncore_g.rootwins==NULL){
522 warn(TR("Could not find a screen to manage."));
526 if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
527 ioncore_x_connection_handler)){
535 static void set_initial_focus()
537 Window root=None, win=None;
543 XQueryPointer(ioncore_g.dpy, None, &root, &win,
544 &x, &y, &wx, &wy, &mask);
546 FOR_ALL_SCREENS(scr){
547 Window scrroot=region_root_of((WRegion*)scr);
548 if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){
554 scr=ioncore_g.screens;
556 region_focuslist_push((WRegion*)scr);
557 region_do_set_focus((WRegion*)scr, FALSE);
561 bool ioncore_startup(const char *display, const char *cfgfile,
567 /* Don't trap termination signals just yet. */
568 sigemptyset(&inittrap);
569 sigaddset(&inittrap, SIGALRM);
570 sigaddset(&inittrap, SIGCHLD);
571 sigaddset(&inittrap, SIGPIPE);
572 sigaddset(&inittrap, SIGUSR2);
573 mainloop_trap_signals(&inittrap);
578 ioncore_register_exports();
580 if(!ioncore_init_x(display, stflags))
585 if(!extl_read_config("ioncore_ext", NULL, TRUE))
588 ioncore_read_main_config(cfgfile);
590 if(!ioncore_init_layout())
593 hook_call_v(ioncore_post_layout_setup_hook);
595 FOR_ALL_ROOTWINS(rootwin)
596 rootwin_manage_initial_windows(rootwin);
607 /*{{{ ioncore_deinit */
610 void ioncore_deinit()
615 ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
617 if(ioncore_g.dpy==NULL)
620 hook_call_v(ioncore_deinit_hook);
622 while(ioncore_g.screens!=NULL)
623 destroy_obj((Obj*)ioncore_g.screens);
625 /*ioncore_unload_modules();*/
627 while(ioncore_g.rootwins!=NULL)
628 destroy_obj((Obj*)ioncore_g.rootwins);
630 ioncore_deinit_bindmaps();
632 mainloop_unregister_input_fd(ioncore_g.conn);
647 /*{{{ Miscellaneous */
651 * Is Ion supporting locale-specifically multibyte-encoded strings?
655 bool ioncore_is_i18n()
657 return ioncore_g.use_mb;
662 * Returns Ioncore version string.
666 const char *ioncore_version()
672 * Returns the name of program using Ioncore.
676 const char *ioncore_progname()
683 * Returns an about message (version, author, copyright notice).
687 const char *ioncore_aboutmsg()
689 return ioncore_about;