Logo Search packages:      
Sourcecode: ion3 version File versions  Download package

ioncore.c

/*
 * ion/ioncore/ioncore.c
 *
 * Copyright (c) Tuomo Valkonen 1999-2007. 
 *
 * Ion is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <ctype.h>
#ifndef CF_NO_LOCALE
#include <locale.h>
#include <langinfo.h>
#include <libintl.h>
#endif

#include <libtu/util.h>
#include <libtu/optparser.h>
#include <libextl/readconfig.h>
#include <libextl/extl.h>
#include <libmainloop/select.h>
#include <libmainloop/signal.h>
#include <libmainloop/hooks.h>
#include <libmainloop/exec.h>

#include "common.h"
#include "rootwin.h"
#include "event.h"
#include "cursor.h"
#include "global.h"
#include "modules.h"
#include "eventh.h"
#include "ioncore.h"
#include "manage.h"
#include "conf.h"
#include "binding.h"
#include "bindmaps.h"
#include "strings.h"
#include "gr.h"
#include "xic.h"
#include "netwm.h"
#include "focus.h"
#include "frame.h"
#include "saveload.h"
#include "infowin.h"
#include "activity.h"
#include "group-cw.h"
#include "group-ws.h"
#include "llist.h"
#include "exec.h"
#include "screen-notify.h"


#include "../version.h"
#include "exports.h"


/*{{{ Variables */


WGlobal ioncore_g;

static const char *progname="ion";

static const char ioncore_copy[]=
    "Ion " ION_VERSION ", copyright (c) Tuomo Valkonen 1999-2007.";

static const char ioncore_license[]=DUMMY_TR(
    "This program is free software; you can redistribute it and/or\n"
    "modify it under the terms of the GNU Lesser General Public\n"
    "License as published by the Free Software Foundation; either\n"
    "version 2.1 of the License, or (at your option) any later version.\n"
    "\n"
    "This program is distributed in the hope that it will be useful,\n"
    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
    "Lesser General Public License for more details.\n");

static const char *ioncore_about=NULL;

WHook *ioncore_post_layout_setup_hook=NULL;
WHook *ioncore_snapshot_hook=NULL;
WHook *ioncore_deinit_hook=NULL;


/*}}}*/


/*{{{ warn_nolog */


void ioncore_warn_nolog(const char *str)
{
    fprintf(stderr, "%s: %s\n", libtu_progname(), str);
}


/*}}}*/


/*{{{ init_locale */


#ifndef CF_NO_LOCALE


static bool check_encoding()
{
    int i;
    char chs[8]=" ";
    wchar_t wc;
    const char *langi, *ctype, *a, *b;
    bool enc_check_ok=FALSE;

    langi=nl_langinfo(CODESET);
    ctype=setlocale(LC_CTYPE, NULL);
    
    if(langi==NULL || ctype==NULL)
        goto integr_err;

    if(strcmp(ctype, "C")==0 || strcmp(ctype, "POSIX")==0)
        return TRUE;
    
    /* Compare encodings case-insensitively, ignoring dashes (-) */
    a=langi; 
    b=strchr(ctype, '.');
    if(b!=NULL){
        b=b+1;
        while(1){
            if(*a=='-'){
                a++;
                continue;
            }
            if(*b=='-'){
                b++;
                continue;
            }
            if(*b=='\0' || *b=='@'){
                enc_check_ok=(*a=='\0');
                break;
            }
            if(*a=='\0' || tolower(*a)!=tolower(*b))
                break;
            a++;
            b++;
        }
        if(!enc_check_ok)
            goto integr_err;
    }else{
        ioncore_warn_nolog(TR("No encoding given in LC_CTYPE."));
    }
        
    if(strcasecmp(langi, "UTF-8")==0 || strcasecmp(langi, "UTF8")==0){
        ioncore_g.enc_sb=FALSE;
        ioncore_g.enc_utf8=TRUE;
        ioncore_g.use_mb=TRUE;
        return TRUE;
    }
    
    for(i=0; i<256; i++){
        chs[0]=i;
        if(mbtowc(&wc, chs, 8)==-1){
            /* Doesn't look like a single-byte encoding. */
            break;
        }
        
    }
    
    if(i==256){
        /* Seems like a single-byte encoding... */
        ioncore_g.use_mb=TRUE;
        return TRUE;
    }

    if(mbtowc(NULL, NULL, 0)!=0){
        warn("Statefull encodings are unsupported.");
        return FALSE;
    }
    
    ioncore_g.enc_sb=FALSE;
    ioncore_g.use_mb=TRUE;

    return TRUE;
    
integr_err:
    warn("Cannot verify locale encoding setting integrity "
         "(LC_CTYPE=%s, nl_langinfo(CODESET)=%s). "
         "The LC_CTYPE environment variable should be of the form "
         "language_REGION.encoding (e.g. en_GB.UTF-8), and encoding "
         "should match the nl_langinfo value above.", ctype, langi);
    return FALSE;
}


