pax_global_header00006660000000000000000000000064125507102700014511gustar00rootroot0000000000000052 comment=70b50a3ba2de04f684c81adb1b2efa09ff706ffc evilwm-1.1.1/000077500000000000000000000000001255071027000130145ustar00rootroot00000000000000evilwm-1.1.1/.gitignore000066400000000000000000000002701255071027000150030ustar00rootroot00000000000000## ignore build files *.o evilwm ## common temporary files (really ought to be in peoples' ~/.gitignore) *~ .*.swp .*.swo .DS_Store ## debian rules often present, but separate debian/ evilwm-1.1.1/ChangeLog000066400000000000000000000351651255071027000146000ustar00rootroot00000000000000Version 1.1.1, Mon Jul 13 2015 * Revert client lowering behaviour from 1.1.0. Also fixes a build error when virtual desktops are disabled. * Change default to not warp pointer. * Don't automatically focus notification windows. * Don't automatically focus dock windows. Version 1.1.0, Sun Jul 3 2011 * Respect maximise states while resizing * Add basic .desktop file * Add configuration file $HOME/.evilwmrc - options are one per line, omitting leading dash. * Add long alternatives to -g, -v and -s -geometry, -vdesk and -sticky. * Record pre-maximisation dimensions in evilwm-specific window properties _EVILWM_UNMAXIMISED_HORZ and _EVILWM_UNMAXIMISED_VERT. * Extensive EWMH support. See doc/standards.html for details. * Mostly new Makefile * Clean up terminology: use "fixed" instead of "sticky": EWMH uses the term "sticky" to mean something different. * Add ability to toggle visible state of docks with Ctrl+Alt+D Usually apps like pagers or launch bars. * Add -dock option to force app to be considered a dock * Snap-to-border and colourmap support no longer optional Though -snap still defaults to 0 (off). * fix: drawing window outline with border_width > 1 [David Flynn] (bottom and right edges wouldn't encompass the border) * build: enable out-of-tree builds [David Flynn] * build: enable ISO c99 [David Flynn] * Fix snapping on multiple screens [Ben Stern] * Window gravity behviour fixes. * Remove warning about strict-aliasing rules [Larry Doolittle] Version 1.0.1, Wed Mar 11 2009 * Fix interaction with X and signal handler [Larry Doolittle] This prevented evilwm from shutting down properly with recent versions of X.org. Version 1.0.0, Mon Jun 18 2007 * Don't account for width_inc and height_inc when maximising * Ignore borders when snapping maximised client to screen edge * When snapping, prefer screen edge to other windows * Add basic Xrandr support [Yura Semashko] Version 0.99.25, Wed Apr 26 2006 * Fix configure requests containing sibling information * Track vdesk separately on each managed screen * Only snap against clients on the same screen Version 0.99.24, Mon Feb 20 2006 * Added -nosoliddrag option [Erik Auerswald] Version 0.99.23, Mon Feb 6 2006 * Fixed -v option parsing [Robert Blacquiere] Version 0.99.22, Thu Jan 26 2006 * EWMH hints added so vdesk/sticky states persist across wm change. * Alt+Tab behaviour modified to be more in line with what most people expect * Added -altmask to override Shift as default control behaviour change key * Added resizing with Ctrl+Alt+Shift+[HJKL] Version 0.99.21, Mon Jan 16 2006 * Various documentation fixes [Larry Doolittle] * new.c: nitems_return in get_property() should be unsigned [Larry Doolittle] * screen.c: draw_outline() is only needed if mouse support or no info banner support is compiled in [Larry Doolittle] * ButtonPress notifications shouldn't be needed on root windows: removed. * events.c: Fix positioning error in handle_configure_request(). Version 0.99.20, Fri Jan 13 2006 * Removed dependency on Motif header files: supply just as much hint structure information as is necessary to interpret Motif hints. * Discard enter events on window move, etc.; simpler than searching through shape extents. * Switch to using a window border for frame's colour, don't touch frame background. This allows aterm's "fast transparency" to work. * Only map a window when managing it if it's supposed to be visible, instead of mapping then immediately unmapping. Version 0.99.19, Fri Dec 30 2005 * Info window movement made a bit more sensible. * Don't remove clients from within error handler, instead flag for removal. * While removing client, only reparent to root if window is withdrawn or window manager is shutting down (fixes xpdf-in-firefox bug). * Window close key with shift held will XKillClient(). * Replace logic to decide if we have a shaped window (cribbed from fvwm-2.5.10). [Larry Doolittle] * Warp mouse pointer to a visible point in shaped windows. [Larry Doolittle] Version 0.99.18, Thu Jun 2 2005 * Fix Alt+Tab crash when there are no clients. * Added -app option to specify default vdesk (incl sticky), positions and dimensions. * Fix so shape information is applied on WM startup. * Split geometry and vdesk out of -app option into separate -g and -v options (each apply to the last -app defined). * Add -mask1 and -mask2 switches to override default modifier grabs. * Handle applications reparenting a window to root - manage them unless in Withdrawn state. * Set border size to frame size when unmanaging window. * Return to properly implemented interpretation of win_gravity on initial mapping of windows. * Restore window's original border width on WM exit. * Remove deprecated 'vdesk' external pager support. * Stricter warning settings for gcc. [Larry Doolittle] * Cast unused parameters to void. [Larry Doolittle] * Declare opt_term statically for now (intent is to allow arguments to program specified with -term). [Larry Doolittle] * Try to print ssize_t correctly and within strict standards. [Larry Doolittle] * Add $(EXTRA_DEFINES) to Makefile. [Larry Doolittle] * Improve punctuation in comments. [Larry Doolittle] * Use long instead of CARD32 for argument to XChangeProperty() due to odd X Consortium decision. [Larry Doolittle] * Un-shadow variable p in remove_client debugging segment. [Larry Doolittle] * Assign opt_term at compile time instead of malloc. [Larry Doolittle] * Fuss with execvp casting and comments. [Larry Doolittle] * Ignore PPosition during window placement (back to old behaviour of positioning window under pointer unless user-specified). Version 0.99.17, Wed Oct 15 2003 * Hopefully *really* fix the "losing control of windows" bug. Testers suggest to me that it is now really gone. * Implement snap-to-border. [Neil Drumm] Run evilwm with -snap num to enable the behaviour (num specifies the proximity in pixels to snap to - 10 is quite nice). It's worth noting that Per Weijnitz sent a similar patch a while ago, but I never got around to using it :( Version 0.99.16, Thu Sep 18 2003 * Fix some Alt+Tab misbehaviour. [Dave Holland] * Fixed "losing control of windows" problem when switching desktops rapidly on slow machines. Version 0.99.15, Sun Jun 29 2003 * Warp mouse to topleft before bottomright for small viewport users. * Fix the xpdf problem (started up in tiny window), hopefully doesn't break anything else (XSizeHints manpage suggests width/height hints are obsolete). * Added identifier shadowing changes, and malloc return value checks. [Larry Doolittle] Version 0.99.14, Wed Jan 15 2003 * First draft proper multihead support - looks like it's working, needs a bit of testing. Version 0.99.13, Tue Nov 26 2002 * Only set focus to PointerRoot on entering root window if we don't have a current window (I'd killed sloppy focus...) Version 0.99.12, Sun Nov 24 2002 * Fix a small memory leak (and free another allocation that wouldn't leak). * Make vdesk-changing "autowarp" an option after realising how annoying it was. * Fixed Alt-Tab-into-nowhere bug that I think would cause an infinite loop if you tried Alt-Tab on an empty virtual desktop. * Extended the NumLock keyboard fixes to the mouse button grabs. * Ripped out most of the VDESK_BOTH code (which didn't work) and replaced it with code that just calls the 'vdesk' application (which does). * Fix small problem with border colours in new code. * Re-fix multihead "support". * Better multihead support - entering managed screen will grab keyboard events, leaving will deselect "current" window. * Documentation updates. Version 0.99.11, Tue Nov 12 2002 * Added -DMWM_HINTS (code taken from aewm) as a way of determining if a border is required for a client. * Using -display will also set an environment variable DISPLAY= to be inherited by children. * Hopefully stop the NumLock related bug reports once and for all by figuring out which modifier is attached to XK_Num_Lock and grabbing all keys with that, caps lock and both. Shifting which modifier Num_Lock is bound to with xmodmap seems to show this working. Version 0.99.10, Sun Nov 10 2002 * Add a call to setsid() to put spawned xterms into their own process group. Should finally stop them disappearing on wm exit. * Fullscreen fixes, tested with xv ('m') and mplayer ('f'). * Hopefully fixed the "grey squares" problem by seeing if an XError occurred before we got to set up a new client window. -DINFOBANNER and -DINFOBANNER_MOVERESIZE are now separated so you can enable it for Ctrl+Alt+I but disable it for move/resize where it would slow everything down. * When a window is removed, don't reset current window unless this *was* the current window. This means all background windows should grey out properly now. Version 0.99.9, Thu Nov 7 2002 * Fix potential divide-by-zero for clients that misreport their geometry divisors. Version 0.99.8, Thu Oct 17 2002 * Adjust position of *new* windows trying to fullscreen. [Dave Holland] * Applied kludge that fixes the IE bug. [David Flynn] IE is basically doing some *REALLY BAD THINGS*, and neither of us know why, but David traced it down to generating a single specific xerror which we can happily ignore. * Manpage updates. * When removing a window, map it before destroying its parent (the border). This should stop things disappearing if you quit evilwm while using virtual desktops (thanks to Paul Whittaker). * Changed default font to 'variable' which should exist even if 'lucidasans-10' doesn't (thanks to Paul Whittaker and Dan Field). * Fix non-soliddrags (broken since 2002-03-26 edit, it seems). * Add a new window information banner (displayed while moving, resizing or holding down Ctrl+Alt+I). Enabled with -DINFOBANNER. Version 0.99.7, Mon Aug 19 2002 * A window that changes size to be >= width/height of screen with x=0 or y=0 is probably trying to fullscreen, so shift it left/up by border width in those cases. * Fixed makefile so it works with BSD make. * Made move/resize raise the window before doing anything. * Changed way spawn() works - it now fork()s twice. * Wait until pointer has moved at least 6 pixels before starting to move a window - hopefully will mostly eliminate my nicely aligned windows becoming slightly crooked just because I raised one. Version 0.99.4, Thu Feb 28 2002 * Added -V option. * Start the run up to 1.0. Hopefully this will involve solving the IE bug, but right now I'm not very concerned about it. * Grab everything with Mod3Mask as well - this should circumvent the NumLock problem. * If an app maps itself (EVIL! EVIL! EVIL!), switch to the virtual desktop it's on before raising it. * Added support for vdesk - an external virtual desktop manager that works by iconifying windows and mapping them back again. [vatten@users.sourceforge.net] * Added support for the combination of vdesk and the internal virtual desktop manager. [vatten@users.sourceforge.net] * Move all the key bindings out into a separate include file and add a line in Makefile to override KILL key (for Cygwin). Version 0.3.11, Sat Jun 2 2001 * Implement Ctrl+Alt+[Left|Right] vdesk switching. [Peter Bailey] Opinion around the office is that this is a Good Thing, but not to let it cycle around (e.g., from vdesk 8 to vdesk 1). So that's what I've put in. Unfortunately reintroduces dependency on XK_1-XK_8 being contiguous. * Fix the ordering problem with virtual desktops. [Dave Holland] Works well, but I think an extra parameter to unhide() is tidier, so I've done that instead. Much thanks anyway :) * Did some research into Wayne Myers' inane crash. Another spurious XError. By simply setting up to ignore the XError, I've "fixed" the bug. Strange. Tempted to blame GTK+ for now. * Changed default Makefile to build virtual desktops in, as that's what seems to break most frequently; and enable solid drags, as that's what I use. Version 0.3.10, Sun Feb 18 2001 * Added maximise/vertical maximise. * Made mouse control optional(!) * Found the bug that was indirectly causing infinite loops with virtual desktops enabled. XNextEvent raising an X error - hmm, doesn't say that could happen in the docs. I don't like the current fix, which is flag handle_xerror to ignore during that call. * Ah. After switching desktops, unmapping one window could cause an enter event for another. By the time the handler is called, that window is also unmapped, so anything you try and do to handle the event causes the xerror. Sorted :) Version 0.3.9, Sat Feb 10 2001 * Fixed unfocused Alt+Tab again. * Colourmap handling optional, only needed for non-truecolour displays. Version 0.3.8, Sun Feb 4 2001 * -DSOLIDDRAG now enables.. er.. solid drags. Which look nice but wouldn't be splendid on a slow machine. * -DSTDIO is needed to include various error/help messages. Basically I looked at swm and saw one of its methods of shaving bytes. INSTALL file provided to explain all the -Ds. * If a window tries to start itself iconised, map it. That way we shouldn't get some weirdnesses (tho it might be rude). * With minimal options, evilwm now comes out to <15K under Linux :) Version 0.3.7, Mon Jan 15 2001 * -bw option to change border width. * Also changed all other options to single - due to making more sense * Stop remembering the name of the window. * Fixed that daft "last window's gone, can't do anything" bug * Realised we don't care if window is transient - nothing happens anyway. * Use some simple fork/exec code instead of system() ... but managed to save space elsewhere just by altering the logic * Merged the virtual window manager hack in - edit Makefile to include -DVWM to use. It should be noted that at the moment for any "official" packages, I'd prefer this was left out... Version 0.3.5, Tue Nov 7 2000 * Looked into the gravity code and made it more right. * Divide XxY display by increments if bit set whilst resizing/moving. Version 0.3.4, Tue Oct 31 2000 * Updated aewm license info by request. * XK_Insert does same as XK_KP_Insert - may have to rethink that as it's a 4-key chord on a happy hacking. Almost as bad as emacs! * Alt+Tab warps account for border width * Now gets the border the right size when a window resizes itself. * First window gets an active border now. Version 0.3.3 * No logs back this far. evilwm-1.1.1/INSTALL000066400000000000000000000004151255071027000140450ustar00rootroot00000000000000BUILDING EVILWM 1) Edit Makefile to include the options you need (most people will not need to do this). Instructions are included by any user-editable option. 2) Run 'make' 3) Run 'make install' That's it. The README file has information on starting evilwm. evilwm-1.1.1/Makefile000066400000000000000000000113361255071027000144600ustar00rootroot00000000000000# do not include any other makefiles above this line. THISMAKEFILE=$(lastword $(MAKEFILE_LIST)) # allow trivial out-of-tree builds src_dir=$(dir $(THISMAKEFILE)) VPATH=$(src_dir) ############################################################################ # Installation paths prefix = /usr bindir = $(prefix)/bin datarootdir = $(prefix)/share mandir = $(datarootdir)/man man1dir = $(mandir)/man1 desktopfilesdir = $(datarootdir)/applications ############################################################################ # Features # Uncomment to enable info banner on holding Ctrl+Alt+I. OPT_CPPFLAGS += -DINFOBANNER # Uncomment to show the same banner on moves and resizes. Can be SLOW! #OPT_CPPFLAGS += -DINFOBANNER_MOVERESIZE # Uncomment to support the Xrandr extension (thanks, Yura Semashko). OPT_CPPFLAGS += -DRANDR OPT_LDLIBS += -lXrandr # Uncomment to support shaped windows. OPT_CPPFLAGS += -DSHAPE OPT_LDLIBS += -lXext # Uncomment to enable solid window drags. This can be slow on old systems. OPT_CPPFLAGS += -DSOLIDDRAG # Uncomment to compile in certain text messages like help. Recommended. OPT_CPPFLAGS += -DSTDIO # Uncomment to support virtual desktops. OPT_CPPFLAGS += -DVWM # Uncomment to move pointer around on certain actions. #OPT_CPPFLAGS += -DWARP_POINTER # Uncomment to use Ctrl+Alt+q instead of Ctrl+Alt+Escape. Useful for Cygwin. #OPT_CPPFLAGS += -DKEY_KILL=XK_q # Uncomment to include whatever debugging messages I've left in this release. #OPT_CPPFLAGS += -DDEBUG # miscellaneous debugging #OPT_CPPFLAGS += -DXDEBUG # show some X calls ############################################################################ # Include file and library paths # Most Linux distributions don't separate out X11 from the rest of the # system, but some other OSs still require extra information: # Solaris 10: #OPT_CPPFLAGS += -I/usr/X11/include #LDFLAGS += -R/usr/X11/lib -L/usr/X11/lib # Solaris <= 9 doesn't support RANDR feature above, so disable it there # Solaris 9 doesn't fully implement ISO C99 libc, to suppress warnings, use: #OPT_CPPFLAGS += -D__EXTENSIONS__ # Mac OS X: #LDFLAGS += -L/usr/X11R6/lib ############################################################################ # Build tools # Change this if you don't use gcc: CC = gcc # Override if desired: CFLAGS = -Os WARN = -Wall -W -Wstrict-prototypes -Wpointer-arith -Wcast-align \ -Wshadow -Waggregate-return -Wnested-externs -Winline -Wwrite-strings \ -Wundef -Wsign-compare -Wmissing-prototypes -Wredundant-decls # Enable to spot explicit casts that strip constant qualifiers. # generally not needed, since an explicit cast should signify # the programmer guarantees no undefined behaviour. #WARN += -Wcast-qual # For Cygwin: #EXEEXT = .exe # Override INSTALL_STRIP if you don't want a stripped binary INSTALL = install INSTALL_STRIP = -s INSTALL_DIR = $(INSTALL) -d -m 0755 INSTALL_FILE = $(INSTALL) -m 0644 INSTALL_PROGRAM = $(INSTALL) -m 0755 $(INSTALL_STRIP) ############################################################################ # You shouldn't need to change anything beyond this point version = 1.1.1 distname = evilwm-$(version) # Generally shouldn't be overridden: # _SVID_SOURCE for strdup and putenv # _POSIX_C_SOURCE=200112L for sigaction EVILWM_CPPFLAGS = $(CPPFLAGS) $(OPT_CPPFLAGS) -DVERSION=\"$(version)\" \ -D_SVID_SOURCE=1 \ -D_POSIX_C_SOURCE=200112L EVILWM_CFLAGS = -std=c99 $(CFLAGS) $(WARN) EVILWM_LDFLAGS = $(LDFLAGS) EVILWM_LDLIBS = -lX11 $(OPT_LDLIBS) $(LDLIBS) HEADERS = evilwm.h keymap.h list.h log.h xconfig.h OBJS = client.o events.o ewmh.o list.o main.o misc.o new.o screen.o xconfig.o .PHONY: all all: evilwm$(EXEEXT) $(OBJS): $(HEADERS) %.o: %.c $(CC) $(EVILWM_CFLAGS) $(EVILWM_CPPFLAGS) -c $< evilwm$(EXEEXT): $(OBJS) $(CC) -o $@ $(OBJS) $(EVILWM_LDFLAGS) $(EVILWM_LDLIBS) .PHONY: install install: evilwm$(EXEEXT) $(INSTALL_DIR) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) evilwm$(EXEEXT) $(DESTDIR)$(bindir)/ $(INSTALL_DIR) $(DESTDIR)$(man1dir) $(INSTALL_FILE) $(src_dir)/evilwm.1 $(DESTDIR)$(man1dir)/ $(INSTALL_DIR) $(DESTDIR)$(desktopfilesdir) $(INSTALL_FILE) $(src_dir)/evilwm.desktop $(DESTDIR)$(desktopfilesdir)/ .PHONY: uninstall uninstall: rm -f $(DESTDIR)$(bindir)/evilwm$(EXEEXT) rm -f $(DESTDIR)$(man1dir)/evilwm.1 rm -f $(DESTDIR)$(desktopfilesdir)/evilwm.desktop .PHONY: dist dist: git archive --format=tar --prefix=$(distname)/ HEAD > ../$(distname).tar gzip -f9 ../$(distname).tar .PHONY: debuild debuild: dist -cd ..; rm -rf $(distname)/ $(distname).orig/ cd ..; mv $(distname).tar.gz evilwm_$(version).orig.tar.gz cd ..; tar xfz evilwm_$(version).orig.tar.gz rsync -axH debian --exclude='debian/.git/' --exclude='debian/_darcs/' ../$(distname)/ cd ../$(distname); debuild .PHONY: clean clean: rm -f evilwm$(EXEEXT) $(OBJS) evilwm-1.1.1/README000066400000000000000000000115221255071027000136750ustar00rootroot00000000000000evilwm 1.1 by Ciaran Anscomb evilwm is a minimalist window manager for the X Window System. The name evil came from Stuart 'Stuii' Ford, who thinks that any software I use must be evil and masochistic. In reality, this window manager is clean and easy to use. FEATURES * No window decorations apart from a simple 1 pixel border. * No icons. * Good keyboard control, including repositioning and maximise toggles. * Solid window drags (optional - may be slow on old machines). * Virtual desktops. * Small binary size (even with everything turned on). * Reasonable EWMH support (though not yet fully compliant). * Configuration file read on startup. INSTALLATION Please see the file INSTALL for details on building evilwm. STARTING EVILWM If you're using a standard X desktop, you can start evilwm by just changing your ~/.xinitrc file. Here's a pretty standard sample file: -----CUT HERE----- [ -f $HOME/.Xdefaults ] && xrdb $HOME/.Xdefaults xsetroot -solid \#400040 -cursor_name top_left_arrow /usr/bin/evilwm -snap 10 & exec xclock -digital -padding 2 -g -0+0 -----CUT HERE----- Note that in this case 'xclock' is the magic process (the one that if you kill it, your session dies). You could change it around and have evilwm be your magic process if you prefer. Also note that it sets the cursor shape and background colour with standard X tools (evilwm won't do this for you - the tools already exist!). If you use the GNOME session manager, you can configure it to use evilwm as its window manager. Run gconf-editor and set the key /desktop/gnome/session/required_components/windowmanager to "evilwm". CONTROLS You can use the mouse to manipulate windows either by click/dragging the 1 pixel border, or by holding down Alt and doing so anywhere in the client window. The controls are: Button 1 Move window. Button 2 Resize window. Button 3 Lower window. Most keyboard controls are used by holding down Control and Alt, then pressing a key. Available functions are: Return Spawn new xterm. Escape Delete current window. Insert Lower current window. H, J, K, L Move window left, down, up or right (16 pixels). Holding Shift resizes the window instead. Y, U, B, N Move window to top-left, top-right, bottom-left or bottom-right of screen. I Show information about current window. Equals Maximise current window vertically (toggle). X Maximise current window (toggle). D Toggle visible state of docks (e.g., pagers and launch bars). If compiled with virtual desktop support, these functions are also available: F Fix or unfix window. 1 - 8 Switch virtual desktop. Left Previous virtual desktop. Right Next virtual desktop. A Switch to the most recently unmapped virtual desktop. In addition to the above, Alt+Tab can be used to cycle through windows on screen. ACKNOWLEDGEMENTS The original code base for this project was that of aewm by Decklin Foster. His code is very clean and, in later versions, very well commented. I'd recommend it if you want to learn more about window manager theory (or indeed want title bars, etc ;). http://www.red-bean.com/~decklin/aewm/ - aewm homepage LICENCE evilwm 1.1 Copyright (C) 1999-2015 Ciaran Anscomb This is free software. You can do what you want to it, but if it breaks something, you get to pay for the counselling. The code was originally based on aewm, so this is distributed under the same terms, which follow. AEWM LICENCE Copyright (c) 1998-2000 Decklin Foster. THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND. IN NO EVENT SHALL THE AUTHOR BE HELD LIABLE FOR ANY DAMAGES CONNECTED WITH THE USE OF THIS PROGRAM. You are granted permission to copy, publish, distribute, and/or sell copies of this program and any modified versions or derived works, provided that this copyright and notice are not removed or altered. Portions of the code were based on 9wm, which contains this license: > 9wm is free software, and is Copyright (c) 1994 by David Hogan. > Permission is granted to all sentient beings to use this software, > to make copies of it, and to distribute those copies, provided > that: > > (1) the copyright and licence notices are left intact > (2) the recipients are aware that it is free software > (3) any unapproved changes in functionality are either > (i) only distributed as patches > or (ii) distributed as a new program which is not called 9wm > and whose documentation gives credit where it is due > (4) the author is not held responsible for any defects > or shortcomings in the software, or damages caused by it. > > There is no warranty for this software. Have a nice day. evilwm-1.1.1/TODO000066400000000000000000000005741255071027000135120ustar00rootroot00000000000000Desired functionality for 1.1.x: * EWMH support, including: + Client control via root window messages. * A few things can probably cease to be compile-time options. * Better interoperation with Xinerama (David Flynn has some patches for this). * Option for new windows to tile as well as possible. Desired functionality for 1.2.x: * EWMH compliance. evilwm-1.1.1/client.c000066400000000000000000000156541255071027000144510ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include "evilwm.h" #include "log.h" #define MAXIMUM_PROPERTY_LENGTH 4096 static int send_xmessage(Window w, Atom a, long x); /* used all over the place. return the client that has specified window as * either window or parent */ Client *find_client(Window w) { struct list *iter; for (iter = clients_tab_order; iter; iter = iter->next) { Client *c = iter->data; if (w == c->parent || w == c->window) return c; } return NULL; } void client_hide(Client *c) { c->ignore_unmap++; /* Ignore unmap so we don't remove client */ XUnmapWindow(dpy, c->parent); set_wm_state(c, IconicState); } void client_show(Client *c) { XMapWindow(dpy, c->parent); set_wm_state(c, NormalState); } void client_raise(Client *c) { XRaiseWindow(dpy, c->parent); clients_stacking_order = list_to_tail(clients_stacking_order, c); ewmh_set_net_client_list_stacking(c->screen); } void client_lower(Client *c) { XLowerWindow(dpy, c->parent); clients_stacking_order = list_to_head(clients_stacking_order, c); ewmh_set_net_client_list_stacking(c->screen); } void set_wm_state(Client *c, int state) { /* Using "long" for the type of "data" looks wrong, but the * fine people in the X Consortium defined it this way * (even on 64-bit machines). */ long data[2]; data[0] = state; data[1] = None; XChangeProperty(dpy, c->window, xa_wm_state, xa_wm_state, 32, PropModeReplace, (unsigned char *)data, 2); } void send_config(Client *c) { XConfigureEvent ce; ce.type = ConfigureNotify; ce.event = c->window; ce.window = c->window; ce.x = c->x; ce.y = c->y; ce.width = c->width; ce.height = c->height; ce.border_width = 0; ce.above = None; ce.override_redirect = False; XSendEvent(dpy, c->window, False, StructureNotifyMask, (XEvent *)&ce); } /* Shift client to show border according to window's gravity. */ void gravitate_border(Client *c, int bw) { int dx = 0, dy = 0; switch (c->win_gravity) { default: case NorthWestGravity: dx = bw; dy = bw; break; case NorthGravity: dy = bw; break; case NorthEastGravity: dx = -bw; dy = bw; break; case EastGravity: dx = -bw; break; case CenterGravity: break; case WestGravity: dx = bw; break; case SouthWestGravity: dx = bw; dy = -bw; break; case SouthGravity: dy = -bw; break; case SouthEastGravity: dx = -bw; dy = -bw; break; } if (c->x != 0 || c->width != DisplayWidth(dpy, c->screen->screen)) { c->x += dx; } if (c->y != 0 || c->height != DisplayHeight(dpy, c->screen->screen)) { c->y += dy; } } void select_client(Client *c) { if (current) XSetWindowBorder(dpy, current->parent, current->screen->bg.pixel); if (c) { unsigned long bpixel; #ifdef VWM if (is_fixed(c)) bpixel = c->screen->fc.pixel; else #endif bpixel = c->screen->fg.pixel; XSetWindowBorder(dpy, c->parent, bpixel); XInstallColormap(dpy, c->cmap); XSetInputFocus(dpy, c->window, RevertToPointerRoot, CurrentTime); } current = c; ewmh_set_net_active_window(c); } #ifdef VWM void client_to_vdesk(Client *c, unsigned int vdesk) { if (valid_vdesk(vdesk)) { c->vdesk = vdesk; if (c->vdesk == c->screen->vdesk || c->vdesk == VDESK_FIXED) { client_show(c); } else { client_hide(c); } ewmh_set_net_wm_desktop(c); select_client(current); } } #endif void remove_client(Client *c) { LOG_ENTER("remove_client(window=%lx, %s)", c->window, c->remove ? "withdrawing" : "wm quitting"); XGrabServer(dpy); ignore_xerror = 1; /* ICCCM 4.1.3.1 * "When the window is withdrawn, the window manager will either * change the state field's value to WithdrawnState or it will * remove the WM_STATE property entirely." * EWMH 1.3 * "The Window Manager should remove the property whenever a * window is withdrawn but it should leave the property in * place when it is shutting down." (both _NET_WM_DESKTOP and * _NET_WM_STATE) */ if (c->remove) { LOG_DEBUG("setting WithdrawnState\n"); set_wm_state(c, WithdrawnState); ewmh_withdraw_client(c); } else { ewmh_deinit_client(c); } gravitate_border(c, -c->border); gravitate_border(c, c->old_border); c->x -= c->old_border; c->y -= c->old_border; XReparentWindow(dpy, c->window, c->screen->root, c->x, c->y); XSetWindowBorderWidth(dpy, c->window, c->old_border); XRemoveFromSaveSet(dpy, c->window); if (c->parent) XDestroyWindow(dpy, c->parent); clients_tab_order = list_delete(clients_tab_order, c); clients_mapping_order = list_delete(clients_mapping_order, c); clients_stacking_order = list_delete(clients_stacking_order, c); /* If the wm is quitting, we'll remove the client list properties * soon enough, otherwise: */ if (c->remove) { ewmh_set_net_client_list(c->screen); ewmh_set_net_client_list_stacking(c->screen); } if (current == c) current = NULL; /* an enter event should set this up again */ free(c); #ifdef DEBUG { struct list *iter; int i = 0; for (iter = clients_tab_order; iter; iter = iter->next) i++; LOG_DEBUG("free(), window count now %d\n", i); } #endif XUngrabServer(dpy); XSync(dpy, False); ignore_xerror = 0; LOG_LEAVE(); } void send_wm_delete(Client *c, int kill_client) { int i, n, found = 0; Atom *protocols; if (!kill_client && XGetWMProtocols(dpy, c->window, &protocols, &n)) { for (i = 0; i < n; i++) if (protocols[i] == xa_wm_delete) found++; XFree(protocols); } if (found) send_xmessage(c->window, xa_wm_protos, xa_wm_delete); else XKillClient(dpy, c->window); } static int send_xmessage(Window w, Atom a, long x) { XEvent ev; ev.type = ClientMessage; ev.xclient.window = w; ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = x; ev.xclient.data.l[1] = CurrentTime; return XSendEvent(dpy, w, False, NoEventMask, &ev); } #ifdef SHAPE void set_shape(Client *c) { int bounding_shaped; int i, b; unsigned int u; /* dummies */ if (!have_shape) return; /* Logic to decide if we have a shaped window cribbed from fvwm-2.5.10. * Previous method (more than one rectangle returned from * XShapeGetRectangles) worked _most_ of the time. */ if (XShapeQueryExtents(dpy, c->window, &bounding_shaped, &i, &i, &u, &u, &b, &i, &i, &u, &u) && bounding_shaped) { LOG_DEBUG("%d shape extents\n", bounding_shaped); XShapeCombineShape(dpy, c->parent, ShapeBounding, 0, 0, c->window, ShapeBounding, ShapeSet); } } #endif void *get_property(Window w, Atom property, Atom req_type, unsigned long *nitems_return) { Atom actual_type; int actual_format; unsigned long bytes_after; unsigned char *prop; if (XGetWindowProperty(dpy, w, property, 0L, MAXIMUM_PROPERTY_LENGTH / 4, False, req_type, &actual_type, &actual_format, nitems_return, &bytes_after, &prop) == Success) { if (actual_type == req_type) return (void *)prop; XFree(prop); } return NULL; } evilwm-1.1.1/doc/000077500000000000000000000000001255071027000135615ustar00rootroot00000000000000evilwm-1.1.1/doc/standards.html000066400000000000000000000160411255071027000164340ustar00rootroot00000000000000 evilwm - standards support

