aewm-1.3.12/0000755000175000001440000000000010734765715012076 5ustar decklinusersaewm-1.3.12/menu.h0000644000175000001440000000105510717144424013201 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #ifndef AEWM_CLIENTS_MENU_H #define AEWM_CLIENTS_MENU_H enum { LAUNCH, SWITCH }; typedef void *(*make_item_func)(void *, char *, char *); extern void setup_switch_atoms(); extern void snprint_wm_name(char *, size_t, Window); extern int is_on_cur_desk(Window); extern int is_skip(Window); extern void raise_win(Window); extern void make_launch_menu(char *, void *, make_item_func); #endif /* AEWM_CLIENTS_MENU_H */ aewm-1.3.12/aedesk.c0000644000175000001440000000334410731560221013460 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include #include #include "common.h" #include "atom.h" #define UMOD(x, y) ((((long)(x) % (long)(y)) + (y)) % (y)) Display *dpy; Window root; Atom net_cur_desk, net_num_desks; static unsigned long parse_desk(char *spec); int main(int argc, char **argv) { unsigned long desk; int i; if (argc < 2) { fprintf(stderr, "usage: aedesk [+-]|-n \n"); exit(2); } dpy = XOpenDisplay(NULL); root = DefaultRootWindow(dpy); net_cur_desk = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); net_num_desks = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); if (!dpy) { fprintf(stderr, "aedesk: can't open display %s\n", getenv("DISPLAY")); exit(1); } for (i = 1; i < argc; i++) { if ARG("setn", "n", 1) { desk = atol(argv[++i]); set_atoms(root, net_num_desks, XA_CARDINAL, &desk, 1); } else { desk = parse_desk(argv[i]); send_xmessage(root, net_cur_desk, desk, SubstructureNotifyMask); } } XCloseDisplay(dpy); return 0; } unsigned long parse_desk(char *spec) { unsigned long ndesks, cur_desk; if (strchr("+-", spec[0])) { if (get_atoms(root, net_cur_desk, XA_CARDINAL, 0, &cur_desk, 1, NULL) && get_atoms(root, net_num_desks, XA_CARDINAL, 0, &ndesks, 1, NULL)) { return UMOD(cur_desk + atol(spec), ndesks); } else { return 0; } } else { return atol(spec); } } aewm-1.3.12/aewm_init.c0000644000175000001440000003200110717144424014177 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include #include #include #include #include #ifdef SHAPE #include #endif #include "aewm.h" #include "atom.h" #include "parser.h" client_t *head; int screen; unsigned long ndesks = 1; unsigned long cur_desk = 0; #ifdef SHAPE Bool shape; int shape_event; #endif XFontStruct *font; #ifdef X_HAVE_UTF8_STRING XFontSet font_set; #endif #ifdef XFT XftFont *xftfont; XftColor xft_fg; #endif Colormap def_cmap; XColor fg; XColor bg; XColor bd; GC invert_gc; GC string_gc; GC border_gc; Cursor map_curs; Cursor move_curs; Cursor resize_curs; char *opt_font = DEF_FONT; #ifdef XFT char *opt_xftfont = DEF_XFTFONT; #endif char *opt_fg = DEF_FG; char *opt_bg = DEF_BG; char *opt_bd = DEF_BD; int opt_bw = DEF_BW; int opt_pad = DEF_PAD; int opt_imap = DEF_IMAP; char *opt_new[] = { DEF_NEW1, DEF_NEW2, DEF_NEW3, DEF_NEW4, DEF_NEW5 }; static void shutdown(void); static void read_config(char *); static void setup_display(void); int main(int argc, char **argv) { int i; struct sigaction act; setlocale(LC_ALL, ""); read_config(NULL); for (i = 1; i < argc; i++) { if ARG("config", "rc", 1) { read_config(argv[++i]); } else if ARG("font", "fn", 1) { opt_font = argv[++i]; #ifdef XFT } else if ARG("xftfont", "fa", 1) { opt_xftfont = argv[++i]; #endif } else if ARG("fgcolor", "fg", 1) { opt_fg = argv[++i]; } else if ARG("bgcolor", "bg", 1) { opt_bg = argv[++i]; } else if ARG("bdcolor", "bd", 1) { opt_bd = argv[++i]; } else if ARG("bdwidth", "bw", 1) { opt_bw = atoi(argv[++i]); } else if ARG("padding", "p", 1) { opt_pad = atoi(argv[++i]); } else if ARG("imap", "i", 0) { opt_imap = 1; } else if ARG("no-imap", "n", 0) { opt_imap = 0; } else if ARG("new1", "1", 1) { opt_new[0] = argv[++i]; } else if ARG("new2", "2", 1) { opt_new[1] = argv[++i]; } else if ARG("new3", "3", 1) { opt_new[2] = argv[++i]; } else if ARG("new4", "4", 1) { opt_new[3] = argv[++i]; } else if ARG("new5", "5", 1) { opt_new[4] = argv[++i]; } else if ARG("version", "v",0) { printf("aewm: version " VERSION "\n"); exit(0); } else if ARG("help", "h",0) { printf(USAGE); exit(0); } else { fprintf(stderr, "aewm: unknown option: '%s'\n" USAGE, argv[i]); exit(2); } } act.sa_handler = sig_handler; act.sa_flags = 0; sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGHUP, &act, NULL); sigaction(SIGCHLD, &act, NULL); setup_display(); event_loop(); return 0; } static void read_config(char *rcfile) { FILE *rc; char buf[BUF_SIZE], token[BUF_SIZE], *p; if (!(rc = open_rc(rcfile, "aewmrc"))) { if (rcfile) fprintf(stderr, "aewm: rc file '%s' not found\n", rcfile); return; } while (get_rc_line(buf, sizeof buf, rc)) { p = buf; while (get_token(&p, token)) { if (strcmp(token, "font") == 0) { if (get_token(&p, token)) opt_font = strdup(token); #ifdef XFT } else if (strcmp(token, "xftfont") == 0) { if (get_token(&p, token)) opt_xftfont = strdup(token); #endif } else if (strcmp(token, "fgcolor") == 0) { if (get_token(&p, token)) opt_fg = strdup(token); } else if (strcmp(token, "bgcolor") == 0) { if (get_token(&p, token)) opt_bg = strdup(token); } else if (strcmp(token, "bdcolor") == 0) { if (get_token(&p, token)) opt_bd = strdup(token); } else if (strcmp(token, "bdwidth") == 0) { if (get_token(&p, token)) opt_bw = atoi(token); } else if (strcmp(token, "padding") == 0) { if (get_token(&p, token)) opt_pad = atoi(token); } else if (strcmp(token, "imap") == 0) { if (get_token(&p, token)) opt_imap = atoi(token); } else if (strcmp(token, "button1") == 0) { if (get_token(&p, token)) opt_new[0] = strdup(token); } else if (strcmp(token, "button2") == 0) { if (get_token(&p, token)) opt_new[1] = strdup(token); } else if (strcmp(token, "button3") == 0) { if (get_token(&p, token)) opt_new[2] = strdup(token); } else if (strcmp(token, "button4") == 0) { if (get_token(&p, token)) opt_new[3] = strdup(token); } else if (strcmp(token, "button5") == 0) { if (get_token(&p, token)) opt_new[4] = strdup(token); } } } fclose(rc); } static void setup_display(void) { #ifdef X_HAVE_UTF8_STRING char **missing; char *def_str; int nmissing; #endif XGCValues gv; XColor exact; XSetWindowAttributes sattr; XWindowAttributes attr; #ifdef SHAPE int shape_err; #endif Window qroot, qparent, *wins; unsigned int nwins, i; client_t *c; dpy = XOpenDisplay(NULL); if (!dpy) { fprintf(stderr, "aewm: can't open $DISPLAY '%s'\n", getenv("DISPLAY")); exit(1); } XSetErrorHandler(handle_xerror); screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); map_curs = XCreateFontCursor(dpy, XC_dotbox); move_curs = XCreateFontCursor(dpy, XC_fleur); resize_curs = XCreateFontCursor(dpy, XC_sizing); def_cmap = DefaultColormap(dpy, screen); XAllocNamedColor(dpy, def_cmap, opt_fg, &fg, &exact); XAllocNamedColor(dpy, def_cmap, opt_bg, &bg, &exact); XAllocNamedColor(dpy, def_cmap, opt_bd, &bd, &exact); font = XLoadQueryFont(dpy, opt_font); if (!font) { fprintf(stderr, "aewm: font '%s' not found\n", opt_font); exit(1); } #ifdef X_HAVE_UTF8_STRING font_set = XCreateFontSet(dpy, opt_font, &missing, &nmissing, &def_str); #endif #ifdef XFT xft_fg.color.red = fg.red; xft_fg.color.green = fg.green; xft_fg.color.blue = fg.blue; xft_fg.color.alpha = 0xffff; xft_fg.pixel = fg.pixel; xftfont = XftFontOpenName(dpy, DefaultScreen(dpy), opt_xftfont); if (!xftfont) { fprintf(stderr, "aewm: Xft font '%s' not found\n", opt_font); exit(1); } #endif gv.function = GXcopy; gv.foreground = fg.pixel; gv.font = font->fid; string_gc = XCreateGC(dpy, root, GCFunction|GCForeground|GCFont, &gv); gv.foreground = bd.pixel; gv.line_width = opt_bw; border_gc = XCreateGC(dpy, root, GCFunction|GCForeground|GCLineWidth, &gv); gv.function = GXinvert; gv.subwindow_mode = IncludeInferiors; invert_gc = XCreateGC(dpy, root, GCFunction|GCSubwindowMode|GCLineWidth|GCFont, &gv); utf8_string = XInternAtom(dpy, "UTF8_STRING", False); wm_protos = XInternAtom(dpy, "WM_PROTOCOLS", False); wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); wm_state = XInternAtom(dpy, "WM_STATE", False); wm_change_state = XInternAtom(dpy, "WM_CHANGE_STATE", False); net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False); net_cur_desk = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); net_num_desks = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); net_client_list = XInternAtom(dpy, "_NET_CLIENT_LIST", False); net_client_stack = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False); net_active_window = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); net_close_window = XInternAtom(dpy, "_NET_CLOSE_WINDOW", False); net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", False); net_wm_desk = XInternAtom(dpy, "_NET_WM_DESKTOP", False); net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); net_wm_state_shaded = XInternAtom(dpy, "_NET_WM_STATE_SHADED", False); net_wm_state_mv = XInternAtom(dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False); net_wm_state_mh = XInternAtom(dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False); net_wm_state_fs = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); net_wm_strut = XInternAtom(dpy, "_NET_WM_STRUT", False); net_wm_strut_partial = XInternAtom(dpy, "_NET_WM_STRUT_PARTIAL", False); net_wm_wintype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); net_wm_type_dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); net_wm_type_menu = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False); net_wm_type_splash = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False); net_wm_type_desk = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); /* The bit about _NET_CLIENT_LIST_STACKING here is an evil lie. */ append_atoms(root, net_supported, XA_ATOM, &net_cur_desk, 1); append_atoms(root, net_supported, XA_ATOM, &net_num_desks, 1); append_atoms(root, net_supported, XA_ATOM, &net_client_list, 1); append_atoms(root, net_supported, XA_ATOM, &net_client_stack, 1); append_atoms(root, net_supported, XA_ATOM, &net_active_window, 1); append_atoms(root, net_supported, XA_ATOM, &net_close_window, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_name, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_desk, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_state, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_state_shaded, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_state_mv, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_state_mh, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_state_fs, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_strut, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_strut_partial, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_wintype, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_type_dock, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_type_menu, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_type_splash, 1); append_atoms(root, net_supported, XA_ATOM, &net_wm_type_desk, 1); get_atoms(root, net_num_desks, XA_CARDINAL, 0, &ndesks, 1, NULL); get_atoms(root, net_cur_desk, XA_CARDINAL, 0, &cur_desk, 1, NULL); #ifdef SHAPE shape = XShapeQueryExtension(dpy, &shape_event, &shape_err); #endif XQueryTree(dpy, root, &qroot, &qparent, &wins, &nwins); for (i = 0; i < nwins; i++) { XGetWindowAttributes(dpy, wins[i], &attr); if (!attr.override_redirect && attr.map_state == IsViewable) { c = new_client(wins[i]); map_client(c); } } XFree(wins); sattr.event_mask = SubMask|ColormapChangeMask|ButtonMask; XChangeWindowAttributes(dpy, root, CWEventMask, &sattr); } void sig_handler(int signum) { switch (signum) { case SIGINT: case SIGTERM: case SIGHUP: shutdown(); break; case SIGCHLD: wait(NULL); break; } } int handle_xerror(Display *dpy, XErrorEvent *e) { #ifdef DEBUG client_t *c = find_client(e->resourceid, MATCH_WINDOW); #endif if (e->error_code == BadAccess && e->resourceid == root) { fprintf(stderr, "aewm: root window unavailable\n"); exit(1); } else { char msg[255]; XGetErrorText(dpy, e->error_code, msg, sizeof msg); fprintf(stderr, "aewm: X error (%#lx): %s\n", e->resourceid, msg); #ifdef DEBUG if (c) dump_info(c); #endif } #ifdef DEBUG if (c) del_client(c, DEL_WITHDRAW); #endif return 0; } /* Ick. Argh. You didn't see this function. */ int ignore_xerror(Display *dpy, XErrorEvent *e) { return 0; } /* We use XQueryTree here to preserve the window stacking order, since * the order in our linked list is different. */ static void shutdown(void) { unsigned int nwins, i; Window qroot, qparent, *wins; client_t *c; XQueryTree(dpy, root, &qroot, &qparent, &wins, &nwins); for (i = 0; i < nwins; i++) { c = find_client(wins[i], MATCH_FRAME); if (c) del_client(c, DEL_REMAP); } XFree(wins); XFreeFont(dpy, font); #ifdef X_HAVE_UTF8_STRING XFreeFontSet(dpy, font_set); #endif #ifdef XFT XftFontClose(dpy, xftfont); #endif XFreeCursor(dpy, map_curs); XFreeCursor(dpy, move_curs); XFreeCursor(dpy, resize_curs); XFreeGC(dpy, invert_gc); XFreeGC(dpy, border_gc); XFreeGC(dpy, string_gc); XInstallColormap(dpy, DefaultColormap(dpy, screen)); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); XDeleteProperty(dpy, root, net_supported); XDeleteProperty(dpy, root, net_client_list); XCloseDisplay(dpy); exit(0); } aewm-1.3.12/common.h0000644000175000001440000000134410717144424013526 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #ifndef AEWM_COMMON_H #define AEWM_COMMON_H #include #define SYS_RC_DIR "/etc/X11/aewm" /* Hooray for magic numbers */ #define DESK_ALL 0xFFFFFFFF #define IS_ON_DESK(w, d) (w == d || w == DESK_ALL) #define BUF_SIZE 2048 #define ARG(longname, shortname, nargs) \ ((strcmp(argv[i], "--" longname) == 0 || \ strcmp(argv[i], "-" shortname) == 0) && i + (nargs) < argc) extern Display *dpy; extern Window root; extern void fork_exec(char *); extern int get_pointer(int *, int *); extern int send_xmessage(Window, Atom, unsigned long, unsigned long); #endif /* AEWM_COMMON_H */ aewm-1.3.12/aemenu.c0000644000175000001440000000565610717144424013515 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include #include #include #include #include "common.h" #include "atom.h" #include "menu.h" void *make_launchitem_cb(void *, char *, char *); void make_clientitem(GtkWidget *, Window); void fork_exec_cb(GtkWidget *, char *); void raise_win_cb(GtkWidget *, Window); int main(int argc, char **argv) { GtkWidget *main_menu; int i, mode = LAUNCH; char *opt_config = NULL; unsigned long read, left; Window w; setlocale(LC_ALL, ""); gtk_init(&argc, &argv); for (i = 1; i < argc; i++) { if ARG("config", "rc", 1) { opt_config = argv[++i]; } else if ARG("launch", "l", 0) { mode = LAUNCH; } else if ARG("switch", "s", 0) { mode = SWITCH; } else { fprintf(stderr, "usage: aemenu [--switch|-s] [--config|-rc ]\n"); exit(2); } } main_menu = gtk_menu_new(); if (mode == LAUNCH) { make_launch_menu(opt_config, main_menu, make_launchitem_cb); } else /* mode == SWITCH */ { dpy = GDK_DISPLAY(); root = GDK_ROOT_WINDOW(); setup_switch_atoms(); for (i = 0, left = 1; left; i += read) { read = get_atoms(root, net_client_list, XA_WINDOW, i, &w, 1, &left); if (read) make_clientitem(main_menu, w); else break; } } gtk_signal_connect_object(GTK_OBJECT(main_menu), "deactivate", GTK_SIGNAL_FUNC(gtk_main_quit), NULL); gtk_menu_popup(GTK_MENU(main_menu), NULL, NULL, NULL, NULL, 0, 0); gtk_main(); return 0; } void *make_launchitem_cb(void *menu, char *label, char *cmd) { GtkWidget *item, *sub_menu = NULL; item = gtk_menu_item_new_with_label(label); gtk_menu_append(GTK_MENU(menu), item); if (cmd) { gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(fork_exec_cb), cmd); } else { sub_menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), sub_menu); } gtk_widget_show(item); return sub_menu; } void make_clientitem(GtkWidget *menu, Window w) { GtkWidget *item; char buf[BUF_SIZE]; if (is_on_cur_desk(w) && !is_skip(w)) { snprint_wm_name(buf, sizeof buf, w); item = gtk_menu_item_new_with_label(buf); gtk_menu_append(GTK_MENU(menu), item); gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(raise_win_cb), (gpointer)w); gtk_widget_show(item); } } void fork_exec_cb(GtkWidget *widget, char *data) { fork_exec(data); gtk_main_quit(); } void raise_win_cb(GtkWidget *widget, Window w) { raise_win(w); gtk_main_quit(); } aewm-1.3.12/doc/0000755000175000001440000000000010734765715012643 5ustar decklinusersaewm-1.3.12/doc/clientsrc.ex0000644000175000001440000000037110500154655015152 0ustar decklinusers# System-wide config file for aewm clients cmd "XTerm" "xterm" cmd "Firefox" "firefox" cmd "Mutt" "xterm -e mutt -y" menu "Etc" cmd "The GIMP" "gimp" cmd "ELinks" "xterm -e elinks" cmd "Gaim" "gaim" end cmd "Logout" "skill aesession" aewm-1.3.12/doc/DESIGN0000644000175000001440000001244210500154745013524 0ustar decklinusersSome Notes on Implementing a Window Manager Decklin Foster , last revised 2002-11-23 ---------------------------------------------------------------------- 0. Introduction These notes are intended to explain aewm and the ICCCM in general to programmers who are already proficient in C, but only have a little experience hacking X. Keep the code visible alongside this file as you read it; my aim is to explain why the code does what it does, not how. 1. The Window Model The X Window System itself says nothing about how windows can be manipulated by the user. All it provides is a tree of windows, which are simply rectangular portions of the display with a geometry (position and size), a stacking order (which window, among siblings, is "in front"), and a few other attributes. All windows are drawn in front of their parent and clipped to their parent's geometry, with the exception of the root window, which takes up the entire display and is the ancestor of all windows. Our job is to present the user with the illusion that the root window is the "background", and that the top-level windows of clients are independent objects which can be moved around, resized, or stacked in a different order along the z-axis. In reality, everything the user sees is a subwindow of a subwindow (etc.) of the root, maybe overlapping other subwindows. How this happens is up to the window manager. The X Protocol allows us to manipulate all the various attributes of other windows, and be informed of important events, using the same interface as any other normal X client. 2. The Window Hierarchy This is what aewm's window hierarchy looks like (please excuse the horrible ASCII art): [ root window ] |--> [ aewm frame window ] | '--> [ client window ] | |--> [ aewm frame window ] | '--> [ client window ] | '--> [ override-redirect window ] When X clients create their top-level windows, they will inform the X server that the root window should be their parent. aewm intercepts these requests (By making a special request to the X server to allow it to do so; this will be explained in depth shortly) and adds an aewm frame window as a new child of the root window instead, and then adds the client window as a child of the aewm frame. Some windows set the override_redirect flag to avoid being reparented in this manner by the WM, so we leave them alone, assuming they can handle any window management functions that the user might want by themselves. All user manipulation of windows, and fonts/graphics drawn by aewm (which is very little, of course) happen in the frame window. This window is slightly bigger than the client, so that there's a "titlebar" space allowing for such interaction. A more complicated WM might have several levels of windows inside each frame. 3. Promoting Ourselves to Management Ordinarily, X clients are not informed when another client asks the server to map a window. To make the above possible, aewm must set the following masks on the root window during initialization (init.c): - SubstructureRedirectMask: this makes the X server send a MapRequest event to us whenever another client tries to create a new child of the root window, instead of actually mapping the window itself. aewm then processes this window as a new client, and maps it if necessary (which is most of the time). Only one client can set this mask simultaneously. - SubstructureNotifyMask: this causes the Xserver to send us a message whenever a client manipulates its own window, so that we can keep track of it and/or take action. - ButtonPressMask and ButtonReleaseMask: aewm also runs programs when the root window is clicked. Technically, evert click is on the root window, but subwindows typically set these masks as well and catch the clicks first. Any click on a client window is also a click on the frame as well -- in particular, with GTK, some parts of the client's window may not set these masks (because they don't need to process mouse clicks), and clicks will "fall through" to the frame. - ColormapChangeMask: for 8-bit displays, this lets us keep track of the root window's colormap (we have to manage changing the colormap ourselves). Not very interesting, but I thought I should list all of them. 3. Idling Once initialization is finished, aewm loops waiting for the X server to report one of the events we have requested, and then processes it (events.c). The XNextEvent call blocks until there is an event to be read, and this is where an aewm process's program counter spends most of the time. 3. Client Creation Now let's assume there are no client windows on the display, and the event loop has started. At this point, there only a few events we can receive, since we have only (so far) selected for events on the root window. Once a client starts and wants to map a window, we get a MapRequest event, and then handle actually mapping it. This involves a few things (new.c): - allocating a new Client structure to keep track of the client - creating a frame window and reparenting the client window into it - deciding if and where to map the frame - select for interesting events on the client window. [FIXME: Add more stuff here.] aewm-1.3.12/doc/aewm.1x0000644000175000001440000000473410711001127014025 0ustar decklinusers.\" aewm - Copyright 1998-2007 Decklin Foster . .\" This program is free software; please see LICENSE for details. .TH "AEWM" 1x .SH "NAME" aewm \- An Exiguous Window Manager .SH "SYNOPSIS" .B aewm .I [options...] .SH "DESCRIPTION" .B aewm is an X11 window manager with very few features. It draws a small titlebar above top-level windows, allows you to manipulate them with the mouse, and starts other programs when the root window is clicked. That's also about all it does, so don't expect anything nifty. .PP The titlebar contains a small box in the upper right corner. Clicking the 1st, 2nd, or 3rd buttons on the main area of the titlebar will raise, move, or lower the window, respectively. Clicking on the box will respectively hide, resize, or close the window. .PP By default, clicking on the root window with button 1 will run .BR aemenu (1x), and button 3 will start a new .BR xterm (1x), and and button 2 will run .BR "aemenu \-\-switch" . .B aemenu (or another equivalent program) is required to unhide hidden windows; .B aewm does not do this itself. .SH "OPTIONS" .TP .BI \-\-config \ file\fP, \ \-rc \ file Read configuration options from .IR file . This overrides options which precede it in the command line. .TP .BI \-\-font \ xfld\fP, \ \-fn \ xfld Draw window titles using the font defined by .IR xfld . .TP .BI \-\-bgcolor \ color\fP, \ \-bg \ color Set the background color of window frames. .TP .BI \-\-fgcolor \ color\fP, \ \-fg \ color Set the foreground color of window frames. .TP .BI \-\-bdcolor \ color\fP, \ \-bd \ color Set the color of window borders. .TP .BI \-\-bdwidth \ width\fP, \ \-bw \ width Draw window borders .I width pixels wide. .TP .BI \-\-padding \ width\fP, \ \-p \ width Draw .I width pixels of space between window borders and window titles. .TP .BI \-\-new1 \ command\fP, \ \-1 \ cmd Run .I command when button 1 is clicked on the root window. .TP .BI \-\-new2 \ command\fP, \ \-2 \ cmd Run .I command when button 2 is clicked on the root window. .TP .BI \-\-new3 \ command\fP, \ \-3 \ cmd Run .I command when button 3 is clicked on the root window. .TP .B \-\-help, \-h Print a short help message to stdout and exit. .TP .B \-\-version, \-v Print version information to stdout and exit. .SH "ENVIRONMENT" .B DISPLAY Sets which X display will be managed by .BR aewm . .SH "SEE ALSO" .BR aeclients (1x), .BR X (7), .I The Inter-Client Communication Conventions Manual (from the X11R6 documentation). .SH "AUTHOR" Decklin Foster aewm-1.3.12/doc/aeclients.1x0000644000175000001440000000437310711001127015042 0ustar decklinusers.\" aewm - Copyright 1998-2007 Decklin Foster . .\" This program is free software; please see LICENSE for details. .TH AECLIENTS 1x .SH "NAME" aemenu, aepanel, aesession, switch\-desk \- programs for use with .BR aewm (1x) .SH "SYNOPSIS" .B aemenu .I [\-\-config|\-rc ] [\-s|\-\-switch] .br .B aepanel .I [\-\-config|\-rc ] [\-b|\-\-bottom] .br .B aesession .br .B aedesk .I [-n ] [[+|-]] .SH "DESCRIPTION" The programs listed above provide several window and session management features to complement .BR aewm (1x). .B aemenu and .B aepanel launch programs and raise existing windows (or unhide iconified ones). .B aemenu uses a popup menu to accomplish this (the .I \-\-switch option changes the function of this menu), while .B aepanel stays on the screen inbetween uses (launching and switching functionality are both always available). .PP Each program comes in both a GTK+ and a Xaw version; .B aemenu is actually a symlink to either .B aemenu\-gtk or .B aemenu\-xaw (likewise for .BR aepanel ). .PP The list of programs to be run by both clients is read from .IR $HOME/.aewm/clientsrc . There are two kinds of directives that may be used in this file: .IP .I cmd \(dqDescription\(dq \(dqcommand\(dq .PP defines a command, which whill be interpreted by .BR sh (1). .IP .I menu \(dqTitle\(dq .br .I (...) .br .I end .PP defines a submenu. The number of submenus that can be nested within each other is limited only by stack space. Double quotes may be included within quoted strings by escaping them with a blackslash .RB ( \e\(dq ), and quotes may be omitted around single-word descriptions or commands. Blank lines and lines starting with '#' are ignored. .PP .B aesession is a do-nothing program; it will simply reap any children it inherits and then sleep. By ending your .I $HOME/.xsession script with "exec aesession", you can exit and restart .BR aewm (1x) without killing your X session. The author does not really expect anyone except WM hackers to find this useful. .PP .B aedesk changes to a new virtual desktop, specfied by .IR num , or sets the number of available desktops, with -n. .SH "FILES" .I $HOME/.aewm/clientsrc .SH "SEE ALSO" .BR aewm (1x) .SH "AUTHORS" Decklin Foster .br Adam Sampson aewm-1.3.12/doc/aewmrc.ex0000644000175000001440000000065310500154655014445 0ustar decklinusers# System-wide configuration for aewm # -------------------------------------------------------------------- # Titlebar font. "fixed" is the default. font "lucidasans-10" # Window colors fgcolor "white" bgcolor "slategray" bdcolor "black" # External border and padding between border/text bdwidth 1 padding 3 # Clients to launch when the root window is clicked button1 "aemenu" button2 "xterm" button3 "aemenu --switch" aewm-1.3.12/aewm.h0000644000175000001440000001352710734764131013177 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #ifndef AEWM_H #define AEWM_H #define VERSION "1.3.12" #include #include #ifdef XFT #include #endif #include "common.h" #include "atom.h" /* Default options you may want to change */ #define DEF_FONT "fixed" #ifdef XFT #define DEF_XFTFONT "Sans:size=8" #endif #define DEF_FG "white" #define DEF_BG "slategray" #define DEF_BD "black" #define DEF_BW 1 #define DEF_PAD 3 #define DEF_IMAP 0 #define DEF_NEW1 "aemenu --switch" #define DEF_NEW2 "xterm" #define DEF_NEW3 "aemenu --launch" #define DEF_NEW4 "aedesk -1" #define DEF_NEW5 "aedesk +1" /* End of options */ #ifdef XFT #define XFT_USAGE " [--xftfont|-fa ]\n" #else #define XFT_USAGE "" #endif #define USAGE \ "usage: aewm [--config|-rc ]\n" \ XFT_USAGE \ " [--font|-fn ]\n" \ " [--fgcolor|-fg ]\n" \ " [--bgcolor|-bg ]\n" \ " [--bdcolor|-bd ]\n" \ " [--bdwidth|-bw ]\n" \ " [--padding|-p ]\n" \ " [--imap|-i] [--no-imap|-n]\n" \ " [--new1|-1 ]\n" \ " [--new2|-2 ]\n" \ " [--new3|-3 ]\n" \ " [--new4|-4 ]\n" \ " [--new5|-5 ]\n" \ " [--help|-h]\n" \ " [--version|-v]\n" #define SubMask (SubstructureRedirectMask|SubstructureNotifyMask) #define ButtonMask (ButtonPressMask|ButtonReleaseMask) #define MouseMask (ButtonMask|PointerMotionMask) #define BW(c) ((c)->decor ? opt_bw : 0) #define GRAV(c) ((c->size.flags & PWinGravity) ? c->size.win_gravity : \ NorthWestGravity) #define CAN_PLACE_SELF(t) ((t) == net_wm_type_dock || \ (t) == net_wm_type_menu || (t) == net_wm_type_splash || \ (t) == net_wm_type_desk) #define HAS_DECOR(t) (!CAN_PLACE_SELF(t)) #define IS_ON_CUR_DESK(c) IS_ON_DESK((c)->desk, cur_desk) #ifdef XFT #define ASCENT (xftfont->ascent) #define DESCENT (xftfont->descent) #else #define ASCENT (font->ascent) #define DESCENT (font->descent) #endif #ifdef DEBUG #define SHOW_EV(name, memb) \ case name: ev_type = #name; w = e.memb.window; break; #define SHOW(name) \ case name: return #name; #endif typedef struct geom geom_t; struct geom { long x; long y; long w; long h; }; typedef struct client client_t; struct client { client_t *next; char *name; Window win, frame, trans; geom_t geom, save; #ifdef XFT XftDraw *xftdraw; #endif XSizeHints size; Colormap cmap; int ignore_unmap; unsigned long desk; #ifdef SHAPE Bool shaped; #endif Bool shaded; Bool zoomed; Bool decor; int old_bw; }; typedef void sweep_func(client_t *, geom_t, int, int, int, int, strut_t *); enum { MATCH_WINDOW, MATCH_FRAME }; /* find_client */ enum { DEL_WITHDRAW, DEL_REMAP }; /* del_client */ enum { SWEEP_UP, SWEEP_DOWN }; /* sweep */ /* init.c */ extern client_t *head; extern int screen; extern unsigned long cur_desk; extern unsigned long ndesks; #ifdef SHAPE extern Bool shape; extern int shape_event; #endif extern XFontStruct *font; #ifdef X_HAVE_UTF8_STRING extern XFontSet font_set; #endif #ifdef XFT extern XftFont *xftfont; extern XftColor xft_fg; #endif extern Colormap cmap; extern XColor fg; extern XColor bg; extern XColor bd; extern GC invert_gc; extern GC string_gc; extern GC border_gc; extern Cursor map_curs; extern Cursor move_curs; extern Cursor resize_curs; extern char *opt_font; #ifdef XFT extern char *opt_xftfont; #endif extern char *opt_fg; extern char *opt_bg; extern char *opt_bd; extern int opt_bw; extern int opt_pad; extern int opt_imap; extern char *opt_new[]; extern void sig_handler(int signum); /* event.c */ extern void event_loop(void); extern int handle_xerror(Display *dpy, XErrorEvent *e); extern int ignore_xerror(Display *dpy, XErrorEvent *e); #ifdef DEBUG extern void show_event(XEvent e); #endif /* client.c */ extern client_t *new_client(Window w); extern client_t *find_client(Window w, int mode); extern void map_client(client_t *); extern int frame_height(client_t *c); extern int set_wm_state(client_t *c, unsigned long state); extern void check_states(client_t *c); extern void parse_state_atom(client_t *, Atom); extern void send_config(client_t *c); extern void redraw_frame(client_t *c); extern geom_t frame_geom(client_t *c); extern void collect_struts(client_t *, strut_t *); #ifdef SHAPE extern void set_shape(client_t *c); #endif extern void del_client(client_t *c, int mode); /* ui.c */ extern void user_action(client_t *c, int x, int y, int button); extern void focus_client(client_t *c); extern void move_client(client_t *c); extern void resize_client(client_t *c); extern void iconify_client(client_t *c); extern void uniconify_client(client_t *c); extern void shade_client(client_t *c); extern void unshade_client(client_t *c); extern void zoom_client(client_t *c); extern void unzoom_client(client_t *c); extern void send_wm_delete(client_t *c); extern void goto_desk(int new_desk); extern void map_if_desk(client_t *c); extern int sweep(client_t *c, Cursor curs, sweep_func cb, int mode, strut_t *s); extern void recalc_map(client_t *c, geom_t orig, int x0, int y0, int x1, int y1, strut_t *s); extern void recalc_move(client_t *c, geom_t orig, int x0, int y0, int x1, int y1, strut_t *s); extern void recalc_resize(client_t *c, geom_t orig, int x0, int y0, int x1, int y1, strut_t *s); #ifdef DEBUG extern void dump_name(client_t *c, const char *label, char flag); extern void dump_win(Window w, const char *label, char flag); extern void dump_info(client_t *c); extern void dump_geom(client_t *c, const char *label); extern void dump_removal(client_t *c, int mode); extern void dump_clients(void); #endif #endif /* AEWM_H */ aewm-1.3.12/parser.c0000644000175000001440000000370710717144424013532 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include "common.h" #include "parser.h" /* If the user specifies an rc file, return NULL immediately if it's not * found; otherwise, search for the usual suspects. */ FILE *open_rc(char *rcfile, char *def) { FILE *rc; char buf[BUF_SIZE]; if (rcfile) { return fopen(rcfile, "r"); } else { snprintf(buf, sizeof buf, "%s/.aewm/%s", getenv("HOME"), def); if ((rc = fopen(buf, "r"))) { return rc; } else { snprintf(buf, sizeof buf, "%s/%s", SYS_RC_DIR, def); return fopen(buf, "r"); } } } char *get_rc_line(char *s, int size, FILE *stream) { while (fgets(s, size, stream)) { if (s[0] == '#' || s[0] == '\n') continue; else return s; } return NULL; } /* Our crappy parser. A token is either a whitespace-delimited word, or a * bunch of words in double quotes (backslashes are permitted in either case). * src points to somewhere in a buffer -- the caller must save the location of * this buffer, because we update src to point past all the tokens found so * far. If we find a token, we write it into dest (caller is responsible for * allocating storage) and return 1. Otherwise return 0. */ int get_token(char **src, char *dest) { int quoted = 0, nchars = 0; while (**src && isspace(**src)) (*src)++; if (**src == '"') { quoted = 1; (*src)++; } while (**src) { if (quoted) { if (**src == '"') { (*src)++; break; } } else { if (isspace(**src)) break; } if (**src == '\\') (*src)++; *dest++ = *(*src)++; nchars++; } *dest = '\0'; return nchars || quoted; } aewm-1.3.12/aepanel.c0000644000175000001440000002225010717146072013636 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "atom.h" #include "menu.h" typedef struct client client_t; struct client { client_t *next; Window win; void *widget; int save; }; void *make_launchitem_cb(void *, char *, char *); void update_clientitem(GtkWidget *, Window); void cleanup_clientitem(client_t *); GdkFilterReturn check_event(GdkXEvent *, GdkEvent *, gpointer); void update_client_list(GtkWidget *); void show_menu_cb(GtkWidget *, gpointer); void raise_win_cb(GtkWidget *, Window); void fork_exec_cb(GtkWidget *, char *); void setup_panel_atoms(); void sig_handler(int); void set_strut(Window, strut_t *); Atom net_wm_strut; Atom net_wm_strut_partial; Atom net_wm_wintype; Atom net_wm_wintype_dock; #define NAME_SIZE 48 client_t *head = NULL; GtkWidget *menu_button = NULL; int opt_bottom; int main(int argc, char **argv) { GtkWidget *toplevel, *hbox, *launch_menu; GtkWidget *clients_box; struct sigaction act; char *opt_config = NULL; strut_t s = { 0, 0, 0, 0 }; client_t *c; int i, rw, rh; unsigned long read, left; Window w; setlocale(LC_ALL, ""); gtk_init(&argc, &argv); gdk_error_trap_push(); for (i = 1; i < argc; i++) { if ARG("config", "rc", 1) { opt_config = argv[++i]; } else if ARG("bottom", "b", 0) { opt_bottom = 1; } else { 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(); setup_switch_atoms(); setup_panel_atoms(); toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(G_OBJECT(toplevel), "delete_event", G_CALLBACK(gtk_main_quit), NULL); launch_menu = gtk_menu_new(); hbox = gtk_hbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 0); gtk_container_add(GTK_CONTAINER(toplevel), hbox); menu_button = gtk_button_new_with_label("Launch"); gtk_signal_connect(GTK_OBJECT(menu_button), "clicked", GTK_SIGNAL_FUNC(show_menu_cb), launch_menu); gtk_box_pack_start(GTK_BOX(hbox), menu_button, FALSE, FALSE, 0); clients_box = gtk_hbox_new(TRUE, 0); gtk_container_add(GTK_CONTAINER(hbox), clients_box); gtk_container_set_resize_mode(GTK_CONTAINER(clients_box), GTK_RESIZE_QUEUE); XSelectInput(dpy, GDK_ROOT_WINDOW(), PropertyChangeMask); gdk_window_add_filter(gdk_get_default_root_window(), check_event, clients_box); make_launch_menu(opt_config, launch_menu, make_launchitem_cb); for (i = 0, left = 1; left; i += read) { read = get_atoms(root, net_client_list, XA_WINDOW, i, &w, 1, &left); if (read) update_clientitem(clients_box, w); else break; } for (c = head; c; c = c->next) cleanup_clientitem(c); gtk_widget_show_all(hbox); gtk_widget_realize(toplevel); gtk_window_set_skip_taskbar_hint(GTK_WINDOW(toplevel), TRUE); gtk_window_set_skip_pager_hint(GTK_WINDOW(toplevel), TRUE); gtk_window_set_decorated(GTK_WINDOW(toplevel), FALSE); gtk_window_stick(GTK_WINDOW(toplevel)); /* This last call is not working for some reason, so we'll just have to * kludge it for now. */ #ifdef _HEY_LOOK_AT_THAT_SOMEBODY_FIXED_THE_GTKWINDOW_TYPE_HINT_CRAP gtk_window_set_type_hint(GTK_WINDOW(toplevel), GDK_WINDOW_TYPE_HINT_DOCK); #else set_atoms(GDK_WINDOW_XID(toplevel->window), net_wm_wintype, XA_ATOM, &net_wm_type_dock, 1); #endif gdk_window_get_size(gdk_get_default_root_window(), &rw, &rh); gtk_widget_set_size_request(toplevel, rw, -1); gtk_window_move(GTK_WINDOW(toplevel), 0, opt_bottom ? rh - toplevel->allocation.height : 0); gtk_widget_show_all(toplevel); if (opt_bottom) s.bottom = toplevel->allocation.height; else s.top = toplevel->allocation.height; set_strut(GDK_WINDOW_XID(toplevel->window), &s); gtk_main(); return 0; } void *make_launchitem_cb(void *menu, char *label, char *cmd) { GtkWidget *item, *sub_menu = NULL; item = gtk_menu_item_new_with_label(label); gtk_menu_append(GTK_MENU(menu), item); if (cmd) { gtk_signal_connect(GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(fork_exec_cb), cmd); } else { sub_menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), sub_menu); } gtk_widget_show(item); return sub_menu; } void update_clientitem(GtkWidget *container, Window w) { char buf[NAME_SIZE]; client_t *c; for (c = head; c; c = c->next) { if (c->win == w) { if (is_on_cur_desk(w)) gtk_widget_show(GTK_WIDGET(c->widget)); else gtk_widget_hide(GTK_WIDGET(c->widget)); c->save = 1; return; } } if (!is_skip(w) && is_on_cur_desk(w)) { c = malloc(sizeof *c); c->next = head; head = c; c->win = w; c->save = 1; snprint_wm_name(buf, sizeof buf, w); c->widget = gtk_button_new_with_label(buf); gtk_signal_connect(GTK_OBJECT(c->widget), "clicked", GTK_SIGNAL_FUNC(raise_win_cb), (gpointer)w); gtk_box_pack_start(GTK_BOX(container), c->widget, TRUE, TRUE, 0); gtk_misc_set_alignment(GTK_MISC(GTK_BIN(c->widget)->child), 0, 0.5); gtk_widget_show(c->widget); XSelectInput(dpy, c->win, PropertyChangeMask); gdk_window_add_filter(gdk_window_lookup(c->win), check_event, container); } } void cleanup_clientitem(client_t *c) { client_t *p; if (c->save) { c->save = 0; } else { gtk_widget_destroy(GTK_WIDGET(c->widget)); if (head == c) { head = c->next; } else { for (p = head; p && p->next; p = p->next) if (p->next == c) p->next = c->next; } free(c); } } GdkFilterReturn check_event(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer container) { XEvent *e = gdk_xevent; client_t *c; char buf[NAME_SIZE]; if (e->type == PropertyNotify) { if (e->xproperty.window == root) { if (e->xproperty.atom == net_cur_desk || e->xproperty.atom == net_client_list) update_client_list(GTK_WIDGET(container)); } else { if (e->xproperty.atom == net_wm_desk) { update_client_list(GTK_WIDGET(container)); } else { /* don't really care which atom changed; just redo it */ for (c = head; c; c = c->next) { if (c->win == e->xproperty.window) { snprint_wm_name(buf, sizeof buf, c->win); gtk_label_set_text( GTK_LABEL(GTK_BIN(c->widget)->child), buf); } } } } } return GDK_FILTER_CONTINUE; } void update_client_list(GtkWidget *container) { client_t *c, *save_next; unsigned long read, left; int i; Window w; for (i = 0, left = 1; left; i += read) { read = get_atoms(root, net_client_list, XA_WINDOW, i, &w, 1, &left); if (read) update_clientitem(container, w); else break; } for (c = head; c; c = save_next) { save_next = c->next; cleanup_clientitem(c); } } void menu_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) { GtkWidget *button = GTK_WIDGET(data); GtkRequisition req; 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) { 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 raise_win_cb(GtkWidget *widget, Window w) { raise_win(w); } void fork_exec_cb(GtkWidget *widget, char *data) { fork_exec(data); } void setup_panel_atoms() { net_wm_strut = XInternAtom(dpy, "_NET_WM_STRUT", False); net_wm_strut_partial = XInternAtom(dpy, "_NET_WM_STRUT_PARTIAL", False); net_wm_wintype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); net_wm_wintype_dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); } void sig_handler(int signal) { if (signal == SIGCHLD) wait(NULL); } void set_strut(Window w, strut_t *s) { unsigned long data[4]; data[0] = s->left; data[1] = s->right; data[2] = s->top; data[3] = s->bottom; XChangeProperty(dpy, w, net_wm_strut, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 4); } aewm-1.3.12/NEWS0000644000175000001440000006076610734764131012603 0ustar decklinusers1.3.12: - Fix wrapping backwards from desk 0. - Swap default Button1/Button3 aemenu bindings. - Restore the old look of draw_outline (from scratch), undoing 1.3.11's changes. Improve and standardize font spacing. - Fatten up transient frames a little, in case padding is 0. 1.3.11: - Fix issues with atom handling on 64-bit systems. - Correct gravity interpretation when we have a large border width. - Remove weird resizing algorithm; it was only supposed to be a placeholder for something even weirder but I never had time to get the math right. Do something more like twm instead (warping the pointer is still evil). - When doing any sort of sweep, draw the geometry in the middle of the frame and don't draw a line for the title. - Save original window border width and restore on exit. 1.3.10: - Respect incremental-resize hints when zooming clients, and don't make GTK+ clients (if they persist their own geom) sometimes forget their zoom state. - Removed the shift-click bindings; buttons 4 and 5 now shade/unshade and zoom/unzoom in the frame and corner box respectively. - Added the ability to map buttons 4 and 5 on the root window. By default, they run aedesk to cycle through the available desktops. - Added -fa (if applicable) to the usage message. - Simplify the initial-position stuff some more, and skip (if enabled) interactive mapping in several more cases. - Make sure the borders of fullscreen windows are offscreen. - Reorganized the source a whole lot. 1.3.9: - Redid the initial-position algorithm again. - Added support for fullscreen hints. - Renamed the mapping options. - Fixed drawing of frame outlines when sweeping to the top of the screen. 1.3.8: - Change the CARD32s to unsigned longs; it seems that Xlib is going to handle things correctly. While doing this, rewrote all of the other code that deals with atoms and allocating memory for them. - Draw the border correctly when moving or resizing a shaded window, and don't unshade it as a side effect of other operations. - Fix response to EWMH close messages. - Reorganized the shared code (and some other stuff), which should simplify/speed up the build a bit. - Fixed some occasional X errors encountered when switching desktops, and a bug where an existing client's desktop could be set incorrectly if it was exposed in a certain order. 1.3.7: - Added some additional hints for window lists/pagers, and taking activation requests. - When mapping interactively, clicking with button 3 will map the window lowered. - Removed support for MWM hints entirely. - Fixed aepanel's initial window mapping. 1.3.6: - Eliminated the secondary frame box, and UI for setting the desktop hint to "all desks" or not. Got rid of the graphical indicators as well, since shaded or zoomed state can easily be ascertained by just looking at the window. Instead, added a shift-click handler. - Changed the operation of resize so that it scales from the center and never warps the pointer (which we are not supposed to do anyway). - Refactored move and resize so that they don't repeat code. This allows us to have interactive mapping essentially for free, so add an option for that. And having interactive mapping provides an opportunity to rewrite the initial-map geometry code yet again; it should be much saner now, although less forgiving of clients which try to kludge around the hints instead of doing the right thing. - Don't make aedesk -n bail out if there is no number of desktops atom set. - Mostly rewrote window gravity and resize hint handlers. These should do a better job of dealing with most cases. 1.3.5: - Allow transient windows to map over struts. - Remove the ndesks option and read _NET_NUMBER_OF_DESKTOPS instead. Added a -n option to switch-desk to set it, and removed the old -n (and -p) in favor of +1 and -1 (or any other integer). - Renamed switch-desk to aedesk (since we broke the options anyway). - Rewrote README. 1.3.4: - Brown paper bag: add missing Xmd.h and limits.h includes. 1.3.3: - Use CARD32 everywhere for all atom values. I believe this is what I should have done to fix the signed/unsigned confusion. Should work on systems where long is 64 bits now. - If the current desktop is unset on startup, set it to 0. - Don't adjust a PPosition hint relative to any top/left struts, unless it's overlapping them. Pretty much every client out there will ignore struts and save their position relative to the screen. - Get rid of SPACING in aepanel-gtk (you know, Fitts' Law). Also remove the frame and the quit button (generally useless). The button for the launch menu is now labeled "Launch" for consistency. - Make switch-desk -p wrap around properly from desk 0. - Added the lib stuff to X11OBJS in clients/Makefile (thanks Doug Vetter). 1.3.2: - Once again, ignore PPosition hints of 0,0. I don't know how this got lost. Perhaps we were doing it wrong all along. - If a client has a private colormap, attempt to allocate our colors in it before mapping. This doesn't mean the client can't later overwrite those colors, but it will at least give us the correct bg intially in most cases. - When iconifying or de-iconifying a client, also iconify or de-iconify all of its transients. - Also fix Adam's email in aeclients.1x and NEWS. - Add --prev and --next options to switch-desk. This necessitated splitting stringatom.c out from atom.c. - In aeclient, remove a client from the list when it moves itself to another desktop. 1.3.1: - Rename this file to NEWS and reformat it. - UTF-8 support. This is only, what, 3 years late or so. Requires X_HAVE_UTF8_STRING (X.Org or XFree86 >= 4.0.2). - Call setlocale on startup (this is necessary for using UTF-8 with core fonts or Xaw; it is not strictly so for Xft or GTK+, but probably a good idea anyway). - The Xft font is now specified separately from the core font, using fontconfig syntax (e.g. "Sans:size=8"). If Xft is not enabled this option will be ignored. - Reimplement remove_from_atom since we no longer have any compulsions about asking the server to send us up to 4 billion items at a time. There was really no other sensible way to do it for UTF-8 strings. - When maximizing, always redraw, since the window's size may not actually change (which is what ordinarily forces the redraw). - Break atom.h out of aeclients.h and include it separately; should fix compile problems on FreeBSD (thanks to John E Hein for noticing this). - Delete atoms (e.g. _NET_WM_STATE) that have had all their values removed. - Set NET_SUPPORTED appropriately on the root window. - Deal properly with clients that set unitialized garbage or off-screen values in position hints. - If a maximized client moves or resizes itself with a ConfigureRequest, unmaximize it. - Remove GTK+ 1.2 support. There's no theoretical reason you can't use 1.2, but I don't have it installed anymore and I just don't feel like chasing down the eventual breakage. - Handle CirculateRequests. I implemented this for something I ended up not using, but if only for the sake of completeness it should be in there. - Sleep in aesession in case there are no children for some reason (reported by Craig Brozefsky). - README update: explain key bindings, clean out some cruft, update Adam's email. - Put all the atom initialization for the clients in couple shared funcs instead of duplicating it. - Do casting within atom_foreach rather than without. Added to TODO about how I don't really want to use long here due to sign issues. For now I am only putting casts in where I *mean* to cast and where ANSI does not not otherwise imply one, not merely to placate -Wall. Let's see if it breaks any archs. - Add half the font height to the size of transient frames. If you're using Xft, you're probably going to want to crank the padding down, which makes them too small otherwise. Or else I am just getting old and need a new eyeglass prescription. - Set the height strut correctly in aepanel-xaw. 1.3.0: - Implemented virtual desktops. Yeah, I lost, feature creep won. It really didn't take that much, once I looked at how to do it using the EWMH spec. Please read the relevant section in README before using. - Added another box to frames, and three new actions to go with it: shading, maximzation, and pinning to all desktops. These states use _NET_WM_STATE and _NET_WM_DESKTOP as defined in the EWMH. - Maintain a _NET_CLIENTS_LIST property on the root window so that we can use any EWMH pager/taskbar. - Rewrote aemenu and aepanel to use the EWMH properties instead of fiddling around with low-level X magic. - Added a small command-line client, switch-desk, to switch virtual desktops. - Remove set-gnome-pda and all support for the GNOME_PANEL_DESKTOP_AREA hint. Instead, we now look for _NET_WM_STRUT(_PARTIAL) on all managed windows. - Renamed, cleaned up, and improved the sanity of a whole bunch of little bits of code, and completely revamped everything that deals with X atoms. - Made aepanel-xaw work like aepanel-gtk. It now has a --bottom option, and you don't need to (and shouldn't, in fact) futz with the geometry resources. 1.2.7: - Backport the following from 1.3.3: - Added the lib stuff to X11OBJS in clients/Makefile (thanks Doug Vetter). 1.2.6: - Backport the following from 1.3.1: - Rename this file to NEWS and reformat it. - Deal properly with clients that set unitialized garbage or off-screen values in position hints. - Handle CirculateRequests. I implemented this for something I ended up not using, but if only for the sake of completeness it should be in there. - Sleep in aesession in case there are no children for some reason (reported by Craig Brozefsky). - README update: explain key bindings, clean out some cruft, update Adam's email. - Add half the font height to the size of transient frames. If you're using Xft, you're probably going to want to crank the padding down, which makes them too small otherwise. Or else I am just getting old and need a new eyeglass prescription. - Backport the following from 1.3.2: - Once again, ignore PPosition hints of 0,0. I don't know how this got lost. Perhaps we were doing it wrong all along. - Also fix Adam's email in aeclients.1x and NEWS. 1.2.5: - Don't exit if a specified rc file can't be found. Also fixed some tiny mem leaks in the rc code. - Draw window outlines properly when moving a client without a frame. - Various minor cleanups all around, as 1.2.x enters bugfix-only-land. 1.2.4: - Get rid of type-punned pointers everywhere to appease gcc 3.3. - Use enums where appropriate, and clarify associated code. - Cleaned up the parser immensely. Now backslashes will work anywhere, not just for \". Thanks to Bill Allombert for the idea. - Handle root window clicks on ButtonRelease, not ButtonPress. This should fix all the awful zombie aemenu problems. - Updated README and LICENSE to reflect the unfortunate death of David Hogan. 1.2.3: - Optionally use GTK+ 2.0 for GTK+ clients. While the changes required were minor, I'd like to get rid of USE_OLD_GTK soon. Please let me know if you really, really need to use GTK+ 1.2. - Don't use ## to paste strings in ARG(). This was always horribly wrong, but prior to version 3.2 GCC's preprocessor let me do it. - Use pkg-config to get the proper CFLAGS and includes for libxft. The old, hardcoded values no longer work with newer versions of the library. - aepanel-gtk can now run at the bottom of the display, instead of just the top. Additionally, it now pops its menu up in the right place (above or below the "Menu" button, depending on what edge of the screen it's on), instead of wherever the pointer happens to be. - Use \- for literal dashes in manpages, so that they render as U+002D and not U+2010 (hyphen) on UTF-8 systems. Also updated aeclients man page to reflect the removal of --show and -vv from set-gnome-pda. - Moved aewm itself into a subdirectory, src. Used this opportunity to clean up Makefiles and #includes a bit. 1.2.2: - In a fit of stupidity I lost get_gnome_pda's error recovery (return all zeros, not uninitialized garbage, if the hint doesn't exist) while moving/editing other parts of it. This caused major problems on a server where the hint doesn't exist yet (e.g. windows mapping offscreen). 1.2.1: - Call setsid(2) in fork_exec() to prevent child processes from dying when aewm is killed or crashes. (thanks, Ciaran Anscomb) - Don't crap out with a divide-by-zero error if a client sets a PResizeInc hint of 0. (ditto) - Improved the appearance of debugging info a bit. It does however assume an 80-character terminal, so you'll have to adjust and recompile if you want more. - Call XFlush() in set_gnome_pda to avoid set-gnome-pda exiting before the change takes effect. Made some more cleanups to the set/get_gnome_pda interface, mostly using a struct instead of 4 ints. - Fixed aepanel-gtk to not clobber parts of the GNOME_PAGER_DESKTOP_AREA hint when starting and exiting. Unfortunately it does so by saving the old hint and restoring it on exit, which is wrong in several cases. However, it's less wrong than before. - Removed multiple levels of verboseness in set-gnome-pda. The extra complexity wasn't worth it. - Added a missing #include in client.c and manage.c. - Used spaces for indentation again, and made some other minor annoying cleanups. Sorry. - Made theight bail out if sent a NULL client_t. I was able to get a stack trace for a few crashes and they (rather inexplicably) pointed here. Obviously, something else is the real problem, since no other function *should* call theight on a NULL or invalidated client, and several other functions assume that they're getting a good pointer. - Made some minor edits to DESIGN, but I am way too lazy to do more real work on it. - Added items to TODO. 1.2.0: - Here we go, another stable release. I haven't updated DESIGN, but I have no idea when woody will freeze and I'd like to get this out the door. - Changed all occurences of "minimalistic" (which is not a word) to "minimalist" (thanks Matt Zimmerman). - Made frame padding (formerly SPACE in aewm.h) configurable at run time. Ideally, the default for this should be calculated from the font. - Added "include" directive to config file parser (thanks azz). - Fixed set-gnome-pda -v and -vv, and made all clients bail out on bad options. 1.1.5: - Fixed default config and config file parsing order. - Added long and short versions of all command line options. - Turned GNOME_PAGER_DESKTOP_AREA on by default. 1.1.4: - Added aewm config file (~/.aewm/aewmrc). - Fixed clients/Makefile so that specifying CFLAGS works, set-gnome-pda gets installed, and ln always uses -sf. - Updated the rest of README to document the new clients. 1.1.3: - Switched to the XFree86 license. - Fixed a segfault that could happen with Xft if a client's window disappeared. I think there is still a possible segv lurking somewhere, but I can't trigger it anymore. If you use Xft, especially with gdkxft, please try to bang on it. - Major reorganization/rewrite of all included clients (formerly referred to as "goodies"). All now support hierarchical menus, and use a new config file. You will need to convert your old .paletterc. - Got rid of sleep in aesession, now it just sleeps in wait(). - Added optional support for Gnome's GNOME_PAGER_DESKTOP_AREA hint. Noticed a badly named dummy variable in my XGetWindowProperty calls while doing this and fixed it. - Wrote set-gnome-pda to allow manipulation of this hint. - Reformatted ChangeLog slightly. - Fix USPosition of 0 for real this time. Also accounted for border width, made other init_position tweaks to accomodate the possibility of GNOME_PAGER_DESKTOP_AREA, and ignore USSizes of 0 (this works around xpdf's brain-damage, and possibly other broken programs). - Included draft of DESIGN document. 1.1.2: - Rewrote init_position again. This fixes the problem with specifying a USPosition containing 0. - Added gtk-menu client contributed by Adam Sampson . I want to fold this stuff into the rest of the goodies eventually. - Added a "-version" option for printing version information from the command line. - Added some proper headers to all the files in goodies/lib/. - Replaced strcpy/strcat in panel-misc.c with snprintf to prevent a possible buffer overflow. - Made some improvements to the .paletterc parser: squish a possible segfault, handle comments/blank lines, ignore menu directives. - Reformatted man pages. 1.1.1: - Fix typo in man page install location. - Use -lXt when compiling Xaw goodies (required to build on SunOS). - Added some more documentation (mostly about things that are likely to break.) 1.1.0: - Xft (freetype extension) support, thanks once again to help from Adam Sampson . - A few Makefile improvements: only link against required libraries, easier enabling/disabling of options, don't attempt to compile against headers in the binary destination tree. - Yet more init_position tweaks -- use x position hint even if y is out of range and vice versa, and take window gravity into account when positioning. - Some additions and improvements to the debug code (mainly printing names of constants instead of cryptic numbers). 1.0.0: - All right, I think we're stable now. New stuff will go into 1.1.x. - Changed init_position so that it doesn't cut off very large windows. This was merely annoying for regular windows, but it was downright buggy for incsized ones. - Fixed compilation bug when shape support was disabled. 0.9.19: - OK, I broke command line parsing again. Yes, I'm an idiot. - Fixed problem with exec()-ing a non-existent program. 0.9.18: - Fixed bug in init_position; we can safely use PPosition now. If windows start popping up in bad places, check their size hints before yelling at me. - Use fork/exec instead of system(3) to run programs. Fixed signal handlers to reflect this (we need POSIX sigaction(2)). - Cleaned up MWM hints/WM_SIZE hints code (they sort of match now), and only request the MWM atom at startup instead of for each call. - Started yet another goodies reorganization, and ended up reverting a lot of it. However what's changed should go a little ways towards cleaning things up. - Fix a build error where some goodies files that needed to be compiled against X were compiled without -I/use/X11R6/lib, failing on systems without a symlink from /usr/include/X11. - Tossed out historical 'iheight' junk in border calculation. 0.9.17: - Use ln -f to ignore overwriting previously installed manpage links. - If MWM hints are enabled, no-border windows will no longer get a border upon exiting. - The order of buttons in *-palette is no longer changed by name updates or unhiding windows. - xaw-palette was fixed to work with proper versions of Xaw (shame on me for testing with Xaw3d!), including the one in XF4. The new internals have ported over to gtk-palette. 0.9.16: - Fixed a shaping bug (which actually existed before changes made in 0.9.15) where a window that used to be shaped, but then became unshaped, wasn't updated properly. - MWM hints support can now be compiled in with -DMWM_HINTS, thanks to help from Adam Sampson . - Set the input focus upon entering a window frame, instead of on entering the child window. 0.9.15: - Display a frame for shaped windows. This also makes the bug where shaped windows were getting gravitated as if they had a frame irrelevant. - Tiny cleanups for the Debian package, including: - Man page for the goodies. - Typo in makefile fixed (tried to strip manpage, ah-reer-reer-reer). - Polished the main man page a bit. 0.9.14: - Free cursors and GCs in quit_nicely. - Switch order of objects and libraries in Makefile to placate Solaris cc. - Rename raise to raise_win -- raise is in signal.h of course (duh!) - Added copyright notice to all goodies and Makefiles due to someone ignoring my license (this has been worked out). - Bail out on lack of default font. You *should* be reading aewm.h first of course ;-) 0.9.13: - Fix really stupid thing I did while refactoring make_new_client, where a window that started as Withdrawn would stay that way instead of getting mapped. - Deal with withdrawing clients properly in *-palette, by watching for the WM_STATE property to be changed instead of looking at UnmapNotify events. 0.9.12: - Clean up clients on receipt of a DestroyNotify. This is needed when a client is already unmapped (i.e, the user iconified it) and it exits. - Preserve window stacking order on exit and restart. - Mucked about with the make_new_client logic; tell me if this breaks something. - Added gtk-*-menu clients contributed by Adam Sampson . 0.9.11: - Got rid of unneeded PropertyChangeMask on root window. Silly me. - Removed the ugly array/linked-list hack from xaw-palette, so that the internal workings of both palette clients are now basically identical. - static'd everything that should have been static. - Updated email, documentation, etc. - Got rid of handle_reparent_event, as a client call to XReparentWindow is taken care of by handle_unmap_event. - Backed out the 0.9.9 change to option-parsing macros, which were causing all options to fail miserably (argh). 0.9.10: - Handle withdrawing windows properly in xaw-palette. Both palette clients are a little saner now. - Updated goodies docs. - ungravitate when printing geometry for move/resize. - Moved more code from individual goodies to common-run.c. - Got rid of unhide() as we never use it. - Replaced GenericWidget typedef silliness with global variables. sometimes you just have to pick the lesser of two evils... 0.9.9: - Finally de-insanified handle_xerror. Instead of going through contortions in remove_client to avoid raising errors, we turn the error handler off during the server grab. - Both palette goodies now track iconification of clients and WM_NAME changes. - gtk-palette is now oriented horizontally, and includes a menu, providing lots more space for client buttons. - Made the parse_option macros reusable (thanks to the comp.lang.c folks for the reminder). - make sure get_wm_name in the goodies doesn't crash for windows that have no name. - Minor typo corrections and stuff. 0.9.8: - Brown paper bag: take debugging stupidity out of gtk-palette. - Overhaul the goodies' build system yet again. - Gtk-palette now gets the screen size correctly. - All switch/palette goodies now cut off long names, use WM_NAME instead of WM_ICON_NAME, and place iconified clients' names in brackets. - No changes to aewm itself in this release. 0.9.7: - Added lots of comments. - General code cleanups, tweaks, reformats all over. - Fixed iconic state problem. - Plugged a memory leak in send_wm_delete. - Added -new3. - Fixed -new[123] allocation bug. - Made debug code considerably saner. - Print "80x25" and such when resizing xterms. - Replaced sprintf with snprintf. - Got rid of -display. set DISPLAY in your environment instead, so that it propagates to child processes. - Messed around with the goodies' build system. - Fixed gtk-palette; events are now filtered properly at the GDK level. - Replaced variable length arrays with malloc/free. - Added xsession, since I've been using it forever. 0.9.6: - Fix for handling inital window geometry. - A few memory leaks plugged. - Wrote a man page - More consistent borders for transient windows. 0.9.5: - Print window geometry while moving/resizing. - Added some WM_NORMAL_HINTS sanity checks - Set the keyboard focus on window enter (for rxvt, etc). 0.9.4: - Root menu replaced by another client. - Added -bw option. - Border-drawing fixes (only visible if you have very wide borders). - Got rid of mouse button exit. 0.9.3: - Put the root menu code back in. - Added more goodies and reorganized them. 0.9.2: - Minor ICCCM compliance fix regarding WM_STATE. 0.9.1: - A small fix to make the palette complain if no rc file exists. 0.9.0: - Initial public release. Before this, aewm was called 'swim' and was used by me as a school project. It was also rather unstable. aewm-1.3.12/atom.h0000644000175000001440000000315010717144424013173 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #ifndef AEWM_ATOM_H #define AEWM_ATOM_H #include struct strut { unsigned long left; unsigned long right; unsigned long top; unsigned long bottom; }; typedef struct strut strut_t; extern Atom utf8_string; extern Atom wm_state; extern Atom wm_change_state; extern Atom wm_protos; extern Atom wm_delete; extern Atom net_supported; extern Atom net_client_list; extern Atom net_client_stack; extern Atom net_active_window; extern Atom net_close_window; extern Atom net_cur_desk; extern Atom net_num_desks; extern Atom net_wm_name; extern Atom net_wm_desk; extern Atom net_wm_state; extern Atom net_wm_state_shaded; extern Atom net_wm_state_mv; extern Atom net_wm_state_mh; extern Atom net_wm_state_fs; extern Atom net_wm_state_skipt; extern Atom net_wm_state_skipp; extern Atom net_wm_strut; extern Atom net_wm_strut_partial; extern Atom net_wm_wintype; extern Atom net_wm_type_dock; extern Atom net_wm_type_menu; extern Atom net_wm_type_splash; extern Atom net_wm_type_desk; extern unsigned long get_atoms(Window, Atom, Atom, unsigned long, unsigned long *, unsigned long, unsigned long *); extern unsigned long set_atoms(Window, Atom, Atom, unsigned long *, unsigned long); extern unsigned long append_atoms(Window, Atom, Atom, unsigned long *, unsigned long); extern void remove_atom(Window, Atom, Atom, unsigned long); extern char *get_wm_name(Window); int get_strut(Window, strut_t *); extern unsigned long get_wm_state(Window); #endif /* AEWM_ATOM_H */ aewm-1.3.12/parser.h0000644000175000001440000000051710717144424013533 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #ifndef AEWM_PARSER_H #define AEWM_PARSER_H #include FILE *open_rc(char *, char *); char *get_rc_line(char *, int, FILE *); int get_token(char **, char *); #endif /* AEWM_PARSER_H */ aewm-1.3.12/LICENSE0000644000175000001440000000204510711001127013053 0ustar decklinusersCopyright 1998-2007 Decklin Foster . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. aewm-1.3.12/Makefile0000644000175000001440000000436610734765704013545 0ustar decklinusers# aewm - Copyright 1998-2007 Decklin Foster . # This program is free software; see LICENSE for details. # Set this to the location where you want to install DESTDIR = XROOT = /usr/X11R6 # Uncomment to enable Shape extension support #OPT_WMFLAGS += -DSHAPE #OPT_WMLIB += -lXext # Uncomment to add Xft support #OPT_WMFLAGS += -DXFT `pkg-config --cflags xft` #OPT_WMLIB += `pkg-config --libs xft` -lXext # Uncomment for debugging (abandon all hope, ye who enter here) #OPT_WMFLAGS += -DDEBUG #OPT_WMLIB += -lefence CC = gcc CFLAGS = -g -O2 -Wall BINDIR = $(DESTDIR)$(XROOT)/bin MANDIR = $(DESTDIR)$(XROOT)/man/man1 CFGDIR = $(DESTDIR)/etc/X11/aewm PLAINOBJ = aesession.o parser.o CLIENTOBJ = common.o atom.o X11OBJ = $(CLIENTOBJ) aedesk.o menu.o WMOBJ = aewm_client.o aewm_event.o aewm_init.o aewm_manip.o GTKOBJ = aemenu.o aepanel.o ALLOBJ = $(PLAINOBJ) $(X11OBJ) $(WMOBJ) $(GTKOBJ) PLAINBIN = aesession X11BIN = aedesk WMBIN = aewm GTKBIN = aemenu aepanel ALLBIN = $(PLAINBIN) $(X11BIN) $(WMBIN) $(GTKBIN) all: $(ALLBIN) aesession: aedesk: $(CLIENTOBJ) aewm: $(CLIENTOBJ) $(WMOBJ) parser.o aemenu: $(CLIENTOBJ) menu.o parser.o aepanel: $(CLIENTOBJ) menu.o parser.o X11FLAGS = -I$(XROOT)/include WMFLAGS = $(X11FLAGS) $(OPT_WMFLAGS) GTKFLAGS = `pkg-config --cflags gtk+-2.0` $(PLAINOBJ): %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ $(X11OBJ): %.o: %.c $(CC) $(CFLAGS) $(X11FLAGS) -c $< -o $@ $(WMOBJ): %.o: %.c $(CC) $(CFLAGS) $(WMFLAGS) -c $< -o $@ $(GTKOBJ): %.o: %.c $(CC) $(CFLAGS) $(GTKFLAGS) -c $< -o $@ X11LIB = -L$(XROOT)/lib -lX11 WMLIB = $(X11LIB) $(OPT_WMLIB) GTKLIB = `pkg-config --libs gtk+-2.0` $(PLAINBIN): %: %.o $(CC) $^ -o $@ $(X11BIN): %: %.o $(CC) $^ $(X11LIB) -o $@ $(WMBIN): %: $(CC) $^ $(WMLIB) -o $@ $(GTKBIN): %: %.o $(CC) $^ $(GTKLIB) -o $@ AEMAN = aewm.1x aeclients.1x AERC = aewmrc clientsrc install: all mkdir -p $(BINDIR) $(MANDIR) $(CFGDIR) install -s $(ALLBIN) $(BINDIR) for i in $(AEMAN); do \ install -m 644 doc/$$i $(MANDIR); \ gzip -9 $(MANDIR)/$$i; \ done for i in $(AERC); do \ install -m 644 doc/$$i.ex $(CFGDIR)/$$i; \ done for i in $(PLAINBIN) $(X11BIN) $(GTKBIN); do \ ln -sf aeclients.1x.gz $(MANDIR)/$$i.1x.gz; \ done clean: rm -f $(ALLBIN) $(ALLOBJ) .PHONY: all install clean aewm-1.3.12/common.c0000644000175000001440000000211210717144424013513 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include #include #include "common.h" Display *dpy; Window root; void fork_exec(char *cmd) { pid_t pid = fork(); switch (pid) { case 0: execlp("/bin/sh", "sh", "-c", cmd, NULL); fprintf(stderr, "exec failed, cleaning up child\n"); exit(1); case -1: fprintf(stderr, "can't fork\n"); } } int get_pointer(int *x, int *y) { Window real_root, real_win; int wx, wy; unsigned int mask; XQueryPointer(dpy, root, &real_root, &real_win, x, y, &wx, &wy, &mask); return mask; } int send_xmessage(Window w, Atom a, unsigned long x, unsigned long mask) { XClientMessageEvent e; e.type = ClientMessage; e.window = w; e.message_type = a; e.format = 32; e.data.l[0] = x; e.data.l[1] = CurrentTime; return XSendEvent(dpy, w, False, mask, (XEvent *)&e); } aewm-1.3.12/aewm_client.c0000644000175000001440000004464010717144424014526 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #ifdef DEBUG #include #endif #include #include #ifdef SHAPE #include #endif #include "aewm.h" #include "atom.h" static void do_map(client_t *, int); static int init_geom(client_t *, strut_t *); static void reparent(client_t *, strut_t *); /* Set up a client structure for the new (not-yet-mapped) window. We have to * ignore two unmap events if the client was already mapped but has * IconicState set (for instance, when we are the second window manager in a * session). That's because there's one for the reparent (which happens on * all viewable windows) and then another for the unmapping itself. */ client_t *new_client(Window w) { client_t *c; XWindowAttributes attr; XColor exact; long supplied; Atom win_type; c = malloc(sizeof *c); c->next = head; head = c; c->name = get_wm_name(w); c->win = w; c->frame = None; c->size.flags = 0; c->ignore_unmap = 0; #ifdef SHAPE c->shaped = 0; #endif c->shaded = 0; c->zoomed = 0; c->decor = 1; XGetWMNormalHints(dpy, c->win, &c->size, &supplied); XGetTransientForHint(dpy, c->win, &c->trans); XGetWindowAttributes(dpy, c->win, &attr); c->geom.x = attr.x; c->geom.y = attr.y; c->geom.w = attr.width; c->geom.h = attr.height; c->cmap = attr.colormap; c->old_bw = attr.border_width; #ifdef DEBUG dump_name(c, "creating", 'w'); dump_geom(c, "initial"); #endif XAllocNamedColor(dpy, c->cmap, opt_fg, &fg, &exact); XAllocNamedColor(dpy, c->cmap, opt_bg, &bg, &exact); XAllocNamedColor(dpy, c->cmap, opt_bd, &bd, &exact); if (get_atoms(c->win, net_wm_wintype, XA_ATOM, 0, &win_type, 1, NULL)) c->decor = HAS_DECOR(win_type); if (get_atoms(c->win, net_wm_desk, XA_CARDINAL, 0, &c->desk, 1, NULL)) { if (c->desk == -1) c->desk = DESK_ALL; /* FIXME */ if (c->desk >= ndesks && c->desk != DESK_ALL) c->desk = cur_desk; } else { set_atoms(c->win, net_wm_desk, XA_CARDINAL, &cur_desk, 1); c->desk = cur_desk; } #ifdef DEBUG dump_info(c); #endif check_states(c); /* We are not actually keeping the stack one in order. However, every * fancy panel uses it and nothing else, no matter what the spec says. * (I'm not sure why, as rearranging the list every time the stacking * changes would be distracting. GNOME's window list applet doesn't.) */ append_atoms(root, net_client_list, XA_WINDOW, &c->win, 1); append_atoms(root, net_client_stack, XA_WINDOW, &c->win, 1); return c; } client_t *find_client(Window w, int mode) { client_t *c; if (mode == MATCH_FRAME) { for (c = head; c; c = c->next) if (c->frame == w) return c; } else /* mode == MATCH_WINDOW */ { for (c = head; c; c = c->next) if (c->win == w) return c; } return NULL; } void map_client(client_t *c) { XWindowAttributes attr; strut_t s = { 0, 0, 0, 0 }; XWMHints *hints; int btn, want_raise = 1; XGrabServer(dpy); XGetWindowAttributes(dpy, c->win, &attr); collect_struts(c, &s); if (attr.map_state == IsViewable) { c->ignore_unmap++; reparent(c, &s); if (get_wm_state(c->win) == IconicState) { c->ignore_unmap++; XUnmapWindow(dpy, c->win); } else { set_wm_state(c, NormalState); do_map(c, want_raise); } } else { if ((hints = XGetWMHints(dpy, c->win))) { if (hints->flags & StateHint) set_wm_state(c, hints->initial_state); XFree(hints); } else { set_wm_state(c, NormalState); } if (!init_geom(c, &s) && opt_imap) { btn = sweep(c, map_curs, recalc_map, SWEEP_DOWN, &s); if (btn == Button2) btn = sweep(c, resize_curs, recalc_resize, SWEEP_UP, &s); if (btn == Button3) want_raise = 0; } #ifdef DEBUG dump_geom(c, "set to"); dump_info(c); #endif reparent(c, &s); if (get_wm_state(c->win) == NormalState) do_map(c, want_raise); } XSync(dpy, False); c->name = get_wm_name(c->win); // horrible kludge XUngrabServer(dpy); } /* This is just a helper to perform the actual mapping, since there are two * different places we might need to do it. */ static void do_map(client_t *c, int do_raise) { if (IS_ON_CUR_DESK(c)) { XMapWindow(dpy, c->win); if (do_raise) { XMapRaised(dpy, c->frame); } else { XLowerWindow(dpy, c->frame); XMapWindow(dpy, c->frame); } } } /* When we're ready to map, we have two things to consider: the literal * geometry of the window (what the client passed to XCreateWindow), and the * size hints (what they set with XSetWMSizeHints, if anything). Generally, * the client doesn't care, and leaves the literal geometry at +0+0. If the * client wants to be mapped in a particular place, though, they either set * this geometry to something different or set a size hint. The size hint * is the recommended method, and takes precedence. If there is already * something in c->geom, though, we just leave it. */ static int init_geom(client_t *c, strut_t *s) { Atom win_type, state; int screen_x = DisplayWidth(dpy, screen); int screen_y = DisplayHeight(dpy, screen); int wmax = screen_x - s->left - s->right; int hmax = screen_y - s->top - s->bottom; int mouse_x, mouse_y; /* We decide the geometry for these types of windows, so we can just * ignore everything and return right away. If c->zoomed is set, that * means we've already set things up, but otherwise, we do it here. */ if (c->zoomed) return 1; if (get_atoms(c->win, net_wm_state, XA_ATOM, 0, &state, 1, NULL) && state == net_wm_state_fs) { c->geom.x = 0; c->geom.y = 0; c->geom.w = screen_x; c->geom.h = screen_y; return 1; } /* Here, we merely set the values; they're in the same place regardless * of whether the user or the program specified them. We'll distinguish * between the two cases later, if we need to. */ if (c->size.flags & (USSize|PSize)) { if (c->size.width > 0) c->geom.w = c->size.width; if (c->size.height > 0) c->geom.h = c->size.height; } if (c->size.flags & (USPosition|PPosition)) { if (c->size.x > 0) c->geom.x = c->size.x; if (c->size.y > 0) c->geom.y = c->size.y; } /* Several types of windows can put themselves wherever they want, but we * need to read the size hints to get that position before returning. */ if (get_atoms(c->win, net_wm_wintype, XA_ATOM, 0, &win_type, 1, NULL) && CAN_PLACE_SELF(win_type)) return 1; /* At this point, maybe nothing was set, or something went horribly wrong * and the values are garbage. So, make a guess, based on the pointer. */ if (c->geom.x <= 0 && c->geom.y <= 0) { get_pointer(&mouse_x, &mouse_y); recalc_map(c, c->geom, mouse_x, mouse_y, mouse_x, mouse_y, s); } /* In any case, if we got this far, we need to do a further sanity check * and make sure that the window isn't overlapping any struts -- except * for transients, because they might be a panel-type client popping up a * notification window over themselves. */ if (!c->trans) { if (c->geom.x + c->geom.w > screen_x - s->right) c->geom.x = screen_x - s->right - c->geom.w; if (c->geom.y + c->geom.h > screen_y - s->bottom) c->geom.y = screen_y - s->bottom - c->geom.h; if (c->geom.x < s->left || c->geom.w > wmax) c->geom.x = s->left; if (c->geom.y < s->top || c->geom.h > hmax) c->geom.y = s->top; } /* Finally, we decide if we were ultimately satisfied with the position * given, or if we had to make something up, so that the caller can * consider using some other method. */ return c->trans || c->size.flags & USPosition; } /* The frame window is not created until we actually do the reparenting here, * and thus the Xft surface cannot exist until this runs. Anything that has to * manipulate the client before we are called must make sure not to attempt to * use either. */ static void reparent(client_t *c, strut_t *s) { XSetWindowAttributes pattr; geom_t f; f = frame_geom(c); pattr.override_redirect = True; pattr.background_pixel = bg.pixel; pattr.border_pixel = bd.pixel; pattr.event_mask = SubMask|ButtonPressMask|ExposureMask|EnterWindowMask; c->frame = XCreateWindow(dpy, root, f.x, f.y, f.w, f.h, BW(c), DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &pattr); #ifdef SHAPE if (shape) { XShapeSelectInput(dpy, c->win, ShapeNotifyMask); set_shape(c); } #endif #ifdef XFT c->xftdraw = XftDrawCreate(dpy, (Drawable)c->frame, DefaultVisual(dpy, DefaultScreen(dpy)), DefaultColormap(dpy, DefaultScreen(dpy))); #endif XAddToSaveSet(dpy, c->win); XSelectInput(dpy, c->win, ColormapChangeMask|PropertyChangeMask); XSetWindowBorderWidth(dpy, c->win, 0); XResizeWindow(dpy, c->win, c->geom.w, c->geom.h); XReparentWindow(dpy, c->win, c->frame, 0, frame_height(c)); send_config(c); } /* For a regular window, c->trans is None (false), and we include enough space * to draw the name. For a transient window we just make a small strip (based * on the font height). */ int frame_height(client_t *c) { if (c && c->decor) return (c->trans ? 0 : ASCENT) + DESCENT + 2*opt_pad + BW(c); else return 0; } int set_wm_state(client_t *c, unsigned long state) { return set_atoms(c->win, wm_state, wm_state, &state, 1); } void check_states(client_t *c) { Atom state; unsigned long read, left; int i; for (i = 0, left = 1; left; i += read) { read = get_atoms(c->win, net_wm_state, XA_ATOM, i, &state, 1, &left); if (read) { if (state == net_wm_state_shaded) shade_client(c); else if (state == net_wm_state_mh || state == net_wm_state_mv) zoom_client(c); } else { break; } } } /* If we frob the geom for some reason, we need to inform the client. */ void send_config(client_t *c) { XConfigureEvent ce; ce.type = ConfigureNotify; ce.event = c->win; ce.window = c->win; ce.x = c->geom.x; ce.y = c->geom.y; ce.width = c->geom.w; ce.height = c->geom.h; ce.border_width = 0; ce.above = None; ce.override_redirect = 0; XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); } /* I've changed this to just clear the window every time. The amount of * ``flicker'' is basically imperceptable. Also, we might be drawing an * anti-aliased font with Xft, in which case we always have to clear to draw * the text properly. This allows us to simplify handle_property_change as * well. * * Unfortunately some fussing with pixels is always necessary. The integer * division here should match X's line algorithms so that proportions are * correct at all border widths. For text, I have subjectively chosen a * horizontal space of 1/2 the descender. Vertically, the decender is part of * the font; it is in addition to opt_pad. */ void redraw_frame(client_t *c) { int x, y; if (c && c->decor) { XClearWindow(dpy, c->frame); if (!c->shaded) XDrawLine(dpy, c->frame, border_gc, 0, frame_height(c) - BW(c) + BW(c)/2, c->geom.w, frame_height(c) - BW(c) + BW(c)/2); XDrawLine(dpy, c->frame, border_gc, c->geom.w - frame_height(c) + BW(c)/2, 0, c->geom.w - frame_height(c) + BW(c)/2, frame_height(c)); if (!c->trans && c->name) { x = opt_pad + DESCENT/2; y = opt_pad + ASCENT; #ifdef XFT #ifdef X_HAVE_UTF8_STRING XftDrawStringUtf8(c->xftdraw, &xft_fg, xftfont, x, y, (unsigned char *)c->name, strlen(c->name)); #else XftDrawString8(c->xftdraw, &xft_fg, xftfont, x, y, (unsigned char *)c->name, strlen(c->name)); #endif #else #ifdef X_HAVE_UTF8_STRING Xutf8DrawString(dpy, c->frame, font_set, string_gc, x, y, c->name, strlen(c->name)); #else XDrawString(dpy, c->frame, string_gc, x, y, c->name, strlen(c->name)); #endif #endif } } } /* The frame is bigger than the client window. Which direction it extends * outside of the theoretical client geom is determined by the window gravity. * The default is NorthWest, which means that the top left corner of the frame * stays where the top left corner of the client window would have been, and * the client window moves down. For SouthEast, etc, the frame moves up. For * Static the client window must not move (same result as South), and for * Center the center point of the frame goes where the center point of the * unmanaged client window was. */ geom_t frame_geom(client_t *c) { geom_t f = c->geom; /* everything else is the same as c->geom was */ f.h = frame_height(c) + (c->shaded ? -BW(c) : c->geom.h); /* X, in its perpetual helpfulness, always does native borders NorthWest * style. This, as usual, ruins everything. So we compensate. */ switch (GRAV(c)) { case NorthWestGravity: break; case NorthGravity: f.x -= BW(c); break; case NorthEastGravity: f.x -= 2 * BW(c); break; case EastGravity: f.x -= 2 * BW(c); f.y -= frame_height(c) / 2 + BW(c); break; case SouthEastGravity: f.x -= 2 * BW(c); f.y -= frame_height(c) + 2 * BW(c); break; case SouthGravity: f.x -= BW(c); f.y -= frame_height(c) + 2 * BW(c); break; case SouthWestGravity: f.y -= frame_height(c) + 2 * BW(c); break; case WestGravity: f.y -= frame_height(c) / 2 + BW(c); break; case StaticGravity: f.y -= frame_height(c) + BW(c); f.x -= BW(c); break; case CenterGravity: f.x -= BW(c); f.y -= frame_height(c) / 2 + BW(c); break; } return f; } void collect_struts(client_t *c, strut_t *s) { client_t *p; XWindowAttributes attr; strut_t temp; for (p = head; p; p = p->next) { if (!IS_ON_CUR_DESK(p) || p == c) continue; XGetWindowAttributes(dpy, p->win, &attr); if (attr.map_state == IsViewable && get_strut(p->win, &temp)) { if (temp.left > s->left) s->left = temp.left; if (temp.right > s->right) s->right = temp.right; if (temp.top > s->top) s->top = temp.top; if (temp.bottom > s->bottom) s->bottom = temp.bottom; } } } /* Well, the man pages for the shape extension say nothing, but I was able to * find a shape.PS.Z on the x.org FTP site. What we want to do here is make * the window shape be a boolean OR (or union) of the client's shape and our * bar for the name. The bar requires both a bound and a clip because it has a * border; the server will paint the border in the region between the two. */ #ifdef SHAPE void set_shape(client_t *c) { int n, order; XRectangle temp, *rects; rects = XShapeGetRectangles(dpy, c->win, ShapeBounding, &n, &order); if (n > 1) { XShapeCombineShape(dpy, c->frame, ShapeBounding, 0, frame_height(c), c->win, ShapeBounding, ShapeSet); temp.x = -BW(c); temp.y = -BW(c); temp.width = c->geom.w + 2*BW(c); temp.height = frame_height(c) + BW(c); XShapeCombineRectangles(dpy, c->frame, ShapeBounding, 0, 0, &temp, 1, ShapeUnion, YXBanded); temp.x = 0; temp.y = 0; temp.width = c->geom.w; temp.height = frame_height(c) - BW(c); XShapeCombineRectangles(dpy, c->frame, ShapeClip, 0, frame_height(c), &temp, 1, ShapeUnion, YXBanded); c->shaped = 1; } else if (c->shaped) { /* I can't find a ``remove all shaping'' function... */ temp.x = -BW(c); temp.y = -BW(c); temp.width = c->geom.w + 2*BW(c); temp.height = c->geom.h + frame_height(c) + 2*BW(c); XShapeCombineRectangles(dpy, c->frame, ShapeBounding, 0, 0, &temp, 1, ShapeSet, YXBanded); } XFree(rects); } #endif /* I've decided to carefully ignore any errors raised by this function, rather * that attempt to determine asychronously if a window is ``valid''. Xlib * calls should only fail here if that a window has removed itself completely * before the Unmap and Destroy events get through the queue to us. It's not * pretty. * * The 'withdrawing' argument specifes if the client is actually destroying * itself or being destroyed by us, or if we are merely cleaning up its data * structures when we exit mid-session. */ void del_client(client_t *c, int mode) { client_t *p; XGrabServer(dpy); XSetErrorHandler(ignore_xerror); #ifdef DEBUG dump_name(c, "removing", 'r'); dump_removal(c, mode); #endif if (mode == DEL_WITHDRAW) { set_wm_state(c, WithdrawnState); } else /* mode == DEL_REMAP */ { if (c->zoomed) { c->geom.x = c->save.x; c->geom.y = c->save.y; c->geom.w = c->save.w; c->geom.h = c->save.h; XResizeWindow(dpy, c->win, c->geom.w, c->geom.h); } XMapWindow(dpy, c->win); } remove_atom(root, net_client_list, XA_WINDOW, c->win); remove_atom(root, net_client_stack, XA_WINDOW, c->win); XSetWindowBorderWidth(dpy, c->win, c->old_bw); #ifdef XFT if (c->xftdraw) XftDrawDestroy(c->xftdraw); #endif XReparentWindow(dpy, c->win, root, c->geom.x, c->geom.y); XRemoveFromSaveSet(dpy, c->win); XDestroyWindow(dpy, c->frame); if (head == c) head = c->next; else for (p = head; p && p->next; p = p->next) if (p->next == c) p->next = c->next; if (c->name) XFree(c->name); free(c); XSync(dpy, False); XSetErrorHandler(handle_xerror); XUngrabServer(dpy); } aewm-1.3.12/README0000644000175000001440000001363210733006331012740 0ustar decklinusersaewm - An Exiguous/Exegetic Window Manager ---------------------------------------------------------------------- aewm is a minimal window manager for X11. It is extremely light on features and memory usage, and suitable for slower or embedded systems. It is also intended to provide an example implementation of most of the ICCCM and some of the EWMH, in a simple and clear way, so that you can easily hack on it or appropriate its code for another program. It is not, however, an obsessively complete "reference" implementation; features that are distracting or unnecessary have been skipped. The user interface is minimal, and as much of it as possible is implemented in separate clients. Installation ---------------------------------------------------------------------- Before compiling, look in ``src/Makefile'' and enable any options your system supports. Optionally, change any of the default settings in ``src/aewm.h'' (DEF_FONT, in particular, should be something that will always exist on your server). Then, run ``make; make install''. Usage ---------------------------------------------------------------------- aewm adds a small frame containing the window's name to each window, with a small box in the upper-right corner. All operation are performed with the mouse, as follows: Button Frame Box ======================= 1 Raise Iconify 2 Move Resize 3 Lower Kill 4 Shade Zoom 5 Unshade Unzoom Button 2 operations require a click, hold and drag; all others are a single click. Iconified windows are unmapped, and their management is left up to other clients. Clicking any button on the root window will launch another client. By default, these are: Button Client ======================= 1 aemenu --switch 2 xterm 3 aemenu --launch 4 aedesk -1 5 aedesk +1 Newly mapped windows are placed relative to available space based on the relative position of the mouse on the entire screen. If interactive mapping is turned on with -map, you can move the mouse to change this position before clicking to set it. Using button 1 for this click will map the newly created window on top of the stack, button 3 on the bottom, and button 2 will start a resize operation. Virtual Desktops ---------------------------------------------------------------------- Versions from 1.3.0 on support virtual desktops, as defined by the EWMH specification. Switching from one desktop to another, or setting the number of desktops available, is accomplished by sending a message to the root window; the included client ``aedesk'' can be used for this. To move an existing client to a different desktop, set the appropriate property on that window. The ``xprop'' command in the X11R6 distribution can do this interactively, with e.g. ``xprop -f _NET_WM_DESKTOP 32c -set _NET_WM_DESKTOP 0''. Clients ---------------------------------------------------------------------- The ``clients'' subdirectory contains several other clients which can be used together to provide a usable windowing environment. ``aemenu'' pops up a menu of programs to be launched, or (when given the ``--switch'' option) other currently running clients. ``aepanel'' stays at the edge of your display and uses buttons to represent other clients. It also allows you to open the same menu of programs. Their config file will be searched for in ``~/.aewm/clientsrc'' and then ``/etc/X11/aewm/clientsrc''. an example is provided in ``doc/clientsrc.ex''. When using aemenu, you can just exec aewm in your ``~/.xsession'' script, but if you prefer to use aepanel, or want to switch/restart window managers without closing your X session, you can start aewm in the background and exec aepanel or aesession instead. (aesession is a program which does nothing but hold the X session open and reap child processes.) Keyboard Bindings ---------------------------------------------------------------------- aewm does not grab any keys. This is by design, and recommendation of the ICCCM. Instead, you can use xbindkeys (which is unfortately not included in the stardard X distribution). For example, in ``~/.xbindkeysrc'': "xwit -opposite -current" Alt + Tab "aedesk 0" Alt + F1 "aedesk 1" Alt + F2 (This "alt-tab" is not quite as useful as GNOME's. Properly circulating only managed, non-panel, non-taskbar-skipped windows would require a client that recognizes those EWMH hints, as X's native window circulation support works on a much lower level.) Other Caveats ---------------------------------------------------------------------- aewm does not set the root cursor. To do this, you can do something like ``xsetroot -cursor_name left_ptr.''. There is no -display option, since there is no stardard way to pass this on to child processes. Instead, use ``DISPLAY=host:0 aewm''. The Makefiles may cause problems with older versions of tradional make. Try upgrading first, or substitute ``gmake'' for ``make''. You may want to adjust with NAME_SIZE in aepanel, as larger values can cause large buttons to crowd out smaller ones, but smaller values can reduce readability. Acknowledgements ---------------------------------------------------------------------- This project would have been nearly impossible without the greatly appreciated work of David Hogan in creating 9wm, on which I based my design. I can't thank him enough. Sadly, David passed away in 2003. A memorial page for him can be found at . Thanks to Christophe Tronche for HTML-ifying the ICCCM and other essential X documentation, available at . Adam Sampson wrote the initial code for MWM hints, Xft support, and menu-ified goodies. Author ---------------------------------------------------------------------- Copyright 1998-2007 Decklin Foster . This program is free software; please see LICENSE for details. aewm's web page is at . aewm-1.3.12/atom.c0000644000175000001440000001742510717144424013200 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include #include #include "common.h" #include "atom.h" Atom utf8_string; Atom wm_state; Atom wm_change_state; Atom wm_protos; Atom wm_delete; Atom net_supported; Atom net_client_list; Atom net_client_stack; Atom net_active_window; Atom net_close_window; Atom net_cur_desk; Atom net_num_desks; Atom net_wm_name; Atom net_wm_desk; Atom net_wm_state; Atom net_wm_state_shaded; Atom net_wm_state_mv; Atom net_wm_state_mh; Atom net_wm_state_fs; Atom net_wm_state_skipt; Atom net_wm_state_skipp; Atom net_wm_strut; Atom net_wm_strut_partial; Atom net_wm_wintype; Atom net_wm_type_dock; Atom net_wm_type_menu; Atom net_wm_type_splash; Atom net_wm_type_desk; static char *get_string_atom(Window, Atom, Atom); /* Despite the fact that all these are 32 bits on the wire, libX11 really does * stuff an array of longs into *data, so you get 64 bits on 64-bit archs. So * we gotta be careful here. */ unsigned long get_atoms(Window w, Atom a, Atom type, unsigned long off, unsigned long *ret, unsigned long nitems, unsigned long *left) { Atom real_type; int i, real_format = 0; unsigned long items_read = 0; unsigned long bytes_left = 0; unsigned long *p; unsigned char *data; XGetWindowProperty(dpy, w, a, off, nitems, False, type, &real_type, &real_format, &items_read, &bytes_left, &data); if (real_format == 32 && items_read) { p = (unsigned long *)data; for (i = 0; i < items_read; i++) *ret++ = *p++; XFree(data); if (left) *left = bytes_left; return items_read; } else { return 0; } } unsigned long set_atoms(Window w, Atom a, Atom type, unsigned long *val, unsigned long nitems) { return (XChangeProperty(dpy, w, a, type, 32, PropModeReplace, (unsigned char *)val, nitems) == Success); } unsigned long append_atoms(Window w, Atom a, Atom type, unsigned long *val, unsigned long nitems) { return (XChangeProperty(dpy, w, a, type, 32, PropModeAppend, (unsigned char *)val, nitems) == Success); } void remove_atom(Window w, Atom a, Atom type, unsigned long remove) { unsigned long tmp, read, left, *new; int i, j = 0; read = get_atoms(w, a, type, 0, &tmp, 1, &left); if (!read) return; new = malloc((read + left) * sizeof *new); if (read && tmp != remove) new[j++] = tmp; for (i = 1, read = left = 1; read && left; i += read) { read = get_atoms(w, a, type, i, &tmp, 1, &left); if (!read) break; else if (tmp != remove) new[j++] = tmp; } if (j) XChangeProperty(dpy, w, a, type, 32, PropModeReplace, (unsigned char *)new, j); else XDeleteProperty(dpy, w, a); } /* Get the window-manager name (aka human-readable "title") for a given * window. There are two ways a client can set this: * * 1. _NET_WM_STRING, which has type UTF8_STRING. This is preferred and * is always used if available. * 2. WM_NAME, which has type COMPOUND_STRING or STRING. This is the old * ICCCM way, which we fall back to in the absence of _NET_WM_STRING. * In this case, we ask X to convert the value of the property to * UTF-8 for us. N.b.: STRING is Latin-1 whatever the locale. * COMPOUND_STRING is the most hideous abomination ever created. * Thankfully we do not have to worry about any of this. * * If UTF-8 conversion is not available (XFree86 < 4.0.2, or any older X * implementation), only WM_NAME will be checked, and, at least for * XFree86 and X.Org, it will only be returned if it has type STRING. * This is due to an inherent limitation in their implementation of * XFetchName. If you have a different X vendor, YMMV. * * In all cases, this function asks X to allocate the returned string, * so it must be freed with XFree. */ char *get_wm_name(Window w) { char *name; #ifdef X_HAVE_UTF8_STRING XTextProperty name_prop; XTextProperty name_prop_converted; char **name_list; int nitems; if ((name = get_string_atom(w, net_wm_name, utf8_string))) { return name; } else if (XGetWMName(dpy, w, &name_prop)) { if (Xutf8TextPropertyToTextList(dpy, &name_prop, &name_list, &nitems) == Success && nitems >= 1) { /* Now we've got a freshly allocated XTextList. Since it might * have multiple items that need to be joined, and we need to * return something that can be freed by XFree, we roll it back up * into an XTextProperty. */ if (Xutf8TextListToTextProperty(dpy, name_list, nitems, XUTF8StringStyle, &name_prop_converted) == Success) { XFreeStringList(name_list); return (char *)name_prop_converted.value; } else { /* Not much we can do here. This should never happen anyway. * Famous last words. */ XFreeStringList(name_list); return NULL; } } else { return (char *)name_prop.value; } } else { /* There is no prop. There is only NULL! */ return NULL; } #else XFetchName(dpy, w, &name); return name; #endif } /* I give up on trying to do this the right way. We'll just request as many * elements as possible. If that's not the entire string, we're fucked. In * reality this should never happen. (That's the second time I get to say * ``this should never happen'' in this file!) * * Standard gripe about casting nonsense applies. */ static char *get_string_atom(Window w, Atom a, Atom type) { Atom real_type; int real_format = 0; unsigned long items_read = 0; unsigned long bytes_left = 0; unsigned char *data; XGetWindowProperty(dpy, w, a, 0, LONG_MAX, False, type, &real_type, &real_format, &items_read, &bytes_left, &data); /* XXX: should check bytes_left here and bail if nonzero, in case someone * wants to store a >4gb string on the server */ if (real_format == 8 && items_read >= 1) return (char *)data; else return NULL; } /* Reads the _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT hint into the args, if it * exists. In the case of _NET_WM_STRUT_PARTIAL we cheat and only take the * first 4 values, because that's all we care about. This means we can use the * same code for both (_NET_WM_STRUT only specifies 4 elements). Each number * is the margin in pixels on that side of the display where we don't want to * place clients. If there is no hint, we act as if it was all zeros (no * margin). */ int get_strut(Window w, strut_t *s) { Atom real_type; int real_format = 0; unsigned long items_read = 0; unsigned long bytes_left = 0; unsigned char *data; unsigned long *strut_data; XGetWindowProperty(dpy, w, net_wm_strut_partial, 0, 12, False, XA_CARDINAL, &real_type, &real_format, &items_read, &bytes_left, &data); if (!(real_format == 32 && items_read >= 12)) XGetWindowProperty(dpy, w, net_wm_strut, 0, 4, False, XA_CARDINAL, &real_type, &real_format, &items_read, &bytes_left, &data); if (real_format == 32 && items_read >= 4) { strut_data = (unsigned long *)data; s->left = strut_data[0]; s->right = strut_data[1]; s->top = strut_data[2]; s->bottom = strut_data[3]; XFree(data); return 1; } else { s->left = 0; s->right = 0; s->top = 0; s->bottom = 0; return 0; } } unsigned long get_wm_state(Window w) { unsigned long state; if (get_atoms(w, wm_state, wm_state, 0, &state, 1, NULL)) return state; else return WithdrawnState; } aewm-1.3.12/aesession.c0000644000175000001440000000063210717144424014221 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include int main(void) { /* ``I gained nothing at all from Supreme Enlightenment, and for that very * reason it is called Supreme Enlightenment.'' -- Buddha */ for (;;) if (wait(NULL) == -1) sleep(1); } aewm-1.3.12/aewm_event.c0000644000175000001440000003172210717144424014366 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #ifdef SHAPE #include #endif #include "aewm.h" #include "atom.h" static void handle_button_press(XButtonEvent *); static void handle_button_release(XButtonEvent *); static void handle_configure_request(XConfigureRequestEvent *); static void handle_circulate_request(XCirculateRequestEvent *); static void handle_map_request(XMapRequestEvent *); static void handle_unmap_event(XUnmapEvent *); static void handle_destroy_event(XDestroyWindowEvent *); static void handle_client_message(XClientMessageEvent *); static void handle_property_change(XPropertyEvent *); static void handle_enter_event(XCrossingEvent *); static void handle_cmap_change(XColormapEvent *); static void handle_expose_event(XExposeEvent *); #ifdef SHAPE static void handle_shape_change(XShapeEvent *); #endif static int root_button_pressed = 0; /* TWM has an interesting and different way of doing this. We might also want * to respond to unknown events. */ void event_loop(void) { XEvent ev; for (;;) { XNextEvent(dpy, &ev); #ifdef DEBUG show_event(ev); #endif switch (ev.type) { case ButtonPress: handle_button_press(&ev.xbutton); break; case ButtonRelease: handle_button_release(&ev.xbutton); break; case ConfigureRequest: handle_configure_request(&ev.xconfigurerequest); break; case CirculateRequest: handle_circulate_request(&ev.xcirculaterequest); break; case MapRequest: handle_map_request(&ev.xmaprequest); break; case UnmapNotify: handle_unmap_event(&ev.xunmap); break; case DestroyNotify: handle_destroy_event(&ev.xdestroywindow); break; case ClientMessage: handle_client_message(&ev.xclient); break; case ColormapNotify: handle_cmap_change(&ev.xcolormap); break; case PropertyNotify: handle_property_change(&ev.xproperty); break; case EnterNotify: handle_enter_event(&ev.xcrossing); break; case Expose: handle_expose_event(&ev.xexpose); break; #ifdef SHAPE default: if (shape && ev.type == shape_event) handle_shape_change((XShapeEvent *)&ev); #endif } } } /* Someone clicked a button. If they clicked on a window, we want the button * press, but if they clicked on the root, we're only interested in the button * release. Thus, two functions. * * If it was on the root, we get the click by default. If it's on a window * frame, we get it as well. If it's on a client window, it may still fall * through to us if the client doesn't select for mouse-click events. The * upshot of this is that you should be able to click on the blank part of a * GTK window with Button2 to move it. */ static void handle_button_press(XButtonEvent *e) { client_t *c; if ((c = find_client(e->window, MATCH_FRAME))) user_action(c, e->x, e->y, e->button); else if (e->window == root) root_button_pressed = 1; } static void handle_button_release(XButtonEvent *e) { if (e->window == root && root_button_pressed) { #ifdef DEBUG dump_clients(); #endif switch (e->button) { case Button1: fork_exec(opt_new[0]); break; case Button2: fork_exec(opt_new[1]); break; case Button3: fork_exec(opt_new[2]); break; case Button4: fork_exec(opt_new[3]); break; case Button5: fork_exec(opt_new[4]); break; } root_button_pressed = 0; } } /* Because we are redirecting the root window, we get ConfigureRequest events * from both clients we're handling and ones that we aren't. For clients we * manage, we need to adjust the frame and the client window, and for * unmanaged windows we have to pass along everything unchanged. * * Most of the assignments here are going to be garbage, but only the ones * that are masked in by e->value_mask will be looked at by the X server. */ static void handle_configure_request(XConfigureRequestEvent *e) { client_t *c; geom_t f; XWindowChanges wc; if ((c = find_client(e->window, MATCH_WINDOW))) { if (GRAV(c) == NorthWestGravity) { if (e->value_mask & CWX) c->geom.x = e->x - BW(c); if (e->value_mask & CWY) c->geom.y = e->y - BW(c); if (e->value_mask & CWWidth) c->geom.w = e->width; if (e->value_mask & CWHeight) c->geom.h = e->height; } else { if (e->value_mask & CWX) c->geom.x = e->x; if (e->value_mask & CWY) c->geom.y = e->y; if (e->value_mask & CWWidth) c->geom.w = e->width; if (e->value_mask & CWHeight) c->geom.h = e->height; } f = frame_geom(c); wc.x = f.x; wc.y = f.y; wc.width = f.w; wc.height = f.h; wc.border_width = BW(c); wc.sibling = e->above; wc.stack_mode = e->detail; #ifdef DEBUG dump_geom(c, "moving to"); #endif XConfigureWindow(dpy, c->frame, e->value_mask, &wc); #ifdef SHAPE if (e->value_mask & (CWWidth|CWHeight)) set_shape(c); #endif if (c->zoomed && e->value_mask & (CWX|CWY|CWWidth|CWHeight)) { c->zoomed = 0; remove_atom(c->win, net_wm_state, XA_ATOM, net_wm_state_mv); remove_atom(c->win, net_wm_state, XA_ATOM, net_wm_state_mh); } send_config(c); } wc.x = c ? 0 : e->x; wc.y = c ? frame_height(c) : e->y; wc.width = e->width; wc.height = e->height; wc.sibling = e->above; wc.stack_mode = e->detail; XConfigureWindow(dpy, e->window, e->value_mask, &wc); } /* The only window that we will circulate children for is the root (because * nothing else would make sense). After a client requests that the root's * children be circulated, the server will determine which window needs to be * raised or lowered, and so all we have to do is make it so. */ static void handle_circulate_request(XCirculateRequestEvent *e) { if (e->parent == root) { if (e->place == PlaceOnBottom) XLowerWindow(dpy, e->window); else /* e->place == PlaceOnTop */ XRaiseWindow(dpy, e->window); } } /* Two possibilities if a client is asking to be mapped. One is that it's a new * window, so we handle that if it isn't in our clients list anywhere. The * other is that it already exists and wants to de-iconify, which is simple to * take care of. Since we iconify all of a window's transients when iconifying * that window, de-iconify them here. */ static void handle_map_request(XMapRequestEvent *e) { client_t *c, *p; if ((c = find_client(e->window, MATCH_WINDOW))) { uniconify_client(c); for (p = head; p; p = p->next) if (p->trans == c->win) uniconify_client(p); } else { c = new_client(e->window); map_client(c); } } /* We don't get to intercept Unmap events, so this is post mortem. If we * caused the unmap ourselves earlier (explictly or by remapping), we will * have incremented c->ignore_unmap. If not, time to destroy the client. * * Because most clients unmap and destroy themselves at once, they're gone * before we even get the Unmap event, never mind the Destroy one. Therefore * we must be extra careful in del_client. */ static void handle_unmap_event(XUnmapEvent *e) { client_t *c; if ((c = find_client(e->window, MATCH_WINDOW))) { if (c->ignore_unmap) c->ignore_unmap--; else del_client(c, DEL_WITHDRAW); } } /* But a window can also go away when it's not mapped, in which case there is * no Unmap event. */ static void handle_destroy_event(XDestroyWindowEvent *e) { client_t *c; if ((c = find_client(e->window, MATCH_WINDOW))) del_client(c, DEL_WITHDRAW); } /* If a client wants to manipulate itself or another window it must send a * special kind of ClientMessage. As of right now, this only responds to the * ICCCM iconify message, but there are more in the EWMH that will be added * later. */ static void handle_client_message(XClientMessageEvent *e) { client_t *c; if (e->window == root) { if (e->message_type == net_cur_desk && e->format == 32) goto_desk(e->data.l[0]); else if (e->message_type == net_num_desks && e->format == 32) ndesks = e->data.l[0]; } else if ((c = find_client(e->window, MATCH_WINDOW))) { if (e->message_type == wm_change_state && e->format == 32 && e->data.l[0] == IconicState) { iconify_client(c); } else if (e->message_type == net_active_window && e->format == 32) { c->desk = cur_desk; map_if_desk(c); uniconify_client(c); XRaiseWindow(dpy, c->frame); } else if (e->message_type == net_close_window && e->format == 32) { send_wm_delete(c); } } } /* If we have something copied to a variable, or displayed on the screen, make * sure it is up to date. If redrawing the name is necessary, clear the window * because Xft uses alpha rendering. */ static void handle_property_change(XPropertyEvent *e) { client_t *c; long supplied; if ((c = find_client(e->window, MATCH_WINDOW))) { if (e->atom == XA_WM_NAME || e->atom == net_wm_name) { if (c->name) XFree(c->name); c->name = get_wm_name(c->win); redraw_frame(c); } else if (e->atom == XA_WM_NORMAL_HINTS) { XGetWMNormalHints(dpy, c->win, &c->size, &supplied); } else if (e->atom == net_wm_state) { check_states(c); } else if (e->atom == net_wm_desk) { if (get_atoms(c->win, net_wm_desk, XA_CARDINAL, 0, &c->desk, 1, NULL)) { if (c->desk == -1) c->desk = DESK_ALL; /* FIXME */ map_if_desk(c); } } } } /* Lazy focus-follows-mouse and colormap-follows-mouse policy. This does not, * however, prevent focus stealing (it's lazy). It is not very efficient * either; we can get a lot of enter events at once when flipping through a * window stack on startup/desktop change. */ static void handle_enter_event(XCrossingEvent *e) { client_t *c; if ((c = find_client(e->window, MATCH_FRAME))) focus_client(c); } /* More colormap policy: when a client installs a new colormap on itself, set * the display's colormap to that. We do this even if it's not focused. */ static void handle_cmap_change(XColormapEvent *e) { client_t *c; if ((c = find_client(e->window, MATCH_WINDOW)) && e->new) { c->cmap = e->colormap; XInstallColormap(dpy, c->cmap); } } /* If we were covered by multiple windows, we will usually get multiple expose * events, so ignore them unless e->count (the number of outstanding exposes) * is zero. */ static void handle_expose_event(XExposeEvent *e) { client_t *c; if ((c = find_client(e->window, MATCH_FRAME)) && e->count == 0) redraw_frame(c); } #ifdef SHAPE static void handle_shape_change(XShapeEvent *e) { client_t *c; if ((c = find_client(e->window, MATCH_WINDOW))) set_shape(c); } #endif #ifdef DEBUG void show_event(XEvent e) { char *ev_type; Window w; client_t *c; switch (e.type) { SHOW_EV(ButtonPress, xbutton) SHOW_EV(ButtonRelease, xbutton) SHOW_EV(ClientMessage, xclient) SHOW_EV(ColormapNotify, xcolormap) SHOW_EV(ConfigureNotify, xconfigure) SHOW_EV(ConfigureRequest, xconfigurerequest) SHOW_EV(CirculateRequest, xcirculaterequest) SHOW_EV(CreateNotify, xcreatewindow) SHOW_EV(DestroyNotify, xdestroywindow) SHOW_EV(EnterNotify, xcrossing) SHOW_EV(Expose, xexpose) SHOW_EV(MapNotify, xmap) SHOW_EV(MapRequest, xmaprequest) SHOW_EV(MappingNotify, xmapping) SHOW_EV(MotionNotify, xmotion) SHOW_EV(PropertyNotify, xproperty) SHOW_EV(ReparentNotify, xreparent) SHOW_EV(ResizeRequest, xresizerequest) SHOW_EV(UnmapNotify, xunmap) default: #ifdef SHAPE if (shape && e.type == shape_event) { ev_type = "ShapeNotify"; w = ((XShapeEvent *)&e)->window; break; } #endif ev_type = "unknown event"; w = None; break; } if ((c = find_client(w, MATCH_WINDOW))) dump_name(c, ev_type, 'w'); else if ((c = find_client(w, MATCH_FRAME))) dump_name(c, ev_type, 'f'); else if (w == root) dump_win(w, ev_type, 'r'); else /* something we are not managing */ dump_win(w, ev_type, 'u'); } #endif aewm-1.3.12/aewm_manip.c0000644000175000001440000003253010717144424014347 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include #include "aewm.h" #include "atom.h" static void do_iconify(client_t *); static void do_shade(client_t *); static void draw_outline(client_t *); static geom_t fix_size(client_t *); void user_action(client_t *c, int x, int y, int button) { if (x >= c->geom.w - frame_height(c) && y <= frame_height(c)) { switch (button) { case Button1: iconify_client(c); break; case Button2: resize_client(c); break; case Button3: send_wm_delete(c); break; case Button4: zoom_client(c); break; case Button5: unzoom_client(c); break; } } else { switch (button) { case Button1: XRaiseWindow(dpy, c->frame); break; case Button2: move_client(c); break; case Button3: XLowerWindow(dpy, c->frame); break; case Button4: shade_client(c); break; case Button5: unshade_client(c); break; } } } /* This can't do anything dangerous. See handle_enter_event. */ void focus_client(client_t *c) { set_atoms(root, net_active_window, XA_WINDOW, &c->win, 1); XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); XInstallColormap(dpy, c->cmap); } void move_client(client_t *c) { geom_t f; if (!c->zoomed) { sweep(c, move_curs, recalc_move, SWEEP_UP, NULL); f = frame_geom(c); XMoveWindow(dpy, c->frame, f.x, f.y); send_config(c); } } /* If we are resizing a client that was zoomed, we have to put it in an * unzoomed state, but we need to start sweeping from the effective geometry * rather than the "real" geometry that unzooming will restore. We get around * this by blatantly cheating. */ void resize_client(client_t *c) { geom_t f; strut_t hold = { 0, 0, 0, 0 }; if (c->zoomed) c->save = c->geom; unzoom_client(c); sweep(c, resize_curs, recalc_resize, SWEEP_UP, &hold); f = frame_geom(c); XMoveResizeWindow(dpy, c->frame, f.x, f.y, f.w, f.h); XMoveResizeWindow(dpy, c->win, 0, frame_height(c), c->geom.w, c->geom.h); send_config(c); } /* Transients will be iconified when their owner is iconified. */ void iconify_client(client_t *c) { client_t *p; do_iconify(c); for (p = head; p; p = p->next) if (p->trans == c->win) do_iconify(p); } void do_iconify(client_t *c) { if (!c->ignore_unmap) c->ignore_unmap++; XUnmapWindow(dpy, c->frame); XUnmapWindow(dpy, c->win); set_wm_state(c, IconicState); } void uniconify_client(client_t *c) { XMapWindow(dpy, c->win); XMapRaised(dpy, c->frame); set_wm_state(c, NormalState); } void shade_client(client_t *c) { if (!c->shaded) { c->shaded = 1; append_atoms(c->win, net_wm_state, XA_ATOM, &net_wm_state_shaded, 1); do_shade(c); } } void unshade_client(client_t *c) { if (c->shaded) { c->shaded = 0; remove_atom(c->win, net_wm_state, XA_ATOM, net_wm_state_shaded); do_shade(c); } } static void do_shade(client_t *c) { geom_t f; if (c->frame) { f = frame_geom(c); XMoveResizeWindow(dpy, c->frame, f.x, f.y, f.w, f.h); } send_config(c); } /* When zooming a window, the old geom gets stuffed into c->save. Once we * unzoom, this should be considered garbage. Despite the existence of * vertical and horizontal hints, we only provide both at once. * * Zooming implies unshading, but the inverse is not true. */ void zoom_client(client_t *c) { strut_t s = { 0, 0, 0, 0 }; if (!c->zoomed) { c->save = c->geom; c->shaded = 0; c->zoomed = 1; collect_struts(c, &s); c->geom.x = s.left; c->geom.y = s.top; c->geom.w = DisplayWidth(dpy, screen) - 2*BW(c) - s.left - s.right; c->geom.h = DisplayHeight(dpy, screen) - 2*BW(c) - frame_height(c) - s.top - s.bottom; fix_size(c); if (c->frame) { XMoveResizeWindow(dpy, c->frame, c->geom.x, c->geom.y, c->geom.w, c->geom.h + frame_height(c)); XResizeWindow(dpy, c->win, c->geom.w, c->geom.h); redraw_frame(c); } remove_atom(c->win, net_wm_state, XA_ATOM, net_wm_state_shaded); append_atoms(c->win, net_wm_state, XA_ATOM, &net_wm_state_mv, 1); append_atoms(c->win, net_wm_state, XA_ATOM, &net_wm_state_mh, 1); send_config(c); } } void unzoom_client(client_t *c) { geom_t f; if (c->zoomed) { c->geom = c->save; c->zoomed = 0; if (c->frame) { f = frame_geom(c); XMoveResizeWindow(dpy, c->frame, f.x, f.y, f.w, f.h); XResizeWindow(dpy, c->win, c->geom.w, c->geom.h); redraw_frame(c); } remove_atom(c->win, net_wm_state, XA_ATOM, net_wm_state_mv); remove_atom(c->win, net_wm_state, XA_ATOM, net_wm_state_mh); send_config(c); } } /* The name of this function is a little misleading: if the client doesn't * listen to WM_DELETE then we just terminate it with extreme prejudice. */ void send_wm_delete(client_t *c) { int i, n, found = 0; Atom *protocols; if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { for (i=0; iwin, wm_protos, wm_delete, NoEventMask); else XKillClient(dpy, c->win); } void goto_desk(int new_desk) { client_t *c; cur_desk = new_desk; set_atoms(root, net_cur_desk, XA_CARDINAL, &cur_desk, 1); for (c = head; c; c = c->next) map_if_desk(c); } void map_if_desk(client_t *c) { if (IS_ON_CUR_DESK(c) && get_wm_state(c->win) == NormalState) XMapWindow(dpy, c->frame); else XUnmapWindow(dpy, c->frame); } int sweep(client_t *c, Cursor curs, sweep_func cb, int mode, strut_t *s) { int x0, y0, mask; geom_t orig = c->geom; XEvent ev; if (XGrabPointer(dpy, root, False, MouseMask, GrabModeAsync, GrabModeAsync, None, curs, CurrentTime) != GrabSuccess) return 0; XGrabServer(dpy); mask = get_pointer(&x0, &y0); cb(c, orig, x0, y0, x0, y0, s); draw_outline(c); /* XXX: Ugh */ if (!(mode == SWEEP_DOWN && (mask & ButtonPressMask))) { for (;;) { XMaskEvent(dpy, MouseMask, &ev); if (ev.type == MotionNotify) { draw_outline(c); cb(c, orig, x0, y0, ev.xmotion.x, ev.xmotion.y, s); draw_outline(c); } else if ((mode == SWEEP_UP && ev.type == ButtonRelease) || (mode == SWEEP_DOWN && ev.type == ButtonPress)) { draw_outline(c); break; } } } XUngrabServer(dpy); XUngrabPointer(dpy, CurrentTime); return ev.xbutton.button; } /* This is simple and dumb: if the cursor is in the center of the screen, * center the window on the available space. If it's at the top left, then * at the top left. As you go between, and to other edges, scale it. */ void recalc_map(client_t *c, geom_t orig, int x0, int y0, int x1, int y1, strut_t *s) { int screen_x = DisplayWidth(dpy, screen); int screen_y = DisplayHeight(dpy, screen); int wmax = screen_x - s->left - s->right; int hmax = screen_y - s->top - s->bottom; c->geom.x = s->left + ((float)x1 / (float)screen_x) * (wmax + 1 - c->geom.w - 2*BW(c)); c->geom.y = s->top + ((float)y1 / (float)screen_y) * (hmax + 1 - c->geom.h - frame_height(c) - 2*BW(c)); } void recalc_move(client_t *c, geom_t orig, int x0, int y0, int x1, int y1, strut_t *s) { c->geom.x = orig.x + x1 - x0; c->geom.y = orig.y + y1 - y0; } /* When considering the distance from the mouse to the center point of the * window (which remains fixed), we actually have to look at the top right * corner of the window (which remains a constant offset from wherever we * clicked in the box relative to the root, but not relative to the window, * because the distance can be negative). After that we just center the new * size. */ void recalc_resize(client_t *c, geom_t orig, int x0, int y0, int x1, int y1, strut_t *hold) { client_t fake = *c; /* FIXME */ geom_t frig; fake.geom = orig; frig = frame_geom(&fake); if (x1 <= frig.x) hold->left = 1; if (y1 <= frig.y) hold->top = 1; if (x1 >= frig.x + frig.w) hold->right = 1; if (y1 >= frig.y + frig.h) hold->bottom = 1; if (hold->left) c->geom.w = orig.x + orig.w - x1; if (hold->top) c->geom.h = orig.y + orig.h - y1; if (hold->right) c->geom.w = x1 - orig.x; if (hold->bottom) c->geom.h = y1 - orig.y; if (x1 > frig.x + frig.w) hold->left = 0; if (y1 > frig.y + frig.h) hold->top = 0; if (x1 < frig.x) hold->right = 0; if (y1 < frig.y) hold->bottom = 0; fix_size(c); if (hold->left) c->geom.x = orig.x + orig.w - c->geom.w; if (hold->top) c->geom.y = orig.y + orig.h - c->geom.h; if (hold->right) c->geom.x = orig.x; if (hold->bottom) c->geom.y = orig.y; } /* If the window in question has a ResizeInc hint, then it wants to be resized * in multiples of some (x,y). We constrain the values in c->geom based on * that and any min/max size hints, and put the ``human readable'' values back * in lw_ret and lh_ret (80x25 for xterm, etc). */ static geom_t fix_size(client_t *c) { int width_inc, height_inc; int base_width, base_height; geom_t adj = { 0, 0, 0, 0 }; if (c->size.flags & PMinSize) { if (c->geom.w < c->size.min_width) c->geom.w = c->size.min_width; if (c->geom.h < c->size.min_height) c->geom.h = c->size.min_height; } if (c->size.flags & PMaxSize) { if (c->geom.w > c->size.max_width) c->geom.w = c->size.max_width; if (c->geom.h > c->size.max_height) c->geom.h = c->size.max_height; } if (c->size.flags & PResizeInc) { width_inc = c->size.width_inc ? c->size.width_inc : 1; height_inc = c->size.height_inc ? c->size.height_inc : 1; base_width = (c->size.flags & PBaseSize) ? c->size.base_width : (c->size.flags & PMinSize) ? c->size.min_width : 0; base_height = (c->size.flags & PBaseSize) ? c->size.base_height : (c->size.flags & PMinSize) ? c->size.min_height : 0; c->geom.w -= (c->geom.w - base_width) % width_inc; c->geom.h -= (c->geom.h - base_height) % height_inc; adj.w = (c->geom.w - base_width) / width_inc; adj.h = (c->geom.h - base_height) / height_inc; } else { adj.w = c->geom.w; adj.h = c->geom.h; } return adj; } /* Match the calculations we use to draw the frames, and also the spacing * of text from the opposite corner. */ static void draw_outline(client_t *c) { geom_t adj, f = frame_geom(c); int re = f.x + f.w + BW(c); int be = f.y + f.h + BW(c); char buf[256]; XDrawRectangle(dpy, root, invert_gc, f.x + BW(c)/2, f.y + BW(c)/2, f.w + BW(c), f.h + BW(c)); if (!c->shaded) XDrawLine(dpy, root, invert_gc, f.x + BW(c), f.y + frame_height(c) + BW(c)/2, re, f.y + frame_height(c) + BW(c)/2); XDrawLine(dpy, root, invert_gc, re - frame_height(c) + BW(c)/2, f.y + BW(c), re - frame_height(c) + BW(c)/2, f.y + frame_height(c)); adj = fix_size(c); snprintf(buf, sizeof buf, "%ldx%ld%+ld%+ld", adj.w, adj.h, c->geom.x, c->geom.y); XDrawString(dpy, root, invert_gc, re - opt_pad - font->descent/2 - XTextWidth(font, buf, strlen(buf)), be - opt_pad - font->descent, buf, strlen(buf)); } #ifdef DEBUG static const char *show_state(client_t *c) { switch (get_wm_state(c->win)) { SHOW(WithdrawnState) SHOW(NormalState) SHOW(IconicState) default: return "unknown state"; } } static const char *show_grav(client_t *c) { if (!(c->size.flags & PWinGravity)) return "no grav (NW)"; switch (c->size.win_gravity) { SHOW(UnmapGravity) SHOW(NorthWestGravity) SHOW(NorthGravity) SHOW(NorthEastGravity) SHOW(WestGravity) SHOW(CenterGravity) SHOW(EastGravity) SHOW(SouthWestGravity) SHOW(SouthGravity) SHOW(SouthEastGravity) SHOW(StaticGravity) default: return "unknown grav"; } } void dump_name(client_t *c, const char *label, char flag) { printf("%15.15s: %#010lx [%c] %-47.47s\n", label, c->win, flag, c->name); } void dump_win(Window w, const char *label, char flag) { printf("%15.15s: %#010lx [%c] %-47.47s\n", label, w, flag, w == root ? "(root window)": "(unknown window)"); } void dump_info(client_t *c) { printf("%28s[i] frame %#0lx, ignore_unmap %d\n", "", c->frame, c->ignore_unmap); printf("%28s[i] desk %ld, %s, %s\n", "", c->desk, show_state(c), show_grav(c)); } void dump_geom(client_t *c, const char *label) { printf("%28s[g] %s %ldx%ld+%ld+%ld\n", "", label, c->geom.w, c->geom.h, c->geom.x, c->geom.y); } void dump_removal(client_t *c, int mode) { printf("%28s[r] %s, %d pending\n", "", mode == DEL_WITHDRAW ? "withdraw" : "remap", XPending(dpy)); } void dump_clients() { client_t *c; for (c = head; c; c = c->next) { dump_name(c, "dump", 'd'); dump_geom(c, "current"); dump_info(c); } } #endif aewm-1.3.12/menu.c0000644000175000001440000001122610717144424013175 0ustar decklinusers/* aewm - Copyright 1998-2007 Decklin Foster . * This program is free software; please see LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include "common.h" #include "parser.h" #include "atom.h" #include "menu.h" void setup_switch_atoms() { utf8_string = XInternAtom(dpy, "UTF8_STRING", False); wm_state = XInternAtom(dpy, "WM_STATE", False); net_client_list = XInternAtom(dpy, "_NET_CLIENT_LIST", False); net_cur_desk = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", False); net_wm_desk = XInternAtom(dpy, "_NET_WM_DESKTOP", False); net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); net_wm_state_skipt = XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False); net_wm_state_skipp = XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False); net_wm_wintype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); net_wm_type_dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); net_wm_type_desk = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); } void snprint_wm_name(char *buf, size_t len, Window w) { char *n; if ((n = get_wm_name(w))) { if (get_wm_state(w) == NormalState) { if (snprintf(buf, len, "%s", n) > len) strcpy(buf+len-4, "..."); } else { if (snprintf(buf, len, "[%s]", n) > len) strcpy(buf+len-5, "...]"); } XFree(n); } else { snprintf(buf, len, "%#lx", w); } } /* The WM receives the ClientMessage here and sets the property in response, * so we only do this after the PropertyNotify. */ int is_on_cur_desk(Window w) { unsigned long w_desk, cur_desk; if (get_atoms(root, net_cur_desk, XA_CARDINAL, 0, &cur_desk, 1, NULL) && (get_atoms(w, net_wm_desk, XA_CARDINAL, 0, &w_desk, 1, NULL))) return IS_ON_DESK(w_desk, cur_desk); else return 1; } int is_skip(Window w) { Atom win_type, state; int i; unsigned long r; if (get_atoms(w, net_wm_wintype, XA_ATOM, 0, &win_type, 1, NULL) && (win_type == net_wm_type_dock || win_type == net_wm_type_desk)) return 1; for (i = 0, r = 1; r; i += r) if ((r = get_atoms(w, net_wm_state, XA_ATOM, i, &state, 1, NULL)) && (state == net_wm_state_skipt || state == net_wm_state_skipp)) return 1; return 0; } /* This XSync call is required, as we don't have any sort of integration with * the real X event loop. */ void raise_win(Window w) { XMapRaised(dpy, w); XSync(dpy, False); } /* XXX: We do not free the command, since make_item_cb gives gives the * toolkit a pointer for use at some point in the future. The label we assume * is copied right away and thus can be freed as soon as the item is created. * * If we want to catch SIGHUP and rebuild the menu, we are going to need to * find some way to make sure the toolkit actually frees this stuff, or come * up with some fantastically ugly way of doing it ourselves. */ static void do_launch_menu(FILE *rc, void *menu, make_item_func make_item_cb) { char buf[BUF_SIZE], token[BUF_SIZE], *p; while (get_rc_line(buf, sizeof buf, rc)) { p = buf; while (get_token(&p, token)) { if (strcmp(token, "menu") == 0) { void *newmenu; if (get_token(&p, token)) { newmenu = make_item_cb(menu, token, NULL); do_launch_menu(rc, newmenu, make_item_cb); } } if (strcmp(token, "cmd") == 0) { if (get_token(&p, token)) { char *label = strdup(token); if (get_token(&p, token)) make_item_cb(menu, label, strdup(token)); free(label); } } if (strcmp(token, "include") == 0) { if (get_token(&p, token)) { FILE *f = fopen(token, "r"); if (f) { do_launch_menu(f, menu, make_item_cb); fclose(f); } } } if (strcmp(token, "end") == 0) return; } } } void make_launch_menu(char *rcfile, void *menu, make_item_func make_item_cb) { FILE *rc; if ((rc = open_rc(rcfile, "clientsrc"))) { do_launch_menu(rc, menu, make_item_cb); fclose(rc); } else { fprintf(stderr, "can't find any rc files\n"); exit(1); } }