static bool init_locale()
{
    const char *p;
    
    p=setlocale(LC_ALL, "");
    
    if(p==NULL){
        warn("setlocale() call failed.");
        return FALSE;
    }

    /*if(strcmp(p, "C")==0 || strcmp(p, "POSIX")==0)
        return TRUE;*/

    if(!XSupportsLocale()){
        warn("XSupportsLocale() failed.");
    }else{
        if(check_encoding())
            return TRUE;
    }
    
    warn("Reverting locale settings to \"C\".");
    
    if(setlocale(LC_ALL, "C")==NULL)
        warn("setlocale() call failed.");
        
    return FALSE;
}

#define TEXTDOMAIN "ion3"

static bool init_messages(const char *localedir)
{
    if(bindtextdomain(TEXTDOMAIN, localedir)==NULL){
        warn_err_obj("bindtextdomain");
        return FALSE;
    }else if(textdomain(TEXTDOMAIN)==NULL){
        warn_err_obj("textdomain");
        return FALSE;
    }
    return TRUE;
}


#endif


/*}}}*/


/*{{{ ioncore_init */


#define INIT_HOOK_(NM)                             \
    NM=mainloop_register_hook(#NM, create_hook()); \
    if(NM==NULL) return FALSE

#define ADD_HOOK_(NM, FN)                          \
    if(!hook_add(NM, (void (*)())FN)) return FALSE

#define INIT_HOOK(NM, DFLT) INIT_HOOK_(NM); ADD_HOOK_(NM, DFLT)

static bool init_hooks()
{
    INIT_HOOK_(ioncore_post_layout_setup_hook);
    INIT_HOOK_(ioncore_snapshot_hook);
    INIT_HOOK_(ioncore_deinit_hook);
    INIT_HOOK_(screen_managed_changed_hook);
    INIT_HOOK_(frame_managed_changed_hook);
    INIT_HOOK_(clientwin_mapped_hook);
    INIT_HOOK_(clientwin_unmapped_hook);
    INIT_HOOK_(clientwin_property_change_hook);
    
    INIT_HOOK_(region_notify_hook);
    ADD_HOOK_(region_notify_hook, ioncore_frame_quasiactivation_notify);
    ADD_HOOK_(region_notify_hook, ioncore_screen_activity_notify);
    
    INIT_HOOK(clientwin_do_manage_alt, clientwin_do_manage_default);
    INIT_HOOK(ioncore_handle_event_alt, ioncore_handle_event);
    INIT_HOOK(region_do_warp_alt, region_do_warp_default);
    INIT_HOOK(ioncore_exec_environ_hook, ioncore_setup_environ);
    
    mainloop_sigchld_hook=mainloop_register_hook("ioncore_sigchld_hook",
                                                 create_hook());
    mainloop_sigusr2_hook=mainloop_register_hook("ioncore_sigusr2_hook",
                                                 create_hook());
    
    return TRUE;
}


static bool register_classes()
{
    int fail=0;
    
    fail|=!ioncore_register_regclass(&CLASSDESCR(WClientWin), 
                                     (WRegionLoadCreateFn*)clientwin_load);
    fail|=!ioncore_register_regclass(&CLASSDESCR(WMPlex), 
                                     (WRegionLoadCreateFn*)mplex_load);
    fail|=!ioncore_register_regclass(&CLASSDESCR(WFrame), 
                                     (WRegionLoadCreateFn*)frame_load);
    fail|=!ioncore_register_regclass(&CLASSDESCR(WInfoWin), 
                                     (WRegionLoadCreateFn*)infowin_load);
    fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupCW), 
                                     (WRegionLoadCreateFn*)groupcw_load);
    fail|=!ioncore_register_regclass(&CLASSDESCR(WGroupWS), 
                                     (WRegionLoadCreateFn*)groupws_load);
    
    return !fail;
}


