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