]> git.decadent.org.uk Git - ion3.git/blob - ioncore/ioncore.c
[svn-inject] Installing original source of ion3
[ion3.git] / ioncore / ioncore.c
1 /*
2  * ion/ioncore/ioncore.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2006. 
5  *
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.
10  */
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <fcntl.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <ctype.h>
20 #ifndef CF_NO_LOCALE
21 #include <locale.h>
22 #include <langinfo.h>
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
60 #include "../version.h"
61 #include "exports.h"
62
63
64 /*{{{ Variables */
65
66
67 WGlobal ioncore_g;
68
69 static const char *progname="ion";
70
71 static const char ioncore_copy[]=
72     "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2006.";
73
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"
79     "\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");
84
85 static const char *ioncore_about=NULL;
86
87 WHook *ioncore_post_layout_setup_hook=NULL;
88 WHook *ioncore_snapshot_hook=NULL;
89 WHook *ioncore_deinit_hook=NULL;
90
91
92 /*}}}*/
93
94
95 /*{{{ warn_nolog */
96
97
98 void ioncore_warn_nolog(const char *str)
99 {
100     fprintf(stderr, "%s: %s\n", prog_execname(), str);
101 }
102
103
104 /*}}}*/
105
106
107 /*{{{ init_locale */
108
109
110 #ifndef CF_NO_LOCALE
111
112
113 static bool check_encoding()
114 {
115     int i;
116     char chs[8]=" ";
117     wchar_t wc;
118     const char *langi, *ctype, *a, *b;
119     bool enc_check_ok=FALSE;
120
121     langi=nl_langinfo(CODESET);
122     ctype=setlocale(LC_CTYPE, NULL);
123     
124     if(langi==NULL || ctype==NULL)
125         goto integr_err;
126
127     if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
128         return TRUE;
129     
130     /* Compare encodings case-insensitively, ignoring dashes (-) */
131     a=langi; 
132     b=strchr(ctype, '.');
133     if(b!=NULL){
134         b=b+1;
135         while(1){
136             if(*a=='-'){
137                 a++;
138                 continue;
139             }
140             if(*b=='-'){
141                 b++;
142                 continue;
143             }
144             if(*b=='\0' || *b=='@'){
145                 enc_check_ok=(*a=='\0');
146                 break;
147             }
148             if(*a=='\0' || tolower(*a)!=tolower(*b))
149                 break;
150             a++;
151             b++;
152         }
153         if(!enc_check_ok)
154             goto integr_err;
155     }else{
156         ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
157     }
158         
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;
163         return TRUE;
164     }
165     
166     for(i=0; i<256; i++){
167         chs[0]=i;
168         if(mbtowc(&wc, chs, 8)==-1){
169             /* Doesn't look like a single-byte encoding. */
170             break;
171         }
172         
173     }
174     
175     if(i==256){
176         /* Seems like a single-byte encoding... */
177         ioncore_g.use_mb=TRUE;
178         return TRUE;
179     }
180
181     if(mbtowc(NULL, NULL, 0)!=0){
182         warn("Statefull encodings are unsupported.");
183         return FALSE;
184     }
185     
186     ioncore_g.enc_sb=FALSE;
187     ioncore_g.use_mb=TRUE;
188
189     return TRUE;
190     
191 integr_err:
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);
197     return FALSE;
198 }
199
200
201 static bool init_locale()
202 {
203     const char *p;
204     
205     p=setlocale(LC_ALL, "");
206     
207     if(p==NULL){
208         warn("setlocale() call failed.");
209         return FALSE;
210     }
211
212     /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
213         return TRUE;*/
214
215     if(!XSupportsLocale()){
216         warn("XSupportsLocale() failed.");
217     }else{
218         if(check_encoding())
219             return TRUE;
220     }
221     
222     warn("Reverting locale settings to \"C\".");
223     
224     if(setlocale(LC_ALL, "C")==NULL)
225         warn("setlocale() call failed.");
226         
227     return FALSE;
228 }
229
230 #define TEXTDOMAIN "ion3"
231
232 static bool init_messages(const char *localedir)
233 {
234     if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
235         warn_err_obj("bindtextdomain");
236         return FALSE;
237     }else if(textdomain(TEXTDOMAIN)==NULL){
238         warn_err_obj("textdomain");
239         return FALSE;
240     }
241     return TRUE;
242 }
243
244
245 #endif
246
247
248 /*}}}*/
249
250
251 /*{{{ ioncore_init */
252
253
254 #define INIT_HOOK_(NM)                             \
255     NM=mainloop_register_hook(#NM, create_hook()); \
256     if(NM==NULL) return FALSE;
257
258 #define INIT_HOOK(NM, DFLT)                           \
259     INIT_HOOK_(NM)                                    \
260     if(!hook_add(NM, (void (*)())DFLT)) return FALSE;
261
262
263 static bool init_hooks()
264 {
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);
276     
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);
280
281     mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
282                                                  create_hook());
283     mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
284                                                  create_hook());
285     
286     return TRUE;
287 }
288
289
290 static bool register_classes()
291 {
292     int fail=0;
293     
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);
306     
307     return !fail;
308 }
309
310
311 static void init_global()
312 {
313     /* argc, argv must be set be the program */
314     ioncore_g.dpy=NULL;
315     ioncore_g.display=NULL;
316
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;
322     
323     ioncore_g.focus_current=NULL;
324
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;
331     
332     ioncore_g.enc_utf8=FALSE;
333     ioncore_g.enc_sb=TRUE;
334     ioncore_g.use_mb=FALSE;
335     
336     ioncore_g.screen_notify=TRUE;
337     
338     ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
339     
340     ioncore_g.framed_transients=TRUE;
341 }
342
343
344 bool ioncore_init(const char *prog, int argc, char *argv[],
345                   const char *localedir)
346 {
347     init_global();
348     
349     progname=prog;
350     ioncore_g.argc=argc;
351     ioncore_g.argv=argv;
352
353 #ifndef CF_NO_LOCALE    
354     init_locale();
355     init_messages(localedir);
356 #endif
357
358     ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
359     
360     if(!ioncore_init_bindmaps())
361         return FALSE;
362     
363     if(!register_classes())
364         return FALSE;
365
366     if(!init_hooks())
367         return FALSE;
368
369     if(!ioncore_init_module_support())
370         return FALSE;
371
372     return TRUE;
373 }
374
375
376 /*}}}*/
377
378
379 /*{{{ ioncore_startup */
380
381
382 static void ioncore_init_session(const char *display)
383 {
384     const char *dpyend=NULL;
385     char *tmp=NULL, *colon=NULL;
386     const char *sm=getenv("SESSION_MANAGER");
387     
388     if(sm!=NULL)
389         ioncore_load_module("mod_sm");
390
391     if(extl_sessiondir()!=NULL)
392         return;
393     
394     /* Not running under SM; use display-specific directory */
395     dpyend=strchr(display, ':');
396     if(dpyend!=NULL)
397         dpyend=strchr(dpyend, '.');
398     if(dpyend==NULL){    
399         libtu_asprintf(&tmp, "default-session-%s", display);
400     }else{
401         libtu_asprintf(&tmp, "default-session-%.*s",
402                        (int)(dpyend-display), display);
403     }
404     
405     if(tmp==NULL)
406         return;
407     
408     colon=tmp;
409     while(1){
410         colon=strchr(colon, ':');
411         if(colon==NULL)
412             break;
413         *colon='-';
414     }
415     
416     extl_set_sessiondir(tmp);
417     free(tmp);
418 }
419     
420
421 static bool ioncore_init_x(const char *display, int stflags)
422 {
423     Display *dpy;
424     int i, drw, nrw;
425     static bool called=FALSE;
426
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.
430      */
431     assert(!called);
432     called=TRUE;
433
434     /* Open the display. */
435     dpy=XOpenDisplay(display);
436     
437     if(dpy==NULL){
438         warn(TR("Could not connect to X display '%s'"), 
439              XDisplayName(display));
440         return FALSE;
441     }
442
443     if(stflags&IONCORE_STARTUP_ONEROOT){
444         drw=DefaultScreen(dpy);
445         nrw=drw+1;
446     }else{
447         drw=0;
448         nrw=ScreenCount(dpy);
449     }
450     
451     /* Initialize */
452     if(display!=NULL){
453         ioncore_g.display=scopy(display);
454         if(ioncore_g.display==NULL){
455             XCloseDisplay(dpy);
456             return FALSE;
457         }
458     }
459     
460     ioncore_g.dpy=dpy;
461     ioncore_g.win_context=XUniqueContext();
462     ioncore_g.conn=ConnectionNumber(dpy);
463     
464     cloexec_braindamage_fix(ioncore_g.conn);
465     
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);
476
477     ioncore_init_xim();
478     ioncore_init_bindings();
479     ioncore_init_cursors();
480
481     netwm_init();
482     
483     ioncore_init_session(XDisplayName(display));
484
485     for(i=drw; i<nrw; i++)
486         ioncore_manage_rootwin(i, stflags&IONCORE_STARTUP_NOXINERAMA);
487
488     if(ioncore_g.rootwins==NULL){
489         if(nrw-drw>1)
490             warn(TR("Could not find a screen to manage."));
491         return FALSE;
492     }
493
494     if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
495                                    ioncore_x_connection_handler)){
496         return FALSE;
497     }
498     
499     return TRUE;
500 }
501
502
503 static void set_initial_focus()
504 {
505     Window root=None, win=None;
506     int x, y, wx, wy;
507     uint mask;
508     WScreen *scr;
509     WWindow *wwin;
510     
511     XQueryPointer(ioncore_g.dpy, None, &root, &win,
512                   &x, &y, &wx, &wy, &mask);
513     
514     FOR_ALL_SCREENS(scr){
515         Window scrroot=region_root_of((WRegion*)scr);
516         if(scrroot==root && rectangle_contains(&REGION_GEOM(scr), x, y)){
517             break;
518         }
519     }
520     
521     if(scr==NULL)
522         scr=ioncore_g.screens;
523     
524     region_focuslist_push((WRegion*)scr);
525     region_do_set_focus((WRegion*)scr, FALSE);
526 }
527
528
529 bool ioncore_startup(const char *display, const char *cfgfile,
530                      int stflags)
531 {
532     WRootWin *rootwin;
533     sigset_t inittrap;
534
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);
542
543     if(!extl_init())
544         return FALSE;
545
546     ioncore_register_exports();
547     
548     if(!ioncore_init_x(display, stflags))
549         return FALSE;
550
551     gr_read_config();
552
553     if(!extl_read_config("ioncore_ext", NULL, TRUE))
554         return FALSE;
555     
556     ioncore_read_main_config(cfgfile);
557     
558     if(!ioncore_init_layout())
559         return FALSE;
560     
561     hook_call_v(ioncore_post_layout_setup_hook);
562     
563     FOR_ALL_ROOTWINS(rootwin)
564         rootwin_manage_initial_windows(rootwin);
565     
566     set_initial_focus();
567     
568     return TRUE;
569 }
570
571
572 /*}}}*/
573
574
575 /*{{{ ioncore_deinit */
576
577
578 void ioncore_deinit()
579 {
580     Display *dpy;
581     WRootWin *rootwin;
582     
583     ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
584     
585     if(ioncore_g.dpy==NULL)
586         return;
587
588     hook_call_v(ioncore_deinit_hook);
589
590     while(ioncore_g.screens!=NULL)
591         destroy_obj((Obj*)ioncore_g.screens);
592
593     /*ioncore_unload_modules();*/
594
595     while(ioncore_g.rootwins!=NULL)
596         destroy_obj((Obj*)ioncore_g.rootwins);
597
598     ioncore_deinit_bindmaps();
599
600     mainloop_unregister_input_fd(ioncore_g.conn);
601     
602     dpy=ioncore_g.dpy;
603     ioncore_g.dpy=NULL;
604     
605     XSync(dpy, True);
606     XCloseDisplay(dpy);
607     
608     extl_deinit();
609 }
610
611
612 /*}}}*/
613
614
615 /*{{{ Miscellaneous */
616
617
618 /*EXTL_DOC
619  * Is Ion supporting locale-specifically multibyte-encoded strings?
620  */
621 EXTL_SAFE
622 EXTL_EXPORT
623 bool ioncore_is_i18n()
624 {
625     return ioncore_g.use_mb;
626 }
627
628
629 /*EXTL_DOC
630  * Returns Ioncore version string.
631  */
632 EXTL_SAFE
633 EXTL_EXPORT
634 const char *ioncore_version()
635 {
636     return ION_VERSION;
637 }
638
639 /*EXTL_DOC
640  * Returns the name of program using Ioncore.
641  */
642 EXTL_SAFE
643 EXTL_EXPORT
644 const char *ioncore_progname()
645 {
646     return progname;
647 }
648
649
650 /*EXTL_DOC
651  * Returns an about message (version, author, copyright notice).
652  */
653 EXTL_SAFE
654 EXTL_EXPORT
655 const char *ioncore_aboutmsg()
656 {
657     return ioncore_about;
658 }
659
660
661 /*}}}*/
662