#define INITSTR(NM)                             \
    ioncore_g.notifies.NM=stringstore_alloc(#NM); \
    if(ioncore_g.notifies.NM==STRINGID_NONE) return FALSE;
    
static bool init_global()
{
    /* argc, argv must be set be the program */
    ioncore_g.dpy=NULL;
    ioncore_g.display=NULL;

    ioncore_g.sm_client_id=NULL;
    ioncore_g.rootwins=NULL;
    ioncore_g.screens=NULL;
    ioncore_g.focus_next=NULL;
    ioncore_g.warp_next=FALSE;
    ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
    
    ioncore_g.focus_current=NULL;

    ioncore_g.input_mode=IONCORE_INPUTMODE_NORMAL;
    ioncore_g.opmode=IONCORE_OPMODE_INIT;
    ioncore_g.dblclick_delay=CF_DBLCLICK_DELAY;
    ioncore_g.opaque_resize=0;
    ioncore_g.warp_enabled=TRUE;
    ioncore_g.switchto_new=TRUE;
    ioncore_g.no_mousefocus=FALSE;
    ioncore_g.unsqueeze_enabled=TRUE;
    ioncore_g.autoraise=TRUE;
    
    ioncore_g.enc_utf8=FALSE;
    ioncore_g.enc_sb=TRUE;
    ioncore_g.use_mb=FALSE;
    
    ioncore_g.screen_notify=TRUE;
    
    ioncore_g.frame_default_index=LLIST_INDEX_AFTER_CURRENT_ACT;
    
    ioncore_g.framed_transients=TRUE;
    
    INITSTR(activated);
    INITSTR(inactivated);
    INITSTR(activity);
    INITSTR(sub_activity);
    INITSTR(name);
    INITSTR(unset_manager);
    INITSTR(set_manager);
    INITSTR(unset_return);
    INITSTR(set_return);
    INITSTR(pseudoactivated);
    INITSTR(pseudoinactivated);
    INITSTR(tag);
    INITSTR(deinit);
    INITSTR(map);
    INITSTR(unmap);
    
    return TRUE;
}


bool ioncore_init(const char *prog, int argc, char *argv[],
                  const char *localedir)
{
    if(!init_global())
        return FALSE;
    
    progname=prog;
    ioncore_g.argc=argc;
    ioncore_g.argv=argv;

#ifndef CF_NO_LOCALE    
    init_locale();
    init_messages(localedir);
#endif

    ioncore_about=scat3(ioncore_copy, "\n\n", TR(ioncore_license));
    
    if(!ioncore_init_bindmaps())
        return FALSE;
    
    if(!register_classes())
        return FALSE;

    if(!init_hooks())
        return FALSE;

    if(!ioncore_init_module_support())
        return FALSE;

    return TRUE;
}


/*}}}*/


/*{{{ ioncore_startup */


static void ioncore_init_session(const char *display)
{
    const char *dpyend=NULL;
    char *tmp=NULL, *colon=NULL;
    const char *sm=getenv("SESSION_MANAGER");
    
    if(sm!=NULL)
        ioncore_load_module("mod_sm");

    if(extl_sessiondir()!=NULL)
        return;
    
    /* Not running under SM; use display-specific directory */
    dpyend=strchr(display, ':');
    if(dpyend!=NULL)
        dpyend=strchr(dpyend, '.');
    if(dpyend==NULL){    
        libtu_asprintf(&tmp, "default-session-%s", display);
    }else{
        libtu_asprintf(&tmp, "default-session-%.*s",
                       (int)(dpyend-display), display);
    }
    
    if(tmp==NULL)
        return;
    
    colon=tmp;
    while(1){
        colon=strchr(colon, ':');
        if(colon==NULL)
            break;
        *colon='-';
    }
    
    extl_set_sessiondir(tmp);
    free(tmp);
}
    

static bool ioncore_init_x(const char *display, int stflags)
{
    Display *dpy;
    int i, drw, nrw;
    static bool called=FALSE;

    /* Sorry, this function can not be re-entered due to laziness
     * towards implementing checking of things already initialized.
     * Nobody would call this twice anyway.
     */
    assert(!called);
    called=TRUE;

    /* Open the display. */
    dpy=XOpenDisplay(display);
    
    if(dpy==NULL){
        warn(TR("Could not connect to X display '%s'"), 
             XDisplayName(display));
        return FALSE;
    }

    if(stflags&IONCORE_STARTUP_ONEROOT){
        drw=DefaultScreen(dpy);
        nrw=drw+1;
    }else{
        drw=0;
        nrw=ScreenCount(dpy);
    }
    
    /* Initialize */
    if(display!=NULL){
        ioncore_g.display=scopy(display);
        if(ioncore_g.display==NULL){
            XCloseDisplay(dpy);
            return FALSE;
        }
    }
    
    ioncore_g.dpy=dpy;
    ioncore_g.win_context=XUniqueContext();
    ioncore_g.conn=ConnectionNumber(dpy);
    
    cloexec_braindamage_fix(ioncore_g.conn);
    
    ioncore_g.atom_wm_state=XInternAtom(dpy, "WM_STATE", False);
    ioncore_g.atom_wm_change_state=XInternAtom(dpy, "WM_CHANGE_STATE", False);
    ioncore_g.atom_wm_protocols=XInternAtom(dpy, "WM_PROTOCOLS", False);
    ioncore_g.atom_wm_delete=XInternAtom(dpy, "WM_DELETE_WINDOW", False);
    ioncore_g.atom_wm_take_focus=XInternAtom(dpy, "WM_TAKE_FOCUS", False);
    ioncore_g.atom_wm_colormaps=XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
    ioncore_g.atom_wm_window_role=XInternAtom(dpy, "WM_WINDOW_ROLE", False);
    ioncore_g.atom_checkcode=XInternAtom(dpy, "_ION_CWIN_RESTART_CHECKCODE", False);
    ioncore_g.atom_selection=XInternAtom(dpy, "_ION_SELECTION_STRING", False);
    ioncore_g.atom_mwm_hints=XInternAtom(dpy, "_MOTIF_WM_HINTS", False);

    ioncore_init_xim();
    ioncore_init_bindings();
    ioncore_init_cursors();

    netwm_init();
    
    ioncore_init_session(XDisplayName(display));

    for(i=drw; i<nrw; i++)
        create_rootwin(i);

    if(ioncore_g.rootwins==NULL){
        if(nrw-drw>1)
            warn(TR("Could not find a screen to manage."));
        return FALSE;
    }

    if(!mainloop_register_input_fd(ioncore_g.conn, NULL,
                                   ioncore_x_connection_handler)){
        return FALSE;
    }
    
    return TRUE;
}


static void set_initial_focus()
{
    Window root=None, win=None;
    int x, y, wx, wy;
    uint mask;
    WScreen *scr;
    WWindow *wwin;
    
    XQueryPointer(ioncore_g.dpy, None, &root, &win,
                  &x, &y, &wx, &wy, &mask);
    
    FOR_ALL_SCREENS(scr){
        Window scrroot=region_root_of((WRegion*)scr);
        if(scrroot==root && rectangle_contains(&REGION_GEOM(scr), x, y)){
            break;
        }
    }
    
    if(scr==NULL)
        scr=ioncore_g.screens;
    
    region_focuslist_push((WRegion*)scr);
    region_do_set_focus((WRegion*)scr, FALSE);
}


bool ioncore_startup(const char *display, const char *cfgfile,
                     int stflags)
{
    WRootWin *rootwin;
    sigset_t inittrap;

    /* Don't trap termination signals just yet. */
    sigemptyset(&inittrap);
    sigaddset(&inittrap, SIGALRM);
    sigaddset(&inittrap, SIGCHLD);
    sigaddset(&inittrap, SIGPIPE);
    sigaddset(&inittrap, SIGUSR2);
    mainloop_trap_signals(&inittrap);

    if(!extl_init())
        return FALSE;

    ioncore_register_exports();
    
    if(!ioncore_init_x(display, stflags))
        return FALSE;

    gr_read_config();

    if(!extl_read_config("ioncore_ext", NULL, TRUE))
        return FALSE;
    
    ioncore_read_main_config(cfgfile);
    
    if(!ioncore_init_layout())
        return FALSE;
    
    hook_call_v(ioncore_post_layout_setup_hook);
    
    FOR_ALL_ROOTWINS(rootwin)
        rootwin_manage_initial_windows(rootwin);
    
    set_initial_focus();
    
    return TRUE;
}


/*}}}*/


/*{{{ ioncore_deinit */


void ioncore_deinit()
{
    Display *dpy;
    WRootWin *rootwin;
    
    ioncore_g.opmode=IONCORE_OPMODE_DEINIT;
    
    if(ioncore_g.dpy==NULL)
        return;

    hook_call_v(ioncore_deinit_hook);

    while(ioncore_g.screens!=NULL)
        destroy_obj((Obj*)ioncore_g.screens);

    /*ioncore_unload_modules();*/

    while(ioncore_g.rootwins!=NULL)
        destroy_obj((Obj*)ioncore_g.rootwins);

    ioncore_deinit_bindmaps();

    mainloop_unregister_input_fd(ioncore_g.conn);
    
    dpy=ioncore_g.dpy;
    ioncore_g.dpy=NULL;
    
    XSync(dpy, True);
    XCloseDisplay(dpy);
    
    extl_deinit();
}


/*}}}*/


/*{{{ Miscellaneous */


/*EXTL_DOC
 * Is Ion supporting locale-specifically multibyte-encoded strings?
 */
EXTL_SAFE
EXTL_EXPORT
bool ioncore_is_i18n()
{
    return ioncore_g.use_mb;
}


/*EXTL_DOC
 * Returns Ioncore version string.
 */
EXTL_SAFE
EXTL_EXPORT
const char *ioncore_version()
{
    return ION_VERSION;
}

/*EXTL_DOC
 * Returns the name of program using Ioncore.
 */
EXTL_SAFE
EXTL_EXPORT
const char *ioncore_progname()
{
    return progname;
}


/*EXTL_DOC
 * Returns an about message (version, author, copyright notice).
 */
EXTL_SAFE
EXTL_EXPORT
const char *ioncore_aboutmsg()
{
    return ioncore_about;
}


/*}}}*/


Generated by  Doxygen 1.6.0   Back to index