2 * ion/ioncore/ioncore.c
4 * Copyright (c) The Notion Team 2011.
5 * Copyright (c) Tuomo Valkonen 1999-2009.
7 * See the included file LICENSE for details.
17 #include <sys/types.h>
27 #include <X11/extensions/shape.h>
28 #include <X11/extensions/Xext.h>
30 #include <libtu/util.h>
31 #include <libtu/optparser.h>
32 #include <libextl/readconfig.h>
33 #include <libextl/extl.h>
34 #include <libmainloop/select.h>
35 #include <libmainloop/signal.h>
36 #include <libmainloop/hooks.h>
37 #include <libmainloop/exec.h>
64 #include "screen-notify.h"
69 #include "../version.h"
78 static const char *progname
="notion";
80 static const char ioncore_copy
[]=
81 "Notion " NOTION_VERSION
", see the README for copyright details.";
83 static const char ioncore_license
[]=DUMMY_TR(
84 "This software is licensed under the GNU Lesser General Public License\n"
85 "(LGPL), version 2.1, extended with terms applying to the use of the\n"
86 "former name of the project, Ion(tm), unless otherwise indicated in\n"
87 "components taken from elsewhere. For details, see the file LICENSE\n"
88 "that you should have received with this software.\n"
90 "This program is distributed in the hope that it will be useful,\n"
91 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
92 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
94 static const char *ioncore_about
=NULL
;
96 WHook
*ioncore_post_layout_setup_hook
=NULL
;
97 WHook
*ioncore_snapshot_hook
=NULL
;
98 WHook
*ioncore_deinit_hook
=NULL
;
107 void ioncore_warn_nolog(const char *str
, ...)
113 if(ioncore_g
.opmode
==IONCORE_OPMODE_INIT
){
114 fprintf(stderr
, "%s: ", libtu_progname());
115 vfprintf(stderr
, str
, args
);
116 fprintf(stderr
, "\n");
133 static bool check_encoding()
138 const char *langi
, *ctype
, *a
, *b
;
139 bool enc_check_ok
=FALSE
;
141 langi
=nl_langinfo(CODESET
);
142 ctype
=setlocale(LC_CTYPE
, NULL
);
144 if(langi
==NULL
|| ctype
==NULL
)
147 if(strcmp(ctype
, "C")==0 || strcmp(ctype
, "POSIX")==0)
150 /* Compare encodings case-insensitively, ignoring dashes (-) */
152 b
=strchr(ctype
, '.');
164 if(*b
=='\0' || *b
=='@'){
165 enc_check_ok
=(*a
=='\0');
168 if(*a
=='\0' || tolower(*a
)!=tolower(*b
))
176 ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
179 if(strcasecmp(langi
, "UTF-8")==0 || strcasecmp(langi
, "UTF8")==0){
180 ioncore_g
.enc_sb
=FALSE
;
181 ioncore_g
.enc_utf8
=TRUE
;
182 ioncore_g
.use_mb
=TRUE
;
186 for(i
=0; i
<256; i
++){
188 if(mbtowc(&wc
, chs
, 8)==-1){
189 /* Doesn't look like a single-byte encoding. */
196 /* Seems like a single-byte encoding... */
197 ioncore_g
.use_mb
=TRUE
;
201 if(mbtowc(NULL
, NULL
, 0)!=0){
202 warn(TR("Statefull encodings are unsupported."));
206 ioncore_g
.enc_sb
=FALSE
;
207 ioncore_g
.use_mb
=TRUE
;
212 warn(TR("Cannot verify locale encoding setting integrity "
213 "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
214 "The LC_CTYPE environment variable should be of the form "
215 "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
216 "should match the nl_langinfo value above."), ctype
, langi
);
221 static bool init_locale()
225 p
=setlocale(LC_ALL
, "");
228 warn("setlocale() call failed.");
232 /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
235 if(!XSupportsLocale()){
236 warn("XSupportsLocale() failed.");
242 warn("Reverting locale settings to \"C\".");
244 if(setlocale(LC_ALL
, "C")==NULL
)
245 warn("setlocale() call failed.");
252 #ifndef CF_NO_GETTEXT
254 #define TEXTDOMAIN "notion"
256 static bool init_messages(const char *localedir
)
258 if(bindtextdomain(TEXTDOMAIN
, localedir
)==NULL
){
259 warn_err_obj("bindtextdomain");
261 }else if(textdomain(TEXTDOMAIN
)==NULL
){
262 warn_err_obj("textdomain");
275 /*{{{ ioncore_init */
278 #define INIT_HOOK_(NM) \
279 NM=mainloop_register_hook(#NM, create_hook()); \
280 if(NM==NULL) return FALSE
282 #define ADD_HOOK_(NM, FN) \
283 if(!hook_add(NM, (void (*)())FN)) return FALSE
285 #define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT)
287 static bool init_hooks()
289 INIT_HOOK_(ioncore_post_layout_setup_hook
);
290 INIT_HOOK_(ioncore_snapshot_hook
);
291 INIT_HOOK_(ioncore_deinit_hook
);
292 INIT_HOOK_(screen_managed_changed_hook
);
293 INIT_HOOK_(frame_managed_changed_hook
);
294 INIT_HOOK_(clientwin_mapped_hook
);
295 INIT_HOOK_(clientwin_unmapped_hook
);
296 INIT_HOOK_(clientwin_property_change_hook
);
297 INIT_HOOK_(ioncore_submap_ungrab_hook
);
299 INIT_HOOK_(region_notify_hook
);
300 ADD_HOOK_(region_notify_hook
, ioncore_screen_activity_notify
);
301 ADD_HOOK_(region_notify_hook
, ioncore_region_notify
);
303 INIT_HOOK(clientwin_do_manage_alt
, clientwin_do_manage_default
);
304 INIT_HOOK(ioncore_handle_event_alt
, ioncore_handle_event
);
305 INIT_HOOK(region_do_warp_alt
, region_do_warp_default
);
306 INIT_HOOK(ioncore_exec_environ_hook
, ioncore_setup_environ
);
308 mainloop_sigchld_hook
=mainloop_register_hook("ioncore_sigchld_hook",
310 mainloop_sigusr2_hook
=mainloop_register_hook("ioncore_sigusr2_hook",
317 static bool register_classes()
321 fail
|=!ioncore_register_regclass(&CLASSDESCR(WClientWin
),
322 (WRegionLoadCreateFn
*)clientwin_load
);
323 fail
|=!ioncore_register_regclass(&CLASSDESCR(WMPlex
),
324 (WRegionLoadCreateFn
*)mplex_load
);
325 fail
|=!ioncore_register_regclass(&CLASSDESCR(WFrame
),
326 (WRegionLoadCreateFn
*)frame_load
);
327 fail
|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin
),
328 (WRegionLoadCreateFn
*)infowin_load
);
329 fail
|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW
),
330 (WRegionLoadCreateFn
*)groupcw_load
);
331 fail
|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS
),
332 (WRegionLoadCreateFn
*)groupws_load
);
338 #define INITSTR(NM) \
339 ioncore_g.notifies.NM=stringstore_alloc(#NM); \
340 if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
342 static bool init_global()
344 /* argc, argv must be set be the program */
346 ioncore_g
.display
=NULL
;
348 ioncore_g
.sm_client_id
=NULL
;
349 ioncore_g
.rootwins
=NULL
;
350 ioncore_g
.screens
=NULL
;
351 ioncore_g
.focus_next
=NULL
;
352 ioncore_g
.warp_next
=FALSE
;
353 ioncore_g
.focus_next_source
=IONCORE_FOCUSNEXT_OTHER
;
355 ioncore_g
.focus_current
=NULL
;
357 ioncore_g
.input_mode
=IONCORE_INPUTMODE_NORMAL
;
358 ioncore_g
.opmode
=IONCORE_OPMODE_INIT
;
359 ioncore_g
.dblclick_delay
=CF_DBLCLICK_DELAY
;
360 ioncore_g
.usertime_diff_current
=CF_USERTIME_DIFF_CURRENT
;
361 ioncore_g
.usertime_diff_new
=CF_USERTIME_DIFF_NEW
;
362 ioncore_g
.opaque_resize
=0;
363 ioncore_g
.warp_enabled
=TRUE
;
364 ioncore_g
.switchto_new
=TRUE
;
365 ioncore_g
.no_mousefocus
=FALSE
;
366 ioncore_g
.unsqueeze_enabled
=TRUE
;
367 ioncore_g
.autoraise
=TRUE
;
368 ioncore_g
.autosave_layout
=TRUE
;
369 ioncore_g
.window_stacking_request
=IONCORE_WINDOWSTACKINGREQUEST_IGNORE
;
371 ioncore_g
.enc_utf8
=FALSE
;
372 ioncore_g
.enc_sb
=TRUE
;
373 ioncore_g
.use_mb
=FALSE
;
375 ioncore_g
.screen_notify
=TRUE
;
377 ioncore_g
.frame_default_index
=LLIST_INDEX_AFTER_CURRENT_ACT
;
379 ioncore_g
.framed_transients
=TRUE
;
381 ioncore_g
.shape_extension
=FALSE
;
382 ioncore_g
.shape_event_basep
=0;
383 ioncore_g
.shape_error_basep
=0;
386 INITSTR(inactivated
);
388 INITSTR(sub_activity
);
390 INITSTR(unset_manager
);
391 INITSTR(set_manager
);
392 INITSTR(unset_return
);
394 INITSTR(pseudoactivated
);
395 INITSTR(pseudoinactivated
);
405 bool ioncore_init(const char *prog
, int argc
, char *argv
[],
406 const char *localedir
)
418 #ifndef CF_NO_GETTEXT
419 init_messages(localedir
);
422 ioncore_about
=scat3(ioncore_copy
, "\n\n", TR(ioncore_license
));
424 if(!ioncore_init_bindmaps())
427 if(!register_classes())
433 if(!ioncore_init_module_support())
443 /*{{{ ioncore_startup */
446 static void ioncore_init_session(const char *display
)
448 const char *dpyend
=NULL
;
449 char *tmp
=NULL
, *colon
=NULL
;
450 const char *sm
=getenv("SESSION_MANAGER");
453 ioncore_load_module("mod_sm");
455 if(extl_sessiondir()!=NULL
)
458 /* Not running under SM; use display-specific directory */
459 dpyend
=strchr(display
, ':');
461 dpyend
=strchr(dpyend
, '.');
463 libtu_asprintf(&tmp
, "default-session-%s", display
);
465 libtu_asprintf(&tmp
, "default-session-%.*s",
466 (int)(dpyend
-display
), display
);
474 colon
=strchr(colon
, ':');
480 extl_set_sessiondir(tmp
);
485 static bool ioncore_init_x(const char *display
, int stflags
)
489 static bool called
=FALSE
;
491 /* Sorry, this function can not be re-entered due to laziness
492 * towards implementing checking of things already initialized.
493 * Nobody would call this twice anyway.
498 /* Open the display. */
499 dpy
=XOpenDisplay(display
);
502 warn(TR("Could not connect to X display '%s'"),
503 XDisplayName(display
));
507 if(stflags
&IONCORE_STARTUP_ONEROOT
){
508 drw
=DefaultScreen(dpy
);
512 nrw
=ScreenCount(dpy
);
517 ioncore_g
.display
=scopy(display
);
518 if(ioncore_g
.display
==NULL
){
525 ioncore_g
.win_context
=XUniqueContext();
526 ioncore_g
.conn
=ConnectionNumber(dpy
);
528 if(XShapeQueryExtension(ioncore_g
.dpy
, &ioncore_g
.shape_event_basep
,
529 &ioncore_g
.shape_error_basep
))
530 ioncore_g
.shape_extension
=TRUE
;
532 XMissingExtension(ioncore_g
.dpy
, "SHAPE");
534 cloexec_braindamage_fix(ioncore_g
.conn
);
536 ioncore_g
.atom_wm_state
=XInternAtom(dpy
, "WM_STATE", False
);
537 ioncore_g
.atom_wm_change_state
=XInternAtom(dpy
, "WM_CHANGE_STATE", False
);
538 ioncore_g
.atom_wm_protocols
=XInternAtom(dpy
, "WM_PROTOCOLS", False
);
539 ioncore_g
.atom_wm_delete
=XInternAtom(dpy
, "WM_DELETE_WINDOW", False
);
540 ioncore_g
.atom_wm_take_focus
=XInternAtom(dpy
, "WM_TAKE_FOCUS", False
);
541 ioncore_g
.atom_wm_colormaps
=XInternAtom(dpy
, "WM_COLORMAP_WINDOWS", False
);
542 ioncore_g
.atom_wm_window_role
=XInternAtom(dpy
, "WM_WINDOW_ROLE", False
);
543 ioncore_g
.atom_checkcode
=XInternAtom(dpy
, "_ION_CWIN_RESTART_CHECKCODE", False
);
544 ioncore_g
.atom_selection
=XInternAtom(dpy
, "_ION_SELECTION_STRING", False
);
545 ioncore_g
.atom_dockapp_hack
=XInternAtom(dpy
, "_ION_DOCKAPP_HACK", False
);
546 ioncore_g
.atom_mwm_hints
=XInternAtom(dpy
, "_MOTIF_WM_HINTS", False
);
549 ioncore_init_bindings();
550 ioncore_init_cursors();
554 ioncore_init_session(XDisplayName(display
));
556 for(i
=drw
; i
<nrw
; i
++)
559 if(ioncore_g
.rootwins
==NULL
){
561 warn(TR("Could not find a screen to manage."));
565 if(!mainloop_register_input_fd(ioncore_g
.conn
, NULL
,
566 ioncore_x_connection_handler
)){
574 static void set_initial_focus()
576 Window root
=None
, win
=None
;
582 XQueryPointer(ioncore_g
.dpy
, None
, &root
, &win
,
583 &x
, &y
, &wx
, &wy
, &mask
);
585 FOR_ALL_SCREENS(scr
){
586 Window scrroot
=region_root_of((WRegion
*)scr
);
587 if(scrroot
==root
&& rectangle_contains(®ION_GEOM(scr
), x
, y
)){
593 scr
=ioncore_g
.screens
;
595 region_focuslist_push((WRegion
*)scr
);
596 region_do_set_focus((WRegion
*)scr
, FALSE
);
600 bool ioncore_startup(const char *display
, const char *cfgfile
,
606 LOG(INFO
, GENERAL
, TR("Starting Notion"));
608 /* Don't trap termination signals just yet. */
609 sigemptyset(&inittrap
);
610 sigaddset(&inittrap
, SIGALRM
);
611 sigaddset(&inittrap
, SIGCHLD
);
612 sigaddset(&inittrap
, SIGPIPE
);
613 sigaddset(&inittrap
, SIGUSR2
);
614 mainloop_trap_signals(&inittrap
);
619 ioncore_register_exports();
621 if(!ioncore_init_x(display
, stflags
))
626 if(!extl_read_config("ioncore_ext", NULL
, TRUE
))
629 ioncore_read_main_config(cfgfile
);
631 if(!ioncore_init_layout())
634 hook_call_v(ioncore_post_layout_setup_hook
);
636 FOR_ALL_ROOTWINS(rootwin
)
637 rootwin_manage_initial_windows(rootwin
);
648 /*{{{ ioncore_deinit */
651 void ioncore_deinit()
656 ioncore_g
.opmode
=IONCORE_OPMODE_DEINIT
;
658 if(ioncore_g
.dpy
==NULL
)
661 hook_call_v(ioncore_deinit_hook
);
663 while(ioncore_g
.screens
!=NULL
)
664 destroy_obj((Obj
*)ioncore_g
.screens
);
666 /*ioncore_unload_modules();*/
668 while(ioncore_g
.rootwins
!=NULL
)
669 destroy_obj((Obj
*)ioncore_g
.rootwins
);
671 ioncore_deinit_bindmaps();
673 mainloop_unregister_input_fd(ioncore_g
.conn
);
688 /*{{{ Miscellaneous */
692 * Is Notion supporting locale-specifically multibyte-encoded strings?
696 bool ioncore_is_i18n()
698 return ioncore_g
.use_mb
;
703 * Returns Ioncore version string.
707 const char *ioncore_version()
713 * Returns the name of program using Ioncore.
717 const char *ioncore_progname()
724 * Returns an about message (version, author, copyright notice).
728 const char *ioncore_aboutmsg()
730 return ioncore_about
;