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>
24 #include <libtu/util.h>
25 #include <libtu/optparser.h>
26 #include <libextl/readconfig.h>
27 #include <libextl/extl.h>
28 #include <libmainloop/select.h>
29 #include <libmainloop/signal.h>
30 #include <libmainloop/hooks.h>
31 #include <libmainloop/exec.h>
58 #include "screen-notify.h"
62 #include "../version.h"
71 static const char *progname="ion";
73 static const char ioncore_copy[]=
74 "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2007.";
76 static const char ioncore_license[]=DUMMY_TR(
77 "This program is free software; you can redistribute it and/or\n"
78 "modify it under the terms of the GNU Lesser General Public\n"
79 "License as published by the Free Software Foundation; either\n"
80 "version 2.1 of the License, or (at your option) any later version.\n"
82 "This program is distributed in the hope that it will be useful,\n"
83 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
84 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
85 "Lesser General Public License for more details.\n");
87 static const char *ioncore_about=NULL;
89 WHook *ioncore_post_layout_setup_hook=NULL;
90 WHook *ioncore_snapshot_hook=NULL;
91 WHook *ioncore_deinit_hook=NULL;
100 void ioncore_warn_nolog(const char *str)
102 fprintf(stderr, "%s: %s\n", libtu_progname(), str);
115 static bool check_encoding()
120 const char *langi, *ctype, *a, *b;
121 bool enc_check_ok=FALSE;
123 langi=nl_langinfo(CODESET);
124 ctype=setlocale(LC_CTYPE, NULL);
126 if(langi==NULL || ctype==NULL)
129 if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
132 /* Compare encodings case-insensitively, ignoring dashes (-) */
134 b=strchr(ctype, '.');
146 if(*b=='\0' || *b=='@'){
147 enc_check_ok=(*a=='\0');
150 if(*a=='\0' || tolower(*a)!=tolower(*b))
158 ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
161 if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
162 ioncore_g.enc_sb=FALSE;
163 ioncore_g.enc_utf8=TRUE;
164 ioncore_g.use_mb=TRUE;
168 for(i=0; i<256; i++){
170 if(mbtowc(&wc, chs, 8)==-1){
171 /* Doesn't look like a single-byte encoding. */
178 /* Seems like a single-byte encoding... */
179 ioncore_g.use_mb=TRUE;
183 if(mbtowc(NULL, NULL, 0)!=0){
184 warn("Statefull encodings are unsupported.");
188 ioncore_g.enc_sb=FALSE;
189 ioncore_g.use_mb=TRUE;
194 warn("Cannot verify locale encoding setting integrity "
195 "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
196 "The LC_CTYPE environment variable should be of the form "
197 "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
198 "should match the nl_langinfo value above.", ctype, langi);
203 static bool init_locale()
207 p=setlocale(LC_ALL, "");
210 warn("setlocale() call failed.");
214 /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
217 if(!XSupportsLocale()){
218 warn("XSupportsLocale() failed.");
224 warn("Reverting locale settings to \"C\".");
226 if(setlocale(LC_ALL, "C")==NULL)
227 warn("setlocale() call failed.");
232 #define TEXTDOMAIN "ion3"
234 static bool init_messages(const char *localedir)
236 if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
237 warn_err_obj("bindtextdomain");
239 }else if(textdomain(TEXTDOMAIN)==NULL){
240 warn_err_obj("textdomain");
253 /*{{{ ioncore_init */
256 #define INIT_HOOK_(NM) \
257 NM=mainloop_register_hook(#NM, create_hook()); \
258 if(NM==NULL) return FALSE
260 #define ADD_HOOK_(NM, FN) \
261 if(!hook_add(NM, (void (*)())FN)) return FALSE
263 #define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT)
265 static bool init_hooks()
267 INIT_HOOK_(ioncore_post_layout_setup_hook);
268 INIT_HOOK_(ioncore_snapshot_hook);
269 INIT_HOOK_(ioncore_deinit_hook);
270 INIT_HOOK_(screen_managed_changed_hook);
271 INIT_HOOK_(frame_managed_changed_hook);
272 INIT_HOOK_(clientwin_mapped_hook);
273 INIT_HOOK_(clientwin_unmapped_hook);
274 INIT_HOOK_(clientwin_property_change_hook);
275 INIT_HOOK_(ioncore_submap_ungrab_hook);
277 INIT_HOOK_(region_notify_hook);
278 ADD_HOOK_(region_notify_hook, ioncore_frame_quasiactivation_notify);
279 ADD_HOOK_(region_notify_hook, ioncore_screen_activity_notify);
281 INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
282 INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
283 INIT_HOOK(region_do_warp_alt, region_do_warp_default);
284 INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ);
286 mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
288 mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
295 static bool register_classes()
299 fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin),
300 (WRegionLoadCreateFn*)clientwin_load);
301 fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex),
302 (WRegionLoadCreateFn*)mplex_load);
303 fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame),
304 (WRegionLoadCreateFn*)frame_load);
305 fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin),
306 (WRegionLoadCreateFn*)infowin_load);
307 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW),
308 (WRegionLoadCreateFn*)groupcw_load);
309 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS),
310 (WRegionLoadCreateFn*)groupws_load);
316 #define INITSTR(NM) \
317 ioncore_g.notifies.NM=stringstore_alloc(#NM); \
318 if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
320 static bool init_global()
322 /* argc, argv must be set be the program */
324 ioncore_g.display=NULL;
326 ioncore_g.sm_client_id=NULL;
327 ioncore_g.rootwins=NULL;
328 ioncore_g.screens=NULL;
329 ioncore_g.focus_next=NULL;
330 ioncore_g.warp_next=FALSE;
331 ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
333 ioncore_g.focus_current=NULL;
335 ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
336 ioncore_g.opmode=IONCORE_OPMODE_INIT;
337 ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
338 ioncore_g.opaque_resize=0;
339 ioncore_g.warp_enabled=TRUE;
340 ioncore_g.switchto_new=TRUE;
341 ioncore_g.no_mousefocus=FALSE;
342 ioncore_g.unsqueeze_enabled=TRUE;
343 ioncore_g.autoraise=TRUE;
345 ioncore_g.enc_utf8=FALSE;
346 ioncore_g.enc_sb=TRUE;
347 ioncore_g.use_mb=FALSE;
349 ioncore_g.screen_notify=TRUE;
351 ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
353 ioncore_g.framed_transients=TRUE;
356 INITSTR(inactivated);
358 INITSTR(sub_activity);
360 INITSTR(unset_manager);
361 INITSTR(set_manager);
362 INITSTR(unset_return);
364 INITSTR(pseudoactivated);
365 INITSTR(pseudoinactivated);
375 bool ioncore_init(const char *prog, int argc, char *argv[],
376 const char *localedir)
387 init_messages(localedir);
390 ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
392 if(!ioncore_init_bindmaps())
395 if(!register_classes())
401 if(!ioncore_init_module_support())
411 /*{{{ ioncore_startup */
414 static void ioncore_init_session(const char *display)
416 const char *dpyend=NULL;
417 char *tmp=NULL, *colon=NULL;
418 const char *sm=getenv("SESSION_MANAGER");
421 ioncore_load_module("mod_sm");
423 if(extl_sessiondir()!=NULL)
426 /* Not running under SM; use display-specific directory */
427 dpyend=strchr(display, ':');
429 dpyend=strchr(dpyend, '.');
431 libtu_asprintf(&tmp, "default-session-%s", display);
433 libtu_asprintf(&tmp, "default-session-%.*s",
434 (int)(dpyend-display), display);
442 colon=strchr(colon, ':');
448 extl_set_sessiondir(tmp);
453 static bool ioncore_init_x(const char *display, int stflags)
457 static bool called=FALSE;
459 /* Sorry, this function can not be re-entered due to laziness
460 * towards implementing checking of things already initialized.
461 * Nobody would call this twice anyway.
466 /* Open the display. */
467 dpy=XOpenDisplay(display);
470 warn(TR("Could not connect to X display '%s'"),
471 XDisplayName(display));
475 if(stflags&IONCORE_STARTUP_ONEROOT){
476 drw=DefaultScreen(dpy);
480 nrw=ScreenCount(dpy);
485 ioncore_g.display=scopy(display);
486 if(ioncore_g.display==NULL){
493 ioncore_g.win_context=XUniqueContext();
494 ioncore_g.conn=ConnectionNumber(dpy);
496 cloexec_braindamage_fix(ioncore_g.conn);
498 ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
499 ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
500 ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
501 ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
502 ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
503 ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
504 ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
505 ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
506 ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
507 ioncore_g.atom_dockapp_hack=XInternAtom(dpy, "_ION_DOCKAPP_HACK", False);
508 ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
511 ioncore_init_bindings();
512 ioncore_init_cursors();
516 ioncore_init_session(XDisplayName(display));
518 for(i=drw; i<nrw; i++)
521 if(ioncore_g.rootwins==NULL){
523 warn(TR("Could not find a screen to manage."));
527 if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
528 ioncore_x_connection_handler)){
536 static void set_initial_focus()
538 Window root=None, win=None;
544 XQueryPointer(ioncore_g.dpy, None, &root, &win,
545 &x, &y, &wx, &wy, &mask);
547 FOR_ALL_SCREENS(scr){
548 Window scrroot=region_root_of((WRegion*)scr);
549 if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){
555 scr=ioncore_g.screens;
557 region_focuslist_push((WRegion*)scr);
558 region_do_set_focus((WRegion*)scr, FALSE);
562 bool ioncore_startup(const char *display, const char *cfgfile,
568 /* Don't trap termination signals just yet. */
569 sigemptyset(&inittrap);
570 sigaddset(&inittrap, SIGALRM);
571 sigaddset(&inittrap, SIGCHLD);
572 sigaddset(&inittrap, SIGPIPE);
573 sigaddset(&inittrap, SIGUSR2);
574 mainloop_trap_signals(&inittrap);
579 ioncore_register_exports();
581 if(!ioncore_init_x(display, stflags))
586 if(!extl_read_config("ioncore_ext", NULL, TRUE))
589 ioncore_read_main_config(cfgfile);
591 if(!ioncore_init_layout())
594 hook_call_v(ioncore_post_layout_setup_hook);
596 FOR_ALL_ROOTWINS(rootwin)
597 rootwin_manage_initial_windows(rootwin);
608 /*{{{ ioncore_deinit */
611 void ioncore_deinit()
616 ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
618 if(ioncore_g.dpy==NULL)
621 hook_call_v(ioncore_deinit_hook);
623 while(ioncore_g.screens!=NULL)
624 destroy_obj((Obj*)ioncore_g.screens);
626 /*ioncore_unload_modules();*/
628 while(ioncore_g.rootwins!=NULL)
629 destroy_obj((Obj*)ioncore_g.rootwins);
631 ioncore_deinit_bindmaps();
633 mainloop_unregister_input_fd(ioncore_g.conn);
648 /*{{{ Miscellaneous */
652 * Is Ion supporting locale-specifically multibyte-encoded strings?
656 bool ioncore_is_i18n()
658 return ioncore_g.use_mb;
663 * Returns Ioncore version string.
667 const char *ioncore_version()
673 * Returns the name of program using Ioncore.
677 const char *ioncore_progname()
684 * Returns an about message (version, author, copyright notice).
688 const char *ioncore_aboutmsg()
690 return ioncore_about;