/*
 *  trans-inactive.c - Daemon to set inactive windows semi-transparent,
 *  and the active window opaque.
 *
 *  Copyright (c) 2006 Brian Tarricone <bjt23@cornell.edu>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *  To compile:
 *  gcc -o trans-inactive trans-inactive.c -L/usr/X11R6/lib -lX11
 */

#include <stdio.h>
#include <limits.h>

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>

/* Set this to the desired opacity of inactive windows.  The example below
 * sets inactive windows 80% opaque (or, equivalently, 20% transparent).
 */
#define INACTIVE_OPACITY (0.80 * 0xffffffff)

static Atom
get_window_type(Display *dpy,
                Window win,
                Atom property)
{
    Atom window_type = None;
    Atom actual_type;
    int actual_format;
    unsigned long nitems, bytes_after;
    unsigned char *prop_return = NULL;
    
    if(Success == XGetWindowProperty(dpy, win, property, 0L, sizeof(Atom),
                                     False, XA_ATOM, &actual_type,
                                     &actual_format, &nitems, &bytes_after,
                                     &prop_return) && prop_return)
    {
        window_type = *(Atom *)prop_return;
        XFree(prop_return);
    }
    
    return window_type;

}

static Window
get_active_win(Display *dpy,
               Window win,
               Atom property)
{
    Window active_win = None;
    Atom actual_type;
    int actual_format;
    unsigned long nitems, bytes_after;
    unsigned char *prop_return = NULL;
    
    if(Success == XGetWindowProperty(dpy, win, property, 0L, sizeof(Window),
                                     False, XA_WINDOW, &actual_type,
                                     &actual_format, &nitems, &bytes_after,
                                     &prop_return) && prop_return)
    {
        active_win = *(Window *)prop_return;
        XFree(prop_return);
    }
    
    return active_win;
}

static Window *
get_client_list(Display *dpy,
                Window root,
                unsigned long *nclients)
{
    Window *clients = NULL;
    Atom actual_type;
    int actual_format;
    unsigned long nitems, bytes_after;
    unsigned char *prop_ret = NULL;
    
    if(Success == XGetWindowProperty(dpy, root, XInternAtom(dpy,
                                                            "_NET_CLIENT_LIST",
                                                            False),
                                     0L, ULONG_MAX, False, XA_WINDOW,
                                     &actual_type, &actual_format, &nitems,
                                     &bytes_after, &prop_ret))
    {
        clients = (Window *)prop_ret;
        *nclients = nitems;
    }
    
    return clients;
}

static void
set_opacity(Display *dpy,
            Window win,
            Atom property,
            unsigned long opacity)
{
    XChangeProperty(dpy, win, property, XA_CARDINAL, 32, PropModeReplace,
                    (unsigned char *)&opacity, 1L);
}

static int
err_noop(Display *dpy,
         XErrorEvent *evt)
{
    /* do nothing */
    return 0;
}

int
main(int argc,
     char **argv)
{
    Display *dpy;
    Window root, old_active_window, active_window = None, *toplevels;
    XWindowAttributes attr;
    unsigned long ntoplevels = 0;
    Atom _NET_ACTIVE_WINDOW, _NET_WM_WINDOW_OPACITY, _NET_WM_WINDOW_TYPE,
         _NET_WM_WINDOW_TYPE_DESKTOP, _NET_WM_WINDOW_TYPE_DOCK, window_type;
    XEvent xev;
    
    dpy = XOpenDisplay(NULL);
    if(!dpy) {
        fprintf(stderr, "Failed to open display\n");
        return 1;
    }
    
    XSetErrorHandler(err_noop);
    
    root = DefaultRootWindow(dpy);
    XGetWindowAttributes(dpy, root, &attr);
    XSelectInput(dpy, root, attr.your_event_mask
                            | PropertyChangeMask | StructureNotifyMask
                            | SubstructureNotifyMask);
    
    _NET_WM_WINDOW_OPACITY = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False);
    _NET_ACTIVE_WINDOW = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
    _NET_WM_WINDOW_TYPE = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
    _NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(dpy,
                                              "_NET_WM_WINDOW_TYPE_DESKTOP",
                                              False);
    _NET_WM_WINDOW_TYPE_DOCK = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK",
                                           False);
    
    old_active_window = get_active_win(dpy, root, _NET_ACTIVE_WINDOW);

    toplevels = get_client_list(dpy, root, &ntoplevels);
    if(toplevels) {
        int i;
        
        for(i = 0; i < ntoplevels; i++) {
            if(toplevels[i] == old_active_window) {
                set_opacity(dpy, toplevels[i], _NET_WM_WINDOW_OPACITY,
                            0xffffffff);
                continue;
            }
            
            window_type = get_window_type(dpy, toplevels[i],
                                          _NET_WM_WINDOW_TYPE);
            if(window_type == _NET_WM_WINDOW_TYPE_DESKTOP
               || window_type == _NET_WM_WINDOW_TYPE_DOCK)
            {
                continue;
            }
            
            XGetWindowAttributes(dpy, toplevels[i], &attr);
            if(attr.override_redirect == True)
                continue;
            
            set_opacity(dpy, toplevels[i], _NET_WM_WINDOW_OPACITY,
                        INACTIVE_OPACITY);
        }
        
        XFree(toplevels);
    }
    
    for(;;) {
        if(!XNextEvent(dpy, &xev)) {
            switch(xev.type) {
                case PropertyNotify:
                    if(xev.xproperty.window != root
                       || xev.xproperty.atom != _NET_ACTIVE_WINDOW)
                    {
                        break;
                    }
                    
                    active_window = get_active_win(dpy, root,
                                                   _NET_ACTIVE_WINDOW);
                    if(active_window != old_active_window) {
                        if(old_active_window != None) {
                            XGetWindowAttributes(dpy, old_active_window, &attr);
                            window_type = get_window_type(dpy,
                                                          old_active_window,
                                                          _NET_WM_WINDOW_TYPE);
                            if(attr.override_redirect == False
                               && window_type != _NET_WM_WINDOW_TYPE_DESKTOP
                               && window_type != _NET_WM_WINDOW_TYPE_DOCK)
                            {
                                set_opacity(dpy, old_active_window,
                                            _NET_WM_WINDOW_OPACITY,
                                            INACTIVE_OPACITY);
                            }
                        }
                        
                        if(active_window != None) {
                            set_opacity(dpy, active_window,
                                        _NET_WM_WINDOW_OPACITY, 0xffffffff);
                        }
                        
                        old_active_window = active_window;
                    }
                    break;
            
                case MapNotify:
                    if(xev.xmap.window != get_active_win(dpy, root,
                                                         _NET_ACTIVE_WINDOW))
                    {
                        XGetWindowAttributes(dpy, xev.xmap.window, &attr);
                        window_type = get_window_type(dpy, xev.xmap.window,
                                                      _NET_WM_WINDOW_TYPE);
                        if(attr.override_redirect == False
                           && window_type != _NET_WM_WINDOW_TYPE_DESKTOP
                           && window_type != _NET_WM_WINDOW_TYPE_DOCK)
                        {
                            set_opacity(dpy, xev.xmap.window,
                                        _NET_WM_WINDOW_OPACITY,
                                        INACTIVE_OPACITY);
                        }
                    }
                    break;
                
                case UnmapNotify:
                    if(xev.xmap.window == old_active_window)
                        old_active_window = None;
                    break;
            }
        }
    }
    
    return 0;
}

