#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <X11/Xlib.h>

Display *dpy;
Window root;
Atom wm_state;
GtkWidget *window, *bbox, *quit_button;
extern gint gdk_error_warnings; /* wonderfully documented, that. */

void add_buttons();
void add_launcher_buttons();
void add_icon_buttons();
int  get_state();
void make_icon_button();
void run_program();
void raise_window();
gint check_event();
void remove_icon_button();

int main(int argc, char *argv[])
{
    dpy = XOpenDisplay(NULL);
    root = DefaultRootWindow(dpy);
    wm_state = XInternAtom(dpy, "WM_STATE", False);
    XSelectInput(dpy, root, SubstructureNotifyMask);

    gtk_init(&argc, &argv);
    gdk_error_warnings = 0;
    add_buttons();
    gtk_timeout_add(100, check_event, NULL);

    gtk_main();
    exit(0);
}

void add_buttons()
{
    window = gtk_window_new(GTK_WINDOW_POPUP);
    gtk_widget_set_usize(window, 75, 768);
    gtk_container_set_resize_mode(GTK_CONTAINER(window), GTK_RESIZE_QUEUE);
    gtk_container_set_border_width(GTK_CONTAINER(window), 3);
    gtk_signal_connect(GTK_OBJECT(window), "destroy",
	GTK_SIGNAL_FUNC(gtk_main_quit), NULL);

    bbox = gtk_vbox_new(FALSE, 3);
    gtk_container_add(GTK_CONTAINER(window), bbox);

    quit_button = gtk_button_new_with_label("Quit");
    gtk_signal_connect(GTK_OBJECT(quit_button), "clicked",
	GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
    gtk_box_pack_end(GTK_BOX(bbox), quit_button, FALSE, FALSE, 0);

    add_launcher_buttons(bbox);
    add_icon_buttons(root, bbox);

    gtk_widget_show_all(window);
}

void add_launcher_buttons(GtkWidget *container)
{
    GtkWidget *button;
    char buf[80];

    while (fgets(buf, 80, stdin)) {
	button = gtk_button_new_with_label(strtok(buf, ":"));
	gtk_signal_connect(GTK_OBJECT(button), "clicked",
	    GTK_SIGNAL_FUNC(run_program), strdup(strtok(NULL, "\n")));
	gtk_box_pack_start(GTK_BOX(container), button, FALSE, FALSE, 0);
    }
}

void add_icon_buttons(Window root, GtkWidget *container)
{
    Window dummy, *wins;
    unsigned int i, nwins;

    if (XQueryTree(dpy, root, &dummy, &dummy, &wins, &nwins) && wins) {
	for (i = 0; i < nwins; i++) {
	    if (get_state(wins[i]) > 0) make_icon_button(wins[i], container);
	    else add_icon_buttons(wins[i], container);
	}
	XFree(wins);
    }
}

int get_state(Window w)
{
    Atom real_type;
    int real_format, state = 0;
    unsigned long n, extra;
    unsigned char *data;

    if (XGetWindowProperty(dpy, w, wm_state, 0L, 2L, False, AnyPropertyType,
    &real_type, &real_format, &n, &extra, &data) == Success && n) {
	state = *(int *)data;
	XFree(data);
    }
    return state;
}

void make_icon_button(Window w, GtkWidget *container)
{
    GtkWidget *button;
    char *buf;

    XSelectInput(dpy, w, StructureNotifyMask);
    XGetIconName(dpy, w, &buf);
    button = gtk_button_new_with_label(buf);
    XFree(buf);

    gtk_signal_connect(GTK_OBJECT(button), "clicked",
	GTK_SIGNAL_FUNC(raise_window), (gpointer) w);
    gtk_object_set_user_data(GTK_OBJECT(button), (gpointer) w);
    gtk_box_pack_end(GTK_BOX(container), button, FALSE, FALSE, 0);
    gtk_widget_show(button);
}

void run_program(GtkWidget *widget, char *data)
{
    char command[strlen(data)+1];

    sprintf(command, "%s&", data);
    system(command);
}

void raise_window(GtkWidget *widget, Window w)
{
    XMapRaised(dpy, w);
    XSync(dpy, False);
}

gint check_event(gpointer dummy)
{
    XEvent ev;
    
    while (XCheckTypedEvent(dpy, MapNotify, &ev)) {
	gtk_container_foreach(GTK_CONTAINER(bbox),
	    remove_icon_button, (gpointer) ev.xmap.window);
	add_icon_buttons(ev.xmap.window, bbox);
    }

    while (XCheckTypedEvent(dpy, UnmapNotify, &ev)) {
	if (!get_state(ev.xunmap.window))
	    gtk_container_foreach(GTK_CONTAINER(bbox),
		remove_icon_button, (gpointer) ev.xdestroywindow.window);
    } 

    return TRUE;
}

void remove_icon_button(GtkWidget *widget, Window w)
{
    if ((Window) gtk_object_get_user_data(GTK_OBJECT(widget)) == w)
	gtk_container_remove(GTK_CONTAINER(bbox), widget);
}