evilwm standards support

evilwm (1.1 pre-release) supports a subset of the X Desktop Group's Extended Window Manager Hints (EWMH), version 1.3. This page lists what is supported, what is not yet supported and where ambiguities have not yet been resolved.

EWMH root window properties (and related messages)

_NET_SUPPORTED
This property is set on each managed root window, and lists all EWMH atoms used within evilwm. This includes all the relevant _NET_WM_ACTION atoms, although I'm not sure it makes sense to include those, as they get listed under the similar per-client _NET_WM_ALLOWED_ACTIONS property.
_NET_CLIENT_LIST
_NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING are kept up to date.
_NET_NUMBER_OF_DESKTOPS
This is currently always set to 8 (the number of virtual desktops evilwm has historically supported). Clients cannot yet alter this value, though I intend to add that ability.
_NET_DESKTOP_GEOMETRY
Set to the display geometry. Modifying not allowed.
_NET_DESKTOP_VIEWPORT
Always set to (0,0).
_NET_CURRENT_DESKTOP
Property is supported, and client messages requesting desktop change are accepted.
_NET_ACTIVE_WINDOW
Property is supported, and client messages requesting a change of active window are accepted if their source indication suggests they came as a result of direct user action.
_NET_WORKAREA
Currently always indicates the entire display as work area (does not calculate anything based on struts).
_NET_SUPPORTING_WM_CHECK
Will refer to evilwm's supporting window.

Other EWMH root window messages

_NET_CLOSE_WINDOW
Message is respected if source indication suggests it came from direct user action.
_NET_MOVERESIZE_WINDOW
Message is respected if source indication suggests it came from direct user action.
_NET_RESTACK_WINDOW
Message is respected if source indication suggests it came from direct user action.
_NET_REQUEST_FRAME_EXTENTS
evilwm always estimates the configured border width for each frame edge. A better value could be determined by analysing the window hints in the same way as creating a new window, but it's only got to be an estimate, so I've not done that yet.

EWMH application window properties

