2 * ion/ioncore/ioncore.c
4 * Copyright (c) Tuomo Valkonen 1999-2006.
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-2006.";
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_(region_activated_hook);
271 INIT_HOOK_(region_inactivated_hook);
272 INIT_HOOK_(clientwin_mapped_hook);
273 INIT_HOOK_(clientwin_unmapped_hook);
274 INIT_HOOK_(clientwin_property_change_hook);
275 INIT_HOOK_(region_notify_hook);
277 INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
278 INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
279 INIT_HOOK(region_do_warp_alt, region_do_warp_default);
281 mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
283 mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
290 static bool register_classes()
294 fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin),
295 (WRegionLoadCreateFn*)clientwin_load);
296 fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex),
297 (WRegionLoadCreateFn*)mplex_load);
298 fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame),
299 (WRegionLoadCreateFn*)frame_load);
300 fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin),
301 (WRegionLoadCreateFn*)infowin_load);
302 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW),
303 (WRegionLoadCreateFn*)groupcw_load);
304 fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS),
305 (WRegionLoadCreateFn*)groupws_load);
311 static void init_global()
313 /* argc, argv must be set be the program */
315 ioncore_g.display=NULL;
317 ioncore_g.sm_client_id=NULL;
318 ioncore_g.rootwins=NULL;
319 ioncore_g.screens=NULL;
320 ioncore_g.focus_next=NULL;
321 ioncore_g.warp_next=FALSE;
323 ioncore_g.focus_current=NULL;
325 ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
326 ioncore_g.opmode=IONCORE_OPMODE_INIT;
327 ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
328 ioncore_g.opaque_resize=0;
329 ioncore_g.warp_enabled=TRUE;
330 ioncore_g.switchto_new=TRUE;
332 ioncore_g.enc_utf8=FALSE;
333 ioncore_g.enc_sb=TRUE;
334 ioncore_g.use_mb=FALSE;
336 ioncore_g.screen_notify=TRUE;
338 ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
340 ioncore_g.framed_transients=TRUE;
344 bool ioncore_init(const char *prog, int argc, char *argv[],
345 const char *localedir)
355 init_messages(localedir);
358 ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
360 if(!ioncore_init_bindmaps())
363 if(!register_classes())
369 if(!ioncore_init_module_support())
379 /*{{{ ioncore_startup */
382 static void ioncore_init_session(const char *display)
384 const char *dpyend=NULL;
385 char *tmp=NULL, *colon=NULL;
386 const char *sm=getenv("SESSION_MANAGER");
389 ioncore_load_module("mod_sm");
391 if(extl_sessiondir()!=NULL)
394 /* Not running under SM; use display-specific directory */
395 dpyend=strchr(display, ':');
397 dpyend=strchr(dpyend, '.');
399 libtu_asprintf(&tmp, "default-session-%s", display);
401 libtu_asprintf(&tmp, "default-session-%.*s",
402 (int)(dpyend-display), display);
410 colon=strchr(colon, ':');
416 extl_set_sessiondir(tmp);
421 static bool ioncore_init_x(const char *display, int stflags)
425 static bool called=FALSE;
427 /* Sorry, this function can not be re-entered due to laziness
428 * towards implementing checking of things already initialized.
429 * Nobody would call this twice anyway.
434 /* Open the display. */
435 dpy=XOpenDisplay(display);
438 warn(TR("Could not connect to X display '%s'"),
439 XDisplayName(display));
443 if(stflags&IONCORE_STARTUP_ONEROOT){
444 drw=DefaultScreen(dpy);
448 nrw=ScreenCount(dpy);
453 ioncore_g.display=scopy(display);
454 if(ioncore_g.display==NULL){
461 ioncore_g.win_context=XUniqueContext();
462 ioncore_g.conn=ConnectionNumber(dpy);
464 cloexec_braindamage_fix(ioncore_g.conn);
466 ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
467 ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
468 ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
469 ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
470 ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
471 ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
472 ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
473 ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
474 ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
475 ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
478 ioncore_init_bindings();
479 ioncore_init_cursors();
483 ioncore_init_session(XDisplayName(display));
485 for(i=drw; i<nrw; i++)
486 ioncore_manage_rootwin(i, stflags&IONCORE_STARTUP_NOXINERAMA);
488 if(ioncore_g.rootwins==NULL){
490 warn(TR("Could not find a screen to manage."));
494 if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
495 ioncore_x_connection_handler)){
503 static void set_initial_focus()
505 Window root=None, win=None;
511 XQueryPointer(ioncore_g.dpy, None, &root, &win,
512 &x, &y, &wx, &wy, &mask);
514 FOR_ALL_SCREENS(scr){
515 Window scrroot=region_root_of((WRegion*)scr);
516 if(scrroot==root && rectangle_contains(®ION_GEOM(scr), x, y)){
522 scr=ioncore_g.screens;
524 region_focuslist_push((WRegion*)scr);
525 region_do_set_focus((WRegion*)scr, FALSE);
529 bool ioncore_startup(const char *display, const char *cfgfile,
535 /* Don't trap termination signals just yet. */
536 sigemptyset(&inittrap);
537 sigaddset(&inittrap, SIGALRM);
538 sigaddset(&inittrap, SIGCHLD);
539 sigaddset(&inittrap, SIGPIPE);
540 sigaddset(&inittrap, SIGUSR2);
541 mainloop_trap_signals(&inittrap);
546 ioncore_register_exports();
548 if(!ioncore_init_x(display, stflags))
553 if(!extl_read_config("ioncore_ext", NULL, TRUE))
556 ioncore_read_main_config(cfgfile);
558 if(!ioncore_init_layout())
561 hook_call_v(ioncore_post_layout_setup_hook);
563 FOR_ALL_ROOTWINS(rootwin)
564 rootwin_manage_initial_windows(rootwin);
575 /*{{{ ioncore_deinit */
578 void ioncore_deinit()
583 ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
585 if(ioncore_g.dpy==NULL)
588 hook_call_v(ioncore_deinit_hook);
590 while(ioncore_g.screens!=NULL)
591 destroy_obj((Obj*)ioncore_g.screens);
593 /*ioncore_unload_modules();*/
595 while(ioncore_g.rootwins!=NULL)
596 destroy_obj((Obj*)ioncore_g.rootwins);
598 ioncore_deinit_bindmaps();
600 mainloop_unregister_input_fd(ioncore_g.conn);
615 /*{{{ Miscellaneous */
619 * Is Ion supporting locale-specifically multibyte-encoded strings?
623 bool ioncore_is_i18n()
625 return ioncore_g.use_mb;
630 * Returns Ioncore version string.
634 const char *ioncore_version()
640 * Returns the name of program using Ioncore.
644 const char *ioncore_progname()
651 * Returns an about message (version, author, copyright notice).
655 const char *ioncore_aboutmsg()
657 return ioncore_about;