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
.focuslist
=NULL
;
356 ioncore_g
.focus_current
=NULL
;
358 ioncore_g
.input_mode
=IONCORE_INPUTMODE_NORMAL
;
359 ioncore_g
.opmode
=IONCORE_OPMODE_INIT
;
360 ioncore_g
.dblclick_delay
=CF_DBLCLICK_DELAY
;
361 ioncore_g
.usertime_diff_current
=CF_USERTIME_DIFF_CURRENT
;
362 ioncore_g
.usertime_diff_new
=CF_USERTIME_DIFF_NEW
;
363 ioncore_g
.opaque_resize
=0;
364 ioncore_g
.warp_enabled
=TRUE
;
365 ioncore_g
.switchto_new
=TRUE
;
366 ioncore_g
.no_mousefocus
=FALSE
;
367 ioncore_g
.unsqueeze_enabled
=TRUE
;
368 ioncore_g
.autoraise
=TRUE
;
369 ioncore_g
.autosave_layout
=TRUE
;
370 ioncore_g
.window_stacking_request
=IONCORE_WINDOWSTACKINGREQUEST_IGNORE
;
371 ioncore_g
.focuslist_insert_delay
=CF_FOCUSLIST_INSERT_DELAY
;
372 ioncore_g
.workspace_indicator_timeout
=CF_WORKSPACE_INDICATOR_TIMEOUT
;
373 ioncore_g
.activity_notification_on_all_screens
=FALSE
;
375 ioncore_g
.enc_utf8
=FALSE
;
376 ioncore_g
.enc_sb
=TRUE
;
377 ioncore_g
.use_mb
=FALSE
;
379 ioncore_g
.screen_notify
=TRUE
;
381 ioncore_g
.frame_default_index
=LLIST_INDEX_AFTER_CURRENT_ACT
;
383 ioncore_g
.framed_transients
=TRUE
;
385 ioncore_g
.shape_extension
=FALSE
;
386 ioncore_g
.shape_event_basep
=0;
387 ioncore_g
.shape_error_basep
=0;
390 INITSTR(inactivated
);
392 INITSTR(sub_activity
);
394 INITSTR(unset_manager
);
395 INITSTR(set_manager
);
396 INITSTR(unset_return
);
398 INITSTR(pseudoactivated
);
399 INITSTR(pseudoinactivated
);
409 bool ioncore_init(const char *prog
, int argc
, char *argv
[],
410 const char *localedir
)
422 #ifndef CF_NO_GETTEXT
423 init_messages(localedir
);
426 ioncore_about
=scat3(ioncore_copy
, "\n\n", TR(ioncore_license
));
428 if(!ioncore_init_bindmaps())
431 if(!register_classes())
437 if(!ioncore_init_module_support())
447 /*{{{ ioncore_startup */
450 static void ioncore_init_session(const char *display
)
452 const char *dpyend
=NULL
;
453 char *tmp
=NULL
, *colon
=NULL
;
454 const char *sm
=getenv("SESSION_MANAGER");
457 ioncore_load_module("mod_sm");
459 if(extl_sessiondir()!=NULL
)
462 /* Not running under SM; use display-specific directory */
463 dpyend
=strchr(display
, ':');
465 dpyend
=strchr(dpyend
, '.');
467 libtu_asprintf(&tmp
, "default-session-%s", display
);
469 libtu_asprintf(&tmp
, "default-session-%.*s",
470 (int)(dpyend
-display
), display
);
478 colon
=strchr(colon
, ':');
484 extl_set_sessiondir(tmp
);
489 static bool ioncore_init_x(const char *display
, int stflags
)
493 static bool called
=FALSE
;
495 /* Sorry, this function can not be re-entered due to laziness
496 * towards implementing checking of things already initialized.
497 * Nobody would call this twice anyway.
502 /* Open the display. */
503 dpy
=XOpenDisplay(display
);
506 warn(TR("Could not connect to X display '%s'"),
507 XDisplayName(display
));
511 if(stflags
&IONCORE_STARTUP_ONEROOT
){
512 drw
=DefaultScreen(dpy
);
516 nrw
=ScreenCount(dpy
);
521 ioncore_g
.display
=scopy(display
);
522 if(ioncore_g
.display
==NULL
){
529 ioncore_g
.win_context
=XUniqueContext();
530 ioncore_g
.conn
=ConnectionNumber(dpy
);
532 if(XShapeQueryExtension(ioncore_g
.dpy
, &ioncore_g
.shape_event_basep
,
533 &ioncore_g
.shape_error_basep
))
534 ioncore_g
.shape_extension
=TRUE
;
536 XMissingExtension(ioncore_g
.dpy
, "SHAPE");
538 cloexec_braindamage_fix(ioncore_g
.conn
);
540 ioncore_g
.atom_wm_state
=XInternAtom(dpy
, "WM_STATE", False
);
541 ioncore_g
.atom_wm_change_state
=XInternAtom(dpy
, "WM_CHANGE_STATE", False
);
542 ioncore_g
.atom_wm_protocols
=XInternAtom(dpy
, "WM_PROTOCOLS", False
);
543 ioncore_g
.atom_wm_delete
=XInternAtom(dpy
, "WM_DELETE_WINDOW", False
);
544 ioncore_g
.atom_wm_take_focus
=XInternAtom(dpy
, "WM_TAKE_FOCUS", False
);
545 ioncore_g
.atom_wm_colormaps
=XInternAtom(dpy
, "WM_COLORMAP_WINDOWS", False
);
546 ioncore_g
.atom_wm_window_role
=XInternAtom(dpy
, "WM_WINDOW_ROLE", False
);
547 ioncore_g
.atom_checkcode
=XInternAtom(dpy
, "_ION_CWIN_RESTART_CHECKCODE", False
);
548 ioncore_g
.atom_selection
=XInternAtom(dpy
, "_ION_SELECTION_STRING", False
);
549 ioncore_g
.atom_dockapp_hack
=XInternAtom(dpy
, "_ION_DOCKAPP_HACK", False
);
550 ioncore_g
.atom_mwm_hints
=XInternAtom(dpy
, "_MOTIF_WM_HINTS", False
);
553 ioncore_init_bindings();
554 ioncore_init_cursors();
558 ioncore_init_session(XDisplayName(display
));
560 for(i
=drw
; i
<nrw
; i
++)
563 if(ioncore_g
.rootwins
==NULL
){
565 warn(TR("Could not find a screen to manage."));
569 if(!mainloop_register_input_fd(ioncore_g
.conn
, NULL
,
570 ioncore_x_connection_handler
)){
578 static void set_initial_focus()
580 Window root
=None
, win
=None
;
585 XQueryPointer(ioncore_g
.dpy
, None
, &root
, &win
,
586 &x
, &y
, &wx
, &wy
, &mask
);
588 FOR_ALL_SCREENS(scr
){
589 Window scrroot
=region_root_of((WRegion
*)scr
);
590 if(scrroot
==root
&& rectangle_contains(®ION_GEOM(scr
), x
, y
)){
596 scr
=ioncore_g
.screens
;
598 region_focuslist_push((WRegion
*)scr
);
599 region_do_set_focus((WRegion
*)scr
, FALSE
);
603 bool ioncore_startup(const char *display
, const char *cfgfile
,
609 LOG(INFO
, GENERAL
, TR("Starting Notion"));
611 /* Don't trap termination signals just yet. */
612 sigemptyset(&inittrap
);
613 sigaddset(&inittrap
, SIGALRM
);
614 sigaddset(&inittrap
, SIGCHLD
);
615 sigaddset(&inittrap
, SIGPIPE
);
616 sigaddset(&inittrap
, SIGUSR2
);
617 mainloop_trap_signals(&inittrap
);
622 ioncore_register_exports();
624 if(!ioncore_init_x(display
, stflags
))
629 if(!extl_read_config("ioncore_ext", NULL
, TRUE
))
632 ioncore_read_main_config(cfgfile
);
634 if(!ioncore_init_layout())
637 hook_call_v(ioncore_post_layout_setup_hook
);
639 FOR_ALL_ROOTWINS(rootwin
)
640 rootwin_manage_initial_windows(rootwin
);
651 /*{{{ ioncore_deinit */
654 void ioncore_deinit()
658 ioncore_g
.opmode
=IONCORE_OPMODE_DEINIT
;
660 if(ioncore_g
.dpy
==NULL
)
663 hook_call_v(ioncore_deinit_hook
);
665 while(ioncore_g
.screens
!=NULL
)
666 destroy_obj((Obj
*)ioncore_g
.screens
);
668 /*ioncore_unload_modules();*/
670 while(ioncore_g
.rootwins
!=NULL
)
671 destroy_obj((Obj
*)ioncore_g
.rootwins
);
673 ioncore_deinit_bindmaps();
675 mainloop_unregister_input_fd(ioncore_g
.conn
);
690 /*{{{ Miscellaneous */
694 * Is Notion supporting locale-specifically multibyte-encoded strings?
698 bool ioncore_is_i18n()
700 return ioncore_g
.use_mb
;
705 * Returns Ioncore version string.
709 const char *ioncore_version()
715 * Returns the name of program using Ioncore.
719 const char *ioncore_progname()
726 * Returns an about message (version, author, copyright notice).
730 const char *ioncore_aboutmsg()
732 return ioncore_about
;