_NET_WM_NAME
This is set to "evilwm" on the supporting window, but not currently consulted for client window titles (the title in WM_NAME is used instead).
_NET_WM_DESKTOP
This is maintained on all client windows. On virtual desktop change, this property is changed for any "fixed" windows to be the new virtual desktop. It is also accepted as a client message indicating user action to change a window's current virtual desktop.
_NET_WM_WINDOW_TYPE
These window types affect behaviour:
_NET_WM_WINDOW_TYPE_DESKTOP
evilwm ignores windows of this type, allowing them to act as a fake root window (e.g., Nautilus).
_NET_WM_WINDOW_TYPE_DOCK
Applications that declare themselves to be a dock can have their visibility toggled with a key combination.
_NET_WM_STATE
Some states are supported:
_NET_WM_STATE_MAXIMIZED_VERT
_NET_WM_STATE_MAXIMIZED_HORZ
_NET_WM_STATE_HIDDEN
Advertised but never set: pagers should show "Iconic" windows (on other virtual desktops) that don't have _NET_WM_STATE_HIDDEN set.
_NET_WM_STATE_FULLSCREEN
Currently, as evilwm does not calculate any sort of "work area", the fullscreen hint is considered equivalent to having both the vertically and horizontally maximised hints set. These hints are respected on initial management of a window, and clients can send _NET_WM_STATE messages to change them.
_NET_WM_ALLOWED_ACTIONS
Some actions are allowed:
_NET_WM_ACTION_MOVE
_NET_WM_ACTION_RESIZE
Listed only if window is capable of being resized (min height != max height).
_NET_WM_ACTION_MAXIMIZE_HORZ
_NET_WM_ACTION_MAXIMIZE_VERT
_NET_WM_ACTION_FULLSCREEN
As noted above, requesting fullscreen is currently equivalent to requesting both vertical and horizonal maximisation.
_NET_WM_ACTION_CHANGE_DESKTOP
_NET_WM_ACTION_CLOSE
_NET_WM_PID
This property is set on the supporting window, but is not consulted when dealing with client windows. Additionally, the WM_CLIENT_MACHINE property is not yet set.
_NET_FRAME_EXTENTS
Each frame extent is set to the border width that applies to the client.

Unsupported EWMH properties

