]> git.decadent.org.uk Git - ion3.git/blob - ioncore/ioncore.c
[svn-upgrade] Integrating new upstream version, ion3 (20070708)
[ion3.git] / ioncore / ioncore.c
1 /*
2  * ion/ioncore/ioncore.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2007. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <string.h>
14 #include <strings.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <ctype.h>
18 #ifndef CF_NO_LOCALE
19 #include <locale.h>
20 #include <langinfo.h>
21 #endif
22 #ifndef CF_NO_GETTEXT
23 #include <libintl.h>
24 #endif
25
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>
34
35 #include "common.h"
36 #include "rootwin.h"
37 #include "event.h"
38 #include "cursor.h"
39 #include "global.h"
40 #include "modules.h"
41 #include "eventh.h"
42 #include "ioncore.h"
43 #include "manage.h"
44 #include "conf.h"
45 #include "binding.h"
46 #include "bindmaps.h"
47 #include "strings.h"
48 #include "gr.h"
49 #include "xic.h"
50 #include "netwm.h"
51 #include "focus.h"
52 #include "frame.h"
53 #include "saveload.h"
54 #include "infowin.h"
55 #include "activity.h"
56 #include "group-cw.h"
57 #include "group-ws.h"
58 #include "llist.h"
59 #include "exec.h"
60 #include "screen-notify.h"
61 #include "key.h"
62
63
64 #include "../version.h"
65 #include "exports.h"
66
67
68 /*{{{ Variables */
69
70
71 WGlobal ioncore_g;
72
73 static const char *progname="ion";
74
75 static const char ioncore_copy[]=
76     "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2007.";
77
78 static const char ioncore_license[]=DUMMY_TR(
79     "This software is essentially licensed under the GNU Lesser General\n"
80     "Public License (LGPL), version 2.1, unless otherwise indicated in\n"
81     "components taken from elsewhere. Additional terms apply to the use\n"
82     "of the name of the project, Ion(tm). For details, see the file\n"
83     "LICENSE that you should have received with this software.\n"
84     "\n"
85     "This program is distributed in the hope that it will be useful,\n"
86     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
87     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); 
88
89 static const char *ioncore_about=NULL;
90
91 WHook *ioncore_post_layout_setup_hook=NULL;
92 WHook *ioncore_snapshot_hook=NULL;
93 WHook *ioncore_deinit_hook=NULL;
94
95
96 /*}}}*/
97
98
99 /*{{{ warn_nolog */
100
101
102 void ioncore_warn_nolog(const char *str)
103 {
104     fprintf(stderr, "%s: %s\n", libtu_progname(), str);
105 }
106
107
108 /*}}}*/
109
110
111 /*{{{ init_locale */
112
113
114 #ifndef CF_NO_LOCALE
115
116
117 static bool check_encoding()
118 {
119     int i;
120     char chs[8]=" ";
121     wchar_t wc;
122     const char *langi, *ctype, *a, *b;
123     bool enc_check_ok=FALSE;
124
125     langi=nl_langinfo(CODESET);
126     ctype=setlocale(LC_CTYPE, NULL);
127     
128     if(langi==NULL || ctype==NULL)
129         goto integr_err;
130
131     if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
132         return TRUE;
133     
134     /* Compare encodings case-insensitively, ignoring dashes (-) */
135     a=langi; 
136     b=strchr(ctype, '.');
137     if(b!=NULL){
138         b=b+1;
139         while(1){
140             if(*a=='-'){
141                 a++;
142                 continue;
143             }
144             if(*b=='-'){
145                 b++;
146                 continue;
147             }
148             if(*b=='\0' || *b=='@'){
149                 enc_check_ok=(*a=='\0');
150                 break;
151             }
152             if(*a=='\0' || tolower(*a)!=tolower(*b))
153                 break;
154             a++;
155             b++;
156         }
157         if(!enc_check_ok)
158             goto integr_err;
159     }else{
160         ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
161     }
162         
163     if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
164         ioncore_g.enc_sb=FALSE;
165         ioncore_g.enc_utf8=TRUE;
166         ioncore_g.use_mb=TRUE;
167         return TRUE;
168     }
169     
170     for(i=0; i<256; i++){
171         chs[0]=i;
172         if(mbtowc(&wc, chs, 8)==-1){
173             /* Doesn't look like a single-byte encoding. */
174             break;
175         }
176         
177     }
178     
179     if(i==256){
180         /* Seems like a single-byte encoding... */
181         ioncore_g.use_mb=TRUE;
182         return TRUE;
183     }
184
185     if(mbtowc(NULL, NULL, 0)!=0){
186         warn("Statefull encodings are unsupported.");
187         return FALSE;
188     }
189     
190     ioncore_g.enc_sb=FALSE;
191     ioncore_g.use_mb=TRUE;
192
193     return TRUE;
194     
195 integr_err:
196     warn("Cannot verify locale encoding setting integrity "
197          "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
198          "The LC_CTYPE environment variable should be of the form "
199          "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
200          "should match the nl_langinfo value above.", ctype, langi);
201     return FALSE;
202 }
203
204
205 static bool init_locale()
206 {
207     const char *p;
208     
209     p=setlocale(LC_ALL, "");
210     
211     if(p==NULL){
212         warn("setlocale() call failed.");
213         return FALSE;
214     }
215
216     /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
217         return TRUE;*/
218
219     if(!XSupportsLocale()){
220         warn("XSupportsLocale() failed.");
221     }else{
222         if(check_encoding())
223             return TRUE;
224     }
225     
226     warn("Reverting locale settings to \"C\".");
227     
228     if(setlocale(LC_ALL, "C")==NULL)
229         warn("setlocale() call failed.");
230         
231     return FALSE;
232 }
233
234 #endif
235
236 #ifndef CF_NO_GETTEXT
237
238 #define TEXTDOMAIN "ion3"
239
240 static bool init_messages(const char *localedir)
241 {
242     if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
243         warn_err_obj("bindtextdomain");
244         return FALSE;
245     }else if(textdomain(TEXTDOMAIN)==NULL){
246         warn_err_obj("textdomain");
247         return FALSE;
248     }
249     return TRUE;
250 }
251
252
253 #endif
254
255
256 /*}}}*/
257
258
259 /*{{{ ioncore_init */
260
261
262 #define INIT_HOOK_(NM)                             \
263     NM=mainloop_register_hook(#NM, create_hook()); \
264     if(NM==NULL) return FALSE
265
266 #define ADD_HOOK_(NM, FN)                          \
267     if(!hook_add(NM, (void (*)())FN)) return FALSE
268
269 #define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT)
270
271 static bool init_hooks()
272 {
273     INIT_HOOK_(ioncore_post_layout_setup_hook);
274     INIT_HOOK_(ioncore_snapshot_hook);
275     INIT_HOOK_(ioncore_deinit_hook);
276     INIT_HOOK_(screen_managed_changed_hook);
277     INIT_HOOK_(frame_managed_changed_hook);
278     INIT_HOOK_(clientwin_mapped_hook);
279     INIT_HOOK_(clientwin_unmapped_hook);
280     INIT_HOOK_(clientwin_property_change_hook);
281     INIT_HOOK_(ioncore_submap_ungrab_hook);
282     
283     INIT_HOOK_(region_notify_hook);
284     ADD_HOOK_(region_notify_hook, ioncore_frame_quasiactivation_notify);
285     ADD_HOOK_(region_notify_hook, ioncore_screen_activity_notify);
286     
287     INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
288     INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
289     INIT_HOOK(region_do_warp_alt, region_do_warp_default);
290     INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ);
291     
292     mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
293                                                  create_hook());
294     mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
295                                                  create_hook());
296     
297     return TRUE;
298 }
299
300
301 static bool register_classes()
302 {
303     int fail=0;
304     
305     fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin), 
306                                      (WRegionLoadCreateFn*)clientwin_load);
307     fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex), 
308                                      (WRegionLoadCreateFn*)mplex_load);
309     fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame), 
310                                      (WRegionLoadCreateFn*)frame_load);
311     fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin), 
312                                      (WRegionLoadCreateFn*)infowin_load);
313     fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW), 
314                                      (WRegionLoadCreateFn*)groupcw_load);
315     fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS), 
316                                      (WRegionLoadCreateFn*)groupws_load);
317     
318     return !fail;
319 }
320
321
322 #define INITSTR(NM)                             \
323     ioncore_g.notifies.NM=stringstore_alloc(#NM); \
324     if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
325     
326 static bool init_global()
327 {
328     /* argc, argv must be set be the program */
329     ioncore_g.dpy=NULL;
330     ioncore_g.display=NULL;
331
332     ioncore_g.sm_client_id=NULL;
333     ioncore_g.rootwins=NULL;
334     ioncore_g.screens=NULL;
335     ioncore_g.focus_next=NULL;
336     ioncore_g.warp_next=FALSE;
337     ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
338     
339     ioncore_g.focus_current=NULL;
340
341     ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
342     ioncore_g.opmode=IONCORE_OPMODE_INIT;
343     ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
344     ioncore_g.opaque_resize=0;
345     ioncore_g.warp_enabled=TRUE;
346     ioncore_g.switchto_new=TRUE;
347     ioncore_g.no_mousefocus=FALSE;
348     ioncore_g.unsqueeze_enabled=TRUE;
349     ioncore_g.autoraise=TRUE;
350     
351     ioncore_g.enc_utf8=FALSE;
352     ioncore_g.enc_sb=TRUE;
353     ioncore_g.use_mb=FALSE;
354     
355     ioncore_g.screen_notify=TRUE;
356     
357     ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
358     
359     ioncore_g.framed_transients=TRUE;
360     
361     INITSTR(activated);
362     INITSTR(inactivated);
363     INITSTR(activity);
364     INITSTR(sub_activity);
365     INITSTR(name);
366     INITSTR(unset_manager);
367     INITSTR(set_manager);
368     INITSTR(unset_return);
369     INITSTR(set_return);
370     INITSTR(pseudoactivated);
371     INITSTR(pseudoinactivated);
372     INITSTR(tag);
373     INITSTR(deinit);
374     INITSTR(map);
375     INITSTR(unmap);
376     
377     return TRUE;
378 }
379
380
381 bool ioncore_init(const char *prog, int argc, char *argv[],
382                   const char *localedir)
383 {
384     if(!init_global())
385         return FALSE;
386     
387     progname=prog;
388     ioncore_g.argc=argc;
389     ioncore_g.argv=argv;
390
391 #ifndef CF_NO_LOCALE    
392     init_locale();
393 #endif
394 #ifndef CF_NO_GETTEXT
395     init_messages(localedir);
396 #endif
397
398     ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
399     
400     if(!ioncore_init_bindmaps())
401         return FALSE;
402     
403     if(!register_classes())
404         return FALSE;
405
406     if(!init_hooks())
407         return FALSE;
408
409     if(!ioncore_init_module_support())
410         return FALSE;
411
412     return TRUE;
413 }
414
415
416 /*}}}*/
417
418
419 /*{{{ ioncore_startup */
420
421
422 static void ioncore_init_session(const char *display)
423 {
424     const char *dpyend=NULL;
425     char *tmp=NULL, *colon=NULL;
426     const char *sm=getenv("SESSION_MANAGER");
427     
428     if(sm!=NULL)
429         ioncore_load_module("mod_sm");
430
431     if(extl_sessiondir()!=NULL)
432         return;
433     
434     /* Not running under SM; use display-specific directory */
435     dpyend=strchr(display, ':');
436     if(dpyend!=NULL)
437         dpyend=strchr(dpyend, '.');
438     if(dpyend==NULL){    
439         libtu_asprintf(&tmp, "default-session-%s", display);
440     }else{
441         libtu_asprintf(&tmp, "default-session-%.*s",
442                        (int)(dpyend-display), display);
443     }
444     
445     if(tmp==NULL)
446         return;
447     
448     colon=tmp;
449     while(1){
450         colon=strchr(colon, ':');
451         if(colon==NULL)
452             break;
453         *colon='-';
454     }
455     
456     extl_set_sessiondir(tmp);
457     free(tmp);
458 }
459     
460
461 static bool ioncore_init_x(const char *display, int stflags)
462 {
463     Display *dpy;
464     int i, drw, nrw;
465     static bool called=FALSE;
466
467     /* Sorry, this function can not be re-entered due to laziness
468      * towards implementing checking of things already initialized.
469      * Nobody would call this twice anyway.
470      */
471     assert(!called);
472     called=TRUE;
473
474     /* Open the display. */
475     dpy=XOpenDisplay(display);
476     
477     if(dpy==NULL){
478         warn(TR("Could not connect to X display '%s'"), 
479              XDisplayName(display));
480         return FALSE;
481     }
482
483     if(stflags&IONCORE_STARTUP_ONEROOT){
484         drw=DefaultScreen(dpy);
485         nrw=drw+1;
486     }else{
487         drw=0;
488         nrw=ScreenCount(dpy);
489     }
490     
491     /* Initialize */
492     if(display!=NULL){
493         ioncore_g.display=scopy(display);
494         if(ioncore_g.display==NULL){
495             XCloseDisplay(dpy);
496             return FALSE;
497         }
498     }
499     
500     ioncore_g.dpy=dpy;
501     ioncore_g.win_context=XUniqueContext();
502     ioncore_g.conn=ConnectionNumber(dpy);
503     
504     cloexec_braindamage_fix(ioncore_g.conn);
505     
506     ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
507     ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
508     ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
509     ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
510     ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
511     ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
512     ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
513     ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
514     ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
515     ioncore_g.atom_dockapp_hack=XInternAtom(dpy, "_ION_DOCKAPP_HACK", False);
516     ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);
517
518     ioncore_init_xim();
519     ioncore_init_bindings();
520     ioncore_init_cursors();
521
522     netwm_init();
523     
524     ioncore_init_session(XDisplayName(display));
525
526     for(i=drw; i<nrw; i++)
527         create_rootwin(i);
528
529     if(ioncore_g.rootwins==NULL){
530         if(nrw-drw>1)
531             warn(TR("Could not find a screen to manage."));
532         return FALSE;
533     }
534
535     if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
536                                    ioncore_x_connection_handler)){
537         return FALSE;
538     }
539     
540     return TRUE;
541 }
542
543
544 static void set_initial_focus()
545 {
546     Window root=None, win=None;
547     int x, y, wx, wy;
548     uint mask;
549     WScreen *scr;
550     WWindow *wwin;
551     
552     XQueryPointer(ioncore_g.dpy, None, &root, &win,
553                   &x, &y, &wx, &wy, &mask);
554     
555     FOR_ALL_SCREENS(scr){
556         Window scrroot=region_root_of((WRegion*)scr);
557         if(scrroot==root && rectangle_contains(&REGION_GEOM(scr), x, y)){
558             break;
559         }
560     }
561     
562     if(scr==NULL)
563         scr=ioncore_g.screens;
564     
565     region_focuslist_push((WRegion*)scr);
566     region_do_set_focus((WRegion*)scr, FALSE);
567 }
568
569
570 bool ioncore_startup(const char *display, const char *cfgfile,
571                      int stflags)
572 {
573     WRootWin *rootwin;
574     sigset_t inittrap;
575
576     /* Don't trap termination signals just yet. */
577     sigemptyset(&inittrap);
578     sigaddset(&inittrap, SIGALRM);
579     sigaddset(&inittrap, SIGCHLD);
580     sigaddset(&inittrap, SIGPIPE);
581     sigaddset(&inittrap, SIGUSR2);
582     mainloop_trap_signals(&inittrap);
583
584     if(!extl_init())
585         return FALSE;
586
587     ioncore_register_exports();
588     
589     if(!ioncore_init_x(display, stflags))
590         return FALSE;
591
592     gr_read_config();
593
594     if(!extl_read_config("ioncore_ext", NULL, TRUE))
595         return FALSE;
596     
597     ioncore_read_main_config(cfgfile);
598     
599     if(!ioncore_init_layout())
600         return FALSE;
601     
602     hook_call_v(ioncore_post_layout_setup_hook);
603     
604     FOR_ALL_ROOTWINS(rootwin)
605         rootwin_manage_initial_windows(rootwin);
606     
607     set_initial_focus();
608     
609     return TRUE;
610 }
611
612
613 /*}}}*/
614
615
616 /*{{{ ioncore_deinit */
617
618
619 void ioncore_deinit()
620 {
621     Display *dpy;
622     WRootWin *rootwin;
623     
624     ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
625     
626     if(ioncore_g.dpy==NULL)
627         return;
628
629     hook_call_v(ioncore_deinit_hook);
630
631     while(ioncore_g.screens!=NULL)
632         destroy_obj((Obj*)ioncore_g.screens);
633
634     /*ioncore_unload_modules();*/
635
636     while(ioncore_g.rootwins!=NULL)
637         destroy_obj((Obj*)ioncore_g.rootwins);
638
639     ioncore_deinit_bindmaps();
640
641     mainloop_unregister_input_fd(ioncore_g.conn);
642     
643     dpy=ioncore_g.dpy;
644     ioncore_g.dpy=NULL;
645     
646     XSync(dpy, True);
647     XCloseDisplay(dpy);
648     
649     extl_deinit();
650 }
651
652
653 /*}}}*/
654
655
656 /*{{{ Miscellaneous */
657
658
659 /*EXTL_DOC
660  * Is Ion supporting locale-specifically multibyte-encoded strings?
661  */
662 EXTL_SAFE
663 EXTL_EXPORT
664 bool ioncore_is_i18n()
665 {
666     return ioncore_g.use_mb;
667 }
668
669
670 /*EXTL_DOC
671  * Returns Ioncore version string.
672  */
673 EXTL_SAFE
674 EXTL_EXPORT
675 const char *ioncore_version()
676 {
677     return ION_VERSION;
678 }
679
680 /*EXTL_DOC
681  * Returns the name of program using Ioncore.
682  */
683 EXTL_SAFE
684 EXTL_EXPORT
685 const char *ioncore_progname()
686 {
687     return progname;
688 }
689
690
691 /*EXTL_DOC
692  * Returns an about message (version, author, copyright notice).
693  */
694 EXTL_SAFE
695 EXTL_EXPORT
696 const char *ioncore_aboutmsg()
697 {
698     return ioncore_about;
699 }
700
701
702 /*}}}*/
703