/* aewm - a minimalist X11 window mananager. vim:sw=4:ts=4:et * * Copyright 1998-2006 Decklin Foster . This * program is free software; please see LICENSE for details. */ #include #include #include #include #include "aeclients.h" #include "launch.h" #include "switch.h" #include "panel.h" void add_menu_item(menu_t, char *, char *); menu_t add_sub_menu(menu_t, char *); void make_client_button(menu_t, Window); void update_client_button(menu_t, Window); void remove_client_button(Window); void watch_window(Window, long); GdkFilterReturn check_event(GdkXEvent *, GdkEvent *, gpointer); void show_menu_cb(GtkWidget *, gpointer); void quit_cb(GtkWidget *, gpointer); void raise_win_cb(GtkWidget *, Window); void fork_exec_cb(GtkWidget *, char *); #define NAME_SIZE 60 GtkWidget *clients_box, *menu_button = NULL; deskarea_t orig_da; int opt_bottom; int main(int argc, char **argv) { GtkWidget *toplevel, *frame, *launch_menu, *bbox, *quit_button; int width, height, i; char *opt_config = NULL; struct sigaction act; deskarea_t new_da; gtk_init(&argc, &argv); #ifdef USE_OLD_GTK gdk_error_warnings = 0; /* gag me with a spoon... */ #else gdk_error_trap_push(); /* slightly better, i guess */ #endif for (i = 1; i < argc; i++) { if ARG("config", "rc", 1) { opt_config = argv[++i]; continue; } if ARG("bottom", "b", 0) { opt_bottom = 1; continue; } /* nothing matched */ fprintf(stderr, "usage: aepanel [--bottom|-b] [--config|-rc ]\n"); exit(2); } act.sa_handler = sig_handler; act.sa_flags = 0; sigaction(SIGCHLD, &act, NULL); dpy = GDK_DISPLAY(); root = GDK_ROOT_WINDOW(); gdk_window_get_size(GDK_ROOT_PARENT(), &width, &height); client_tab = XUniqueContext(); wm_state = XInternAtom(dpy, "WM_STATE", False); gnome_pda = XInternAtom(dpy, "GNOME_PANEL_DESKTOP_AREA", False); watch_window(root, SubstructureNotifyMask); toplevel = gtk_window_new(GTK_WINDOW_POPUP); gtk_widget_set_usize(toplevel, width, 0); launch_menu = gtk_menu_new(); frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT); gtk_container_add(GTK_CONTAINER(toplevel), frame); bbox = gtk_hbox_new(FALSE, SPACING); gtk_container_set_border_width(GTK_CONTAINER(bbox), SPACING-1); gtk_container_add(GTK_CONTAINER(frame), bbox); menu_button = gtk_button_new_with_label("Menu"); gtk_signal_connect(GTK_OBJECT(menu_button), "clicked", GTK_SIGNAL_FUNC(show_menu_cb), launch_menu); gtk_box_pack_start(GTK_BOX(bbox), menu_button, FALSE, FALSE, 0); quit_button = gtk_button_new_with_label("Quit"); gtk_signal_connect(GTK_OBJECT(quit_button), "clicked", GTK_SIGNAL_FUNC(quit_cb), NULL); gtk_box_pack_start(GTK_BOX(bbox), quit_button, FALSE, FALSE, 0); clients_box = gtk_hbox_new(TRUE, SPACING); gtk_container_add(GTK_CONTAINER(bbox), clients_box); gtk_container_set_resize_mode(GTK_CONTAINER(clients_box), GTK_RESIZE_QUEUE); make_launch_menu(opt_config, launch_menu, add_menu_item, add_sub_menu); update_clients(clients_box, root, make_client_button, update_client_button); gtk_widget_show_all(toplevel); if (opt_bottom) gtk_widget_set_uposition(toplevel, 0, height - toplevel->allocation.height); gdk_window_get_size(toplevel->window, &width, &height); get_gnome_pda(&orig_da); new_da = orig_da; if (opt_bottom) new_da.bottom = height; else new_da.top = height; set_gnome_pda(&new_da); gtk_main(); return 0; } void add_menu_item(menu_t menu, char *label, char *cmd) { GtkWidget *item; item = gtk_menu_item_new_with_label(label); gtk_menu_append(GTK_MENU(menu), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(fork_exec_cb), cmd); gtk_widget_show(item); } menu_t add_sub_menu(menu_t menu, char *label) { GtkWidget *item, *newmenu; newmenu = gtk_menu_new(); item = gtk_menu_item_new_with_label(label); gtk_menu_append(GTK_MENU(menu), item); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), newmenu); gtk_widget_show(item); return newmenu; } void make_client_button(menu_t clients_box, Window w) { GtkWidget *button; char buf[NAME_SIZE]; get_wm_name(w, buf, sizeof buf); button = gtk_button_new_with_label(buf); gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(raise_win_cb), (gpointer) w); gtk_box_pack_start(GTK_BOX(clients_box), button, TRUE, TRUE, 0); gtk_misc_set_alignment(GTK_MISC(GTK_BIN(button)->child), 0, 0.5); gtk_widget_show(button); XSaveContext(dpy, w, client_tab, (XPointer)button); watch_window(w, StructureNotifyMask|PropertyChangeMask); } void update_client_button(menu_t clients_box, Window w) { XPointer button; char buf[NAME_SIZE]; if (get_wm_state(w) == WithdrawnState) { remove_client_button(w); } else if (XFindContext(dpy, w, client_tab, &button) == Success) { get_wm_name(w, buf, sizeof buf); gtk_label_set_text(GTK_LABEL(GTK_BIN(button)->child), buf); } } void remove_client_button(Window w) { XPointer button; if (XFindContext(dpy, w, client_tab, &button) == Success) { gtk_container_remove(GTK_CONTAINER(clients_box), GTK_WIDGET(button)); XDeleteContext(dpy, w, client_tab); } } void watch_window(Window w, long mask) { GdkWindow *gdkwin = gdk_window_lookup(w); XSelectInput(dpy, w, mask); gdk_window_add_filter(gdkwin, check_event, NULL); } GdkFilterReturn check_event(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer dummy) { XEvent *e = (XEvent *)gdk_xevent; switch (e->type) { case MapNotify: update_clients(clients_box, e->xmap.window, make_client_button, update_client_button); break; case PropertyNotify: update_clients(clients_box, e->xproperty.window, make_client_button, update_client_button); break; case DestroyNotify: remove_client_button(e->xdestroywindow.window); break; } return GDK_FILTER_CONTINUE; } #ifdef USE_OLD_GTK void menu_position(GtkMenu *menu, gint *x, gint *y, gpointer data) #else void menu_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) #endif { GtkWidget *button = GTK_WIDGET(data); gint wx, wy; gdk_window_get_root_origin(button->window, &wx, &wy); *x = wx + button->allocation.x; *y = wy + button->allocation.y; if (opt_bottom) { /* This is crap. I shouldn't have to do anything here, but if * the menu-position func gives coords near the bottom of the * screen, GTK+ will blindly try to follow them rather than * flipping the menu up (around the coord parallel to the * screen edge) like it would do in every other case (that is, * any instance where the coords are determined automatically). * Feh. All toolkits suck. Not only that, but this hack seems * to make drawing the menu a little bit slower. */ GtkRequisition req; gtk_widget_size_request(GTK_WIDGET(menu), &req); *y -= req.height; } else { *y += button->allocation.height; } } void show_menu_cb(GtkWidget *widget, gpointer menu) { gtk_menu_popup(menu, NULL, NULL, menu_position, menu_button, 0, 0); } void quit_cb(GtkWidget *widget, gpointer menu) { set_gnome_pda(&orig_da); gtk_main_quit(); } void raise_win_cb(GtkWidget *widget, Window w) { raise_win(w); } void fork_exec_cb(GtkWidget *widget, char *data) { fork_exec(data); }