_NET_DESKTOP_NAMES
_NET_VIRTUAL_ROOTS
evilwm does not use virtual root windows.
_NET_DESKTOP_LAYOUT
_NET_SHOWING_DESKTOP
evilwm does not have a "showing the desktop" mode.
_NET_WM_MOVERESIZE
_NET_WM_VISIBLE_NAME
The real WM_NAME is displayed when requested.
_NET_WM_ICON_NAME
_NET_WM_VISIBLE_ICON_NAME
evilwm does not support iconification.
_NET_WM_WINDOW_TYPE
These window types are unsupported:
_NET_WM_WINDOW_TYPE_TOOLBAR
_NET_WM_WINDOW_TYPE_MENU
_NET_WM_WINDOW_TYPE_UTILITY
_NET_WM_WINDOW_TYPE_SPLASH
_NET_WM_WINDOW_TYPE_DIALOG
_NET_WM_WINDOW_TYPE_NORMAL
_NET_WM_STATE
These states are unsupported:
_NET_WM_STATE_MODAL
_NET_WM_STATE_STICKY
_NET_WM_STATE_SHADED
_NET_WM_STATE_SKIP_TASKBAR
_NET_WM_STATE_SKIP_PAGER
_NET_WM_STATE_ABOVE
_NET_WM_STATE_BELOW
_NET_WM_STATE_DEMANDS_ATTENTION
_NET_WM_ALLOWED_ACTIONS
These actions are unsupported:
_NET_WM_ACTION_MINIMIZE
_NET_WM_ACTION_SHADE
_NET_WM_ACTION_STICK
_NET_WM_STRUT
_NET_WM_STRUT_PARTIAL
_NET_WM_ICON_GEOMETRY
_NET_WM_ICON
_NET_WM_HANDLED_ICONS
evilwm does not support iconification.
_NET_WM_USER_TIME
_NET_WM_PING
_NET_WM_SYNC_REQUEST
evilwm-1.1.1/events.c000066400000000000000000000343031255071027000144670ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include #include #include #include "evilwm.h" #include "log.h" static int interruptibleXNextEvent(XEvent *event); #ifdef DEBUG const char *debug_atom_name(Atom a); const char *debug_atom_name(Atom a) { static char buf[48]; char *atom_name = XGetAtomName(dpy, a); strncpy(buf, atom_name, sizeof(buf)); buf[sizeof(buf)-1] = 0; return buf; } #endif static void handle_key_event(XKeyEvent *e) { KeySym key = XkbKeycodeToKeysym(dpy, e->keycode, 0, 0); Client *c; int width_inc, height_inc; ScreenInfo *current_screen = find_current_screen(); switch(key) { case KEY_NEW: spawn((const char *const *)opt_term); break; case KEY_NEXT: next(); if (XGrabKeyboard(dpy, e->root, False, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) { XEvent ev; do { XMaskEvent(dpy, KeyPressMask|KeyReleaseMask, &ev); if (ev.type == KeyPress && XkbKeycodeToKeysym(dpy, ev.xkey.keycode, 0, 0) == KEY_NEXT) next(); } while (ev.type == KeyPress || XkbKeycodeToKeysym(dpy, ev.xkey.keycode, 0, 0) == KEY_NEXT); XUngrabKeyboard(dpy, CurrentTime); } ewmh_select_client(current); break; case KEY_DOCK_TOGGLE: set_docks_visible(current_screen, !current_screen->docks_visible); break; #ifdef VWM case XK_1: case XK_2: case XK_3: case XK_4: case XK_5: case XK_6: case XK_7: case XK_8: switch_vdesk(current_screen, KEY_TO_VDESK(key)); break; case KEY_PREVDESK: if (current_screen->vdesk > 0) { switch_vdesk(current_screen, current_screen->vdesk - 1); } break; case KEY_NEXTDESK: if (current_screen->vdesk < VDESK_MAX) { switch_vdesk(current_screen, current_screen->vdesk + 1); } break; case KEY_TOGGLEDESK: switch_vdesk(current_screen, current_screen->old_vdesk); break; #endif default: break; } c = current; if (c == NULL) return; width_inc = (c->width_inc > 1) ? c->width_inc : 16; height_inc = (c->height_inc > 1) ? c->height_inc : 16; switch (key) { case KEY_LEFT: if (e->state & altmask) { if ((c->width - width_inc) >= c->min_width) c->width -= width_inc; } else { c->x -= 16; } goto move_client; case KEY_DOWN: if (e->state & altmask) { if (!c->max_height || (c->height + height_inc) <= c->max_height) c->height += height_inc; } else { c->y += 16; } goto move_client; case KEY_UP: if (e->state & altmask) { if ((c->height - height_inc) >= c->min_height) c->height -= height_inc; } else { c->y -= 16; } goto move_client; case KEY_RIGHT: if (e->state & altmask) { if (!c->max_width || (c->width + width_inc) <= c->max_width) c->width += width_inc; } else { c->x += 16; } goto move_client; case KEY_TOPLEFT: c->x = c->border; c->y = c->border; goto move_client; case KEY_TOPRIGHT: c->x = DisplayWidth(dpy, c->screen->screen) - c->width-c->border; c->y = c->border; goto move_client; case KEY_BOTTOMLEFT: c->x = c->border; c->y = DisplayHeight(dpy, c->screen->screen) - c->height-c->border; goto move_client; case KEY_BOTTOMRIGHT: c->x = DisplayWidth(dpy, c->screen->screen) - c->width-c->border; c->y = DisplayHeight(dpy, c->screen->screen) - c->height-c->border; goto move_client; case KEY_KILL: send_wm_delete(c, e->state & altmask); break; case KEY_LOWER: case KEY_ALTLOWER: client_lower(c); break; case KEY_INFO: show_info(c, e->keycode); break; case KEY_MAX: maximise_client(c, NET_WM_STATE_TOGGLE, MAXIMISE_HORZ|MAXIMISE_VERT); break; case KEY_MAXVERT: maximise_client(c, NET_WM_STATE_TOGGLE, MAXIMISE_VERT); break; #ifdef VWM case KEY_FIX: if (is_fixed(c)) { client_to_vdesk(c, current_screen->vdesk); } else { client_to_vdesk(c, VDESK_FIXED); } break; #endif default: break; } return; move_client: if (abs(c->x) == c->border && c->oldw != 0) c->x = 0; if (abs(c->y) == c->border && c->oldh != 0) c->y = 0; moveresize(c); #ifdef WARP_POINTER setmouse(c->window, c->width + c->border - 1, c->height + c->border - 1); #endif discard_enter_events(c); return; } static void handle_button_event(XButtonEvent *e) { Client *c = find_client(e->window); if (c) { switch (e->button) { case Button1: drag(c); break; case Button2: sweep(c); break; case Button3: client_lower(c); break; default: break; } } } static void do_window_changes(int value_mask, XWindowChanges *wc, Client *c, int gravity) { if (gravity == 0) gravity = c->win_gravity_hint; c->win_gravity = gravity; if (value_mask & CWX) c->x = wc->x; if (value_mask & CWY) c->y = wc->y; if (value_mask & (CWWidth|CWHeight)) { int dw = 0, dh = 0; if (!(value_mask & (CWX|CWY))) { gravitate_border(c, -c->border); } if (value_mask & CWWidth) { int neww = wc->width; if (neww < c->min_width) neww = c->min_width; if (c->max_width && neww > c->max_width) neww = c->max_width; dw = neww - c->width; c->width = neww; } if (value_mask & CWHeight) { int newh = wc->height; if (newh < c->min_height) newh = c->min_height; if (c->max_height && newh > c->max_height) newh = c->max_height; dh = newh - c->height; c->height = newh; } /* only apply position fixes if not being explicitly moved */ if (!(value_mask & (CWX|CWY))) { switch (gravity) { default: case NorthWestGravity: break; case NorthGravity: c->x -= (dw / 2); break; case NorthEastGravity: c->x -= dw; break; case WestGravity: c->y -= (dh / 2); break; case CenterGravity: c->x -= (dw / 2); c->y -= (dh / 2); break; case EastGravity: c->x -= dw; c->y -= (dh / 2); break; case SouthWestGravity: c->y -= dh; break; case SouthGravity: c->x -= (dw / 2); c->y -= dh; break; case SouthEastGravity: c->x -= dw; c->y -= dh; break; } value_mask |= CWX|CWY; gravitate_border(c, c->border); } } wc->x = c->x - c->border; wc->y = c->y - c->border; wc->border_width = c->border; XConfigureWindow(dpy, c->parent, value_mask, wc); XMoveResizeWindow(dpy, c->window, 0, 0, c->width, c->height); if ((value_mask & (CWX|CWY)) && !(value_mask & (CWWidth|CWHeight))) { send_config(c); } } static void handle_configure_request(XConfigureRequestEvent *e) { Client *c = find_client(e->window); XWindowChanges wc; wc.x = e->x; wc.y = e->y; wc.width = e->width; wc.height = e->height; wc.border_width = 0; wc.sibling = e->above; wc.stack_mode = e->detail; if (c) { if (e->value_mask & CWStackMode && e->value_mask & CWSibling) { Client *sibling = find_client(e->above); if (sibling) { wc.sibling = sibling->parent; } } do_window_changes(e->value_mask, &wc, c, 0); if (c == current) { discard_enter_events(c); } } else { LOG_XENTER("XConfigureWindow(window=%lx, value_mask=%lx)", (unsigned int)e->window, e->value_mask); XConfigureWindow(dpy, e->window, e->value_mask, &wc); LOG_XLEAVE(); } } static void handle_map_request(XMapRequestEvent *e) { Client *c = find_client(e->window); LOG_ENTER("handle_map_request(window=%lx)", e->window); if (c) { #ifdef VWM if (!is_fixed(c) && c->vdesk != c->screen->vdesk) switch_vdesk(c->screen, c->vdesk); #endif client_show(c); client_raise(c); } else { XWindowAttributes attr; XGetWindowAttributes(dpy, e->window, &attr); make_new_client(e->window, find_screen(attr.root)); } LOG_LEAVE(); } static void handle_unmap_event(XUnmapEvent *e) { Client *c = find_client(e->window); LOG_ENTER("handle_unmap_event(window=%lx)", e->window); if (c) { if (c->ignore_unmap) { c->ignore_unmap--; LOG_DEBUG("ignored (%d ignores remaining)\n", c->ignore_unmap); } else { LOG_DEBUG("flagging client for removal\n"); c->remove = 1; need_client_tidy = 1; } } else { LOG_DEBUG("unknown client!\n"); } LOG_LEAVE(); } static void handle_colormap_change(XColormapEvent *e) { Client *c = find_client(e->window); if (c && e->new) { c->cmap = e->colormap; XInstallColormap(dpy, c->cmap); } } static void handle_property_change(XPropertyEvent *e) { Client *c = find_client(e->window); if (c) { LOG_ENTER("handle_property_change(window=%lx, atom=%s)", e->window, debug_atom_name(e->atom)); if (e->atom == XA_WM_NORMAL_HINTS) { get_wm_normal_hints(c); LOG_DEBUG("geometry=%dx%d+%d+%d\n", c->width, c->height, c->x, c->y); } else if (e->atom == xa_net_wm_window_type) { get_window_type(c); if (!c->is_dock #ifdef VWM && (is_fixed(c) || (c->vdesk == c->screen->vdesk)) #endif ) { client_show(c); } } LOG_LEAVE(); } } static void handle_enter_event(XCrossingEvent *e) { Client *c; if ((c = find_client(e->window))) { #ifdef VWM if (!is_fixed(c) && c->vdesk != c->screen->vdesk) return; #endif select_client(c); ewmh_select_client(c); } } static void handle_mappingnotify_event(XMappingEvent *e) { XRefreshKeyboardMapping(e); if (e->request == MappingKeyboard) { int i; for (i = 0; i < num_screens; i++) { grab_keys_for_screen(&screens[i]); } } } #ifdef SHAPE static void handle_shape_event(XShapeEvent *e) { Client *c = find_client(e->window); if (c) set_shape(c); } #endif static void handle_client_message(XClientMessageEvent *e) { #ifdef VWM ScreenInfo *s = find_current_screen(); #endif Client *c; LOG_ENTER("handle_client_message(window=%lx, format=%d, type=%s)", e->window, e->format, debug_atom_name(e->message_type)); #ifdef VWM if (e->message_type == xa_net_current_desktop) { switch_vdesk(s, e->data.l[0]); LOG_LEAVE(); return; } #endif c = find_client(e->window); if (!c && e->message_type == xa_net_request_frame_extents) { ewmh_set_net_frame_extents(e->window); LOG_LEAVE(); return; } if (!c) { LOG_LEAVE(); return; } if (e->message_type == xa_net_active_window) { /* Only do this if it came from direct user action */ if (e->data.l[0] == 2) { #ifdef VWM if (c->screen == s) #endif select_client(c); } LOG_LEAVE(); return; } if (e->message_type == xa_net_close_window) { /* Only do this if it came from direct user action */ if (e->data.l[1] == 2) { send_wm_delete(c, 0); } LOG_LEAVE(); return; } if (e->message_type == xa_net_moveresize_window) { /* Only do this if it came from direct user action */ int source_indication = (e->data.l[0] >> 12) & 3; if (source_indication == 2) { int value_mask = (e->data.l[0] >> 8) & 0x0f; int gravity = e->data.l[0] & 0xff; XWindowChanges wc; wc.x = e->data.l[1]; wc.y = e->data.l[2]; wc.width = e->data.l[3]; wc.height = e->data.l[4]; do_window_changes(value_mask, &wc, c, gravity); } LOG_LEAVE(); return; } if (e->message_type == xa_net_restack_window) { /* Only do this if it came from direct user action */ if (e->data.l[0] == 2) { XWindowChanges wc; wc.sibling = e->data.l[1]; wc.stack_mode = e->data.l[2]; do_window_changes(CWSibling | CWStackMode, &wc, c, c->win_gravity); } LOG_LEAVE(); return; } #ifdef VWM if (e->message_type == xa_net_wm_desktop) { /* Only do this if it came from direct user action */ if (e->data.l[1] == 2) { client_to_vdesk(c, e->data.l[0]); } LOG_LEAVE(); return; } #endif if (e->message_type == xa_net_wm_state) { int i, maximise_hv = 0; /* Message can contain up to two state changes: */ for (i = 1; i <= 2; i++) { if ((Atom)e->data.l[i] == xa_net_wm_state_maximized_vert) { maximise_hv |= MAXIMISE_VERT; } else if ((Atom)e->data.l[i] == xa_net_wm_state_maximized_horz) { maximise_hv |= MAXIMISE_HORZ; } else if ((Atom)e->data.l[i] == xa_net_wm_state_fullscreen) { maximise_hv |= MAXIMISE_VERT|MAXIMISE_HORZ; } } if (maximise_hv) { maximise_client(c, e->data.l[0], maximise_hv); } LOG_LEAVE(); return; } LOG_LEAVE(); } void event_main_loop(void) { union { XEvent xevent; #ifdef SHAPE XShapeEvent xshape; #endif } ev; /* main event loop here */ while (!wm_exit) { if (interruptibleXNextEvent(&ev.xevent)) { switch (ev.xevent.type) { case KeyPress: handle_key_event(&ev.xevent.xkey); break; case ButtonPress: handle_button_event(&ev.xevent.xbutton); break; case ConfigureRequest: handle_configure_request(&ev.xevent.xconfigurerequest); break; case MapRequest: handle_map_request(&ev.xevent.xmaprequest); break; case ColormapNotify: handle_colormap_change(&ev.xevent.xcolormap); break; case EnterNotify: handle_enter_event(&ev.xevent.xcrossing); break; case PropertyNotify: handle_property_change(&ev.xevent.xproperty); break; case UnmapNotify: handle_unmap_event(&ev.xevent.xunmap); break; case MappingNotify: handle_mappingnotify_event(&ev.xevent.xmapping); break; case ClientMessage: handle_client_message(&ev.xevent.xclient); break; default: #ifdef SHAPE if (have_shape && ev.xevent.type == shape_event) { handle_shape_event(&ev.xshape); } #endif #ifdef RANDR if (have_randr && ev.xevent.type == randr_event_base + RRScreenChangeNotify) { XRRUpdateConfiguration(&ev.xevent); } #endif break; } } if (need_client_tidy) { struct list *iter, *niter; need_client_tidy = 0; for (iter = clients_tab_order; iter; iter = niter) { Client *c = iter->data; niter = iter->next; if (c->remove) remove_client(c); } } } } /* interruptibleXNextEvent() is taken from the Blender source and comes with * the following copyright notice: */ /* Copyright (c) Mark J. Kilgard, 1994, 1995, 1996. */ /* This program is freely distributable without licensing fees * and is provided without guarantee or warrantee expressed or * implied. This program is -not- in the public domain. */ /* Unlike XNextEvent, if a signal arrives, interruptibleXNextEvent will * return zero. */ static int interruptibleXNextEvent(XEvent *event) { fd_set fds; int rc; int dpy_fd = ConnectionNumber(dpy); for (;;) { if (XPending(dpy)) { XNextEvent(dpy, event); return 1; } FD_ZERO(&fds); FD_SET(dpy_fd, &fds); rc = select(dpy_fd + 1, &fds, NULL, NULL, NULL); if (rc < 0) { if (errno == EINTR) { return 0; } else { LOG_ERROR("interruptibleXNextEvent(): select()\n"); } } } } evilwm-1.1.1/evilwm.1000066400000000000000000000122031255071027000143770ustar00rootroot00000000000000.TH evilwm 1 "October 13, 2010" "" "" .SH NAME evilwm \- Minimalist Window Manager for X .SH SYNOPSIS \fBevilwm\fP [ \fB\-display\fP \fIdisplay\fP ] [ \fB\-term\fP \fItermprog\fP ] [ \fB\-fn\fP \fIfontname\fP ] [ \fB\-fg\fP \fIforeground-colour\fP ] [ \fB\-fc\fP \fIfixed-colour\fP ] [ \fB\-bg\fP \fIbackground-colour\fP ] [ \fB\-bw\fP \fIborderwidth\fP ] [ \fB\-snap\fP \fInum\fP ] [ \fB\-mask1\fP \fImodifiers\fP ] [ \fB\-mask2\fP \fImodifiers\fP ] [ \fB\-altmask\fP \fImodifier\fP ] [ [ \fB\-app\fP \fIname/class\fP ] [ \fB\-geometry\fP \fIgeometry\fP ] [ \fB\-dock\fP ] [ \fB\-vdesk\fP \fIvdesk\fP ] [ \fB\-fixed\fP ] ]... [ \fB\-nosoliddrag\fP ] [ \fB\-V\fP ] .SH DESCRIPTION .B evilwm is a minimalist window manager based on aewm, extended to feature many keyboard controls, and otherwise altered to be more friendly. .PP In .BR evilwm , the focus follows the mouse pointer, and focus is not lost if you stray onto the root window. The current window border is shaded gold, with other windows left as a dark grey. .PP You can use the mouse to manipulate windows either by click/dragging the 1 pixel border, or by holding down Alt and doing so anywhere in the client window. The controls are: .TP Button 1 Move window. .TP Button 2 Resize window. .TP Button 3 Lower window. .PP Most keyboard controls are used by holding down Control and Alt, then pressing a key. Available functions are: .TP Return Spawn new terminal. .TP Escape Delete current window. Hold Shift as well to force kill a client. .TP Insert Lower current window. .TP H, J, K, L Move window left, down, up or right (16 pixels). Holding Shift resizes the window instead. .TP Y, U, B, N Move window to top-left, top-right, bottom-left or bottom-right of screen. .TP I Show information about current window. .TP Equals Maximise current window vertically (toggle). .TP X Maximise current window (toggle). .TP D Toggle visible state of docks (e.g., pagers and launch bars). .PP If compiled with virtual desktop support, these functions are also available: .TP F Fix or unfix current window. .TP 1--8 Switch virtual desktop. .TP Left Previous virtual desktop. .TP Right Next virtual desktop. .TP A Switch to the most recently unmapped virtual desktop. .PP In addition to the above, Alt+Tab can be used to cycle through windows on screen. .PP To make .B evilwm exit, you have to kill the process. .SH OPTIONS .TP \-display \fIdisplay\fP specifies the X display to run on. .TP \-term \fItermprog\fP specifies an alternative program to run when spawning a new terminal (defaults to xterm, or x\-terminal\-emulator in Debian). Separate arguments with whitespace, and escape needed whitespace with a backslash. Remember that special characters will also need to be protected from the shell. .TP \-fn \fIfontname\fP specify a font to use when resizing or displaying window titles. .TP \-fg \fIforeground-colour\fP frame colour of currently active window. .TP \-fc \fIfixed-colour\fP frame colour of active fixed windows. .TP \-bg \fIbackground-colour\fP frame colour of inactive windows. .TP \-bw \fIborderwidth\fP width of window borders in pixels. .TP \-snap \fInum\fP enable snap-to-border support. num gives the proximity in pixels to snap to. .TP \-mask1 \fImodifiers\fP, \-mask2 \fImodifiers\fP, \-altmask \fImodifier\fP override the default keyboard modifiers used to grab keys for window manager functionality. \fImask1\fP is used for most keyboard controls (default: control+alt), and \fImask2\fP is used for mouse button controls and cycling windows (default: alt). \fIaltmask\fP is used to modify the behaviour of certain controls (default: shift). Modifiers may be separated with + signs. Valid modifiers are shift, lock, control, alt, mod1, mod2, mod3, mod4, mod5. .TP \-app \fIname/class\fP match an application by instance name and class (for help in finding these, use the \fBxprop\fP tool to extract the \fIWM_CLASS\fP property). Subsequent \fI\-geometry\fP, \fI\-dock\fP, \fI\-vdesk\fP and \fI\-fixed\fP options will apply to this match. .TP \-g, -geometry \fIgeometry\fP apply a geometry (using a standard X geometry string) to applications matching the last .I \-app. .TP \-dock specify that application should be considered to be a dock, even if it lacks the appropriate property. .TP \-v, -vdesk \fIvdesk\fP specify a default virtual desktop for applications matching the last .I \-app. Note that desktops are numbered from 0. .TP \-f, -fixed specify that application is to start with a fixed client window (\fI\-s\fP is also accepted to be compatible with previous versions). .TP \-nosoliddrag draw a window outline while moving or resizing. .TP \-V print version number. .PP .B evilwm will also read options, one per line, from a file called \fI.evilwmrc\fP in the user's home directory. Options listed in a configuration file should omit the leading dash. Options specified on the command line override those found in the configuration file. .SH FILES .I $HOME/.evilwmrc .SH BUGS The author's idea of friendly may differ to that of many other people. .SH AUTHOR Ciaran Anscomb . .br aewm was written by Decklin Foster . .br 9wm was written by David Hogan . .SH "SEE ALSO" .BR xterm (1), .BR xprop (1) evilwm-1.1.1/evilwm.desktop000066400000000000000000000003741255071027000157160ustar00rootroot00000000000000[Desktop Entry] Type=Application Encoding=UTF-8 Name=evilwm Exec=evilwm NoDisplay=true X-GNOME-WMName=evilwm # evilwm doesn't talk XSMP yet, so don't start in the WindowManager phase: X-GNOME-Autostart-Phase=Applications X-GNOME-Provides=windowmanager evilwm-1.1.1/evilwm.h000066400000000000000000000213301255071027000144670ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include #include #include #include #ifdef SHAPE #include #endif #ifdef RANDR #include #endif #ifndef __GNUC__ # define __attribute__(x) #endif #include "keymap.h" #include "list.h" /* Required for interpreting MWM hints: */ #define _XA_MWM_HINTS "_MOTIF_WM_HINTS" #define PROP_MWM_HINTS_ELEMENTS 3 #define MWM_HINTS_DECORATIONS (1L << 1) #define MWM_DECOR_ALL (1L << 0) #define MWM_DECOR_BORDER (1L << 1) typedef struct { unsigned long flags; unsigned long functions; unsigned long decorations; } PropMwmHints; /* sanity on options */ #if defined(INFOBANNER_MOVERESIZE) && !defined(INFOBANNER) # define INFOBANNER #endif /* default settings */ #define DEF_FONT "variable" #define DEF_FG "goldenrod" #define DEF_BG "grey50" #define DEF_BW 1 #define DEF_FC "blue" #define SPACE 3 #ifdef DEBIAN #define DEF_TERM "x-terminal-emulator" #else #define DEF_TERM "xterm" #endif /* readability stuff */ #define VDESK_NONE (0xfffffffe) #define VDESK_FIXED (0xffffffff) #define VDESK_MAX (7) #define KEY_TO_VDESK(key) ((key) - XK_1) #define valid_vdesk(v) ((v) == VDESK_FIXED || (v) <= VDESK_MAX) #define RAISE 1 #define NO_RAISE 0 /* for unhide() */ /* EWMH hints use these definitions, so for simplicity my functions * will too: */ #define NET_WM_STATE_REMOVE 0 /* remove/unset property */ #define NET_WM_STATE_ADD 1 /* add/set property */ #define NET_WM_STATE_TOGGLE 2 /* toggle property */ /* EWMH window type bits */ #define EWMH_WINDOW_TYPE_DESKTOP (1<<0) #define EWMH_WINDOW_TYPE_DOCK (1<<1) #define EWMH_WINDOW_TYPE_NOTIFICATION (1<<2) #define MAXIMISE_HORZ (1<<0) #define MAXIMISE_VERT (1<<1) /* some coding shorthand */ #define ChildMask (SubstructureRedirectMask|SubstructureNotifyMask) #define ButtonMask (ButtonPressMask|ButtonReleaseMask) #define MouseMask (ButtonMask|PointerMotionMask) #define grab_pointer(w, mask, curs) \ (XGrabPointer(dpy, w, False, mask, GrabModeAsync, GrabModeAsync, \ None, curs, CurrentTime) == GrabSuccess) #define grab_button(w, mask, button) do { \ XGrabButton(dpy, button, (mask), w, False, ButtonMask, \ GrabModeAsync, GrabModeSync, None, None); \ XGrabButton(dpy, button, LockMask|(mask), w, False, ButtonMask,\ GrabModeAsync, GrabModeSync, None, None); \ XGrabButton(dpy, button, numlockmask|(mask), w, False, \ ButtonMask, GrabModeAsync, GrabModeSync, \ None, None); \ XGrabButton(dpy, button, numlockmask|LockMask|(mask), w, False,\ ButtonMask, GrabModeAsync, GrabModeSync, \ None, None); \ } while (0) #define setmouse(w, x, y) XWarpPointer(dpy, None, w, 0, 0, 0, 0, x, y) #define get_mouse_position(xp,yp,root) do { \ Window dw; \ int di; \ unsigned int dui; \ XQueryPointer(dpy, root, &dw, &dw, xp, yp, &di, &di, &dui); \ } while (0) #define is_fixed(c) (c->vdesk == VDESK_FIXED) #define add_fixed(c) c->vdesk = VDESK_FIXED #define remove_fixed(c) c->vdesk = c->screen->vdesk /* screen structure */ typedef struct ScreenInfo ScreenInfo; struct ScreenInfo { int screen; Window root; Window supporting; /* Dummy window for EWMH */ GC invert_gc; XColor fg, bg; #ifdef VWM unsigned int vdesk; XColor fc; unsigned old_vdesk; /* most recently unmapped vdesk, so user may toggle back to it */ #endif char *display; int docks_visible; }; /* client structure */ typedef struct Client Client; struct Client { Window window; Window parent; ScreenInfo *screen; Colormap cmap; int ignore_unmap; int x, y, width, height; int border; int oldx, oldy, oldw, oldh; /* used when maximising */ int min_width, min_height; int max_width, max_height; int width_inc, height_inc; int base_width, base_height; int win_gravity_hint; int win_gravity; int old_border; #ifdef VWM unsigned int vdesk; #endif int is_dock; int remove; /* set when client needs to be removed */ }; typedef struct Application Application; struct Application { char *res_name; char *res_class; int geometry_mask; int x, y; unsigned int width, height; int is_dock; #ifdef VWM unsigned int vdesk; #endif }; /* Declarations for global variables in main.c */ /* Commonly used X information */ extern Display *dpy; extern XFontStruct *font; extern Cursor move_curs; extern Cursor resize_curs; extern int num_screens; extern ScreenInfo *screens; #ifdef SHAPE extern int have_shape, shape_event; #endif #ifdef RANDR extern int have_randr, randr_event_base; #endif /* Standard X protocol atoms */ extern Atom xa_wm_state; extern Atom xa_wm_protos; extern Atom xa_wm_delete; extern Atom xa_wm_cmapwins; /* Motif atoms */ extern Atom mwm_hints; /* evilwm atoms */ extern Atom xa_evilwm_unmaximised_horz; extern Atom xa_evilwm_unmaximised_vert; /* EWMH: Root Window Properties (and Related Messages) */ #ifdef VWM extern Atom xa_net_current_desktop; #endif extern Atom xa_net_active_window; /* EWMH: Other Root Window Messages */ extern Atom xa_net_close_window; extern Atom xa_net_moveresize_window; extern Atom xa_net_restack_window; extern Atom xa_net_request_frame_extents; /* EWMH: Application Window Properties */ #ifdef VWM extern Atom xa_net_wm_desktop; #endif extern Atom xa_net_wm_window_type; extern Atom xa_net_wm_window_type_dock; extern Atom xa_net_wm_state; extern Atom xa_net_wm_state_maximized_vert; extern Atom xa_net_wm_state_maximized_horz; extern Atom xa_net_wm_state_fullscreen; extern Atom xa_net_frame_extents; /* Things that affect user interaction */ extern unsigned int numlockmask; extern unsigned int grabmask1; extern unsigned int grabmask2; extern unsigned int altmask; extern char **opt_term; extern int opt_bw; extern int opt_snap; #ifdef SOLIDDRAG extern int no_solid_drag; #else # define no_solid_drag (1) #endif extern struct list *applications; /* Client tracking information */ extern struct list *clients_tab_order; extern struct list *clients_mapping_order; extern struct list *clients_stacking_order; extern Client *current; extern volatile Window initialising; /* Event loop will run until this flag is set */ extern int wm_exit; /* client.c */ Client *find_client(Window w); void client_hide(Client *c); void client_show(Client *c); void client_raise(Client *c); void client_lower(Client *c); void gravitate_border(Client *c, int bw); void select_client(Client *c); #ifdef VWM void client_to_vdesk(Client *c, unsigned int vdesk); #endif void remove_client(Client *c); void send_config(Client *c); void send_wm_delete(Client *c, int kill_client); void set_wm_state(Client *c, int state); void set_shape(Client *c); void *get_property(Window w, Atom property, Atom req_type, unsigned long *nitems_return); /* events.c */ void event_main_loop(void); /* misc.c */ extern int need_client_tidy; extern int ignore_xerror; int handle_xerror(Display *dsply, XErrorEvent *e); void spawn(const char *const cmd[]); void handle_signal(int signo); void discard_enter_events(Client *except); /* new.c */ void make_new_client(Window w, ScreenInfo *s); long get_wm_normal_hints(Client *c); void get_window_type(Client *c); /* screen.c */ void drag(Client *c); void moveresize(Client *c); void maximise_client(Client *c, int action, int hv); void show_info(Client *c, unsigned int keycode); void sweep(Client *c); void next(void); #ifdef VWM void switch_vdesk(ScreenInfo *s, unsigned int v); #endif void set_docks_visible(ScreenInfo *s, int is_visible); ScreenInfo *find_screen(Window root); ScreenInfo *find_current_screen(void); void grab_keys_for_screen(ScreenInfo *s); /* ewmh.c */ void ewmh_init(void); void ewmh_init_screen(ScreenInfo *s); void ewmh_deinit_screen(ScreenInfo *s); void ewmh_init_client(Client *c); void ewmh_deinit_client(Client *c); void ewmh_withdraw_client(Client *c); void ewmh_select_client(Client *c); void ewmh_set_net_client_list(ScreenInfo *s); void ewmh_set_net_client_list_stacking(ScreenInfo *s); #ifdef VWM void ewmh_set_net_current_desktop(ScreenInfo *s); #endif void ewmh_set_net_active_window(Client *c); #ifdef VWM void ewmh_set_net_wm_desktop(Client *c); #endif unsigned int ewmh_get_net_wm_window_type(Window w); void ewmh_set_net_wm_state(Client *c); void ewmh_set_net_frame_extents(Window w); evilwm-1.1.1/ewmh.c000066400000000000000000000315551255071027000141310ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include "evilwm.h" #include "log.h" /* Standard X protocol atoms */ Atom xa_wm_state; Atom xa_wm_protos; Atom xa_wm_delete; Atom xa_wm_cmapwins; /* Motif atoms */ Atom mwm_hints; /* evilwm atoms */ Atom xa_evilwm_unmaximised_horz; Atom xa_evilwm_unmaximised_vert; /* Root Window Properties (and Related Messages) */ static Atom xa_net_supported; static Atom xa_net_client_list; static Atom xa_net_client_list_stacking; #ifdef VWM static Atom xa_net_number_of_desktops; #endif static Atom xa_net_desktop_geometry; static Atom xa_net_desktop_viewport; #ifdef VWM Atom xa_net_current_desktop; #endif Atom xa_net_active_window; static Atom xa_net_workarea; static Atom xa_net_supporting_wm_check; /* Other Root Window Messages */ Atom xa_net_close_window; Atom xa_net_moveresize_window; Atom xa_net_restack_window; Atom xa_net_request_frame_extents; /* Application Window Properties */ static Atom xa_net_wm_name; #ifdef VWM Atom xa_net_wm_desktop; #endif Atom xa_net_wm_window_type; Atom xa_net_wm_window_type_desktop; Atom xa_net_wm_window_type_dock; static Atom xa_net_wm_window_type_notification; Atom xa_net_wm_state; Atom xa_net_wm_state_maximized_vert; Atom xa_net_wm_state_maximized_horz; Atom xa_net_wm_state_fullscreen; Atom xa_net_wm_state_hidden; static Atom xa_net_wm_allowed_actions; static Atom xa_net_wm_action_move; static Atom xa_net_wm_action_resize; static Atom xa_net_wm_action_maximize_horz; static Atom xa_net_wm_action_maximize_vert; static Atom xa_net_wm_action_fullscreen; static Atom xa_net_wm_action_change_desktop; static Atom xa_net_wm_action_close; static Atom xa_net_wm_pid; Atom xa_net_frame_extents; /* Maintain a reasonably sized allocated block of memory for lists * of windows (for feeding to XChangeProperty in one hit). */ static Window *window_array = NULL; static Window *alloc_window_array(void); void ewmh_init(void) { /* Standard X protocol atoms */ xa_wm_state = XInternAtom(dpy, "WM_STATE", False); xa_wm_protos = XInternAtom(dpy, "WM_PROTOCOLS", False); xa_wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False); xa_wm_cmapwins = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False); /* Motif atoms */ mwm_hints = XInternAtom(dpy, _XA_MWM_HINTS, False); /* evilwm atoms */ xa_evilwm_unmaximised_horz = XInternAtom(dpy, "_EVILWM_UNMAXIMISED_HORZ", False); xa_evilwm_unmaximised_vert = XInternAtom(dpy, "_EVILWM_UNMAXIMISED_VERT", False); /* * extended windowmanager hints */ /* Root Window Properties (and Related Messages) */ xa_net_supported = XInternAtom(dpy, "_NET_SUPPORTED", False); xa_net_client_list = XInternAtom(dpy, "_NET_CLIENT_LIST", False); xa_net_client_list_stacking = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False); #ifdef VWM xa_net_number_of_desktops = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False); #endif xa_net_desktop_geometry = XInternAtom(dpy, "_NET_DESKTOP_GEOMETRY", False); xa_net_desktop_viewport = XInternAtom(dpy, "_NET_DESKTOP_VIEWPORT", False); #ifdef VWM xa_net_current_desktop = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False); #endif xa_net_active_window = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); xa_net_workarea = XInternAtom(dpy, "_NET_WORKAREA", False); xa_net_supporting_wm_check = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); /* Other Root Window Messages */ xa_net_close_window = XInternAtom(dpy, "_NET_CLOSE_WINDOW", False); xa_net_moveresize_window = XInternAtom(dpy, "_NET_MOVERESIZE_WINDOW", False); xa_net_restack_window = XInternAtom(dpy, "_NET_RESTACK_WINDOW", False); xa_net_request_frame_extents = XInternAtom(dpy, "_NET_REQUEST_FRAME_EXTENTS", False); /* Application Window Properties */ xa_net_wm_name = XInternAtom(dpy, "_NET_WM_NAME", False); #ifdef VWM xa_net_wm_desktop = XInternAtom(dpy, "_NET_WM_DESKTOP", False); #endif xa_net_wm_window_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); xa_net_wm_window_type_desktop = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False); xa_net_wm_window_type_dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); xa_net_wm_window_type_notification = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False); xa_net_wm_state = XInternAtom(dpy, "_NET_WM_STATE", False); xa_net_wm_state_maximized_vert = XInternAtom(dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False); xa_net_wm_state_maximized_horz = XInternAtom(dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False); xa_net_wm_state_fullscreen = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); xa_net_wm_state_hidden = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False); xa_net_wm_allowed_actions = XInternAtom(dpy, "_NET_WM_ALLOWED_ACTIONS", False); xa_net_wm_action_move = XInternAtom(dpy, "_NET_WM_ACTION_MOVE", False); xa_net_wm_action_resize = XInternAtom(dpy, "_NET_WM_ACTION_RESIZE", False); xa_net_wm_action_maximize_horz = XInternAtom(dpy, "_NET_WM_ACTION_MAXIMIZE_HORZ", False); xa_net_wm_action_maximize_vert = XInternAtom(dpy, "_NET_WM_ACTION_MAXIMIZE_VERT", False); xa_net_wm_action_fullscreen = XInternAtom(dpy, "_NET_WM_ACTION_FULLSCREEN", False); xa_net_wm_action_change_desktop = XInternAtom(dpy, "_NET_WM_ACTION_CHANGE_DESKTOP", False); xa_net_wm_action_close = XInternAtom(dpy, "_NET_WM_ACTION_CLOSE", False); xa_net_wm_pid = XInternAtom(dpy, "_NET_WM_PID", False); xa_net_frame_extents = XInternAtom(dpy, "_NET_FRAME_EXTENTS", False); } void ewmh_init_screen(ScreenInfo *s) { unsigned long pid = getpid(); Atom supported[] = { xa_net_client_list, xa_net_client_list_stacking, #ifdef VWM xa_net_number_of_desktops, #endif xa_net_desktop_geometry, xa_net_desktop_viewport, #ifdef VWM xa_net_current_desktop, #endif xa_net_active_window, xa_net_workarea, xa_net_supporting_wm_check, xa_net_close_window, xa_net_moveresize_window, xa_net_restack_window, xa_net_request_frame_extents, #ifdef VWM xa_net_wm_desktop, #endif xa_net_wm_window_type, xa_net_wm_window_type_desktop, xa_net_wm_window_type_dock, xa_net_wm_state, xa_net_wm_state_maximized_vert, xa_net_wm_state_maximized_horz, xa_net_wm_state_fullscreen, xa_net_wm_state_hidden, xa_net_wm_allowed_actions, /* Not sure if it makes any sense including every action here * as they'll already be listed per-client in the * _NET_WM_ALLOWED_ACTIONS property, but EWMH spec is unclear. * */ xa_net_wm_action_move, xa_net_wm_action_resize, xa_net_wm_action_maximize_horz, xa_net_wm_action_maximize_vert, xa_net_wm_action_fullscreen, xa_net_wm_action_change_desktop, xa_net_wm_action_close, xa_net_frame_extents, }; #ifdef VWM unsigned long num_desktops = 8; unsigned long vdesk = s->vdesk; #endif unsigned long workarea[4] = { 0, 0, DisplayWidth(dpy, s->screen), DisplayHeight(dpy, s->screen) }; s->supporting = XCreateSimpleWindow(dpy, s->root, 0, 0, 1, 1, 0, 0, 0); XChangeProperty(dpy, s->root, xa_net_supported, XA_ATOM, 32, PropModeReplace, (unsigned char *)&supported, sizeof(supported) / sizeof(Atom)); #ifdef VWM XChangeProperty(dpy, s->root, xa_net_number_of_desktops, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num_desktops, 1); #endif XChangeProperty(dpy, s->root, xa_net_desktop_geometry, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&workarea[2], 2); XChangeProperty(dpy, s->root, xa_net_desktop_viewport, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&workarea[0], 2); #ifdef VWM XChangeProperty(dpy, s->root, xa_net_current_desktop, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&vdesk, 1); #endif XChangeProperty(dpy, s->root, xa_net_workarea, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&workarea, 4); XChangeProperty(dpy, s->root, xa_net_supporting_wm_check, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&s->supporting, 1); XChangeProperty(dpy, s->supporting, xa_net_supporting_wm_check, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&s->supporting, 1); XChangeProperty(dpy, s->supporting, xa_net_wm_name, XA_STRING, 8, PropModeReplace, (const unsigned char *)"evilwm", 6); XChangeProperty(dpy, s->supporting, xa_net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); } void ewmh_deinit_screen(ScreenInfo *s) { XDeleteProperty(dpy, s->root, xa_net_supported); XDeleteProperty(dpy, s->root, xa_net_client_list); XDeleteProperty(dpy, s->root, xa_net_client_list_stacking); #ifdef VWM XDeleteProperty(dpy, s->root, xa_net_number_of_desktops); #endif XDeleteProperty(dpy, s->root, xa_net_desktop_geometry); XDeleteProperty(dpy, s->root, xa_net_desktop_viewport); #ifdef VWM XDeleteProperty(dpy, s->root, xa_net_current_desktop); #endif XDeleteProperty(dpy, s->root, xa_net_active_window); XDeleteProperty(dpy, s->root, xa_net_workarea); XDeleteProperty(dpy, s->root, xa_net_supporting_wm_check); XDestroyWindow(dpy, s->supporting); } void ewmh_init_client(Client *c) { Atom allowed_actions[] = { xa_net_wm_action_move, xa_net_wm_action_maximize_horz, xa_net_wm_action_maximize_vert, xa_net_wm_action_fullscreen, xa_net_wm_action_change_desktop, xa_net_wm_action_close, /* nelements reduced to omit this if not possible: */ xa_net_wm_action_resize, }; int nelements = sizeof(allowed_actions) / sizeof(Atom); /* Omit resize element if resizing not possible: */ if (c->max_width && c->max_width == c->min_width && c->max_height && c->max_height == c->min_height) nelements--; XChangeProperty(dpy, c->window, xa_net_wm_allowed_actions, XA_ATOM, 32, PropModeReplace, (unsigned char *)&allowed_actions, nelements); } void ewmh_deinit_client(Client *c) { XDeleteProperty(dpy, c->window, xa_net_wm_allowed_actions); } void ewmh_withdraw_client(Client *c) { #ifdef VWM XDeleteProperty(dpy, c->window, xa_net_wm_desktop); #endif XDeleteProperty(dpy, c->window, xa_net_wm_state); } void ewmh_select_client(Client *c) { clients_tab_order = list_to_head(clients_tab_order, c); } void ewmh_set_net_client_list(ScreenInfo *s) { Window *windows = alloc_window_array(); struct list *iter; int i = 0; for (iter = clients_mapping_order; iter; iter = iter->next) { Client *c = iter->data; if (c->screen == s) { windows[i++] = c->window; } } XChangeProperty(dpy, s->root, xa_net_client_list, XA_WINDOW, 32, PropModeReplace, (unsigned char *)windows, i); } void ewmh_set_net_client_list_stacking(ScreenInfo *s) { Window *windows = alloc_window_array(); struct list *iter; int i = 0; for (iter = clients_stacking_order; iter; iter = iter->next) { Client *c = iter->data; if (c->screen == s) { windows[i++] = c->window; } } XChangeProperty(dpy, s->root, xa_net_client_list_stacking, XA_WINDOW, 32, PropModeReplace, (unsigned char *)windows, i); } #ifdef VWM void ewmh_set_net_current_desktop(ScreenInfo *s) { unsigned long vdesk = s->vdesk; XChangeProperty(dpy, s->root, xa_net_current_desktop, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&vdesk, 1); } #endif void ewmh_set_net_active_window(Client *c) { int i; for (i = 0; i < num_screens; i++) { Window w; if (c && i == c->screen->screen) { w = c->window; } else { w = None; } XChangeProperty(dpy, screens[i].root, xa_net_active_window, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); } } #ifdef VWM void ewmh_set_net_wm_desktop(Client *c) { unsigned long vdesk = c->vdesk; XChangeProperty(dpy, c->window, xa_net_wm_desktop, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&vdesk, 1); } #endif unsigned int ewmh_get_net_wm_window_type(Window w) { Atom *aprop; unsigned long nitems, i; unsigned int type = 0; if ( (aprop = get_property(w, xa_net_wm_window_type, XA_ATOM, &nitems)) ) { for (i = 0; i < nitems; i++) { if (aprop[i] == xa_net_wm_window_type_desktop) type |= EWMH_WINDOW_TYPE_DESKTOP; if (aprop[i] == xa_net_wm_window_type_dock) type |= EWMH_WINDOW_TYPE_DOCK; if (aprop[i] == xa_net_wm_window_type_notification) type |= EWMH_WINDOW_TYPE_NOTIFICATION; } XFree(aprop); } return type; } void ewmh_set_net_wm_state(Client *c) { Atom state[3]; int i = 0; if (c->oldh) state[i++] = xa_net_wm_state_maximized_vert; if (c->oldw) state[i++] = xa_net_wm_state_maximized_horz; if (c->oldh && c->oldw) state[i++] = xa_net_wm_state_fullscreen; XChangeProperty(dpy, c->window, xa_net_wm_state, XA_ATOM, 32, PropModeReplace, (unsigned char *)&state, i); } void ewmh_set_net_frame_extents(Window w) { unsigned long extents[4]; extents[0] = extents[1] = extents[2] = extents[3] = opt_bw; XChangeProperty(dpy, w, xa_net_frame_extents, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&extents, 4); } static Window *alloc_window_array(void) { struct list *iter; unsigned int count = 0; for (iter = clients_mapping_order; iter; iter = iter->next) { count++; } if (count == 0) count++; /* Round up to next block of 128 */ count = (count + 127) & ~127; window_array = realloc(window_array, count * sizeof(Window)); return window_array; } evilwm-1.1.1/keymap.h000066400000000000000000000015471255071027000144620ustar00rootroot00000000000000#ifndef _KEYMAP_H #define _KEYMAP_H #define KEY_NEXT XK_Tab #define KEY_NEW XK_Return #define KEY_TOPLEFT XK_y #define KEY_TOPRIGHT XK_u #define KEY_BOTTOMLEFT XK_b #define KEY_BOTTOMRIGHT XK_n #define KEY_LEFT XK_h #define KEY_RIGHT XK_l #define KEY_DOWN XK_j #define KEY_UP XK_k #define KEY_LOWER XK_Insert #define KEY_ALTLOWER XK_KP_Insert #define KEY_INFO XK_i #define KEY_MAXVERT XK_equal #define KEY_MAX XK_x #define KEY_DOCK_TOGGLE XK_d #ifdef VWM # define KEY_FIX XK_f # define KEY_PREVDESK XK_Left # define KEY_NEXTDESK XK_Right # define KEY_TOGGLEDESK XK_a #endif /* Mixtures of Ctrl, Alt an Escape are used for things like VMWare and * XFree86/Cygwin, so the KILL key is an option in the Makefile */ #ifndef KEY_KILL # define KEY_KILL XK_Escape #endif #endif evilwm-1.1.1/list.c000066400000000000000000000043041255071027000141340ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ /* Basic linked list handling code. Operations that modify the list * return the new list head. */ #include #include "list.h" /* Wrap data in a new list container */ static struct list *list_new(void *data) { struct list *new; new = malloc(sizeof(struct list)); if (!new) return NULL; new->next = NULL; new->data = data; return new; } /* Insert new data before given position */ struct list *list_insert_before(struct list *list, struct list *before, void *data) { struct list *elem = list_new(data); struct list *iter; if (!elem) return list; if (!list) return elem; elem->next = before; if (before == list) return elem; for (iter = list; iter; iter = iter->next) { if (!iter->next || iter->next == before) { iter->next = elem; break; } } return list; } /* Add new data to head of list */ struct list *list_prepend(struct list *list, void *data) { return list_insert_before(list, list, data); } /* Add new data to tail of list */ struct list *list_append(struct list *list, void *data) { return list_insert_before(list, NULL, data); } /* Delete list element containing data */ struct list *list_delete(struct list *list, void *data) { struct list **elemp; if (!data) return list; for (elemp = &list; *elemp; elemp = &(*elemp)->next) { if ((*elemp)->data == data) break; } if (*elemp) { struct list *elem = *elemp; *elemp = elem->next; free(elem); } return list; } /* Move existing list element containing data to head of list */ struct list *list_to_head(struct list *list, void *data) { if (!data) return list; list = list_delete(list, data); return list_prepend(list, data); } /* Move existing list element containing data to tail of list */ struct list *list_to_tail(struct list *list, void *data) { if (!data) return list; list = list_delete(list, data); return list_append(list, data); } /* Find list element containing data */ struct list *list_find(struct list *list, void *data) { struct list *elem; for (elem = list; elem; elem = elem->next) { if (elem->data == data) return elem; } return NULL; } evilwm-1.1.1/list.h000066400000000000000000000014441255071027000141430ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #ifndef __LIST_H__ #define __LIST_H__ struct list { struct list *next; void *data; }; /* Each of these return the new pointer to the head of the list: */ struct list *list_insert_before(struct list *list, struct list *before, void *data); struct list *list_prepend(struct list *list, void *data); struct list *list_append(struct list *list, void *data); struct list *list_delete(struct list *list, void *data); struct list *list_to_head(struct list *list, void *data); struct list *list_to_tail(struct list *list, void *data); /* Returns element in list: */ struct list *list_find(struct list *list, void *data); #endif /* def __LIST_H__ */ evilwm-1.1.1/log.h000066400000000000000000000026311255071027000137500ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #ifndef __LOG_H__ #define __LOG_H__ #if defined(STDIO) || defined(DEBUG) || defined(XDEBUG) # include #endif #ifdef DEBUG extern int log_indent; #endif #ifdef STDIO # define LOG_INFO(...) printf(__VA_ARGS__); # define LOG_ERROR(...) fprintf(stderr, __VA_ARGS__); #else # define LOG_INFO(...) # define LOG_ERROR(...) #endif #ifdef DEBUG # define LOG_INDENT() do { int ii; for (ii = 0; ii < log_indent; ii++) fprintf(stderr, " "); } while (0) # define LOG_ENTER(...) do { LOG_INDENT(); log_indent++; fprintf(stderr, __VA_ARGS__); fprintf(stderr, " at %s:%d\n", __FILE__, __LINE__); } while (0) # define LOG_LEAVE() do { if (log_indent > 0) log_indent--; } while (0) # define LOG_DEBUG(...) do { LOG_INDENT(); fprintf(stderr, __VA_ARGS__); } while (0) # define LOG_DEBUG_(...) fprintf(stderr, __VA_ARGS__) #else # define LOG_ENTER(...) # define LOG_LEAVE(...) # define LOG_DEBUG(...) # define LOG_DEBUG_(...) #endif #ifdef XDEBUG # define LOG_XENTER(...) LOG_ENTER(__VA_ARGS__) # define LOG_XLEAVE(...) LOG_LEAVE(__VA_ARGS__) # define LOG_XDEBUG(...) LOG_DEBUG(__VA_ARGS__) # define LOG_XDEBUG_(...) LOG_DEBUG_(__VA_ARGS__) #else # define LOG_XENTER(...) # define LOG_XLEAVE(...) # define LOG_XDEBUG(...) # define LOG_XDEBUG_(...) #endif #endif /* __LOG_H__ */ evilwm-1.1.1/main.c000066400000000000000000000271241255071027000141120ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include #include #include #include "evilwm.h" #include "log.h" #include "xconfig.h" #ifdef DEBUG int log_indent = 0; #endif /* Commonly used X information */ Display *dpy; XFontStruct *font; Cursor move_curs; Cursor resize_curs; int num_screens; ScreenInfo *screens; #ifdef SHAPE int have_shape, shape_event; #endif #ifdef RANDR int have_randr, randr_event_base; #endif /* Things that affect user interaction */ #define CONFIG_FILE ".evilwmrc" static const char *opt_display = ""; static const char *opt_font = DEF_FONT; static const char *opt_fg = DEF_FG; static const char *opt_bg = DEF_BG; #ifdef VWM static const char *opt_fc = DEF_FC; #endif static char *opt_grabmask1 = NULL; static char *opt_grabmask2 = NULL; static char *opt_altmask = NULL; unsigned int numlockmask = 0; unsigned int grabmask1 = ControlMask|Mod1Mask; unsigned int grabmask2 = Mod1Mask; unsigned int altmask = ShiftMask; static const char *const def_term[] = { DEF_TERM, NULL }; char **opt_term = (char **)def_term; int opt_bw = DEF_BW; int opt_snap = 0; #ifdef SOLIDDRAG int no_solid_drag = 0; /* use solid drag by default */ #endif struct list *applications = NULL; /* Client tracking information */ struct list *clients_tab_order = NULL; struct list *clients_mapping_order = NULL; struct list *clients_stacking_order = NULL; Client *current = NULL; volatile Window initialising = None; /* Event loop will run until this flag is set */ int wm_exit; static void set_app(const char *arg); static void set_app_geometry(const char *arg); static void set_app_dock(void); #ifdef VWM static void set_app_vdesk(const char *arg); static void set_app_fixed(void); #endif static struct xconfig_option evilwm_options[] = { { XCONFIG_STRING, "fn", &opt_font }, { XCONFIG_STRING, "display", &opt_display }, { XCONFIG_STRING, "fg", &opt_fg }, { XCONFIG_STRING, "bg", &opt_bg }, #ifdef VWM { XCONFIG_STRING, "fc", &opt_fc }, #endif { XCONFIG_INT, "bw", &opt_bw }, { XCONFIG_STR_LIST, "term", &opt_term }, { XCONFIG_INT, "snap", &opt_snap }, { XCONFIG_STRING, "mask1", &opt_grabmask1 }, { XCONFIG_STRING, "mask2", &opt_grabmask2 }, { XCONFIG_STRING, "altmask", &opt_altmask }, { XCONFIG_CALL_1, "app", &set_app }, { XCONFIG_CALL_1, "geometry", &set_app_geometry }, { XCONFIG_CALL_1, "g", &set_app_geometry }, { XCONFIG_CALL_0, "dock", &set_app_dock }, #ifdef VWM { XCONFIG_CALL_1, "vdesk", &set_app_vdesk }, { XCONFIG_CALL_1, "v", &set_app_vdesk }, { XCONFIG_CALL_0, "fixed", &set_app_fixed }, { XCONFIG_CALL_0, "f", &set_app_fixed }, { XCONFIG_CALL_0, "s", &set_app_fixed }, #endif #ifdef SOLIDDRAG { XCONFIG_BOOL, "nosoliddrag", &no_solid_drag }, #endif { XCONFIG_END, NULL, NULL } }; static void setup_display(void); static void *xmalloc(size_t size); static unsigned int parse_modifiers(char *s); #ifdef STDIO static void helptext(void) { puts( "usage: evilwm [-display display] [-term termprog] [-fn fontname]\n" " [-fg foreground]" #ifdef VWM " [-fc fixed]" #endif " [-bg background] [-bw borderwidth]\n" " [-mask1 modifiers] [-mask2 modifiers] [-altmask modifiers]\n" " [-snap num]" " [-app name/class] [-g geometry] [-dock]\n" #ifdef VWM " [-v vdesk] [-s]" #endif #ifdef SOLIDDRAG " [-nosoliddrag]" #endif " [-V]" ); } #else #define helptext() #endif int main(int argc, char *argv[]) { struct sigaction act; int argn = 1, ret; { const char *home = getenv("HOME"); if (home) { char *conffile = xmalloc(strlen(home) + sizeof(CONFIG_FILE) + 2); strcpy(conffile, home); strcat(conffile, "/" CONFIG_FILE); xconfig_parse_file(evilwm_options, conffile); free(conffile); } } ret = xconfig_parse_cli(evilwm_options, argc, argv, &argn); if (ret == XCONFIG_MISSING_ARG) { fprintf(stderr, "%s: missing argument to `%s'\n", argv[0], argv[argn]); exit(1); } else if (ret == XCONFIG_BAD_OPTION) { if (0 == strcmp(argv[argn], "-h") || 0 == strcmp(argv[argn], "--help")) { helptext(); exit(0); #ifdef STDIO } else if (0 == strcmp(argv[argn], "-V") || 0 == strcmp(argv[argn], "--version")) { LOG_INFO("evilwm version " VERSION "\n"); exit(0); #endif } else { helptext(); exit(1); } } if (opt_grabmask1) grabmask1 = parse_modifiers(opt_grabmask1); if (opt_grabmask2) grabmask2 = parse_modifiers(opt_grabmask2); if (opt_altmask) altmask = parse_modifiers(opt_altmask); wm_exit = 0; act.sa_handler = handle_signal; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGTERM, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGHUP, &act, NULL); setup_display(); event_main_loop(); /* Quit Nicely */ while (clients_stacking_order) remove_client(clients_stacking_order->data); XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); if (font) XFreeFont(dpy, font); { int i; for (i = 0; i < num_screens; i++) { ewmh_deinit_screen(&screens[i]); XFreeGC(dpy, screens[i].invert_gc); XInstallColormap(dpy, DefaultColormap(dpy, i)); } } free(screens); XCloseDisplay(dpy); return 0; } static void *xmalloc(size_t size) { void *ptr = malloc(size); if (!ptr) { /* C99 defines the 'z' printf modifier for variables of * type size_t. Fall back to casting to unsigned long. */ #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L LOG_ERROR("out of memory, looking for %zu bytes\n", size); #else LOG_ERROR("out of memory, looking for %lu bytes\n", (unsigned long)size); #endif exit(1); } return ptr; } static void setup_display(void) { XGCValues gv; XSetWindowAttributes attr; XColor dummy; XModifierKeymap *modmap; /* used in scanning windows (XQueryTree) */ unsigned int i, j, nwins; Window dw1, dw2, *wins; XWindowAttributes winattr; LOG_ENTER("setup_display()"); dpy = XOpenDisplay(opt_display); if (!dpy) { LOG_ERROR("can't open display %s\n", opt_display); exit(1); } XSetErrorHandler(handle_xerror); /* XSynchronize(dpy, True); */ /* Standard & EWMH atoms */ ewmh_init(); font = XLoadQueryFont(dpy, opt_font); if (!font) font = XLoadQueryFont(dpy, DEF_FONT); if (!font) { LOG_ERROR("couldn't find a font to use: try starting with -fn fontname\n"); exit(1); } move_curs = XCreateFontCursor(dpy, XC_fleur); resize_curs = XCreateFontCursor(dpy, XC_plus); /* find out which modifier is NumLock - we'll use this when grabbing * every combination of modifiers we can think of */ modmap = XGetModifierMapping(dpy); for (i = 0; i < 8; i++) { for (j = 0; j < (unsigned int)modmap->max_keypermod; j++) { if (modmap->modifiermap[i*modmap->max_keypermod+j] == XKeysymToKeycode(dpy, XK_Num_Lock)) { numlockmask = (1<fid; /* set up root window attributes - same for each screen */ attr.event_mask = ChildMask | EnterWindowMask | ColormapChangeMask; /* SHAPE extension? */ #ifdef SHAPE { int e_dummy; have_shape = XShapeQueryExtension(dpy, &shape_event, &e_dummy); } #endif /* Xrandr extension? */ #ifdef RANDR { int e_dummy; have_randr = XRRQueryExtension(dpy, &randr_event_base, &e_dummy); if (!have_randr) { LOG_DEBUG("XRandR is not supported on this display.\n"); } } #endif /* now set up each screen in turn */ num_screens = ScreenCount(dpy); if (num_screens < 0) { LOG_ERROR("Can't count screens\n"); exit(1); } screens = xmalloc(num_screens * sizeof(ScreenInfo)); for (i = 0; i < (unsigned int)num_screens; i++) { char *ds, *colon, *dot; ds = DisplayString(dpy); /* set up DISPLAY environment variable to use */ colon = strrchr(ds, ':'); screens[i].display = xmalloc(14 + strlen(ds)); strcpy(screens[i].display, "DISPLAY="); strcat(screens[i].display, ds); if (colon && num_screens > 1) { colon = strrchr(screens[i].display, ':'); dot = strchr(colon, '.'); if (!dot) dot = colon + strlen(colon); snprintf(dot, 5, ".%d", i); } screens[i].screen = i; screens[i].root = RootWindow(dpy, i); #ifdef RANDR if (have_randr) { XRRSelectInput(dpy, screens[i].root, RRScreenChangeNotifyMask); } #endif #ifdef VWM screens[i].vdesk = KEY_TO_VDESK(XK_1); #endif XAllocNamedColor(dpy, DefaultColormap(dpy, i), opt_fg, &screens[i].fg, &dummy); XAllocNamedColor(dpy, DefaultColormap(dpy, i), opt_bg, &screens[i].bg, &dummy); #ifdef VWM XAllocNamedColor(dpy, DefaultColormap(dpy, i), opt_fc, &screens[i].fc, &dummy); #endif screens[i].invert_gc = XCreateGC(dpy, screens[i].root, GCFunction | GCSubwindowMode | GCLineWidth | GCFont, &gv); XChangeWindowAttributes(dpy, screens[i].root, CWEventMask, &attr); grab_keys_for_screen(&screens[i]); screens[i].docks_visible = 1; /* scan all the windows on this screen */ LOG_XENTER("XQueryTree(screen=%d)", i); XQueryTree(dpy, screens[i].root, &dw1, &dw2, &wins, &nwins); LOG_XDEBUG("%d windows\n", nwins); LOG_XLEAVE(); for (j = 0; j < nwins; j++) { XGetWindowAttributes(dpy, wins[j], &winattr); if (!winattr.override_redirect && winattr.map_state == IsViewable) make_new_client(wins[j], &screens[i]); } XFree(wins); ewmh_init_screen(&screens[i]); } ewmh_set_net_active_window(NULL); LOG_LEAVE(); } /**************************************************************************/ /* Option parsing callbacks */ static void set_app(const char *arg) { Application *new = xmalloc(sizeof(Application)); char *tmp; new->res_name = new->res_class = NULL; new->geometry_mask = 0; new->is_dock = 0; #ifdef VWM new->vdesk = VDESK_NONE; #endif if ((tmp = strchr(arg, '/'))) { *(tmp++) = 0; } if (strlen(arg) > 0) { new->res_name = xmalloc(strlen(arg)+1); strcpy(new->res_name, arg); } if (tmp && strlen(tmp) > 0) { new->res_class = xmalloc(strlen(tmp)+1); strcpy(new->res_class, tmp); } applications = list_prepend(applications, new); } static void set_app_geometry(const char *arg) { if (applications) { Application *app = applications->data; app->geometry_mask = XParseGeometry(arg, &app->x, &app->y, &app->width, &app->height); } } static void set_app_dock(void) { if (applications) { Application *app = applications->data; app->is_dock = 1; } } #ifdef VWM static void set_app_vdesk(const char *arg) { unsigned int v = atoi(arg); if (applications && valid_vdesk(v)) { Application *app = applications->data; app->vdesk = v; } } static void set_app_fixed(void) { if (applications) { Application *app = applications->data; app->vdesk = VDESK_FIXED; } } #endif /* Used for overriding the default WM modifiers */ static unsigned int parse_modifiers(char *s) { static struct { const char *name; unsigned int mask; } modifiers[9] = { { "shift", ShiftMask }, { "lock", LockMask }, { "control", ControlMask }, { "alt", Mod1Mask }, { "mod1", Mod1Mask }, { "mod2", Mod2Mask }, { "mod3", Mod3Mask }, { "mod4", Mod4Mask }, { "mod5", Mod5Mask } }; char *tmp = strtok(s, ",+"); unsigned int ret = 0; int i; if (!tmp) return 0; do { for (i = 0; i < 9; i++) { if (!strcmp(modifiers[i].name, tmp)) ret |= modifiers[i].mask; } tmp = strtok(NULL, ",+"); } while (tmp); return ret; } evilwm-1.1.1/misc.c000066400000000000000000000051111255071027000141110ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include #include #include #include #include #include "evilwm.h" #include "log.h" int need_client_tidy = 0; int ignore_xerror = 0; /* Now do this by fork()ing twice so we don't have to worry about SIGCHLDs */ void spawn(const char *const cmd[]) { ScreenInfo *current_screen = find_current_screen(); pid_t pid; if (current_screen && current_screen->display) putenv(current_screen->display); if (!(pid = fork())) { setsid(); switch (fork()) { /* execvp()'s prototype is (char *const *) suggesting that it * modifies the contents of the strings. The prototype is this * way due to SUS maintaining compatability with older code. * However, execvp guarantees not to modify argv, so the following * cast is valid. */ case 0: execvp(cmd[0], (char *const *)cmd); default: _exit(0); } } if (pid > 0) wait(NULL); } void handle_signal(int signo) { (void)signo; /* unused */ wm_exit = 1; } int handle_xerror(Display *dsply, XErrorEvent *e) { Client *c; (void)dsply; /* unused */ LOG_ENTER("handle_xerror(error=%d, request=%d/%d, resourceid=%lx)", e->error_code, e->request_code, e->minor_code, e->resourceid); if (ignore_xerror) { LOG_DEBUG("ignoring...\n"); LOG_LEAVE(); return 0; } /* If this error actually occurred while setting up the new * window, best let make_new_client() know not to bother */ if (initialising != None && e->resourceid == initialising) { LOG_DEBUG("error caught while initialising window=%lx\n", initialising); initialising = None; LOG_LEAVE(); return 0; } if (e->error_code == BadAccess && e->request_code == X_ChangeWindowAttributes) { LOG_ERROR("root window unavailable (maybe another wm is running?)\n"); exit(1); } c = find_client(e->resourceid); if (c) { LOG_DEBUG("flagging client for removal\n"); c->remove = 1; need_client_tidy = 1; } else { LOG_DEBUG("unknown error: not handling\n"); } LOG_LEAVE(); return 0; } /* Remove all enter events from the queue except the last of any corresponding * to "except"s parent. */ void discard_enter_events(Client *except) { XEvent tmp, putback_ev; int putback = 0; XSync(dpy, False); while (XCheckMaskEvent(dpy, EnterWindowMask, &tmp)) { if (tmp.xcrossing.window == except->parent) { memcpy(&putback_ev, &tmp, sizeof(XEvent)); putback = 1; } } if (putback) { XPutBackEvent(dpy, &putback_ev); } } evilwm-1.1.1/new.c000066400000000000000000000275651255071027000137700ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include #include #include "evilwm.h" #include "log.h" static void init_geometry(Client *c); static void reparent(Client *c); static void update_window_type_flags(Client *c, unsigned int type); #ifdef XDEBUG static const char *map_state_string(int map_state); static const char *gravity_string(int gravity); static void debug_wm_normal_hints(XSizeHints *size); #else # define debug_wm_normal_hints(s) #endif void make_new_client(Window w, ScreenInfo *s) { Client *c; char *name; XClassHint *class; unsigned int window_type; LOG_ENTER("make_new_client(window=%lx)", w); XGrabServer(dpy); /* First a bit of interaction with the error handler due to X's * tendency to batch event notifications. We set a global variable to * the id of the window we're initialising then do simple X call on * that window. If an error is raised by this (and nothing else should * do so as we've grabbed the server), the error handler resets the * variable indicating the window has already disappeared, so we stop * trying to manage it. */ initialising = w; XFetchName(dpy, w, &name); XSync(dpy, False); /* If 'initialising' is now set to None, that means doing the * XFetchName raised BadWindow - the window has been removed before * we got a chance to grab the server. */ if (initialising == None) { LOG_DEBUG("XError occurred for initialising window - aborting...\n"); XUngrabServer(dpy); LOG_LEAVE(); return; } initialising = None; LOG_DEBUG("screen=%d\n", s->screen); LOG_DEBUG("name=%s\n", name ? name : "Untitled"); if (name) XFree(name); window_type = ewmh_get_net_wm_window_type(w); /* Don't manage DESKTOP type windows */ if (window_type & EWMH_WINDOW_TYPE_DESKTOP) { XMapWindow(dpy, w); XUngrabServer(dpy); return; } c = malloc(sizeof(Client)); /* Don't crash the window manager, just fail the operation. */ if (!c) { LOG_ERROR("out of memory in new_client; limping onward\n"); LOG_LEAVE(); XUngrabServer(dpy); return; } clients_tab_order = list_prepend(clients_tab_order, c); clients_mapping_order = list_append(clients_mapping_order, c); clients_stacking_order = list_append(clients_stacking_order, c); c->screen = s; c->window = w; c->ignore_unmap = 0; c->remove = 0; /* Ungrab the X server as soon as possible. Now that the client is * malloc()ed and attached to the list, it is safe for any subsequent * X calls to raise an X error and thus flag it for removal. */ XUngrabServer(dpy); c->border = opt_bw; update_window_type_flags(c, window_type); init_geometry(c); #ifdef DEBUG { struct list *iter; int i = 0; for (iter = clients_tab_order; iter; iter = iter->next) i++; LOG_DEBUG("new window %dx%d+%d+%d, wincount=%d\n", c->width, c->height, c->x, c->y, i); } #endif XSelectInput(dpy, c->window, ColormapChangeMask | EnterWindowMask | PropertyChangeMask); reparent(c); #ifdef SHAPE if (have_shape) { XShapeSelectInput(dpy, c->window, ShapeNotifyMask); set_shape(c); } #endif /* Read instance/class information for client and check against list * built with -app options */ class = XAllocClassHint(); if (class) { struct list *aiter = applications; XGetClassHint(dpy, w, class); while (aiter) { Application *a = aiter->data; if ((!a->res_name || (class->res_name && !strcmp(class->res_name, a->res_name))) && (!a->res_class || (class->res_class && !strcmp(class->res_class, a->res_class)))) { if (a->geometry_mask & WidthValue) c->width = a->width * c->width_inc; if (a->geometry_mask & HeightValue) c->height = a->height * c->height_inc; if (a->geometry_mask & XValue) { if (a->geometry_mask & XNegative) c->x = a->x + DisplayWidth(dpy, s->screen)-c->width-c->border; else c->x = a->x + c->border; } if (a->geometry_mask & YValue) { if (a->geometry_mask & YNegative) c->y = a->y + DisplayHeight(dpy, s->screen)-c->height-c->border; else c->y = a->y + c->border; } moveresize(c); if (a->is_dock) c->is_dock = 1; #ifdef VWM if (a->vdesk != VDESK_NONE) c->vdesk = a->vdesk; #endif } aiter = aiter->next; } XFree(class->res_name); XFree(class->res_class); XFree(class); } ewmh_init_client(c); ewmh_set_net_client_list(c->screen); ewmh_set_net_client_list_stacking(c->screen); /* Only map the window frame (and thus the window) if it's supposed * to be visible on this virtual desktop. */ #ifdef VWM if (is_fixed(c) || c->vdesk == s->vdesk) #endif { client_show(c); client_raise(c); if (!(window_type & (EWMH_WINDOW_TYPE_DOCK|EWMH_WINDOW_TYPE_NOTIFICATION))) { select_client(c); #ifdef WARP_POINTER setmouse(c->window, c->width + c->border - 1, c->height + c->border - 1); #endif discard_enter_events(c); } } #ifdef VWM else { set_wm_state(c, IconicState); } ewmh_set_net_wm_desktop(c); #endif LOG_LEAVE(); } /* Calls XGetWindowAttributes, XGetWMHints and XGetWMNormalHints to determine * window's initial geometry. */ static void init_geometry(Client *c) { long size_flags; XWindowAttributes attr; unsigned long *eprop; unsigned long nitems; PropMwmHints *mprop; #ifdef VWM unsigned long *lprop; #endif if ( (mprop = get_property(c->window, mwm_hints, mwm_hints, &nitems)) ) { if (nitems >= PROP_MWM_HINTS_ELEMENTS && (mprop->flags & MWM_HINTS_DECORATIONS) && !(mprop->decorations & MWM_DECOR_ALL) && !(mprop->decorations & MWM_DECOR_BORDER)) { c->border = 0; } XFree(mprop); } #ifdef VWM c->vdesk = c->screen->vdesk; if ( (lprop = get_property(c->window, xa_net_wm_desktop, XA_CARDINAL, &nitems)) ) { /* NB, Xlib not only returns a 32bit value in a long (which may * not be 32bits), it also sign extends the 32bit value */ if (nitems && valid_vdesk(lprop[0] & UINT32_MAX)) { c->vdesk = lprop[0] & UINT32_MAX; } XFree(lprop); } #endif get_window_type(c); /* Get current window attributes */ LOG_XENTER("XGetWindowAttributes(window=%lx)", c->window); XGetWindowAttributes(dpy, c->window, &attr); LOG_XDEBUG("(%s) %dx%d+%d+%d, bw = %d\n", map_state_string(attr.map_state), attr.width, attr.height, attr.x, attr.y, attr.border_width); LOG_XLEAVE(); c->old_border = attr.border_width; c->oldw = c->oldh = 0; c->cmap = attr.colormap; if ( (eprop = get_property(c->window, xa_evilwm_unmaximised_horz, XA_CARDINAL, &nitems)) ) { if (nitems == 2) { c->oldx = eprop[0]; c->oldw = eprop[1]; } XFree(eprop); } if ( (eprop = get_property(c->window, xa_evilwm_unmaximised_vert, XA_CARDINAL, &nitems)) ) { if (nitems == 2) { c->oldy = eprop[0]; c->oldh = eprop[1]; } XFree(eprop); } size_flags = get_wm_normal_hints(c); if ((attr.width >= c->min_width) && (attr.height >= c->min_height)) { /* if (attr.map_state == IsViewable || (size_flags & (PSize | USSize))) { */ c->width = attr.width; c->height = attr.height; } else { c->width = c->min_width; c->height = c->min_height; send_config(c); } if ((attr.map_state == IsViewable) || (size_flags & (/*PPosition |*/ USPosition))) { c->x = attr.x; c->y = attr.y; } else { int xmax = DisplayWidth(dpy, c->screen->screen); int ymax = DisplayHeight(dpy, c->screen->screen); int x, y; get_mouse_position(&x, &y, c->screen->root); c->x = (x * (xmax - c->border - c->width)) / xmax; c->y = (y * (ymax - c->border - c->height)) / ymax; send_config(c); } LOG_DEBUG("window started as %dx%d +%d+%d\n", c->width, c->height, c->x, c->y); if (attr.map_state == IsViewable) { /* The reparent that is to come would trigger an unmap event */ c->ignore_unmap++; } c->x += c->old_border; c->y += c->old_border; gravitate_border(c, -c->old_border); gravitate_border(c, c->border); } static void reparent(Client *c) { XSetWindowAttributes p_attr; p_attr.border_pixel = c->screen->bg.pixel; p_attr.override_redirect = True; p_attr.event_mask = ChildMask | ButtonPressMask | EnterWindowMask; c->parent = XCreateWindow(dpy, c->screen->root, c->x-c->border, c->y-c->border, c->width, c->height, c->border, DefaultDepth(dpy, c->screen->screen), CopyFromParent, DefaultVisual(dpy, c->screen->screen), CWOverrideRedirect | CWBorderPixel | CWEventMask, &p_attr); XAddToSaveSet(dpy, c->window); XSetWindowBorderWidth(dpy, c->window, 0); XReparentWindow(dpy, c->window, c->parent, 0, 0); XMapWindow(dpy, c->window); grab_button(c->parent, grabmask2, AnyButton); grab_button(c->parent, grabmask2 | altmask, AnyButton); } /* Get WM_NORMAL_HINTS property */ long get_wm_normal_hints(Client *c) { XSizeHints *size; long flags; long dummy; size = XAllocSizeHints(); LOG_XENTER("XGetWMNormalHints(window=%lx)", c->window); XGetWMNormalHints(dpy, c->window, size, &dummy); debug_wm_normal_hints(size); LOG_XLEAVE(); flags = size->flags; if (flags & PMinSize) { c->min_width = size->min_width; c->min_height = size->min_height; } else { c->min_width = c->min_height = 0; } if (flags & PMaxSize) { c->max_width = size->max_width; c->max_height = size->max_height; } else { c->max_width = c->max_height = 0; } if (flags & PBaseSize) { c->base_width = size->base_width; c->base_height = size->base_height; } else { c->base_width = c->min_width; c->base_height = c->min_height; } c->width_inc = c->height_inc = 1; if (flags & PResizeInc) { c->width_inc = size->width_inc ? size->width_inc : 1; c->height_inc = size->height_inc ? size->height_inc : 1; } if (!(flags & PMinSize)) { c->min_width = c->base_width + c->width_inc; c->min_height = c->base_height + c->height_inc; } if (flags & PWinGravity) { c->win_gravity_hint = size->win_gravity; } else { c->win_gravity_hint = NorthWestGravity; } c->win_gravity = c->win_gravity_hint; XFree(size); return flags; } static void update_window_type_flags(Client *c, unsigned int type) { c->is_dock = (type & EWMH_WINDOW_TYPE_DOCK) ? 1 : 0; } /* Determine window type and update flags accordingly */ void get_window_type(Client *c) { unsigned int type = ewmh_get_net_wm_window_type(c->window); update_window_type_flags(c, type); } #ifdef XDEBUG static const char *map_state_string(int map_state) { const char *map_states[4] = { "IsUnmapped", "IsUnviewable", "IsViewable", "Unknown" }; return ((unsigned int)map_state < 3) ? map_states[map_state] : map_states[3]; } static const char *gravity_string(int gravity) { const char *gravities[12] = { "ForgetGravity", "NorthWestGravity", "NorthGravity", "NorthEastGravity", "WestGravity", "CenterGravity", "EastGravity", "SouthWestGravity", "SouthGravity", "SouthEastGravity", "StaticGravity", "Unknown" }; return ((unsigned int)gravity < 11) ? gravities[gravity] : gravities[11]; } static void debug_wm_normal_hints(XSizeHints *size) { if (size->flags & 15) { LOG_XDEBUG(""); if (size->flags & USPosition) { LOG_XDEBUG_("USPosition "); } if (size->flags & USSize) { LOG_XDEBUG_("USSize "); } if (size->flags & PPosition) { LOG_XDEBUG_("PPosition "); } if (size->flags & PSize) { LOG_XDEBUG_("PSize"); } LOG_XDEBUG_("\n"); } if (size->flags & PMinSize) { LOG_XDEBUG("PMinSize: min_width = %d, min_height = %d\n", size->min_width, size->min_height); } if (size->flags & PMaxSize) { LOG_XDEBUG("PMaxSize: max_width = %d, max_height = %d\n", size->max_width, size->max_height); } if (size->flags & PResizeInc) { LOG_XDEBUG("PResizeInc: width_inc = %d, height_inc = %d\n", size->width_inc, size->height_inc); } if (size->flags & PAspect) { LOG_XDEBUG("PAspect: min_aspect = %d/%d, max_aspect = %d/%d\n", size->min_aspect.x, size->min_aspect.y, size->max_aspect.x, size->max_aspect.y); } if (size->flags & PBaseSize) { LOG_XDEBUG("PBaseSize: base_width = %d, base_height = %d\n", size->base_width, size->base_height); } if (size->flags & PWinGravity) { LOG_XDEBUG("PWinGravity: %s\n", gravity_string(size->win_gravity)); } } #endif evilwm-1.1.1/screen.c000066400000000000000000000363751255071027000144550ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include #include "evilwm.h" #include "log.h" #ifdef INFOBANNER Window info_window = None; static void create_info_window(Client *c); static void update_info_window(Client *c); static void remove_info_window(void); static void grab_keysym(Window w, unsigned int mask, KeySym keysym); static void create_info_window(Client *c) { info_window = XCreateSimpleWindow(dpy, c->screen->root, -4, -4, 2, 2, 0, c->screen->fg.pixel, c->screen->fg.pixel); XMapRaised(dpy, info_window); update_info_window(c); } static void update_info_window(Client *c) { char *name; char buf[27]; int namew, iwinx, iwiny, iwinw, iwinh; int width_inc = c->width_inc, height_inc = c->height_inc; if (!info_window) return; snprintf(buf, sizeof(buf), "%dx%d+%d+%d", (c->width-c->base_width)/width_inc, (c->height-c->base_height)/height_inc, c->x, c->y); iwinw = XTextWidth(font, buf, strlen(buf)) + 2; iwinh = font->max_bounds.ascent + font->max_bounds.descent; XFetchName(dpy, c->window, &name); if (name) { namew = XTextWidth(font, name, strlen(name)); if (namew > iwinw) iwinw = namew + 2; iwinh = iwinh * 2; } iwinx = c->x + c->border + c->width - iwinw; iwiny = c->y - c->border; if (iwinx + iwinw > DisplayWidth(dpy, c->screen->screen)) iwinx = DisplayWidth(dpy, c->screen->screen) - iwinw; if (iwinx < 0) iwinx = 0; if (iwiny + iwinh > DisplayHeight(dpy, c->screen->screen)) iwiny = DisplayHeight(dpy, c->screen->screen) - iwinh; if (iwiny < 0) iwiny = 0; XMoveResizeWindow(dpy, info_window, iwinx, iwiny, iwinw, iwinh); XClearWindow(dpy, info_window); if (name) { XDrawString(dpy, info_window, c->screen->invert_gc, 1, iwinh / 2 - 1, name, strlen(name)); XFree(name); } XDrawString(dpy, info_window, c->screen->invert_gc, 1, iwinh - 1, buf, strlen(buf)); } static void remove_info_window(void) { if (info_window) XDestroyWindow(dpy, info_window); info_window = None; } #endif /* INFOBANNER */ static void draw_outline(Client *c) { #ifndef INFOBANNER_MOVERESIZE char buf[27]; int width_inc = c->width_inc, height_inc = c->height_inc; #endif /* ndef INFOBANNER_MOVERESIZE */ XDrawRectangle(dpy, c->screen->root, c->screen->invert_gc, c->x - c->border, c->y - c->border, c->width + 2*c->border-1, c->height + 2*c->border-1); #ifndef INFOBANNER_MOVERESIZE snprintf(buf, sizeof(buf), "%dx%d+%d+%d", (c->width-c->base_width)/width_inc, (c->height-c->base_height)/height_inc, c->x, c->y); XDrawString(dpy, c->screen->root, c->screen->invert_gc, c->x + c->width - XTextWidth(font, buf, strlen(buf)) - SPACE, c->y + c->height - SPACE, buf, strlen(buf)); #endif /* ndef INFOBANNER_MOVERESIZE */ } static void recalculate_sweep(Client *c, int x1, int y1, int x2, int y2, unsigned force) { if (force || c->oldw == 0) { c->oldw = 0; c->width = abs(x1 - x2); c->width -= (c->width - c->base_width) % c->width_inc; if (c->min_width && c->width < c->min_width) c->width = c->min_width; if (c->max_width && c->width > c->max_width) c->width = c->max_width; c->x = (x1 <= x2) ? x1 : x1 - c->width; } if (force || c->oldh == 0) { c->oldh = 0; c->height = abs(y1 - y2); c->height -= (c->height - c->base_height) % c->height_inc; if (c->min_height && c->height < c->min_height) c->height = c->min_height; if (c->max_height && c->height > c->max_height) c->height = c->max_height; c->y = (y1 <= y2) ? y1 : y1 - c->height; } } void sweep(Client *c) { XEvent ev; int old_cx = c->x; int old_cy = c->y; if (!grab_pointer(c->screen->root, MouseMask, resize_curs)) return; client_raise(c); #ifdef INFOBANNER_MOVERESIZE create_info_window(c); #endif XGrabServer(dpy); draw_outline(c); setmouse(c->window, c->width, c->height); for (;;) { XMaskEvent(dpy, MouseMask, &ev); switch (ev.type) { case MotionNotify: if (ev.xmotion.root != c->screen->root) break; draw_outline(c); /* clear */ XUngrabServer(dpy); recalculate_sweep(c, old_cx, old_cy, ev.xmotion.x, ev.xmotion.y, ev.xmotion.state & altmask); #ifdef INFOBANNER_MOVERESIZE update_info_window(c); #endif XSync(dpy, False); XGrabServer(dpy); draw_outline(c); break; case ButtonRelease: draw_outline(c); /* clear */ XUngrabServer(dpy); #ifdef INFOBANNER_MOVERESIZE remove_info_window(); #endif XUngrabPointer(dpy, CurrentTime); moveresize(c); /* In case maximise state has changed: */ ewmh_set_net_wm_state(c); return; default: break; } } } /** predicate_keyrepeatpress: * predicate function for use with XCheckIfEvent. * When used with XCheckIfEvent, this function will return true if * there is a KeyPress event queued of the same keycode and time * as @arg. * * @arg must be a poiner to an XEvent of type KeyRelease */ static Bool predicate_keyrepeatpress(Display *dummy, XEvent *ev, XPointer arg) { (void) dummy; XEvent* release_event = (XEvent*) arg; if (ev->type != KeyPress) return False; if (release_event->xkey.keycode != ev->xkey.keycode) return False; return release_event->xkey.time == ev->xkey.time; } void show_info(Client *c, unsigned int keycode) { XEvent ev; XKeyboardState keyboard; if (XGrabKeyboard(dpy, c->screen->root, False, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) return; /* keyboard repeat might not have any effect, newer X servers seem to * only change the keyboard control after all keys have been physically * released. */ XGetKeyboardControl(dpy, &keyboard); XChangeKeyboardControl(dpy, KBAutoRepeatMode, &(XKeyboardControl){.auto_repeat_mode = AutoRepeatModeOff}); #ifdef INFOBANNER create_info_window(c); #else XGrabServer(dpy); draw_outline(c); #endif do { XMaskEvent(dpy, KeyReleaseMask, &ev); if (ev.xkey.keycode != keycode) continue; if (XCheckIfEvent(dpy, &ev, predicate_keyrepeatpress, (XPointer)&ev)) { /* This is a key press event with the same time as the previous * key release event. */ continue; } break; /* escape */ } while (1); #ifdef INFOBANNER remove_info_window(); #else draw_outline(c); XUngrabServer(dpy); #endif XChangeKeyboardControl(dpy, KBAutoRepeatMode, &(XKeyboardControl){.auto_repeat_mode = keyboard.global_auto_repeat}); XUngrabKeyboard(dpy, CurrentTime); } static int absmin(int a, int b) { if (abs(a) < abs(b)) return a; return b; } static void snap_client(Client *c) { int dx, dy; int dpy_width = DisplayWidth(dpy, c->screen->screen); int dpy_height = DisplayHeight(dpy, c->screen->screen); struct list *iter; Client *ci; /* snap to other windows */ dx = dy = opt_snap; for (iter = clients_tab_order; iter; iter = iter->next) { ci = iter->data; if (ci == c) continue; if (ci->screen != c->screen) continue; #ifdef VWM if (!is_fixed(ci) && ci->vdesk != c->screen->vdesk) continue; #endif if (ci->is_dock && !c->screen->docks_visible) continue; if (ci->y - ci->border - c->border - c->height - c->y <= opt_snap && c->y - c->border - ci->border - ci->height - ci->y <= opt_snap) { dx = absmin(dx, ci->x + ci->width - c->x + c->border + ci->border); dx = absmin(dx, ci->x + ci->width - c->x - c->width); dx = absmin(dx, ci->x - c->x - c->width - c->border - ci->border); dx = absmin(dx, ci->x - c->x); } if (ci->x - ci->border - c->border - c->width - c->x <= opt_snap && c->x - c->border - ci->border - ci->width - ci->x <= opt_snap) { dy = absmin(dy, ci->y + ci->height - c->y + c->border + ci->border); dy = absmin(dy, ci->y + ci->height - c->y - c->height); dy = absmin(dy, ci->y - c->y - c->height - c->border - ci->border); dy = absmin(dy, ci->y - c->y); } } if (abs(dx) < opt_snap) c->x += dx; if (abs(dy) < opt_snap) c->y += dy; /* snap to screen border */ if (abs(c->x - c->border) < opt_snap) c->x = c->border; if (abs(c->y - c->border) < opt_snap) c->y = c->border; if (abs(c->x + c->width + c->border - dpy_width) < opt_snap) c->x = dpy_width - c->width - c->border; if (abs(c->y + c->height + c->border - dpy_height) < opt_snap) c->y = dpy_height - c->height - c->border; if (abs(c->x) == c->border && c->width == dpy_width) c->x = 0; if (abs(c->y) == c->border && c->height == dpy_height) c->y = 0; } void drag(Client *c) { XEvent ev; int x1, y1; int old_cx = c->x; int old_cy = c->y; if (!grab_pointer(c->screen->root, MouseMask, move_curs)) return; client_raise(c); get_mouse_position(&x1, &y1, c->screen->root); #ifdef INFOBANNER_MOVERESIZE create_info_window(c); #endif if (no_solid_drag) { XGrabServer(dpy); draw_outline(c); } for (;;) { XMaskEvent(dpy, MouseMask, &ev); switch (ev.type) { case MotionNotify: if (ev.xmotion.root != c->screen->root) break; if (no_solid_drag) { draw_outline(c); /* clear */ XUngrabServer(dpy); } c->x = old_cx + (ev.xmotion.x - x1); c->y = old_cy + (ev.xmotion.y - y1); if (opt_snap && !(ev.xmotion.state & altmask)) snap_client(c); #ifdef INFOBANNER_MOVERESIZE update_info_window(c); #endif if (no_solid_drag) { XSync(dpy, False); XGrabServer(dpy); draw_outline(c); } else { XMoveWindow(dpy, c->parent, c->x - c->border, c->y - c->border); send_config(c); } break; case ButtonRelease: if (no_solid_drag) { draw_outline(c); /* clear */ XUngrabServer(dpy); } #ifdef INFOBANNER_MOVERESIZE remove_info_window(); #endif XUngrabPointer(dpy, CurrentTime); if (no_solid_drag) { moveresize(c); } return; default: break; } } } void moveresize(Client *c) { client_raise(c); XMoveResizeWindow(dpy, c->parent, c->x - c->border, c->y - c->border, c->width, c->height); XMoveResizeWindow(dpy, c->window, 0, 0, c->width, c->height); send_config(c); } void maximise_client(Client *c, int action, int hv) { if (hv & MAXIMISE_HORZ) { if (c->oldw) { if (action == NET_WM_STATE_REMOVE || action == NET_WM_STATE_TOGGLE) { c->x = c->oldx; c->width = c->oldw; c->oldw = 0; XDeleteProperty(dpy, c->window, xa_evilwm_unmaximised_horz); } } else { if (action == NET_WM_STATE_ADD || action == NET_WM_STATE_TOGGLE) { unsigned long props[2]; c->oldx = c->x; c->oldw = c->width; c->x = 0; c->width = DisplayWidth(dpy, c->screen->screen); props[0] = c->oldx; props[1] = c->oldw; XChangeProperty(dpy, c->window, xa_evilwm_unmaximised_horz, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&props, 2); } } } if (hv & MAXIMISE_VERT) { if (c->oldh) { if (action == NET_WM_STATE_REMOVE || action == NET_WM_STATE_TOGGLE) { c->y = c->oldy; c->height = c->oldh; c->oldh = 0; XDeleteProperty(dpy, c->window, xa_evilwm_unmaximised_vert); } } else { if (action == NET_WM_STATE_ADD || action == NET_WM_STATE_TOGGLE) { unsigned long props[2]; c->oldy = c->y; c->oldh = c->height; c->y = 0; c->height = DisplayHeight(dpy, c->screen->screen); props[0] = c->oldy; props[1] = c->oldh; XChangeProperty(dpy, c->window, xa_evilwm_unmaximised_vert, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&props, 2); } } } ewmh_set_net_wm_state(c); moveresize(c); discard_enter_events(c); } void next(void) { struct list *newl = list_find(clients_tab_order, current); Client *newc = current; do { if (newl) { newl = newl->next; if (!newl && !current) return; } if (!newl) newl = clients_tab_order; if (!newl) return; newc = newl->data; if (newc == current) return; } #ifdef VWM /* NOTE: Checking against newc->screen->vdesk implies we can Alt+Tab * across screen boundaries. Is this what we want? */ while ((!is_fixed(newc) && (newc->vdesk != newc->screen->vdesk)) || (newc->is_dock && !newc->screen->docks_visible)); #else while (0); #endif if (!newc) return; client_show(newc); client_raise(newc); select_client(newc); #ifdef WARP_POINTER setmouse(newc->window, newc->width + newc->border - 1, newc->height + newc->border - 1); #endif discard_enter_events(newc); } #ifdef VWM void switch_vdesk(ScreenInfo *s, unsigned int v) { struct list *iter; #ifdef DEBUG int hidden = 0, raised = 0; #endif if (v == s->vdesk) return; LOG_ENTER("switch_vdesk(screen=%d, from=%d, to=%d)", s->screen, s->vdesk, v); if (current && !is_fixed(current)) { select_client(NULL); } for (iter = clients_tab_order; iter; iter = iter->next) { Client *c = iter->data; if (c->screen != s) continue; if (c->vdesk == s->vdesk) { client_hide(c); #ifdef DEBUG hidden++; #endif } else if (c->vdesk == v) { if (!c->is_dock || s->docks_visible) client_show(c); #ifdef DEBUG raised++; #endif } } /* cache the value of the current vdesk, so that user may toggle back to it */ s->old_vdesk = s->vdesk; s->vdesk = v; ewmh_set_net_current_desktop(s); LOG_DEBUG("%d hidden, %d raised\n", hidden, raised); LOG_LEAVE(); } #endif /* def VWM */ void set_docks_visible(ScreenInfo *s, int is_visible) { struct list *iter; LOG_ENTER("set_docks_visible(screen=%d, is_visible=%d)", s->screen, is_visible); s->docks_visible = is_visible; for (iter = clients_tab_order; iter; iter = iter->next) { Client *c = iter->data; if (c->screen != s) continue; if (c->is_dock) { if (is_visible) { #ifdef VWM if (is_fixed(c) || (c->vdesk == s->vdesk)) { #endif client_show(c); client_raise(c); #ifdef VWM } #endif } else { client_hide(c); } } } LOG_LEAVE(); } ScreenInfo *find_screen(Window root) { int i; for (i = 0; i < num_screens; i++) { if (screens[i].root == root) return &screens[i]; } return NULL; } ScreenInfo *find_current_screen(void) { Window cur_root, dw; int di; unsigned int dui; /* XQueryPointer is useful for getting the current pointer root */ XQueryPointer(dpy, screens[0].root, &cur_root, &dw, &di, &di, &di, &di, &dui); return find_screen(cur_root); } static void grab_keysym(Window w, unsigned int mask, KeySym keysym) { KeyCode keycode = XKeysymToKeycode(dpy, keysym); XGrabKey(dpy, keycode, mask, w, True, GrabModeAsync, GrabModeAsync); XGrabKey(dpy, keycode, mask|LockMask, w, True, GrabModeAsync, GrabModeAsync); if (numlockmask) { XGrabKey(dpy, keycode, mask|numlockmask, w, True, GrabModeAsync, GrabModeAsync); XGrabKey(dpy, keycode, mask|numlockmask|LockMask, w, True, GrabModeAsync, GrabModeAsync); } } static KeySym keys_to_grab[] = { #ifdef VWM KEY_FIX, KEY_PREVDESK, KEY_NEXTDESK, KEY_TOGGLEDESK, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, #endif KEY_NEW, KEY_KILL, KEY_TOPLEFT, KEY_TOPRIGHT, KEY_BOTTOMLEFT, KEY_BOTTOMRIGHT, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, KEY_LOWER, KEY_ALTLOWER, KEY_INFO, KEY_MAXVERT, KEY_MAX, KEY_DOCK_TOGGLE }; #define NUM_GRABS (int)(sizeof(keys_to_grab) / sizeof(KeySym)) static KeySym alt_keys_to_grab[] = { KEY_KILL, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP }; #define NUM_ALT_GRABS (int)(sizeof(alt_keys_to_grab) / sizeof(KeySym)) void grab_keys_for_screen(ScreenInfo *s) { int i; /* Release any previous grabs */ XUngrabKey(dpy, AnyKey, AnyModifier, s->root); /* Grab key combinations we're interested in */ for (i = 0; i < NUM_GRABS; i++) { grab_keysym(s->root, grabmask1, keys_to_grab[i]); } for (i = 0; i < NUM_ALT_GRABS; i++) { grab_keysym(s->root, grabmask1 | altmask, alt_keys_to_grab[i]); } grab_keysym(s->root, grabmask2, KEY_NEXT); } evilwm-1.1.1/xconfig.c000066400000000000000000000073611255071027000146240ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #include #include #include #include #include "xconfig.h" /* Break a space-separated string into an array of strings. * Backslash escapes next character. */ static char **split_string(const char *arg) { int nelem = 0, elem = 0; char **list = NULL; char *string, *head, *tail; head = tail = string = malloc(strlen(arg) + 1); if (string == NULL) return NULL; for (;;) { if (*arg == '\\' && *(arg+1) != 0) { arg++; *(tail++) = *(arg++); } else if (*arg == 0 || isspace(*arg)) { *tail = 0; if (*head) { if ((elem + 1) >= nelem) { char **nlist; nelem += 4; nlist = realloc(list, nelem * sizeof(char *)); if (nlist == NULL) { if (list) free(list); free(string); return NULL; } list = nlist; } list[elem++] = head; tail++; head = tail; } while (isspace(*arg)) { arg++; } if (*arg == 0) { break; } } else { *(tail++) = *(arg++); } } if (elem == 0) { free(string); return NULL; } list[elem] = NULL; return list; } static struct xconfig_option *find_option(struct xconfig_option *options, const char *opt) { int i; for (i = 0; options[i].type != XCONFIG_END; i++) { if (0 == strcmp(options[i].name, opt)) { return &options[i]; } } return NULL; } static void set_option(struct xconfig_option *option, const char *arg) { switch (option->type) { case XCONFIG_BOOL: *(int *)option->dest = 1; break; case XCONFIG_INT: *(int *)option->dest = strtol(arg, NULL, 0); break; case XCONFIG_STRING: *(char **)option->dest = strdup(arg); break; case XCONFIG_STR_LIST: *(char ***)option->dest = split_string(arg); break; case XCONFIG_CALL_0: ((void (*)(void))option->dest)(); break; case XCONFIG_CALL_1: ((void (*)(const char *))option->dest)(arg); break; default: break; } } /* Simple parser: one directive per line, "option argument" */ enum xconfig_result xconfig_parse_file(struct xconfig_option *options, const char *filename) { struct xconfig_option *option; char buf[256]; char *line, *opt, *arg; FILE *cfg; cfg = fopen(filename, "r"); if (cfg == NULL) return XCONFIG_FILE_ERROR; while ((line = fgets(buf, sizeof(buf), cfg))) { while (isspace((int)*line)) line++; if (*line == 0 || *line == '#') continue; opt = strtok(line, "\t\n\v\f\r ="); if (opt == NULL) continue; option = find_option(options, opt); if (option == NULL) { fclose(cfg); return XCONFIG_BAD_OPTION; } if (option->type == XCONFIG_STR_LIST) { /* special case: spaces here mean something */ arg = strtok(NULL, "\n\v\f\r"); while (isspace(*arg) || *arg == '=') { arg++; } } else { arg = strtok(NULL, "\t\n\v\f\r ="); } set_option(option, arg); } fclose(cfg); return XCONFIG_OK; } enum xconfig_result xconfig_parse_cli(struct xconfig_option *options, int argc, char **argv, int *argn) { struct xconfig_option *option; int _argn; char *optstr; _argn = argn ? *argn : 1; while (_argn < argc) { if (argv[_argn][0] != '-') { break; } if (0 == strcmp("--", argv[_argn])) { _argn++; break; } optstr = argv[_argn]+1; if (*optstr == '-') optstr++; option = find_option(options, optstr); if (option == NULL) { if (argn) *argn = _argn; return XCONFIG_BAD_OPTION; } if (option->type == XCONFIG_BOOL || option->type == XCONFIG_CALL_0) { set_option(option, NULL); _argn++; continue; } if ((_argn + 1) >= argc) { if (argn) *argn = _argn; return XCONFIG_MISSING_ARG; } set_option(option, argv[_argn+1]); _argn += 2; } if (argn) *argn = _argn; return XCONFIG_OK; } evilwm-1.1.1/xconfig.h000066400000000000000000000013621255071027000146240ustar00rootroot00000000000000/* evilwm - Minimalist Window Manager for X * Copyright (C) 1999-2015 Ciaran Anscomb * see README for license and other details. */ #ifndef __XCONFIG_H__ #define __XCONFIG_H__ enum xconfig_result { XCONFIG_OK = 0, XCONFIG_BAD_OPTION, XCONFIG_MISSING_ARG, XCONFIG_FILE_ERROR }; enum xconfig_option_type { XCONFIG_BOOL, XCONFIG_INT, XCONFIG_STRING, XCONFIG_STR_LIST, XCONFIG_CALL_0, XCONFIG_CALL_1, XCONFIG_END }; struct xconfig_option { enum xconfig_option_type type; const char *name; void *dest; }; enum xconfig_result xconfig_parse_file(struct xconfig_option *options, const char *filename); enum xconfig_result xconfig_parse_cli(struct xconfig_option *options, int argc, char **argv, int *argn); #endif /* __XCONFIG_H__ */