#define WINDOW_MAX 256 /*============================================================================ /\/\in\/\//\/\ v1 Minimal Window Manager [MinWM] by Timothy Farrar ------------------------------------------------------------------------------ ABOUT ----- Simple yet very useful single screen X Window Manager. Designed to use minimal system resources. Designed to minimize wasted user time interacting with windows. No configuration files. Under 10KB x86-64 binary. ------------------------------------------------------------------------------ KEYS ---- ALT+ESC .......... Close window. ALT+TAB .......... Cycle through window list (like Windows). ALT+` ............ Cycle window shape between full, and left/right halves. ------------------------------------------------------------------------------ WINDOW LIST ----------- The windows list is ordered as follows, { most recently used, 2nd most recently used, ..., last used } While ALT is held down pressing TAB will cycle through list, going to the last reciently used window from the current window. It will wrap around at the end. After ALT is released, the list is updated. The new current window is moved to the front of the list. ------------------------------------------------------------------------------ COMPILE ------- Only requires a C compiler and the X11 library. Try something like, gcc minwm.c -Os -o minwm -I/usr/X11/include -L/usr/X11/lib -lX11 strip minwm Then setup your .xinitrc file like, xrdb -merge $HOME/.Xresources xterm -rv -ls +sb -sl 4096 & exec $HOME/minwm Then run xinit and then start programs from the terminal. ------------------------------------------------------------------------------ QUESTIONS --------- Q. Why no tabs? A. My tabs are 1 character wide, yours are N characters wide. Q. Why is it designed to crash with over WINDOW_MAX-1 windows? A. When have you had over WINDOW_MAX-1 windows? Q. Why an O(N) time to find the window. A. Far better than your O(N^N) time to get work done when using N windows. Q. Why memmove() the entire windows list when window focus changes? A. See the 2nd question. Q. Why am I reading this? A. Taking a break from overwhelming complexity of the universe? ============================================================================*/ #include #include #include #include #include #include #include #include #include #include /*--------------------------------------------------------------------------*/ enum { INDEX_ROOT, INDEX_TOP, INDEX_2ND }; enum { KEY_TAB, KEY_ESC, KEY_TILDE, KEY_ALT, KEYS }; enum { SHAPE_FULL, SHAPE_LEFT, SHAPE_RIGHT, SHAPES }; /*--------------------------------------------------------------------------*/ static void GrabKeys(Display* display, Window root, const int* __restrict keycode) { uint32_t i; const static unsigned int modifiers[KEYS*2] = { Mod1Mask, // KEY_TAB Mod1Mask, // KEY_ESC Mod1Mask, // KEY_TILDE 0, // KEY_ALT Mod1Mask | Mod2Mask, Mod1Mask | Mod2Mask, Mod1Mask | Mod2Mask, Mod2Mask }; for(i = 0; i < KEYS*2; i++) { XGrabKey(display, keycode[i & (KEYS-1)], modifiers[i], root, True, GrabModeAsync, GrabModeAsync); } } static Window Parent(Display* display, Window window) { Window root; Window parent; Window* children; unsigned int num; if(XQueryTree(display, window, &root, &parent, &children, &num) == 0) return 0; if(num && children) XFree(children); return parent; } static void InstallColormap(Display* display, Colormap colormap) { if(colormap != None) XInstallColormap(display, colormap); } // Lookup() returns count on window not found. static uint32_t Lookup( Window* __restrict windows, Window window, uint32_t count) { windows[count] = window; const Window* __restrict table = windows; uint32_t index; while(table[index] != window) index++; return index; } static void Use(uint32_t index, Display* display, const Window* __restrict windows, const Colormap* __restrict colormaps) { InstallColormap(display, colormaps[index]); if(index != INDEX_ROOT) { XRaiseWindow(display, windows[index]); XSetInputFocus( display, windows[index], RevertToPointerRoot, CurrentTime); } } static void Move(uint32_t dst, uint32_t src, uint32_t count, Window* windows, Colormap* colormaps, uint8_t* shapes) { if(count == 0) return; memmove(windows + dst, windows + src, count * sizeof(Window)); memmove(colormaps + dst, colormaps + src, count * sizeof(Colormap)); memmove(shapes + dst, shapes + src, count * sizeof(uint8_t)); } // New() returns new count value. static uint32_t New(Display* display, Window window, uint32_t count, Window* windows, Colormap* colormaps, uint8_t* shapes) { XWindowAttributes att; XGetWindowAttributes(display, window, &att); if((att.override_redirect == True) || (Parent(display, window) != windows[INDEX_ROOT])) return count; Move(INDEX_2ND, INDEX_TOP, count - 1, windows, colormaps, shapes); count++; colormaps[INDEX_TOP] = att.colormap; windows[INDEX_TOP] = window; shapes[INDEX_TOP] = SHAPE_RIGHT; XMapWindow(display, window); Use(INDEX_TOP, display, windows, colormaps); return count; } static int Handler(Display* display, XErrorEvent* event) { return(0); } /*--------------------------------------------------------------------------*/ static const char stupidUser[] = "\n" "--/\\/\\in\\/\\//\\/\\-------------------------------------------------\n" "No windows found!\n" "Before starting xinit, set `.xinitrc` to run a term before minwm,\n" "\n" " xterm -rv -ls +sb -sl 4096 &\n" " $HOME/minwm\n" "\n"; /*--------------------------------------------------------------------------*/ int main(int unused, char** alsoUnused) { XEvent event; Colormap colormaps[WINDOW_MAX]; Window windows[WINDOW_MAX]; uint8_t shapes[WINDOW_MAX]; Display* display = XOpenDisplay(0); if(display == 0) exit(0); XSetErrorHandler(Handler); windows[INDEX_ROOT] = RootWindow(display, 0); XSelectInput(display, windows[INDEX_ROOT], PropertyChangeMask | SubstructureRedirectMask | SubstructureNotifyMask); colormaps[INDEX_ROOT] = DefaultColormap(display, 0); uint32_t count = 1; uint32_t top = INDEX_TOP; XClientMessageEvent close; close.type = ClientMessage; close.send_event = True; close.display = display; close.format = 32; close.data.l[0] = XInternAtom(display, "WM_DELETE_WINDOW", 0); close.message_type = XInternAtom(display, "WM_PROTOCOLS", 0); XWindowChanges changes[SHAPES]; memset(changes, 0, sizeof(changes)); changes[SHAPE_FULL].width = DisplayWidth(display, 0); changes[SHAPE_LEFT].width = changes[SHAPE_RIGHT].width = changes[SHAPE_RIGHT].x = changes[SHAPE_FULL].width >> 1; changes[SHAPE_FULL].height = changes[SHAPE_LEFT].height = changes[SHAPE_RIGHT].height = DisplayHeight(display, 0); const int keycode[KEYS] = { XKeysymToKeycode(display, XK_Tab), XKeysymToKeycode(display, XK_Escape), XKeysymToKeycode(display, XK_asciitilde), XKeysymToKeycode(display, XK_Alt_L) }; GrabKeys(display, windows[INDEX_ROOT], keycode); { uint32_t tries = 0; while(1) { Window root; Window parent; Window* children; unsigned int num; XQueryTree(display, windows[INDEX_ROOT], &root, &parent, &children, &num); if(num) { const Window* __restrict list = children; while(num--) { count = New(display, list[0], count, windows, colormaps, shapes); list++; } if(children) XFree(children); } if(count > 1) break; usleep(100*1000); tries++; if(tries > 20) { printf(stupidUser); exit(0); } } } XSync(display, False); while(1) { XNextEvent(display, &event); switch(event.type) { case FocusIn: case FocusOut: if(event.xfocus.mode == NotifyGrab) GrabKeys(display, windows[INDEX_ROOT], keycode); break; case ConfigureRequest: { XWindowChanges changes; changes.x = event.xconfigurerequest.x; changes.y = event.xconfigurerequest.y; changes.width = event.xconfigurerequest.width; changes.height = event.xconfigurerequest.height; changes.border_width = event.xconfigurerequest.border_width; XConfigureWindow(display, event.xconfigurerequest.window, event.xconfigurerequest.value_mask & (~(CWSibling | CWStackMode)), &changes); } break; case ColormapNotify: { XWindowAttributes att; uint32_t index = Lookup(windows, event.xcolormap.window, count); if(index != count) { XGetWindowAttributes(display, event.xcolormap.window, &att); colormaps[index] = att.colormap; if(index == top) InstallColormap(display, att.colormap); } } break; case MapRequest: { XWindowAttributes att; XGetWindowAttributes(display, event.xmaprequest.window, &att); if((Lookup(windows, event.xmaprequest.window, count) == count) && (Parent(display, event.xmaprequest.window) == windows[INDEX_ROOT]) && (att.override_redirect == False)) { count = New(display, event.xmaprequest.window, count, windows, colormaps, shapes); top = INDEX_TOP; } else XMapRaised(display, event.xmaprequest.window); } break; case KeyRelease: if((event.xkey.keycode == keycode[KEY_ALT]) && (top > INDEX_TOP)) { Colormap colormap = colormaps[top]; Window window = windows[top]; uint8_t shape = shapes[top]; Move(INDEX_2ND, INDEX_TOP, top - 1, windows, colormaps, shapes); colormaps[INDEX_TOP] = colormap; windows[INDEX_TOP] = window; shapes[INDEX_TOP] = shape; top = INDEX_TOP; } else XSendEvent(display, windows[top], True, 0, &event); break; case KeyPress: if(event.xkey.keycode == keycode[KEY_TAB]) { top = top + 1; top = (top == count) ? INDEX_TOP : top; Use(top, display, windows, colormaps); } else if(top != INDEX_ROOT) { if(event.xkey.keycode == keycode[KEY_TILDE]) { uint32_t shape = shapes[top] + 1; shape = (shape == SHAPES) ? 0 : shape; shapes[top] = shape; XConfigureWindow(display, windows[top], CWX | CWY | CWWidth | CWHeight, changes + shape); } else if(event.xkey.keycode == keycode[KEY_ESC]) { close.window = windows[top]; XSendEvent(display, close.window, False, 0, (XEvent*)&close); } } else XSendEvent(display, windows[top], True, 0, &event); break; case UnmapNotify: { uint32_t index = Lookup(windows, event.xunmap.window, count); if(index != count) { count--; if(count <= 1) exit(0); Move(index, index + 1, count - 1, windows, colormaps, shapes); Use(INDEX_TOP, display, windows, colormaps); top = INDEX_TOP; } } break; } } }