pax_global_header00006660000000000000000000000064143325172320014514gustar00rootroot0000000000000052 comment=2a6888229777ae2c6acf01bce124a7e759d45786 evilwm-1.4.2/000077500000000000000000000000001433251723200130235ustar00rootroot00000000000000evilwm-1.4.2/.gitignore000066400000000000000000000003021433251723200150060ustar00rootroot00000000000000## ignore build files config.mk *.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.4.2/ChangeLog000066400000000000000000000402441433251723200146010ustar00rootroot00000000000000Version 1.4.2, Tue Nov 8 2022 * Ignore NumLock correctly when processing events [Kacper Gutowski] Version 1.4.1, Wed Nov 2 2022 * Add "raise" function to complement "lower" [Juan Pedro Vallejo] * Allow '=' in option arguments, needed for -bind [Burkhard Kleemeier] Version 1.4.0, Tue Sep 27 2022 * Send more accurate response to _NET_REQUEST_FRAME_EXTENTS. * Support arbitrary key binds and semi-arbitrary button binds. * Reread config and remanage all windows on SIGHUP. Version 1.3.1, Fri Jul 30 2021 * Documentation updates & fixes only. Version 1.3.0, Fri Jul 16 2021 * Add EWMH 1.5 _NET_WM_STATE_FOCUSED support. * Add basic multi-monitor support when Randr >= 1.5 available. * Remove border when window is maximised (useful for multi-monitor). * Set _NET_FRAME_EXTENTS whenever relevant. * Add new -wholescreen option to reinstate old behaviour of treating screen as one large display. * Remove -DVWM build option. User can run with "-numvdesks 1" if necessary. Version 1.2.1, Mon Jul 5 2021 * Fix QWERTZ_KEYMAP build option [Sime Ramov] Version 1.2.0, Mon Jul 5 2021 * Code reorder. Better code separation and many more/better comments. * Don't focus mapped window if pointer not on same screen [by Ben Stern] * Added QWERTZ_KEYMAP build option [by Thomas Egerer] * Remove option to exclude help and error messages. * Set focus to root window when closing current [adapted from Arch patch] * Add tiny configure script to help with Debian multiarch cross-builds. * Generate some documentation from HTML source. Version 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.4.2/INSTALL000066400000000000000000000061751433251723200140650ustar00rootroot00000000000000EVILWM INSTALLATION GUIDE Building from source See the evilwm home page (https://www.6809.org.uk/evilwm/) for downloads. Once you have a copy of the source code, either the git repository or by downloading a tar archive, building is very straightforward. In addition to the standard C compiler tools, you will need X11 development files installed. Under Debian, the x11proto-dev, libx11-dev and libxrandr-dev packages should suffice. The source distribution does contain a configure script, but this is not from the GNU build system. It is a minimal bash script provided to simplify multiarch cross-builds under Debian. Instead, edit the Makefile to modify build flags for your platform. Then, building and installing is very simple: $ make $ make install That's it! But if you need to install to a different prefix, you can override with something like: $ make install prefix=/usr/local Starting evilwm The install process puts a file called evilwm.desktop into /usr/share/applications, so depending on your desktop manager, you may simply be able to pick evilwm from a menu. Otherwise, most managers will run Xsession, which will look for a file in your home directory called .xsession and run it (so be sure it has execute permission). Here's a simple example .xsession file: #!/bin/sh test -f $HOME/.Xresources && xrdb -merge $HOME/.Xresources xsetroot -solid \#400040 -cursor_name left_ptr ( /usr/bin/evilwm -snap 10 >/dev/null 2>&1 & ) exec xclock -digital -padding 2 -g -0+0 The cursor shape and background colour are set with standard X tools (evilwm won't do this for you). xclock becomes the "magic process" (session terminates when it is killed). evilwm itself is started in a subshell otherwise xclock would become its parent, and killing evilwm to restart it may result in a zombie process. You could also avoid this by not exec-ing the "magic process"; the shell will handle child sig- nals properly. See here for a more complete example of a .xsession file (xses- sion.txt). Obviously, it depends on a lot of external packages. You used to be able to configure the GNOME session manager to use evilwm as its window manager by running gconf-editor and setting the key /desktop/gnome/session/required_components/windowmanager to evilwm. The package information for gconf-editor suggests this approach is out of date, however. If you typically start X by typing startx from the console, you might need the .xsession file to be called .xinitrc. A symlink should suf- fice. After starting, you should be able to bring up an xterm with Con- trol+Alt+Enter. For more information on configuring and using evilwm, see the manual (https://www.6809.org.uk/evilwm/manual.shtml). evilwm-1.4.2/Makefile000066400000000000000000000127331433251723200144710ustar00rootroot00000000000000############################################################################ # evilwm - minimalist window manager for X11 # Copyright (C) 1999-2022 Ciaran Anscomb # see README for license and other details. # 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 # Note: some options to reconfigure keyboard mappings have been removed, as the # "-bind" option should allow that at runtime. # Uncomment to enable use of sqrt() function in monitor distance calculations. OPT_CPPFLAGS += -DHAVE_MATH_H OPT_LDLIBS += -lm # 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 move pointer around on certain actions. #OPT_CPPFLAGS += -DWARP_POINTER # Uncomment to include whatever debugging messages I've left in this release. #OPT_CPPFLAGS += -DDEBUG # miscellaneous debugging #OPT_CPPFLAGS += -DXDEBUG # show some X calls OPT_CPPFLAGS += -DNDEBUG # disable asserts # Uncomment to map KEY_TOPLEFT to XK_z (suitable for quertz keyboards) #OPT_CPPFLAGS += -DQWERTZ_KEYMAP ############################################################################ # 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 #OPT_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__ # OpenBSD 6.2 #OPT_CPPFLAGS += -I/usr/X11R6/include #OPT_LDFLAGS += -L/usr/X11R6/lib # Mac OS X: #OPT_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 INSTALL = install STRIP = strip INSTALL_DIR = $(INSTALL) -d -m 0755 INSTALL_FILE = $(INSTALL) -m 0644 INSTALL_PROGRAM = $(INSTALL) -m 0755 # If you do not use GNU Make, you may need to comment out this line (and the # output from 'configure' will not be used): -include config.mk ############################################################################ # You shouldn't need to change anything beyond this point version = 1.4.2 distdir = evilwm-$(version) # Generally shouldn't be overridden: # _XOPEN_SOURCE=700 incorporates POSIX.1-2008, for putenv, sigaction and strdup EVILWM_CPPFLAGS = $(CPPFLAGS) $(OPT_CPPFLAGS) -DVERSION=\"$(version)\" \ -D_XOPEN_SOURCE=700 -DHAVE_CONFIG_H EVILWM_CFLAGS = -std=c99 $(CFLAGS) $(WARN) EVILWM_LDFLAGS = $(LDFLAGS) EVILWM_LDLIBS = -lX11 $(OPT_LDLIBS) $(LDLIBS) HEADERS = bind.h client.h config.h display.h events.h evilwm.h func.h \ list.h log.h screen.h util.h xalloc.h xconfig.h OBJS = bind.o client.o client_move.o client_new.o display.o events.o ewmh.o \ func.o list.o log.o main.o screen.o util.o xconfig.o xmalloc.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: install-strip install-strip: install $(STRIP) $(DESTDIR)$(bindir)/evilwm$(EXEEXT) .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=$(distdir)/ HEAD > $(distdir).tar gzip -f9 $(distdir).tar .PHONY: debuild debuild: dist -cd ..; rm -rf $(distdir)/ $(distdir).orig/ mv $(distdir).tar.gz ../evilwm_$(version).orig.tar.gz cd ..; tar xfz evilwm_$(version).orig.tar.gz rsync -axH debian --exclude='debian/.git/' --exclude='debian/_darcs/' ../$(distdir)/ cd ../$(distdir); debuild .PHONY: clean clean: rm -f evilwm$(EXEEXT) $(OBJS) .PHONY: distclean distclean: clean rm -f config.mk evilwm-1.4.2/README000066400000000000000000000331631433251723200137110ustar00rootroot00000000000000evilwm(1) General Commands Manual evilwm(1) NAME evilwm--minimalist window manager for X11 SYNOPSIS evilwm [OPTION]... DESCRIPTION evilwm is a minimalist window manager for the X Window System. It features plenty of reconfigurable mouse and keyboard controls while providing a clean display, uncluttered by less useful window furniture (like title bars). OPTIONS --display display specifies the X display to run on. Usually this can be inferred from the DISPLAY environment variable. --term termprog 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. --fn fontname specify a font to use when resizing or displaying window titles. --fg colour --fc colour --bg colour frame colour of currently active, fixed active, and inactive windows respectively. Either specify an X11 colour name like goldenrod, or a hex triplet like #DAA520. --bw borderwidth width of window borders in pixels. --snap distance enable snap-to-border support. distance is the proximity in pixels to snap to. --wholescreen ignore monitor geometry and use the whole screen dimensions. This is the old behaviour from before multi-monitor support was implemented, and may still be useful, eg when one large monitor is driven from multiple outputs. --numvdesks value number of virtual desktops to provide. Defaults to 8. Any extras will only be accessible by pagers or using Control+Alt+(Left/Right). --nosoliddrag draw a window outline while moving or resizing. --mask1 modifier[+modifier]... --mask2 modifier[+modifier]... --altmask modifier[+modifier]... override the default keyboard modifiers used to grab keys for window manager functionality. mask1 is used for most keyboard controls (default: control+alt), and mask2 is used for mouse button controls and cycling windows (default: alt). altmask is used to modify the behaviour of certain controls (default: shift). Modifiers may be separated with + signs. Valid modifiers are 'shift', 'control', 'alt', 'mod1'...'mod5'. --bind key[+modifier]...=[function[,flag]...] bind a key pressed with specified modifiers to a window manager function. key is an X11 keysym name, modifiers are as above, but may also include 'mask1', 'mask2' and 'altmask' to refer to the globally-configured combinations. See FUNCTIONS for a list of available functions and the flags they recognise. If function is empty, a bind is removed. --bind button=[function[,flag]...] bind a mouse button to a window manager function. While modifiers can be specified, they will be ignored; the button on its own will trigger if pressed within a window's frame, or with 'mask2' held anywhere within a window. Function and flags is as with key binds above. Valid buttons are 'button1'...'button5'. --app name/class match an application by instance name and class (for help in finding these, use the xprop tool to extract the WM_CLASS property). Subsequent --geometry, --dock, --vdesk and --fixed options will apply to this match. -g, --geometry geometry apply a geometry (using a standard X geometry string) to applications matching the last --app. --dock specify that application should be considered to be a dock, even if it lacks the appropriate property. -v, --vdesk vdesk specify a default virtual desktop for applications matching the last --app. Note that virtual desktops are numbered from zero. -f, --fixed specify that application is to start with a fixed client window. -h, --help show help -V, --version show program version evilwm will also read options, one per line, from a file called .evilwmrc in the user's home directory. Options listed in a configuration file should omit the leading dash(es). Options specified on the command line override those found in the configuration file. USAGE In 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 (unless it is fixed, in which case blue), with other windows left as a dark grey. You can use the mouse to manipulate windows either by click/dragging the single-pixel border (easier when they align with a screen edge), or by holding down Alt and doing so anywhere in the 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 terminal. Escape Delete current window. Hold Shift as well to force kill a client. 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 the top-left, top-right, bottom-left or bottom- right of the current monitor. I Show information about current window. Equals Maximise current window vertically on current monitor (toggle). Holding Shift toggles horizontal maximization. X Maximise current window to current monitor (toggle). D Toggle visible state of docks, eg pagers and launch bars. If compiled with virtual desktop support, these functions are also available: F Fix or unfix current window. Fixed windows remain visible when you switch virtual desktop. 1--8 Switch to specific virtual desktop (internally, desktops are numbered from zero, so this actually switches to desktops 0--7; this only becomes important if you use application matching). Left Switch to next lower numbered virtual desktop. Right Switch to next higher numbered virtual desktop. A Switch to the previously selected virtual desktop. In addition to the above, Alt+Tab can be used to cycle through windows. To make evilwm reread its config, send a HUP signal to the process. To make it quit, kill it, ie send a TERM signal. FUNCTIONS The keyboard and mouse button controls can be configured with the --bind option to a number of built-in functions. Typically, these functions respond to an additional set of flags that modify their behaviour. delete Delete a window. This is the co-operative way of closing applications, as it sends the client a signal indicating that they should shut down. dock When called with the toggle flag, toggles visibility of any window claiming to be a dock. fix With the toggle flag, toggle whether a window is fixed (visible on all virtual desktops) or not. info Shows extra information about the current window for as long as the key is held. kill Kill a window. A more forceful way of closing an application if it is not responding to delete requests. lower Lower the current window. move When bound to a button, moves a window with the mouse. When bound to a key, if the relative flag is specified, moves a window in the direction indicated by other flags: up, down, left or right. Without the relative flag, moves a window in the direction specified by other flag to the edge of the monitor. next Cycle to the next window. raise Raises the current window. resize When bound to a button, resizes a window with the mouse. When bound to a key, if the relative flag is specified, modifies the width or height of the window as indicated by other flags: up (reduce height), down (increase height), left (reduce width) or right (increase width). If instead the toggle flag is specified, maximises along axes specified by other flags: horizontal, vertical or both. spawn Start a terminal. vdesk With the toggle flag specified, switch to the previously visible vdesk. With the relative flag set, either increase vdesk number (with up flag) or decrease it (with down flag). If neither flag is specified, a numerical argument indicates which vdesk to switch to. DEFAULT BINDS These are the default lists of modifiers, button and keyboard binds. The built-in binds use the globally-configurable modifier combinations 'mask1', 'mask2' and 'altmask', making a sweeping change to a different modifier combination easy. Note that 'mod1' typically refers to the Alt key. Modifiers mask1 control+mod1 mask2 mod1 altmask shift Button binds bind button1=move bind button2=resize bind button3=lower Keyboard binds bind mask1+Return=spawn bind mask1+Escape=delete bind mask1+altmask+Escape=kill bind mask1+Insert=lower bind mask1+KP_Insert=lower bind mask1+i=info bind mask2+Tab=next bind mask1+h=move,relative+left bind mask1+j=move,relative+down bind mask1+k=move,relative+up bind mask1+l=move,relative+right bind mask1+y=move,top+left bind mask1+u=move,top+right bind mask1+b=move,bottom+left bind mask1+n=move,bottom+right bind mask1+altmask+h=resize,relative+left bind mask1+altmask+j=resize,relative+down bind mask1+altmask+k=resize,relative+up bind mask1+altmask+l=resize,relative+right bind mask1+equal=resize,toggle+v bind mask1+altmask+equal=resize,toggle+h bind mask1+x=resize,toggle+v+h bind mask1+d=dock,toggle bind mask1+f=fix,toggle bind mask1+1=vdesk,0 bind mask1+2=vdesk,1 bind mask1+3=vdesk,2 bind mask1+4=vdesk,3 bind mask1+5=vdesk,4 bind mask1+6=vdesk,5 bind mask1+7=vdesk,6 bind mask1+8=vdesk,7 bind mask1+Left=vdesk,relative+down bind mask1+Right=vdesk,relative+up bind mask1+a=vdesk,toggle FILES $HOME/.evilwmrc LICENCE Copyright (C) 1999-2022 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 fol- low. 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, pro- vided 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. SEE ALSO xterm (1), xprop (1) evilwm-1.4 October 2022 evilwm(1) evilwm-1.4.2/TODO000066400000000000000000000020021433251723200135050ustar00rootroot00000000000000# evilwm TODO ## Simpler tasks Consider determining initial vdesk from focussed window. Allow specifying a matrix for vdesk organisation, e.g., "-numvdesks 4x2". Add C+A+Up/Down controls to navigate them. ## Intermediate tasks Investigate SHAPEd windows for client sweep/drag. As it stands, non-solid drags and all sweeps draw a border by inverting pixels on the screen, erasing by re-inverting. This requires the X server to be grabbed, which is undesirable. Although the current methods work well enough, it would be nice to not have to grab the X server so often. e.g., when adding or removing a client, maintain a set of windows that the error handler should treat differently. RandR-based multi-monitor support has now been implemented, but it would be nice if, when a client is moved due to removing a monitor, it could be moved back to its original position if the _same_ monitor is reattached. Whether a monitor is the "same" is probably just down to whether it occupies the same geometry on the screen. evilwm-1.4.2/bind.c000066400000000000000000000326041433251723200141100ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Keyboard and button function definitions // // Static lists of binds for now, but the point of having them like this is to // enable configuration of key and button binds. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "bind.h" #include "client.h" #include "display.h" #include "evilwm.h" #include "func.h" #include "list.h" #include "log.h" #include "screen.h" #include "util.h" #include "xalloc.h" // Configurable modifier bits. For client operations, grabmask2 is always // allowed for button presses. #define KEY_STATE_MASK ( ShiftMask | ControlMask | Mod1Mask | \ Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask ) #define BUTTON_STATE_MASK ( KEY_STATE_MASK & ~grabmask2 ) // Map modifier name to mask struct name_to_modifier name_to_modifier[] = { // The order of the first three entries here is important, as they are // referenced directly: { "mask1", ControlMask|Mod1Mask }, { "mask2", Mod1Mask }, { "altmask", ShiftMask }, { "shift", ShiftMask }, { "control", ControlMask }, { "ctrl", ControlMask }, { "alt", Mod1Mask }, { "mod1", Mod1Mask }, { "mod2", Mod2Mask }, { "mod3", Mod3Mask }, { "mod4", Mod4Mask }, { "mod5", Mod5Mask } }; #define NUM_NAME_TO_MODIFIER (int)(sizeof(name_to_modifier) / sizeof(name_to_modifier[0])) // Map button name to identifier struct name_to_button { const char *name; unsigned button; }; static struct name_to_button name_to_button[] = { { "button1", Button1 }, { "button2", Button2 }, { "button3", Button3 }, { "button4", Button4 }, { "button5", Button5 }, }; #define NUM_NAME_TO_BUTTON (int)(sizeof(name_to_button) / sizeof(name_to_button[0])) // All bindable functions present the same call interface. 'sptr' should point // to a relevant data structure (controlled by flags FL_SCREEN or FL_CLIENT). typedef void (*func_dispatch)(void *sptr, XEvent *e, unsigned flags); // Maps a user-specifiable function name to internal function and the base // flags required to call it, including whether 'sptr' should be (struct screen // *) or (struct client *). Futher flags may be ORed in. struct function_def { const char *name; func_dispatch func; unsigned flags; }; static struct function_def name_to_func[] = { { "delete", func_delete, FL_CLIENT|0 }, { "kill", func_delete, FL_CLIENT|1 }, { "dock", func_dock, FL_SCREEN }, { "info", func_info, FL_CLIENT }, { "lower", func_lower, FL_CLIENT }, { "move", func_move, FL_CLIENT }, { "next", func_next, 0 }, { "raise", func_raise, FL_CLIENT }, { "resize", func_resize, FL_CLIENT }, { "spawn", func_spawn, 0 }, { "vdesk", func_vdesk, FL_SCREEN }, { "fix", func_vdesk, FL_CLIENT }, }; #define NUM_NAME_TO_FUNC (int)(sizeof(name_to_func) / sizeof(name_to_func[0])) // Map flag name to flag struct name_to_flags { const char *name; unsigned flags; }; static struct name_to_flags name_to_flags[] = { { "up", FL_UP }, { "down", FL_DOWN }, { "left", FL_LEFT }, { "right", FL_RIGHT }, { "top", FL_TOP }, { "bottom", FL_BOTTOM }, { "relative", FL_RELATIVE }, { "rel", FL_RELATIVE }, { "toggle", FL_TOGGLE }, { "vertical", FL_VERT }, { "v", FL_VERT }, { "horizontal", FL_HORZ }, { "h", FL_HORZ }, }; #define NUM_NAME_TO_FLAGS (int)(sizeof(name_to_flags) / sizeof(name_to_flags[0])) // Lists of built-in binds. static struct { const char *ctl; const char *func; } control_builtins[] = { // Move client { "mask1+k", "move,relative+up" }, { "mask1+j", "move,relative+down" }, { "mask1+h", "move,relative+left" }, { "mask1+l", "move,relative+right" }, #ifndef QWERTZ_KEYMAP { "mask1+y", "move,top+left" }, #else { "mask1+z", "move,top+left" }, #endif { "mask1+u", "move,top+right" }, { "mask1+b", "move,bottom+left" }, { "mask1+n", "move,bottom+right" }, // Resize client { "mask1+altmask+k", "resize,relative+up" }, { "mask1+altmask+j", "resize,relative+down" }, { "mask1+altmask+h", "resize,relative+left" }, { "mask1+altmask+l", "resize,relative+right" }, { "mask1+x", "resize,toggle+v+h" }, { "mask1+equal", "resize,toggle+v" }, { "mask1+altmask+equal", "resize,toggle+h" }, // Client misc { "mask1+Escape", "delete" }, { "mask1+altmask+Escape", "kill" }, { "mask1+i", "info" }, { "mask1+Insert", "lower" }, { "mask1+KP_Insert", "lower" }, { "mask2+Tab", "next" }, { "mask1+Return", "spawn" }, { "mask1+f", "fix,toggle" }, // Virtual desktops { "mask1+1", "vdesk,0" }, { "mask1+2", "vdesk,1" }, { "mask1+3", "vdesk,2" }, { "mask1+4", "vdesk,3" }, { "mask1+5", "vdesk,4" }, { "mask1+6", "vdesk,5" }, { "mask1+7", "vdesk,6" }, { "mask1+8", "vdesk,7" }, { "mask1+a", "vdesk,toggle" }, { "mask1+Left", "vdesk,relative+down" }, { "mask1+Right", "vdesk,relative+up" }, // Screen misc { "mask1+d", "dock,toggle" }, // Button controls { "button1", "move" }, { "button2", "resize" }, { "button3", "lower" }, }; #define NUM_CONTROL_BUILTINS (int)(sizeof(control_builtins) / sizeof(control_builtins[0])) struct bind { // KeyPress or ButtonPress int type; // Bound key or button union { KeySym key; unsigned button; } control; // Modifier state unsigned state; // Dispatch function func_dispatch func; // Control flags unsigned flags; }; static struct list *controls = NULL; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // String-to-value mapping helper functions static struct name_to_modifier *modifier_by_name(const char *name) { for (int i = 0; i < NUM_NAME_TO_MODIFIER; i++) { if (!strcmp(name_to_modifier[i].name, name)) { return &name_to_modifier[i]; } } return NULL; } static unsigned button_by_name(const char *name) { for (int i = 0; i < NUM_NAME_TO_BUTTON; i++) { if (!strcmp(name_to_button[i].name, name)) { return name_to_button[i].button; } } return 0; } static struct function_def *func_by_name(const char *name) { for (int i = 0; i < NUM_NAME_TO_FUNC; i++) { if (!strcmp(name_to_func[i].name, name)) { return &name_to_func[i]; } } return NULL; } static unsigned flags_by_name(const char *name) { for (int i = 0; i < NUM_NAME_TO_FLAGS; i++) { if (!strcmp(name_to_flags[i].name, name)) { return name_to_flags[i].flags; } } return 0; } // Manage list of binds void bind_reset(void) { // unbind _all_ controls while (controls) { struct bind *b = controls->data; controls = list_delete(controls, b); free(b); } // then rebind what's configured for (int i = 0; i < NUM_CONTROL_BUILTINS; i++) { bind_control(control_builtins[i].ctl, control_builtins[i].func); } } void bind_modifier(const char *modname, const char *modstr) { if (!modstr) return; struct name_to_modifier *destm = modifier_by_name(modname); if (!destm) return; destm->value = 0; // parse modstr char *moddup = xstrdup(modstr); for (char *tmp = strtok(moddup, ",+"); tmp; tmp = strtok(NULL, ",+")) { // is this a modifier? struct name_to_modifier *m = modifier_by_name(tmp); if (m) { destm->value |= m->value; continue; } } free(moddup); if (!strcmp(modname, "altmask")) altmask = destm->value; } void bind_control(const char *ctlname, const char *func) { // Parse control string char *ctldup = xstrdup(ctlname); if (!ctldup) return; struct bind *newbind = xmalloc(sizeof(*newbind)); *newbind = (struct bind){0}; for (char *tmp = strtok(ctldup, ",+"); tmp; tmp = strtok(NULL, ",+")) { // is this a modifier? struct name_to_modifier *m = modifier_by_name(tmp); if (m) { newbind->state |= m->value; continue; } // only consider first key or button listed if (newbind->type) continue; // maybe it's a button? unsigned b = button_by_name(tmp); if (b) { newbind->type = ButtonPress; newbind->control.button = b; continue; } // ok, see if it's recognised as a key name KeySym k = XStringToKeysym(tmp); if (k != NoSymbol) { newbind->type = KeyPress; newbind->control.key = k; continue; } } free(ctldup); // No known control type? Abort. if (!newbind->type) { free(newbind); return; } // always unbind any existing matching control for (struct list *l = controls; l; l = l->next) { struct bind *b = l->data; if (newbind->type == KeyPress && b->state == newbind->state && b->control.key == newbind->control.key) { controls = list_delete(controls, b); free(b); break; } if (newbind->type == ButtonPress && b->state == newbind->state && b->control.button == newbind->control.button) { controls = list_delete(controls, b); free(b); break; } } // empty function definition implies unbind. already done, so return. if (!func || *func == 0) { free(newbind); return; } // parse the second string for function & flags char *funcdup = xstrdup(func); if (!funcdup) return; for (char *tmp = strtok(funcdup, ",+"); tmp; tmp = strtok(NULL, ",+")) { // function name? struct function_def *fn = func_by_name(tmp); if (fn) { newbind->func = fn->func; newbind->flags = fn->flags; continue; } // a simple number? if (*tmp >= '0' && *tmp <= '9') { newbind->flags &= ~FL_VALUEMASK; newbind->flags |= strtol(tmp, NULL, 0) & FL_VALUEMASK; continue; } // treat it as a flag name then newbind->flags |= flags_by_name(tmp); } if (newbind->func) { controls = list_prepend(controls, newbind); } else { free(newbind); } free(funcdup); } static void grab_keysym(KeySym keysym, unsigned modmask, Window w) { KeyCode keycode = XKeysymToKeycode(display.dpy, keysym); XGrabKey(display.dpy, keycode, modmask, w, True, GrabModeAsync, GrabModeAsync); XGrabKey(display.dpy, keycode, modmask|LockMask, w, True, GrabModeAsync, GrabModeAsync); if (numlockmask) { XGrabKey(display.dpy, keycode, modmask|numlockmask, w, True, GrabModeAsync, GrabModeAsync); XGrabKey(display.dpy, keycode, modmask|numlockmask|LockMask, w, True, GrabModeAsync, GrabModeAsync); } } static void grab_button(unsigned button, unsigned modifiers, Window w) { XGrabButton(display.dpy, button, modifiers, w, False, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeSync, None, None); XGrabButton(display.dpy, button, modifiers|LockMask, w, False, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeSync, None, None); if (numlockmask) { XGrabButton(display.dpy, button, modifiers|numlockmask, w, False, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeSync, None, None); XGrabButton(display.dpy, button, modifiers|numlockmask|LockMask, w, False, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeSync, None, None); } } void bind_grab_for_screen(struct screen *s) { // Release any previous grabs XUngrabKey(display.dpy, AnyKey, AnyModifier, s->root); for (struct list *l = controls; l; l = l->next) { struct bind *b = l->data; if (b->type == KeyPress) { grab_keysym(b->control.key, b->state, s->root); } } } void bind_grab_for_client(struct client *c) { // Button binds way less configurable than key binds for now. // Modifiers in the bind are ignored, and we ONLY use 'mask2' and // 'mask2+altmask'. for (struct list *l = controls; l; l = l->next) { struct bind *b = l->data; if (b->type == ButtonPress) { grab_button(b->control.button, grabmask2, c->parent); grab_button(b->control.button, grabmask2|altmask, c->parent); } } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Handle keyboard events. void bind_handle_key(XKeyEvent *e) { KeySym key = XkbKeycodeToKeysym(display.dpy, e->keycode, 0, 0); for (struct list *l = controls; l; l = l->next) { struct bind *bind = l->data; if (bind->type == KeyPress && key == bind->control.key && (e->state & KEY_STATE_MASK & ~numlockmask) == (bind->state & ~numlockmask)) { void *sptr = NULL; if (bind->flags & FL_CLIENT) { sptr = current; if (!sptr) return; } else if (bind->flags & FL_SCREEN) { sptr = find_current_screen(); if (!sptr) return; } bind->func(sptr, (XEvent *)e, bind->flags); break; } } } // Handle mousebutton events. void bind_handle_button(XButtonEvent *e) { for (struct list *l = controls; l; l = l->next) { struct bind *bind = l->data; if (bind->type == ButtonPress && e->button == bind->control.button && (e->state & BUTTON_STATE_MASK & ~numlockmask) == (bind->state & ~numlockmask)) { void *sptr = NULL; if (bind->flags & FL_CLIENT) { sptr = find_client(e->window); } else if (bind->flags & FL_SCREEN) { sptr = find_current_screen(); } if (!sptr) return; bind->func(sptr, (XEvent *)e, bind->flags); break; } } } evilwm-1.4.2/bind.h000066400000000000000000000024671433251723200141210ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Maps keyboard and button controls to window manager functions. Handles // keypress and buttonpress events. #ifndef EVILWM_BIND_H_ #define EVILWM_BIND_H_ #include #include struct client; struct screen; // Modifier binds are kept in an array of mappings: struct name_to_modifier { const char *name; unsigned value; }; extern struct name_to_modifier name_to_modifier[]; // These modifiers are currently used explicitly in button-based controls, and // are not reconfigurable beyond changing the modifier bind. #define grabmask2 (name_to_modifier[1].value) #define altmask (name_to_modifier[2].value) // Reset list of binds to the built-ins void bind_reset(void); // Alter modifier by name - only used for mask1, mask2, altmask void bind_modifier(const char *modname, const char *modspec); // Bind a control to a function + flags void bind_control(const char *ctlspec, const char *funcspec); // Apply grabs relevant to screen void bind_grab_for_screen(struct screen *s); // Apply grabs relevant to client void bind_grab_for_client(struct client *c); void bind_handle_key(XKeyEvent *e); void bind_handle_button(XButtonEvent *e); #endif evilwm-1.4.2/client.c000066400000000000000000000403641433251723200144540ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Client management. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_MATH_H #include #endif #include #include #include #ifdef SHAPE #include #endif #include "client.h" #include "display.h" #include "evilwm.h" #include "ewmh.h" #include "list.h" #include "log.h" #include "screen.h" #include "util.h" // Client tracking information struct list *clients_tab_order = NULL; struct list *clients_mapping_order = NULL; struct list *clients_stacking_order = NULL; struct client *current = NULL; // Get WM_NORMAL_HINTS property. Populates appropriate parts of the client // structure and returns the hint flags (which indicates whether sizes or // positions were user- or program-specified). long get_wm_normal_hints(struct client *c) { long flags; long dummy; XSizeHints *size = XAllocSizeHints(); LOG_XENTER("XGetWMNormalHints(window=%lx)", (unsigned long)c->window); XGetWMNormalHints(display.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; } // Determine EWMH "window type" and update client flags accordingly. The only // windows we currently treat any differently are docks. void get_window_type(struct client *c) { unsigned type = ewmh_get_net_wm_window_type(c->window); update_window_type_flags(c, type); } void update_window_type_flags(struct client *c, unsigned type) { c->is_dock = (type & EWMH_WINDOW_TYPE_DOCK) ? 1 : 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Managed windows are all reparented, so most client operations act on the // parent window. // find_client() is used all over the place. Return the client that has // specified window as either window or parent. NULL if not found. struct client *find_client(Window w) { for (struct list *iter = clients_tab_order; iter; iter = iter->next) { struct client *c = iter->data; if (w == c->parent || w == c->window) return c; } return NULL; } static int imin(int a, int b) { return (a < b) ? a : b; } static int imax(int a, int b) { return (a > b) ? a : b; } // Determine which monitor to consider "closest" for the client. This is // either largest intersection ratio or, if no intersection with any monitor // (ie not visible), closest mid points. // // Largest ratio should mean that if one monitor is a subset of another, a // window within it will be considered to be "closest" to the smaller monitor // for the purpose of maximising, etc. // // 'intersects' is set to represent whether client intersects with any monitor. struct monitor *client_monitor(struct client *c, Bool *intersects) { int cx1 = c->x - c->border; int cy1 = c->y - c->border; int cx2 = cx1 + c->width + c->border*2; int cy2 = cy1 + c->height + c->border*2; int cmidx = (cx1 + cx2)/2; int cmidy = (cy1 + cy2)/2; struct monitor *best = NULL; Bool have_intersection = 0; double best_area_ratio = 0.0; double best_distance = 0.0;; for (int i = 0; i < c->screen->nmonitors; i++) { struct monitor *m = &c->screen->monitors[i]; int mx2 = m->x + m->width; int my2 = m->y + m->height; int iw = imax(0, imin(mx2, cx2) - imax(m->x, cx1)); int ih = imax(0, imin(my2, cy2) - imax(m->y, cy1)); int iarea = iw * ih; // XXX if you're building on a platform without any floating // point, you could consider simply returning the first monitor // for which an intersection is found. if (iarea > 0) { // Found an intersection. Higher ratio wins. double iarea_ratio = (double)iarea / (double)m->area; if (!have_intersection || iarea_ratio > best_area_ratio) { have_intersection = 1; best_area_ratio = iarea_ratio; best = m; continue; } } // Otherwise, any previous intersection trumps distance. if (have_intersection) continue; #ifdef HAVE_MATH_H // No intersections yet, calculate distance between midpoints. int mmidx = (m->x + mx2)/2; int mmidy = (m->y + my2)/2; int dx = abs(cmidx - mmidx); int dy = abs(cmidy - mmidy); double d = sqrt(dx*dx + dy*dy); if (!best || d < best_distance) { best_distance = d; best = m; } #endif } if (!best) { best = &c->screen->monitors[0]; } if (intersects) { *intersects = have_intersection; } return best; } // "Hides" the client (unmaps and flags it as iconified). Used to simulate // virtual desktops by hiding all clients not on the current vdesk. void client_hide(struct client *c) { c->ignore_unmap++; // ignore unmap so we don't remove client XUnmapWindow(display.dpy, c->parent); set_wm_state(c, IconicState); } // Show client (and flag it as normal - not iconified). Used for vdesks and // initial managing of client. void client_show(struct client *c) { XMapWindow(display.dpy, c->parent); set_wm_state(c, NormalState); } // Raise client. Maintains clients_stacking_order list and EWMH hints. void client_raise(struct client *c) { XRaiseWindow(display.dpy, c->parent); clients_stacking_order = list_to_tail(clients_stacking_order, c); ewmh_set_net_client_list_stacking(c->screen); } // Lower client. Maintains clients_stacking_order list and EWMH hints. void client_lower(struct client *c) { XLowerWindow(display.dpy, c->parent); clients_stacking_order = list_to_head(clients_stacking_order, c); ewmh_set_net_client_list_stacking(c->screen); } // Set window state. This is either NormalState (visible), IconicState // (hidden) or WithdrawnState (removing). void set_wm_state(struct 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] = { state, None }; XChangeProperty(display.dpy, c->window, X_ATOM(WM_STATE), X_ATOM(WM_STATE), 32, PropModeReplace, (unsigned char *)data, 2); } // Inform client of changed geometry by sending ConfigureNotify to its window. void send_config(struct client *c) { XEvent ev = { .xconfigure = { .type = ConfigureNotify, .event = c->window, .window = c->window, .x = c->x, .y = c->y, .width = c->width, .height = c->height, .border_width = 0, .above = None, .override_redirect = False } }; XSendEvent(display.dpy, c->window, False, StructureNotifyMask, &ev); } // Offset client to show border according to window's gravity. e.g., // SouthEastGravity will offset the client up and left by the supplied border // width. void client_gravitate(struct 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; } // Don't gravitate if client would be maximised along either axis // (unless it's offset already). if (c->x != 0 || c->width != DisplayWidth(display.dpy, c->screen->screen)) { c->x += dx; } if (c->y != 0 || c->height != DisplayHeight(display.dpy, c->screen->screen)) { c->y += dy; } } // Activate a client. Colours its border (and uncolours the // previously-selected), installs any colourmap, sets input focus and updates // EWMH properties. void select_client(struct client *c) { struct client *old_current = current; if (current) XSetWindowBorder(display.dpy, current->parent, current->screen->bg.pixel); if (c) { unsigned long bpixel; if (is_fixed(c)) bpixel = c->screen->fc.pixel; else bpixel = c->screen->fg.pixel; XSetWindowBorder(display.dpy, c->parent, bpixel); XInstallColormap(display.dpy, c->cmap); XSetInputFocus(display.dpy, c->window, RevertToPointerRoot, CurrentTime); } current = c; // Update _NET_WM_STATE_FOCUSED for old current and _NET_ACTIVE_WINDOW // on its screen root. if (old_current) ewmh_set_net_wm_state(old_current); // Now do same for new current. if (c) ewmh_set_net_wm_state(c); } // Move a client to a specific vdesk. If that means it should no longer be // visible, hide it. void client_to_vdesk(struct client *c, unsigned 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); } } // Stop managing a client. Undoes any transformations that were made when // managing it. void remove_client(struct client *c) { LOG_ENTER("remove_client(window=%lx, %s)", (unsigned long)c->window, c->remove ? "withdrawing" : "wm quitting"); // Grab the server so any X errors are guaranteed to come from our actions. XGrabServer(display.dpy); // Flag to ignore any X errors we trigger. The window may well already // have been deleted from the server, so anything we try to do to it // here would raise one. 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"); if (c == current) { XSetInputFocus(display.dpy, PointerRoot, RevertToPointerRoot, CurrentTime); } set_wm_state(c, WithdrawnState); ewmh_withdraw_client(c); } else { ewmh_remove_allowed_actions(c); } // Undo the geometry changes applied when we managed the client client_gravitate(c, -c->border); client_gravitate(c, c->old_border); c->x -= c->old_border; c->y -= c->old_border; // Reparent window back to the root XReparentWindow(display.dpy, c->window, c->screen->root, c->x, c->y); // Restore any old border XSetWindowBorderWidth(display.dpy, c->window, c->old_border); // Remove window from "save set": we are no longer its parent, so if we // die now, the window should be fine. XRemoveFromSaveSet(display.dpy, c->window); // Destroy parent window if (c->parent) { XDestroyWindow(display.dpy, c->parent); } // Remove from the client lists 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, update them: if (c->remove) { ewmh_set_net_client_list(c->screen); ewmh_set_net_client_list_stacking(c->screen); } // Deselect if this client were previously selected if (current == c) { current = NULL; // Remove _NET_WM_STATE_FOCUSED from client window and // _NET_ACTIVE_WINDOW from screen if necessary. ewmh_set_net_wm_state(c); } free(c); #ifdef DEBUG { int i = 0; for (struct list *iter = clients_tab_order; iter; iter = iter->next) i++; LOG_DEBUG("free(), window count now %d\n", i); } #endif XUngrabServer(display.dpy); XSync(display.dpy, False); ignore_xerror = 0; LOG_LEAVE(); } // Delete a window. Sends WM_DELETE_WINDOW to a client if that protocol is // found to be supported. Otherwise (or if forced by setting kill_client), use // XKillClient (terminates its connection to the server). void send_wm_delete(struct client *c, int kill_client) { _Bool delete_supported = 0; int n; Atom *protocols; if (!kill_client && XGetWMProtocols(display.dpy, c->window, &protocols, &n)) { for (int i = 0; i < n; i++) if (protocols[i] == X_ATOM(WM_DELETE_WINDOW)) delete_supported = 1; XFree(protocols); } if (delete_supported) { XEvent ev = { .xclient = { .type = ClientMessage, .window = c->window, .message_type = X_ATOM(WM_PROTOCOLS), .format = 32, .data.l = { X_ATOM(WM_DELETE_WINDOW), CurrentTime } } }; XSendEvent(display.dpy, c->window, False, NoEventMask, &ev); } else { XKillClient(display.dpy, c->window); } } #ifdef SHAPE // Query the shape "extents" applied to a window and duplicate them for the // parent. void set_shape(struct client *c) { Bool bounding_shaped; Bool b; // dummy int i; // dummy unsigned u; // dummy if (!display.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(display.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(display.dpy, c->parent, ShapeBounding, 0, 0, c->window, ShapeBounding, ShapeSet); } } #endif // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Compiling with -DINFOBANNER enables a client information window #ifdef INFOBANNER void create_info_window(struct client *c) { display.info_window = XCreateSimpleWindow(display.dpy, c->screen->root, -4, -4, 2, 2, 0, c->screen->fg.pixel, c->screen->fg.pixel); XMapRaised(display.dpy, display.info_window); update_info_window(c); } void update_info_window(struct 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 (!display.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(display.font, buf, strlen(buf)) + 2; iwinh = display.font->max_bounds.ascent + display.font->max_bounds.descent; XFetchName(display.dpy, c->window, &name); if (name) { namew = XTextWidth(display.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(display.dpy, c->screen->screen)) iwinx = DisplayWidth(display.dpy, c->screen->screen) - iwinw; if (iwinx < 0) iwinx = 0; if (iwiny + iwinh > DisplayHeight(display.dpy, c->screen->screen)) iwiny = DisplayHeight(display.dpy, c->screen->screen) - iwinh; if (iwiny < 0) iwiny = 0; XMoveResizeWindow(display.dpy, display.info_window, iwinx, iwiny, iwinw, iwinh); XClearWindow(display.dpy, display.info_window); if (name) { XDrawString(display.dpy, display.info_window, c->screen->invert_gc, 1, iwinh / 2 - 1, name, strlen(name)); XFree(name); } XDrawString(display.dpy, display.info_window, c->screen->invert_gc, 1, iwinh - 1, buf, strlen(buf)); } void remove_info_window(void) { if (display.info_window) XDestroyWindow(display.dpy, display.info_window); display.info_window = None; } #endif evilwm-1.4.2/client.h000066400000000000000000000067161433251723200144640ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Client management. // // For each managed window, a structure is held in memory collecting the // window, another window into which the original is "reparented" (parent // window), and various associated metadata. #ifndef EVILWM_CLIENT_H_ #define EVILWM_CLIENT_H_ struct list; struct screen; struct monitor; // Maximise flags #define MAXIMISE_HORZ (1<<0) #define MAXIMISE_VERT (1<<1) #define MAXIMISE_SCREEN (1<<2) // maximise to screen, not monitor // Virtual desktop macros #define VDESK_NONE (0xfffffffe) #define VDESK_FIXED (0xffffffff) #define VDESK_MAX (option.vdesks - 1) #define valid_vdesk(v) ((v) == VDESK_FIXED || (v) < option.vdesks) struct client { Window window; // actual application window Window parent; // parent window that we control struct screen *screen; // screen this client is on Colormap cmap; // colourmap to install when focussed // Virtual desktop unsigned vdesk; // Sometimes unmap events occur that we know aren't the client // disappearing, flagged here: int ignore_unmap; // Geometry int x, y, width, height; int normal_border; // normal border when unmaximised int border; // current border // Old geometry while maximising int oldx, oldy, oldw, oldh; // Old border width - only used to restore when quitting int old_border; // Old monitor offset as proportion of monitor geometry double mon_offx, mon_offy; // Flag set when we need to remove client from management int remove; // Various window metadata determined by examining properties 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 is_dock; }; // Client tracking information extern struct list *clients_tab_order; extern struct list *clients_mapping_order; extern struct list *clients_stacking_order; extern struct client *current; #define is_fixed(c) (c->vdesk == VDESK_FIXED) // client_new.c: newly manage a window void client_manage_new(Window w, struct screen *s); long get_wm_normal_hints(struct client *c); void get_window_type(struct client *c); void update_window_type_flags(struct client *c, unsigned type); // client_move.c: user window manipulation void client_resize_sweep(struct client *c, unsigned button); void client_move_drag(struct client *c, unsigned button); void client_show_info(struct client *c, XEvent *e); void client_moveresize(struct client *c); void client_moveresizeraise(struct client *c); void client_maximise(struct client *c, int action, int hv); void client_select_next(void); // client.c: various other client functions struct client *find_client(Window w); struct monitor *client_monitor(struct client *c, Bool *intersects); void client_hide(struct client *c); void client_show(struct client *c); void client_raise(struct client *c); void client_lower(struct client *c); void client_gravitate(struct client *c, int bw); void select_client(struct client *c); void client_to_vdesk(struct client *c, unsigned vdesk); void remove_client(struct client *c); void send_config(struct client *c); void send_wm_delete(struct client *c, int kill_client); void set_wm_state(struct client *c, int state); void set_shape(struct client *c); #ifdef INFOBANNER void create_info_window(struct client *c); void update_info_window(struct client *c); void remove_info_window(void); #endif #endif evilwm-1.4.2/client_move.c000066400000000000000000000346311433251723200155020ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Client management: user window manipulation #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "bind.h" #include "client.h" #include "display.h" #include "evilwm.h" #include "ewmh.h" #include "list.h" #include "screen.h" #include "util.h" #define SPACE 3 // Use the inverting graphics context to draw an outline for the client. // Drawing it a second time will erase it. If INFOBANNER_MOVERESIZE is // defined, the information window is shown for the duration (but this can be // slow on old X servers). static void draw_outline(struct client *c) { #ifndef INFOBANNER_MOVERESIZE char buf[27]; int width_inc = c->width_inc, height_inc = c->height_inc; #endif XDrawRectangle(display.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(display.dpy, c->screen->root, c->screen->invert_gc, c->x + c->width - XTextWidth(display.font, buf, strlen(buf)) - SPACE, c->y + c->height - SPACE, buf, strlen(buf)); #endif } static int absmin(int a, int b) { if (abs(a) < abs(b)) return a; return b; } // Snap a client to the edges of other clients (if on same screen, and visible) // or to the screen border. static void snap_client(struct client *c, struct monitor *monitor) { int dx, dy; int dpy_width = monitor->width; int dpy_height = monitor->height; // Snap to other windows dx = dy = option.snap; for (struct list *iter = clients_tab_order; iter; iter = iter->next) { struct client *ci = iter->data; if (ci == c) continue; if (ci->screen != c->screen) continue; if (!is_fixed(ci) && ci->vdesk != c->screen->vdesk) continue; if (ci->is_dock && !c->screen->docks_visible) continue; if (ci->y - ci->border - c->border - c->height - c->y <= option.snap && c->y - c->border - ci->border - ci->height - ci->y <= option.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 <= option.snap && c->x - c->border - ci->border - ci->width - ci->x <= option.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) < option.snap) c->x += dx; if (abs(dy) < option.snap) c->y += dy; // Snap to screen border if (abs(c->x - c->border - monitor->x) < option.snap) c->x = monitor->x + c->border; if (abs(c->y - c->border - monitor->y) < option.snap) c->y = monitor->y + c->border; if (abs(c->x + c->width + c->border - monitor->x - dpy_width) < option.snap) c->x = monitor->x + dpy_width - c->width - c->border; if (abs(c->y + c->height + c->border - monitor->y - dpy_height) < option.snap) c->y = monitor->y + dpy_height - c->height - c->border; if (abs(c->x) == monitor->x + c->border && c->width == dpy_width) c->x = monitor->x; if (abs(c->y) == monitor->y + c->border && c->height == dpy_height) c->y = monitor->y; } // During a sweep (resize interaction), recalculate new dimensions for a window // based on mouse position relative to top-left corner. static void recalculate_sweep(struct client *c, int x1, int y1, int x2, int y2, _Bool 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; } } // Handle user resizing a window with the mouse. Takes over processing X // motion events until the mouse button is released. // // Note that because of the way this draws an outline, other events are blocked // until the mouse is moved. TODO: consider using a SHAPEd window for this, // where available. void client_resize_sweep(struct client *c, unsigned button) { // Ensure we can grab pointer events. if (!grab_pointer(c->screen->root, display.resize_curs)) return; // Sweeping always raises. client_raise(c); int old_cx = c->x; int old_cy = c->y; #ifdef INFOBANNER_MOVERESIZE create_info_window(c); #endif XGrabServer(display.dpy); draw_outline(c); // draw // Warp pointer to the bottom-right of the client for resizing setmouse(c->window, c->width, c->height); for (;;) { XEvent ev; XMaskEvent(display.dpy, ButtonPressMask|ButtonReleaseMask|PointerMotionMask, &ev); switch (ev.type) { case MotionNotify: if (ev.xmotion.root != c->screen->root) break; draw_outline(c); // erase XUngrabServer(display.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(display.dpy, False); XGrabServer(display.dpy); draw_outline(c); // draw break; case ButtonRelease: if (ev.xbutton.button != button) continue; draw_outline(c); // erase XUngrabServer(display.dpy); #ifdef INFOBANNER_MOVERESIZE remove_info_window(); #endif XUngrabPointer(display.dpy, CurrentTime); client_moveresizeraise(c); // In case maximise state has changed: ewmh_set_net_wm_state(c); return; default: break; } } } // Handle user moving a window with the mouse. Takes over processing X motion // events until the mouse button is released. // // If solid drag is disabled, an outline is drawn, which leads to the same // limitations as in the sweep() function. void client_move_drag(struct client *c, unsigned button) { // Ensure we can grab pointer events. if (!grab_pointer(c->screen->root, display.move_curs)) return; // Dragging always raises. client_raise(c); // Initial pointer and window positions; new coordinates calculated // relative to these. int x1, y1; int old_cx = c->x; int old_cy = c->y; get_pointer_root_xy(c->screen->root, &x1, &y1); struct monitor *monitor = client_monitor(c, NULL); #ifdef INFOBANNER_MOVERESIZE create_info_window(c); #endif if (option.no_solid_drag) { XGrabServer(display.dpy); draw_outline(c); // draw } for (;;) { XEvent ev; XMaskEvent(display.dpy, ButtonPressMask|ButtonReleaseMask|PointerMotionMask, &ev); switch (ev.type) { case MotionNotify: if (ev.xmotion.root != c->screen->root) break; if (option.no_solid_drag) { draw_outline(c); // erase XUngrabServer(display.dpy); } c->x = old_cx + (ev.xmotion.x - x1); c->y = old_cy + (ev.xmotion.y - y1); if (option.snap && !(ev.xmotion.state & altmask)) snap_client(c, monitor); #ifdef INFOBANNER_MOVERESIZE update_info_window(c); #endif if (option.no_solid_drag) { XSync(display.dpy, False); XGrabServer(display.dpy); draw_outline(c); // draw } else { XMoveWindow(display.dpy, c->parent, c->x - c->border, c->y - c->border); send_config(c); } break; case ButtonRelease: if (ev.xbutton.button != button) continue; if (option.no_solid_drag) { draw_outline(c); // erase XUngrabServer(display.dpy); } #ifdef INFOBANNER_MOVERESIZE remove_info_window(); #endif XUngrabPointer(display.dpy, CurrentTime); if (option.no_solid_drag) { // For solid drags, the client was // moved with the mouse. For non-solid // drags, we need a final move/raise: client_moveresizeraise(c); } return; default: break; } } } // Predicate function for use with XCheckIfEvent. // // This is used to detect when a keyrelease is followed by a keypress with the // same keycode and timestamp, indicating autorepeat. 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; } // Show information window until the key used to activate (keycode) is // released. // // This routine used to disable autorepeat for the duration, but modern X // servers seem to only change the keyboard control after all keys have been // physically released, which is not so useful here. Instead, we detect when a // key release is followed by a key press with the same code and timestamp, // which indicates autorepeat. void client_show_info(struct client *c, XEvent *e) { unsigned input; if (e->type == KeyPress) { input = e->xkey.keycode; if (XGrabKeyboard(display.dpy, c->screen->root, False, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) return; } else { input = e->xbutton.button; if (!grab_pointer(c->screen->root, None)) return; } #ifdef INFOBANNER create_info_window(c); #else XGrabServer(display.dpy); draw_outline(c); #endif for (;;) { XEvent ev; if (e->type == KeyPress) { XMaskEvent(display.dpy, KeyReleaseMask, &ev); if (ev.xkey.keycode != input) continue; if (XCheckIfEvent(display.dpy, &ev, predicate_keyrepeatpress, (XPointer)&ev)) { // Autorepeat keypress detected - ignore! continue; } } else { XMaskEvent(display.dpy, ButtonReleaseMask, &ev); if (ev.xbutton.button != input) continue; } break; } #ifdef INFOBANNER remove_info_window(); #else draw_outline(c); XUngrabServer(display.dpy); #endif if (e->type == KeyPress) { XUngrabKeyboard(display.dpy, CurrentTime); } else { XUngrabPointer(display.dpy, CurrentTime); } } // Move window to (potentially updated) client coordinates. void client_moveresize(struct client *c) { XMoveResizeWindow(display.dpy, c->parent, c->x - c->border, c->y - c->border, c->width, c->height); XMoveResizeWindow(display.dpy, c->window, 0, 0, c->width, c->height); send_config(c); } // Same, but raise the client first. void client_moveresizeraise(struct client *c) { client_raise(c); client_moveresize(c); } // Maximise (or de-maximise) horizontally, vertically, or both. // // Updates EWMH properties, but also stores old dimensions in evilwm-specific // properties so that they persist across window manager restarts. void client_maximise(struct client *c, int action, int hv) { int monitor_x, monitor_y; int monitor_width, monitor_height; // Maximising to monitor or screen? if (hv & MAXIMISE_SCREEN) { monitor_x = monitor_y = 0; monitor_width = DisplayWidth(display.dpy, c->screen->screen); monitor_height = DisplayHeight(display.dpy, c->screen->screen); } else { struct monitor *monitor = client_monitor(c, NULL); monitor_x = monitor->x; monitor_y = monitor->y; monitor_width = monitor->width; monitor_height = monitor->height; } 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(display.dpy, c->window, X_ATOM(_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 = monitor_x; c->width = monitor_width; props[0] = c->oldx; props[1] = c->oldw; XChangeProperty(display.dpy, c->window, X_ATOM(_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(display.dpy, c->window, X_ATOM(_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 = monitor_y; c->height = monitor_height; props[0] = c->oldy; props[1] = c->oldh; XChangeProperty(display.dpy, c->window, X_ATOM(_EVILWM_UNMAXIMISED_VERT), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&props, 2); } } } _Bool change_border = 0; if (c->oldw && c->oldh) { // maximised - remove border if (c->border) { c->border = 0; change_border = 1; } } else { // not maximised - add border if (!c->border && c->normal_border) { c->border = c->normal_border; change_border = 1; } } if (change_border) { XSetWindowBorderWidth(display.dpy, c->parent, c->border); ewmh_set_net_frame_extents(c->window, c->border); } ewmh_set_net_wm_state(c); client_moveresizeraise(c); discard_enter_events(c); } // Find and select the "next" client, relative to the currently selected one // (basically, handle Alt+Tab). Order is most-recently-used (maintained in the // clients_tab_order list). void client_select_next(void) { struct list *newl = list_find(clients_tab_order, current); struct client *newc; 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; } while ((!is_fixed(newc) && (newc->vdesk != newc->screen->vdesk)) || (newc->is_dock && !newc->screen->docks_visible)); if (!newc) return; client_show(newc); // XXX why would it be hidden? client_raise(newc); select_client(newc); // Optionally force the pointer to jump to the newly-selected window. // I think this was the default behaviour in much earlier versions of // evilwm (possibly to generate the enter event and handle selecting // the client as a result of that), but I don't like it now. #ifdef WARP_POINTER setmouse(newc->window, newc->width + newc->border - 1, newc->height + newc->border - 1); #endif discard_enter_events(newc); } evilwm-1.4.2/client_new.c000066400000000000000000000261061433251723200153230ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Client management: manage new client. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifdef SHAPE #include #endif #include "bind.h" #include "client.h" #include "display.h" #include "evilwm.h" #include "ewmh.h" #include "list.h" #include "log.h" #include "screen.h" #include "util.h" static void init_geometry(struct client *c); static void reparent(struct client *c); // client_manage_new is called when a map request event for an unmanaged window // is handled, and on startup for all windows found. void client_manage_new(Window w, struct screen *s) { struct client *c; char *name; XClassHint *class; unsigned window_type; LOG_ENTER("client_manage_new(window=%lx)", (unsigned long)w); XGrabServer(display.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(display.dpy, w, &name); XSync(display.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(display.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(display.dpy, w); XUngrabServer(display.dpy); return; } // If allocation fails, don't crash the window manager. Just don't // manage the window. c = malloc(sizeof(struct client)); if (!c) { LOG_ERROR("out of memory allocating new client\n"); XMapWindow(display.dpy, w); XUngrabServer(display.dpy); LOG_LEAVE(); 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(display.dpy); update_window_type_flags(c, window_type); init_geometry(c); #ifdef DEBUG { int i = 0; for (struct list *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(display.dpy, c->window, ColormapChangeMask | EnterWindowMask | PropertyChangeMask); reparent(c); #ifdef SHAPE if (display.have_shape) { XShapeSelectInput(display.dpy, c->window, ShapeNotifyMask); set_shape(c); } #endif // Read name/class information for client and check against list built // with -app options. class = XAllocClassHint(); if (class) { XGetClassHint(display.dpy, w, class); for (struct list *iter = applications; iter; iter = iter->next) { struct application *a = iter->data; // Does resource name and class match? 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)))) { // Override width or height? if (a->geometry_mask & WidthValue) c->width = a->width * c->width_inc; if (a->geometry_mask & HeightValue) c->height = a->height * c->height_inc; // Override X or Y? if (a->geometry_mask & XValue) { if (a->geometry_mask & XNegative) c->x = a->x + DisplayWidth(display.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(display.dpy, s->screen)-c->height-c->border; else c->y = a->y + c->border; } // XXX better way of updating window geometry? client_moveresizeraise(c); // Force treating this app as a dock? if (a->is_dock) c->is_dock = 1; // Force app to specific vdesk? if (a->vdesk != VDESK_NONE) c->vdesk = a->vdesk; } } XFree(class->res_name); XFree(class->res_class); XFree(class); } // Set EWMH property on client advertising WM features ewmh_set_allowed_actions(c); // Update EWMH client list hints for screen 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. Otherwise, set it to // IconicState (hidden). if (is_fixed(c) || c->vdesk == s->vdesk) { client_show(c); client_raise(c); // Don't focus windows that aren't on the same display as the // pointer. if (get_pointer_root_xy(c->window, NULL, NULL) && !(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); } } else { set_wm_state(c, IconicState); } // Ensure whichever vdesk it ended up on is reflected in the EWMH hints ewmh_set_net_wm_desktop(c); LOG_LEAVE(); } // Fetches various hints to determine a window's initial geometry. static void init_geometry(struct client *c) { unsigned long nitems; XWindowAttributes attr; // Normal border size from MWM hints c->normal_border = window_normal_border(c->window); // Possible get a value for initial virtual desktop from EWMH hint unsigned long *lprop; c->vdesk = c->screen->vdesk; if ( (lprop = get_property(c->window, X_ATOM(_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); } // Get current window attributes LOG_XENTER("XGetWindowAttributes(window=%lx)", (unsigned long)c->window); XGetWindowAttributes(display.dpy, c->window, &attr); debug_window_attributes(&attr); LOG_XLEAVE(); // We remove any client border, so preserve its old value to restore on // emulator quit. c->old_border = attr.border_width; c->cmap = attr.colormap; // Default to no unmaximised width/height. c->oldw = c->oldh = 0; // If the _EVILWM_UNMAXIMISED_HORZ is present, it was previously // managed by evilwm and this property contains the unmaximised X // coordinate and width. These are unrepresented in EWMH hints, so // would otherwise not survive window manager restart. unsigned long *eprop; if ( (eprop = get_property(c->window, X_ATOM(_EVILWM_UNMAXIMISED_HORZ), XA_CARDINAL, &nitems)) ) { if (nitems == 2) { c->oldx = eprop[0]; c->oldw = eprop[1]; } XFree(eprop); } // Similarly _EVILWM_UNMAXIMISED_VERT will contain the unmaximised Y // coordinate and height. if ( (eprop = get_property(c->window, X_ATOM(_EVILWM_UNMAXIMISED_VERT), XA_CARDINAL, &nitems)) ) { if (nitems == 2) { c->oldy = eprop[0]; c->oldh = eprop[1]; } XFree(eprop); } c->border = (c->oldw && c->oldh) ? 0 : c->normal_border; // Update some client info from the WM_NORMAL_HINTS property. The // flags returned will indicate whether certain values were user- or // program-specified. long size_flags = get_wm_normal_hints(c); _Bool need_send_config = 0; // If the current window dimensions conform to the minimums specified // in WM_NORMAL_HINTS, use them. Otherwise, use the mimimums. if ((attr.width >= c->min_width) && (attr.height >= c->min_height)) { c->width = attr.width; c->height = attr.height; } else { c->width = c->min_width; c->height = c->min_height; need_send_config = 1; } // If the window was already visible (as we manage existing windows on // startup), or if its screen position was user-specified, use its // current position. Otherwise (new window, post startup), calculate a // position for it based on where the pointer is. // XXX: if an existing window would be mapped off the screen, would it // be sensible to move it somewhere visible? if ((attr.map_state == IsViewable) || (size_flags & USPosition)) { c->x = attr.x; c->y = attr.y; } else { int xmax = DisplayWidth(display.dpy, c->screen->screen); int ymax = DisplayHeight(display.dpy, c->screen->screen); int x, y; get_pointer_root_xy(c->screen->root, &x, &y); c->x = (x * (xmax - c->border - c->width)) / xmax; c->y = (y * (ymax - c->border - c->height)) / ymax; need_send_config = 1; } if (need_send_config) send_config(c); ewmh_set_net_frame_extents(c->window, c->border); LOG_DEBUG("window started as %dx%d +%d+%d\n", c->width, c->height, c->x, c->y); // If the window was already viewable (existed while window manager // starts), that means the reparent to come would send an unmap request // to the root window. Set a flag to ignore this. if (attr.map_state == IsViewable) { c->ignore_unmap++; } // Account for removed old_border c->x += c->old_border; c->y += c->old_border; client_gravitate(c, -c->old_border); client_gravitate(c, c->border); } // Create parent window for a client and reparent. static void reparent(struct client *c) { XSetWindowAttributes p_attr; // Default border is unselected (bg) p_attr.border_pixel = c->screen->bg.pixel; // We want to handle events for this parent window p_attr.override_redirect = True; // The events we need to manage the window p_attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | ButtonPressMask | EnterWindowMask; // Create parent window, accounting for border width c->parent = XCreateWindow(display.dpy, c->screen->root, c->x-c->border, c->y-c->border, c->width, c->height, c->border, DefaultDepth(display.dpy, c->screen->screen), CopyFromParent, DefaultVisual(display.dpy, c->screen->screen), CWOverrideRedirect | CWBorderPixel | CWEventMask, &p_attr); // Adding the original window to our "save set" means that if we die // unexpectedly, the window will be reparented back to the root. XAddToSaveSet(display.dpy, c->window); // Kill any internal border on the application window XSetWindowBorderWidth(display.dpy, c->window, 0); // Reparent into our new parent window XReparentWindow(display.dpy, c->window, c->parent, 0, 0); // Map the window (shows up within parent) XMapWindow(display.dpy, c->window); // Grab mouse button actions on the parent window bind_grab_for_client(c); } evilwm-1.4.2/config.h000066400000000000000000000005611433251723200144430ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // This file is not generated, it is used to sanity check compile options. // INFOBANNER_MOVERESIZE depends on INFOBANNER #if defined(INFOBANNER_MOVERESIZE) && !defined(INFOBANNER) # define INFOBANNER #endif evilwm-1.4.2/configure000077500000000000000000000015231433251723200147330ustar00rootroot00000000000000#!/bin/bash # THIS IS NOT GNU CONFIGURE # It is a reasonably minimal 'configure' script that can do just enough to # enable multiarch cross-builds in Debian. unset host unset prefix #unset CFLAGS LDFLAGS while test -n "$1"; do opt="$1" case "$opt" in --) shift; break; ;; --host=*) host=${opt#*=}; ;; --prefix=*) prefix=${opt#*=}; ;; --mandir=*) mandir=${opt#*=}; ;; CFLAGS=*) CFLAGS=${opt#*=}; ;; LDFLAGS=*) LDFLAGS=${opt#*=}; ;; --help|-h) echo "THIS IS NOT GNU CONFIGURE"; exit 0; ;; esac shift done cat /dev/null > config.mk test -n "$host" && echo "CC = ${host}-gcc" >> config.mk test -n "$prefix" && echo "prefix = $prefix" >> config.mk test -n "$mandir" && echo "mandir = $mandir" >> config.mk test -n "$CFLAGS" && echo "CFLAGS = $CFLAGS" >> config.mk test -n "$LDFLAGS" && echo "LDFLAGS = $LDFLAGS" >> config.mk exit 0 evilwm-1.4.2/display.c000066400000000000000000000143361433251723200146430ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Display management #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #ifdef SHAPE #include #endif #ifdef RANDR #include #endif #include "client.h" #include "display.h" #include "evilwm.h" #include "ewmh.h" #include "list.h" #include "log.h" #include "screen.h" #include "util.h" #include "xalloc.h" // evilwm only supports one display at a time; this variable is global: struct display display = { 0 }; // List of atom names. Reflect any changes here in the enum in display.h. static const char *atom_list[] = { // Standard X protocol atoms "WM_STATE", "WM_PROTOCOLS", "WM_DELETE_WINDOW", "WM_COLORMAP_WINDOWS", // Motif atoms "_MOTIF_WM_HINTS", // evilwm atoms "_EVILWM_UNMAXIMISED_HORZ", "_EVILWM_UNMAXIMISED_VERT", // EWMH: Root Window Properties (and Related Messages) "_NET_SUPPORTED", "_NET_CLIENT_LIST", "_NET_CLIENT_LIST_STACKING", "_NET_NUMBER_OF_DESKTOPS", "_NET_DESKTOP_GEOMETRY", "_NET_DESKTOP_VIEWPORT", "_NET_CURRENT_DESKTOP", "_NET_ACTIVE_WINDOW", "_NET_WORKAREA", "_NET_SUPPORTING_WM_CHECK", // EWMH: Other Root Window Messages "_NET_CLOSE_WINDOW", "_NET_MOVERESIZE_WINDOW", "_NET_RESTACK_WINDOW", "_NET_REQUEST_FRAME_EXTENTS", // EWMH: Application Window Properties "_NET_WM_NAME", "_NET_WM_DESKTOP", "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_DESKTOP", "_NET_WM_WINDOW_TYPE_DOCK", "_NET_WM_WINDOW_TYPE_NOTIFICATION", "_NET_WM_STATE", "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ", "_NET_WM_STATE_HIDDEN", "_NET_WM_STATE_FULLSCREEN", "_NET_WM_STATE_FOCUSED", "_NET_WM_ALLOWED_ACTIONS", "_NET_WM_ACTION_MOVE", "_NET_WM_ACTION_RESIZE", "_NET_WM_ACTION_MAXIMIZE_HORZ", "_NET_WM_ACTION_MAXIMIZE_VERT", "_NET_WM_ACTION_FULLSCREEN", "_NET_WM_ACTION_CHANGE_DESKTOP", "_NET_WM_ACTION_CLOSE", "_NET_WM_PID", "_NET_FRAME_EXTENTS", }; // Open and initialise display. Exits the process on failure. void display_open(void) { LOG_ENTER("display_open()"); display.dpy = XOpenDisplay(option.display); if (!display.dpy) { LOG_ERROR("can't open display %s\n", option.display); exit(1); } display.info_window = None; XSetErrorHandler(handle_xerror); // While debugging, synchronous behaviour may be desirable: //XSynchronize(display.dpy, True); // Sanity check that there's a name for each atom in the enum: assert(NUM_ATOMS == (sizeof(atom_list)/sizeof(atom_list[0]))); // Get or create all required atom IDs for (int i = 0; i < NUM_ATOMS; i++) { display.atom[i] = XInternAtom(display.dpy, atom_list[i], False); } // Get the font used for window info display.font = XLoadQueryFont(display.dpy, option.font); if (!display.font) { LOG_DEBUG("failed to load specified font, trying default: %s\n", DEF_FONT); display.font = XLoadQueryFont(display.dpy, DEF_FONT); } if (!display.font) { LOG_ERROR("couldn't find a font to use: try starting with -fn fontname\n"); exit(1); } // Cursors used for different actions display.move_curs = XCreateFontCursor(display.dpy, XC_fleur); display.resize_curs = XCreateFontCursor(display.dpy, XC_plus); // Find out which modifier is NumLock - for every grab, we need to also // grab the combination where this is set. numlockmask = 0; XModifierKeymap *modmap = XGetModifierMapping(display.dpy); for (unsigned i = 0; i < 8; i++) { for (unsigned j = 0; j < (unsigned)modmap->max_keypermod; j++) { if (modmap->modifiermap[i*modmap->max_keypermod+j] == XKeysymToKeycode(display.dpy, XK_Num_Lock)) { numlockmask = (1<root, &dw1, &dw2, &wins, &nwins); LOG_XDEBUG("%u windows\n", nwins); LOG_XLEAVE(); // Manage all relevant windows for (unsigned j = 0; j < nwins; j++) { XWindowAttributes winattr; XGetWindowAttributes(display.dpy, wins[j], &winattr); // Override redirect implies a pop-up that we should ignore. // If map_state is not IsViewable, it shouldn't be shown right // now, so don't try to manage it. if (!winattr.override_redirect && winattr.map_state == IsViewable) client_manage_new(wins[j], s); } XFree(wins); } } void display_unmanage_clients(void) { while (clients_stacking_order) remove_client(clients_stacking_order->data); } evilwm-1.4.2/display.h000066400000000000000000000054321433251723200146450ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Display management. // // One evilwm process can manage one display. #ifndef EVILWM_DISPLAY_H_ #define EVILWM_DISPLAY_H_ #include #include #include struct screen; // List of atoms. Reflect any changes here in atom_list[] in display.c. enum { // Standard X protocol atoms X_ATOM_WM_STATE, X_ATOM_WM_PROTOCOLS, X_ATOM_WM_DELETE_WINDOW, X_ATOM_WM_COLORMAP_WINDOWS, // Motif atoms X_ATOM__MOTIF_WM_HINTS, // evilwm atoms X_ATOM__EVILWM_UNMAXIMISED_HORZ, X_ATOM__EVILWM_UNMAXIMISED_VERT, // EWMH: Root Window Properties (and Related Messages) X_ATOM__NET_SUPPORTED, X_ATOM__NET_CLIENT_LIST, X_ATOM__NET_CLIENT_LIST_STACKING, X_ATOM__NET_NUMBER_OF_DESKTOPS, X_ATOM__NET_DESKTOP_GEOMETRY, X_ATOM__NET_DESKTOP_VIEWPORT, X_ATOM__NET_CURRENT_DESKTOP, X_ATOM__NET_ACTIVE_WINDOW, X_ATOM__NET_WORKAREA, X_ATOM__NET_SUPPORTING_WM_CHECK, // EWMH: Other Root Window Messages X_ATOM__NET_CLOSE_WINDOW, X_ATOM__NET_MOVERESIZE_WINDOW, X_ATOM__NET_RESTACK_WINDOW, X_ATOM__NET_REQUEST_FRAME_EXTENTS, // EWMH: Application Window Properties X_ATOM__NET_WM_NAME, X_ATOM__NET_WM_DESKTOP, X_ATOM__NET_WM_WINDOW_TYPE, X_ATOM__NET_WM_WINDOW_TYPE_DESKTOP, X_ATOM__NET_WM_WINDOW_TYPE_DOCK, X_ATOM__NET_WM_WINDOW_TYPE_NOTIFICATION, X_ATOM__NET_WM_STATE, X_ATOM__NET_WM_STATE_MAXIMIZED_VERT, X_ATOM__NET_WM_STATE_MAXIMIZED_HORZ, X_ATOM__NET_WM_STATE_HIDDEN, X_ATOM__NET_WM_STATE_FULLSCREEN, X_ATOM__NET_WM_STATE_FOCUSED, X_ATOM__NET_WM_ALLOWED_ACTIONS, X_ATOM__NET_WM_ACTION_MOVE, X_ATOM__NET_WM_ACTION_RESIZE, X_ATOM__NET_WM_ACTION_MAXIMIZE_HORZ, X_ATOM__NET_WM_ACTION_MAXIMIZE_VERT, X_ATOM__NET_WM_ACTION_FULLSCREEN, X_ATOM__NET_WM_ACTION_CHANGE_DESKTOP, X_ATOM__NET_WM_ACTION_CLOSE, X_ATOM__NET_WM_PID, X_ATOM__NET_FRAME_EXTENTS, NUM_ATOMS }; #define X_ATOM(a) display.atom[X_ATOM_ ## a] struct display { // The display handle Display *dpy; // Atoms Atom atom[NUM_ATOMS]; // Font XFontStruct *font; // Cursors Cursor move_curs; Cursor resize_curs; // Extensions #ifdef SHAPE Bool have_shape; int shape_event; #endif #ifdef RANDR Bool have_randr; int randr_event_base; #endif // Screens int nscreens; struct screen *screens; // Information window #ifdef INFOBANNER Window info_window; #endif }; // evilwm only supports one display at a time; this variable is global: extern struct display display; // Open and initialise display. Exits the process on failure. void display_open(void); // Close display. void display_close(void); // Manage all relevant windows. void display_manage_clients(void); // Remove all windows from management. void display_unmanage_clients(void); #endif evilwm-1.4.2/doc/000077500000000000000000000000001433251723200135705ustar00rootroot00000000000000evilwm-1.4.2/doc/.gitignore000066400000000000000000000000661433251723200155620ustar00rootroot00000000000000/evilwm.1 /evilwm.pdf /evilwm.txt /build.1 /build.txt evilwm-1.4.2/doc/Makefile000066400000000000000000000007751433251723200152410ustar00rootroot00000000000000.PHONY: all all: evilwm.pdf evilwm.txt build.txt evilwm.1: manpage-header evilwm.html (cat manpage-header; hxnormalize -xe -l 99999 evilwm.html | hxpipe | ./html2man.pl) > $@ build.1: manpage-header build.html (cat manpage-header; hxnormalize -xe -l 99999 build.html | hxpipe | ./html2man.pl) > $@ evilwm.pdf: evilwm.1 ./man2pdf.sh $< $@ evilwm.txt: evilwm.1 ./man2txt.sh $< $@ build.txt: build.1 ./man2txt.sh $< $@ .PHONY: all clean: rm -f evilwm.1 evilwm.pdf evilwm.txt rm -f build.1 build.txt evilwm-1.4.2/doc/build.html000066400000000000000000000120531433251723200155560ustar00rootroot00000000000000

evilwm installation guide

Building from source

See the evilwm home page for downloads. Once you have a copy of the source code, either the git repository or by downloading a tar archive, building is very straightforward.

In addition to the standard C compiler tools, you will need X11 development files installed. Under Debian, the x11proto-dev, libx11-dev and libxrandr-dev packages should suffice.

The source distribution does contain a configure script, but this is not from the GNU build system. It is a minimal bash script provided to simplify multiarch cross-builds under Debian. Instead, edit the Makefile to modify build flags for your platform.

Then, building and installing is very simple:

$ make
$ make install

That's it! But if you need to install to a different prefix, you can override with something like:

$ make install prefix=/usr/local

Starting evilwm

The install process puts a file called evilwm.desktop into /usr/share/applications, so depending on your desktop manager, you may simply be able to pick evilwm from a menu. Otherwise, most managers will run Xsession, which will look for a file in your home directory called .xsession and run it (so be sure it has execute permission). Here's a simple example .xsession file:

#!/bin/sh
test -f $HOME/.Xresources && xrdb -merge $HOME/.Xresources
xsetroot -solid \#400040 -cursor_name left_ptr
( /usr/bin/evilwm -snap 10 >/dev/null 2>&1 & )
exec xclock -digital -padding 2 -g -0+0

The cursor shape and background colour are set with standard X tools (evilwm won't do this for you). xclock becomes the "magic process" (session terminates when it is killed). evilwm itself is started in a subshell otherwise xclock would become its parent, and killing evilwm to restart it may result in a zombie process. You could also avoid this by not exec-ing the "magic process"; the shell will handle child signals properly.

See here for a more complete example of a .xsession file. Obviously, it depends on a lot of external packages.

You used to be able to configure the GNOME session manager to use evilwm as its window manager by running gconf-editor and setting the key /desktop/gnome/session/required_components/windowmanager to evilwm. The package information for gconf-editor suggests this approach is out of date, however.

If you typically start X by typing startx from the console, you might need the .xsession file to be called .xinitrc. A symlink should suffice.

After starting, you should be able to bring up an xterm with Control+Alt+Enter. For more information on configuring and using evilwm, see the manual. evilwm-1.4.2/doc/evilwm.html000066400000000000000000000367311433251723200157730ustar00rootroot00000000000000

evilwm

NAME

evilwm—minimalist window manager for X11

SYNOPSIS

evilwm [OPTION]…

DESCRIPTION

evilwm is a minimalist window manager for the X Window System. It features plenty of reconfigurable mouse and keyboard controls while providing a clean display, uncluttered by less useful window furniture (like title bars).

OPTIONS

--display display
specifies the X display to run on. Usually this can be inferred from the DISPLAY environment variable.
--term termprog
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.
--fn fontname
specify a font to use when resizing or displaying window titles.
--fg colour
--fc colour
--bg colour
frame colour of currently active, fixed active, and inactive windows respectively. Either specify an X11 colour name like goldenrod, or a hex triplet like #DAA520.
--bw borderwidth
width of window borders in pixels.
--snap distance
enable snap-to-border support. distance is the proximity in pixels to snap to.
--wholescreen
ignore monitor geometry and use the whole screen dimensions. This is the old behaviour from before multi-monitor support was implemented, and may still be useful, eg when one large monitor is driven from multiple outputs.
--numvdesks value
number of virtual desktops to provide. Defaults to 8. Any extras will only be accessible by pagers or using Control+Alt+(Left/Right).
--nosoliddrag
draw a window outline while moving or resizing.
--mask1 modifier[+modifier]…
--mask2 modifier[+modifier]…
--altmask modifier[+modifier]…
override the default keyboard modifiers used to grab keys for window manager functionality.

mask1 is used for most keyboard controls (default: control+alt), and mask2 is used for mouse button controls and cycling windows (default: alt). altmask is used to modify the behaviour of certain controls (default: shift). Modifiers may be separated with + signs. Valid modifiers are 'shift', 'control', 'alt', 'mod1'…'mod5'.

--bind key[+modifier]…=[function[,flag]…]
bind a key pressed with specified modifiers to a window manager function. key is an X11 keysym name, modifiers are as above, but may also include 'mask1', 'mask2' and 'altmask' to refer to the globally-configured combinations. See FUNCTIONS for a list of available functions and the flags they recognise. If function is empty, a bind is removed.
--bind button=[function[,flag]…]
bind a mouse button to a window manager function. While modifiers can be specified, they will be ignored; the button on its own will trigger if pressed within a window's frame, or with 'mask2' held anywhere within a window. Function and flags is as with key binds above. Valid buttons are 'button1'…'button5'.
--app name/class
match an application by instance name and class (for help in finding these, use the xprop tool to extract the WM_CLASS property).

Subsequent --geometry, --dock, --vdesk and --fixed options will apply to this match.

-g, --geometry geometry
apply a geometry (using a standard X geometry string) to applications matching the last --app.
--dock
specify that application should be considered to be a dock, even if it lacks the appropriate property.
-v, --vdesk vdesk
specify a default virtual desktop for applications matching the last --app. Note that virtual desktops are numbered from zero.
-f, --fixed
specify that application is to start with a fixed client window.
-h, --help
show help
-V, --version
show program version

evilwm will also read options, one per line, from a file called .evilwmrc in the user's home directory. Options listed in a configuration file should omit the leading dash(es). Options specified on the command line override those found in the configuration file.

USAGE

In 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 (unless it is fixed, in which case blue), with other windows left as a dark grey.

You can use the mouse to manipulate windows either by click/dragging the single-pixel border (easier when they align with a screen edge), or by holding down Alt and doing so anywhere in the 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 terminal.
Escape
Delete current window. Hold Shift as well to force kill a client.
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 the top-left, top-right, bottom-left or bottom-right of the current monitor.
I
Show information about current window.
Equals
Maximise current window vertically on current monitor (toggle). Holding Shift toggles horizontal maximization.
X
Maximise current window to current monitor (toggle).
D
Toggle visible state of docks, eg pagers and launch bars.

If compiled with virtual desktop support, these functions are also available:

F
Fix or unfix current window. Fixed windows remain visible when you switch virtual desktop.
18
Switch to specific virtual desktop (internally, desktops are numbered from zero, so this actually switches to desktops 0—7; this only becomes important if you use application matching).
Left
Switch to next lower numbered virtual desktop.
Right
Switch to next higher numbered virtual desktop.
A
Switch to the previously selected virtual desktop.

In addition to the above, Alt+Tab can be used to cycle through windows.

To make evilwm reread its config, send a HUP signal to the process. To make it quit, kill it, ie send a TERM signal.

FUNCTIONS

The keyboard and mouse button controls can be configured with the --bind option to a number of built-in functions. Typically, these functions respond to an additional set of flags that modify their behaviour.

delete
Delete a window. This is the co-operative way of closing applications, as it sends the client a signal indicating that they should shut down.
dock
When called with the toggle flag, toggles visibility of any window claiming to be a dock.
fix
With the toggle flag, toggle whether a window is fixed (visible on all virtual desktops) or not.
info
Shows extra information about the current window for as long as the key is held.
kill
Kill a window. A more forceful way of closing an application if it is not responding to delete requests.
lower
Lower the current window.
move
When bound to a button, moves a window with the mouse.

When bound to a key, if the relative flag is specified, moves a window in the direction indicated by other flags: up, down, left or right. Without the relative flag, moves a window in the direction specified by other flag to the edge of the monitor.

next
Cycle to the next window.
raise

Raises the current window.

resize
When bound to a button, resizes a window with the mouse.

When bound to a key, if the relative flag is specified, modifies the width or height of the window as indicated by other flags: up (reduce height), down (increase height), left (reduce width) or right (increase width). If instead the toggle flag is specified, maximises along axes specified by other flags: horizontal, vertical or both.

spawn
Start a terminal.
vdesk
With the toggle flag specified, switch to the previously visible vdesk. With the relative flag set, either increase vdesk number (with up flag) or decrease it (with down flag).

If neither flag is specified, a numerical argument indicates which vdesk to switch to.

DEFAULT BINDS

These are the default lists of modifiers, button and keyboard binds. The built-in binds use the globally-configurable modifier combinations 'mask1', 'mask2' and 'altmask', making a sweeping change to a different modifier combination easy.

Note that 'mod1' typically refers to the Alt key.

Modifiers

mask1 control+mod1
mask2 mod1
altmask shift

Button binds

bind button1=move
bind button2=resize
bind button3=lower

Keyboard binds

bind mask1+Return=spawn
bind mask1+Escape=delete
bind mask1+altmask+Escape=kill
bind mask1+Insert=lower
bind mask1+KP_Insert=lower
bind mask1+i=info
bind mask2+Tab=next

bind mask1+h=move,relative+left
bind mask1+j=move,relative+down
bind mask1+k=move,relative+up
bind mask1+l=move,relative+right
bind mask1+y=move,top+left
bind mask1+u=move,top+right
bind mask1+b=move,bottom+left
bind mask1+n=move,bottom+right

bind mask1+altmask+h=resize,relative+left
bind mask1+altmask+j=resize,relative+down
bind mask1+altmask+k=resize,relative+up
bind mask1+altmask+l=resize,relative+right

bind mask1+equal=resize,toggle+v
bind mask1+altmask+equal=resize,toggle+h
bind mask1+x=resize,toggle+v+h

bind mask1+d=dock,toggle
bind mask1+f=fix,toggle
bind mask1+1=vdesk,0
bind mask1+2=vdesk,1
bind mask1+3=vdesk,2
bind mask1+4=vdesk,3
bind mask1+5=vdesk,4
bind mask1+6=vdesk,5
bind mask1+7=vdesk,6
bind mask1+8=vdesk,7
bind mask1+Left=vdesk,relative+down
bind mask1+Right=vdesk,relative+up
bind mask1+a=vdesk,toggle

FILES

$HOME/.evilwmrc

LICENCE

Copyright (C) 1999-2022 Ciaran Anscomb <evilwm@6809.org.uk>

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.

SEE ALSO

xterm (1), xprop (1) evilwm-1.4.2/doc/html2man.pl000077500000000000000000000305241433251723200156560ustar00rootroot00000000000000#!/usr/bin/perl -wT # usage: hxnormalize -xe -l 99999 | hxpipe | html2man.pl use strict; use utf8; use Data::Dumper; use Getopt::Long; binmode STDIN, ":utf8"; binmode STDOUT, ":utf8"; my $nroff = 0; GetOptions( "nroff|n" => \$nroff, ); # always allow my %inline = ( "strong" => 1, "b" => 1, "em" => 1, "i" => 1, "var" => 1, "code" => 1, "samp" => 1, "a" => 1, "kbd" => 1, "tt" => 1, "cite" => 1, ); my %block = ( "" => { "html" => 1, }, "html" => { "head" => 1, "body" => 1, }, "head" => { "title" => 1, "meta" => 1, "style" => 1, }, "body" => { "h1" => 1, "h2" => 1, "h3" => 1, "h4" => 1, "p" => 1, "pre" => 1, "ul" => 1, "dl" => 1, "table" => 1, "div" => 1, }, "ul" => { "li" => 1, }, "dl" => { "dt" => 1, "dd" => 1, }, "dd" => { "p" => 1, }, "table" => { "thead" => 1, "tbody" => 1, "tr" => 1, }, "thead" => { "tr" => 1, }, "tbody" => { "tr" => 1, }, "tr" => { "th" => 1, "td" => 1, }, "p" => { }, ); my %handler = ( "body" => [ \&o_body, \&c_body ], "h1" => [ undef, \&c_h1 ], "h2" => [ undef, \&c_h2 ], "h3" => [ undef, \&c_h3 ], "h4" => [ undef, \&c_h4 ], "pre" => [ \&o_pre, \&c_pre ], "p" => [ \&o_p, \&c_p ], "dt" => [ undef, \&c_dt ], "dd" => [ undef, \&c_dd ], "li" => [ undef, \&c_li ], "strong" => [ \&o_strong, \&c_strong ], "b" => [ \&o_strong, \&c_strong ], "code" => [ \&o_code, \&c_code ], "em" => [ \&o_em, \&c_em ], "i" => [ \&o_em, \&c_em ], "var" => [ \&o_em, \&c_em ], "a" => [ \&o_a, \&c_a ], "table" => [ \&o_table, \&c_table ], "tbody" => [ \&o_tbody, undef ], "tr" => [ \&o_tr, undef ], "th" => [ undef, \&c_th ], "td" => [ undef, \&c_td ], ); my @stack = (); my $top = { 'tag' => '', 'elist' => [] }; push @stack, $top; my $current = $top; my $attrs = {}; sub close_tag { my $tag = shift; while ($tag ne $current->{'tag'}) { $current = pop @stack; return unless @stack; $current = pop @stack; push @stack, $current; } $current = pop @stack; return unless @stack; $current = pop @stack; push @stack, $current; } while (<>) { chomp; my $cmd = substr($_, 0, 1); my $arg = substr($_, 1); if ($cmd eq 'A') { my ($a, $type, $data) = split(/ /, $arg, 3); $attrs->{$a} = $data; } elsif ($cmd eq '(') { my $tag = $arg; if (!exists $inline{$tag}) { my $trace = join(" ", map { "<".$_->{'tag'}.">" } @stack); my $parent = $current; my $parent_tag = $parent->{'tag'}; if (exists $block{$parent_tag}) { while (@stack && !exists $block{$parent_tag}->{$tag}) { close_tag($parent_tag); $parent = $current; $parent_tag = $parent->{'tag'}; } if (!@stack) { print STDERR "<$tag> not inline or allowed anywhere in:\n $trace\n"; exit 1; } } } my $parent = $current; my $list = $parent->{'elist'}; $current = { 'tag' => $tag, 'attrs' => $attrs, 'elnum' => scalar(@{$list}), 'parent' => $parent, 'elist' => [] }; push @{$list}, $current; push @stack, $current; $attrs = {}; } elsif ($cmd eq ')') { my $tag = $arg; close_tag($tag); } elsif ($cmd eq '-') { my $parent = $current; my $list = $parent->{'elist'}; my $text .= from_html($arg); my $elem = { 'text' => $text, 'elnum' => scalar(@{$list}), 'parent' => $parent }; push @{$list}, $elem; } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - my $ctx = { 'font' => '\\fR' }; parse_elem($ctx, $top); exit 0; # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub prev_elem { my ($ctx, $elem) = @_; return undef unless exists $elem->{'elnum'}; my $elnum = $elem->{'elnum'} - 1; my $parent = $elem->{'parent'}; return undef if $elnum < 0; return undef if $elnum >= scalar(@{$parent->{'elist'}}); return $parent->{'elist'}->[$elnum]; } sub next_elem { my ($ctx, $elem) = @_; return undef unless exists $elem->{'elnum'}; my $elnum = $elem->{'elnum'} + 1; my $parent = $elem->{'parent'}; return undef if $elnum < 0; return undef if $elnum >= scalar(@{$parent->{'elist'}}); return $parent->{'elist'}->[$elnum]; } sub prev_tag { my ($ctx, $elem) = @_; my $prev = $elem; while ($prev = prev_elem($ctx, $prev)) { return $prev->{'tag'} if exists $prev->{'tag'}; } return ""; } sub prev_word { my ($ctx, $elem) = @_; my $word = ""; if ($ctx->{'text'} =~ /^(.*)?\s*(\S+)$/s) { $ctx->{'text'} = $1; $word = $2; } return $word; } sub next_word { my ($ctx, $elem) = @_; my $next = next_elem($ctx, $elem); return "" unless defined $next; return "" if exists $next->{'tag'}; return "" unless exists $next->{'text'}; my $word = ""; if ($next->{'text'} =~ /^(\S+)\s*(.*)$/) { $word = $1; $next->{'text'} = $2; } return $word; } sub parse_elem { my ($ctx, $elem) = @_; if (exists $elem->{'tag'}) { my $tag = $elem->{'tag'}; my $parent = $elem->{'parent'}; my $elnum = $elem->{'elnum'}; if (exists $handler{$tag} && defined $handler{$tag}->[0]) { &{$handler{$tag}->[0]}($ctx, $elem); } if (exists $elem->{'elist'}) { for my $e (@{$elem->{'elist'}}) { parse_elem($ctx, $e); } } if (exists $handler{$tag} && defined $handler{$tag}->[1]) { &{$handler{$tag}->[1]}($ctx, $elem); } } else { my $text = to_man($elem->{'text'}); if ($ctx->{'output_enabled'}) { if ($ctx->{'monospace'}) { $text =~ s/-/\\-/g; $text =~ s/\^/\\(ha/g; $text =~ s/~/\\(ti/g; } if ($ctx->{'verbatim'}) { $text =~ s/ /\\ /g; $text =~ s/-/\\-/g; } else { if ($elem->{'elnum'} == 0) { $text =~ s/^\s*//s; } $text =~ s/\s+/ /s; } $ctx->{'text'} .= $text; } } } sub prune_text { my ($ctx, $elem) = @_; my $text = $ctx->{'text'}; $ctx->{'text'} = ""; if ($ctx->{'verbatim'}) { $text =~ s/^\s*\n*//gs; $text =~ s/\s*\n/\n/gs; $text =~ s/\n*$//gs; } else { $text =~ s/^\s*//s; $text =~ s/\s*$//s; } return $text; } sub o_body { my ($ctx, $elem) = @_; $ctx->{'output_enabled'} = 1; } sub c_body { my ($ctx, $elem) = @_; $ctx->{'output_enabled'} = 0; } sub c_h1 { my ($ctx, $elem) = @_; my $text = prune_text($ctx); my $attrs = $elem->{'attrs'}; my $date = $attrs->{'man:date'} // ""; my $section = $attrs->{'man:section'} // ""; my $dist = $attrs->{'man:dist'} // ""; print ".ie \\\\n(mP \\{\\\n"; print ". nr PDFOUTLINE.FOLDLEVEL 3\n"; print ". pdfview /PageMode /UseOutlines\n"; if (exists $attrs->{'pdf:title'}) { print ". pdfinfo /Title $attrs->{'pdf:title'}\n"; } else { print ". pdfinfo /Title $text\n"; } if (exists $attrs->{'pdf:author'}) { print ". pdfinfo /Author $attrs->{'pdf:author'}\n"; } print ".\\}\n"; print ".\n"; print ".TH \"$text\" \"$section\" \"$date\" \"$dist\"\n"; print ".hy 0\n"; # no hyphenation print ".nh\n"; # no hyphenation #print ".ad l\n"; # left justify } sub c_h2 { my ($ctx, $elem) = @_; my $text = prune_text($ctx); print ".H1 $text\n"; } sub c_h3 { my ($ctx, $elem) = @_; my $text = prune_text($ctx); print ".H2 $text\n"; } sub c_h4 { my ($ctx, $elem) = @_; my $text = prune_text($ctx); print ".H3 $text\n"; } sub o_pre { my ($ctx, $elem) = @_; $ctx->{'verbatim'} = 1; } sub c_pre { my ($ctx, $elem) = @_; my $text = prune_text($ctx); print ".IP\n"; print ".EX\n"; print "$text\n"; print ".EE\n"; $ctx->{'verbatim'} = 0; } sub o_p { my ($ctx, $elem) = @_; my $ptag = $elem->{'parent'}->{'tag'}; if ($ptag eq 'dd') { c_dd($ctx, $elem); } } sub c_p { my ($ctx, $elem) = @_; my $text = prune_text($ctx); my $ptag = $elem->{'parent'}->{'tag'}; if ($ptag eq 'dd') { if (!$ctx->{'suppress_ip'}) { print ".IP\n"; } } else { print ".PP\n"; } print "$text\n"; } sub c_dt { my ($ctx, $elem) = @_; my $text = prune_text($ctx); my $ptag = prev_tag($ctx, $elem); if ($ptag eq 'dt') { print ".TQ\n"; } else { print ".TP\n"; } print "$text\n"; } sub c_dd { my ($ctx, $elem) = @_; my $text = prune_text($ctx); my $ptag = prev_tag($ctx, $elem); $ctx->{'suppress_ip'} = ($text eq ''); if (!$ctx->{'suppress_ip'}) { print "$text\n"; } } sub c_li { my ($ctx, $elem) = @_; my $text = prune_text($ctx); my $ptag = prev_tag($ctx, $elem); print ".IP \\(bu 2\n"; print "$text\n"; } sub pushfont { my ($ctx, $elem, $font) = @_; $ctx->{'text'} .= $font; push @{$ctx->{'fontstack'}}, $ctx->{'font'}; $ctx->{'font'} = "\\fB"; } sub popfont { my ($ctx, $elem) = @_; $ctx->{'font'} = pop @{$ctx->{'fontstack'}}; $ctx->{'text'} .= $ctx->{'font'}; } sub o_strong { my ($ctx, $elem) = @_; pushfont($ctx, $elem, "\\fB"); } sub c_strong { my ($ctx, $elem) = @_; popfont($ctx, $elem); } sub o_code { my ($ctx, $elem) = @_; $ctx->{'monospace'} = 1; pushfont($ctx, $elem, "\\f(CB"); } sub c_code { my ($ctx, $elem) = @_; $ctx->{'monospace'} = 0; popfont($ctx, $elem); } sub o_em { my ($ctx, $elem) = @_; pushfont($ctx, $elem, "\\fI"); } sub c_em { my ($ctx, $elem) = @_; popfont($ctx, $elem); } sub o_a { my ($ctx, $elem) = @_; my $attrs = $elem->{'attrs'}; return if !exists $attrs->{'href'}; my $href = $attrs->{'href'}; return if ($href =~ /^#/); $ctx->{'urlhref'} = $href; $ctx->{'urlpre'} = prev_word($ctx, $elem); $ctx->{'urlsave'} = $ctx->{'text'}; $ctx->{'text'} = ""; } sub c_a { my ($ctx, $elem) = @_; return if (!exists $ctx->{'urlhref'}); my $save = $ctx->{'urlsave'}; my $pre = $ctx->{'urlpre'}; my $href = $ctx->{'urlhref'}; my $post = next_word($ctx, $elem); my $text = $ctx->{'text'}; $ctx->{'text'} = "$save"; if ($text ne $href) { $ctx->{'text'} .= " $text"; $pre .= "("; $post = ")$post"; } $ctx->{'text'} .= "\n.UU \"$pre\" \"$href\" \"$post\"\n"; delete $ctx->{'urlsave'}; delete $ctx->{'urlpre'}; delete $ctx->{'urlhref'}; } sub o_table { my ($ctx, $elem) = @_; my $attrs = $elem->{'attrs'}; my $tab = $attrs->{'tab'} // ';'; my $align = [ split(/,/, $attrs->{'align'} // 'l,l') ]; my $columns = scalar(@{$align}); $ctx->{'tbltab'} = $tab; $ctx->{'tblalign'} = $align; $ctx->{'tblcolumns'} = $columns; print ".RS\n"; print ".TS\n"; print "tab($tab);\n"; print join(" | ", @{$align}).".\n"; } sub o_tbody { print ".T&\n"; my $align = $ctx->{'tblalign'}; my $columns = $ctx->{'tblcolumns'}; print join(" | ", ("_") x $columns)."\n"; print join(" | ", @{$align}).".\n"; } sub c_table { my ($ctx, $elem) = @_; print ".TE\n"; print ".RE\n"; $ctx->{'text'} = ""; } sub o_tr { my ($ctx, $elem) = @_; $ctx->{'tblcolumn'} = 0; $ctx->{'text'} = ""; } sub c_th { my ($ctx, $elem) = @_; my $text = prune_text($ctx); my $column = $ctx->{'tblcolumn'}; my $tab = $ctx->{'tbltab'}; my $align = $ctx->{'tblalign'}->[$column]; print $tab if ($column > 0); print " " if ($align eq 'c' || $align eq 'r'); print "\\fB$text".$ctx->{'font'}; $column++; if ($column eq $ctx->{'tblcolumns'}) { print "\n"; } else { print " " if ($align eq 'c' || $align eq 'l'); } $ctx->{'tblcolumn'} = $column; } sub c_td { my ($ctx, $elem) = @_; my $text = prune_text($ctx); my $column = $ctx->{'tblcolumn'}; my $tab = $ctx->{'tbltab'}; my $align = $ctx->{'tblalign'}->[$column]; print $tab if ($column > 0); print " " if ($align eq 'c' || $align eq 'r'); print "$text"; $column++; if ($column eq $ctx->{'tblcolumns'}) { print "\n"; } else { print " " if ($align eq 'c' || $align eq 'l'); } $ctx->{'tblcolumn'} = $column; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub from_html { my $t = shift; $t =~ s/%/%25/g; $t =~ s/&/%26/g; $t =~ s/—/—/g; $t =~ s/“/“/g; $t =~ s/”/”/g; $t =~ s/‘/‘/g; $t =~ s/’/’/g; $t =~ s/…/…/g; $t =~ s/<//g; $t =~ s/ /\xa0/g; $t =~ s/%([0-9a-f]{2})/pack("c",hex($1))/ge; $t =~ s/\\n/\n/g; $t =~ s/\\\\/\\/g; return $t; } sub to_man { my $t = shift; $t =~ s/\\/\\\\/g; if ($nroff) { $t =~ s/—/ \\- /g; $t =~ s/“/"/g; $t =~ s/”/"/g; $t =~ s/‘/'/g; $t =~ s/’/'/g; $t =~ s/…/.../g; } else { # protect, %, use it to escape brackets $t =~ s/%/%25/g; $t =~ s/\[/%5b/g; $t =~ s/\]/%5d/g; $t =~ s/%5b/\\[lB]/g; $t =~ s/%5d/\\[rB]/g; $t =~ s/%([0-9a-f]{2})/pack("c",hex($1))/ge; $t =~ s/—/\\[em]/g; $t =~ s/“/\\[lq]/g; $t =~ s/”/\\[rq]/g; $t =~ s/‘/\\[oq]/g; $t =~ s/’/\\[cq]/g; $t =~ s/…/\\[...]/g; $t =~ s/'/\\[aq]/g; # apostrophe quote, not curly $t =~ s/\^/\\[ha]/g; # different glyph for hat $t =~ s/~/\\[ti]/g; # different glyph for tilde $t =~ s/\xa0/\\~/g; # non-breaking space } return $t; } evilwm-1.4.2/doc/man2pdf.sh000077500000000000000000000002301433251723200154510ustar00rootroot00000000000000#!/bin/sh in="$1" out="$2" test -z "$out" && { echo "usage: $0 infile outfile" >&2; exit 1; } groff -Tpdf -P-pa4 -t -mpdfmark -mandoc "$in" > "$out" evilwm-1.4.2/doc/man2txt.sh000077500000000000000000000002331433251723200155220ustar00rootroot00000000000000#!/bin/sh in="$1" out="$2" test -z "$out" && { echo "usage: $0 infile outfile" >&2; exit 1; } groff -Tascii -P-bcou -t -mpdfmark -mandoc "$in" > "$out" evilwm-1.4.2/doc/manpage-header000066400000000000000000000020721433251723200163520ustar00rootroot00000000000000'\" t . .\" ASCII for Unicode ellipsis is three dots .schar \[u2026] ... .\" New escape [...] maps to Unicode ellipsis .char \[...] \[u2026] . .\" an-ext.tmac: Check whether we are using grohtml. .nr mH 0 .if \n(.g \ . if '\*(.T'html' \ . nr mH 1 . .\" What about gropdf? .nr mP 0 .if \n(.g \ . if '\*(.T'pdf' \ . nr mP 1 . .\" an-ext.tmac: Start example. .de EX . nr mE \\n(.f . nf . nh . ft CW .. . .\" an-ext.tmac: End example. .de EE . ft \\n(mE . fi . hy \\n(HY .. . .\" Top level heading; wraps .SH. This seems to confuse lintian. .de H1 . if \\n(mP .pdfhref O 1 \\$* . SH \\$* .. . .\" 2nd level heading; wraps .SS .de H2 . if \\n(mP .pdfhref O 2 \\$* . SS \\$* .. . .\" 3rd level heading; bold font, no indent .de H3 . if \\n(.$ \{\ . if \\n(mP .pdfhref O 3 \\$* . .B \&"\\$*" . \} . br .. . .\" Render URL .de UU . ie \\n(mH \{\ \\$1\c . do HTML-NS "" \\$2\c . do HTML-NS "" \\$3 . \} . el \{\ . ie \\n(mP \{\ . pdfhref -W -P "\\$1" -A "\\$3" "\\$2" . \} . el \{\ \\$1\\$2\\$3 . \} . \} .. . .\" . evilwm-1.4.2/doc/xsession.txt000066400000000000000000000074631433251723200162160ustar00rootroot00000000000000#!/bin/bash # .xsession # executed by Xsession as part of standard desktop startup # symlink ".xinitrc" to this file for running startx # I like to include the full paths to applications I start from here; no alarms # and no surprises. Anything that needs to keep running, I tend to start in a # subshell to avoid zombies. # Local environment setup MACHINE=$(uname -n) WM=/usr/bin/evilwm PATH="/usr/local/bin:/usr/bin:/bin:/usr/games" test -d "$HOME/bin" && PATH="$HOME/bin:$PATH" export PATH LANG=en_GB.UTF-8 LC_CTYPE=en_GB.UTF-8 LC_COLLATE=C export LANG LC_CTYPE LC_COLLATE # Tells chromium, et al. to query system proxy settings. XDG_CURRENT_DESKTOP=GNOME export XDG_CURRENT_DESKTOP # Ensure gpg-agent is running and relevant environment variables are present # for all child processes. eval $(/usr/bin/gpg-agent --daemon --sh) # Merge .Xresources test -f $HOME/.Xresources && /usr/bin/xrdb -merge $HOME/.Xresources # Set monitor DPI - see also .Xresources, .xsettingsd /usr/bin/xrandr --dpi 168 # Generated by arandr, configure multi-monitor geometry: #/usr/bin/xrandr --output eDP1 --primary --mode 1920x1080 --pos 0x0 --rotate normal \ # --output DP1 --off --output DP2 --off \ # --output HDMI1 --mode 1920x1080 --pos 1920x0 --rotate normal \ # --output HDMI2 --off --output VIRTUAL1 --off # Load keymap for this machine, if present test -f $HOME/.xkb-$MACHINE && /usr/bin/xkbcomp $HOME/.xkb-$MACHINE :0 >/dev/null 2>&1 # If a monitor calibration file exists for this machine, load it test -f $HOME/.icc-$MACHINE && /usr/bin/xcalib $HOME/.icc-$MACHINE >/dev/null 2>&1 # XSETTINGS handling ( /usr/bin/xsettingsd >/dev/null 2>&1 & ) # Various things seem to need this running now ( /usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1 >/dev/null 2>&1 & ) # Background power management ( /usr/bin/xfce4-power-manager >/dev/null 2>&1 & ) # NetworkManager applet doesn't seem to appear in xfce4-panel's "Add New # Items", so start it here: ( /usr/bin/nm-applet >/dev/null 2>&1 & ) # xss-lock runs xscreensaver when handling screen saver/lock signals. ( /usr/bin/xss-lock -- /usr/bin/xscreensaver-command -lock >/dev/null 2>&1 & ) ( /usr/bin/xscreensaver -no-splash >/dev/null 2>&1 & ) # Touchpad/mouse configuration. This makes my Thinkpad behave: /usr/bin/synclient TapButton1=1 TapButton2=2 # This approach may work better for certain devices: #/usr/bin/xinput set-int-prop "SynPS/2 Synaptics TouchPad" "libinput Tapping Enabled" 8 1 #/usr/bin/xinput set-int-prop "SynPS/2 Synaptics TouchPad" "libinput Horizonal Scroll Enabled" 8 0 #/usr/bin/xinput set-float-prop "SynPS/2 Synaptics TouchPad" "libinput Accel Speed" 0.5 #/usr/bin/xinput set-button-map "SynPS/2 Synaptics TouchPad" 1 3 2 4 5 6 7 # Oh how I dislike the X server beeping at me /usr/bin/xset b off # Fire up window manager and hot key handler ( $WM >/dev/null 2>&1 & ) ( /usr/bin/xbindkeys >/dev/null 2>&1 & ) # Set simple background pattern if present, else a solid colour if test -f "$HOME/.bgbitmap"; then /usr/bin/xsetroot -bitmap "$HOME/.bgbitmap" -fg \#402040 -bg \#381838 -cursor_name left_ptr else /usr/bin/xsetroot -solid \#402040 -cursor_name left_ptr fi # Lots of Xterms spawned in fixed locations by default ( /usr/bin/uxterm -g 80x11+0+0 >/dev/null 2>&1 & ) ( /usr/bin/uxterm -g 80x11+646+0 >/dev/null 2>&1 & ) ( /usr/bin/uxterm -g 64x11+1292+0 >/dev/null 2>&1 & ) ( /usr/bin/uxterm -g 96x24+0+204 >/dev/null 2>&1 & ) ( /usr/bin/uxterm -g 96x24+0-0 >/dev/null 2>&1 & ) ( /usr/bin/uxterm -g 96x24+774+204 >/dev/null 2>&1 & ) ( /usr/bin/uxterm -g 96x24+774-0 >/dev/null 2>&1 & ) # Hand children back to the nanny disown -a # For a laptop, xfce4-panel makes a good magic process, as it gives visibility # to the NetworkManager control applet and provides a "Log Out" menu option. # Be aware that Xfce is a victim of Client-Side Decoration. exec /usr/bin/xfce4-panel evilwm-1.4.2/events.c000066400000000000000000000270541433251723200145030ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // X11 event processing. This is the core of the window manager and processes // events from clients and user interaction in a loop until signalled to exit. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef SHAPE #include #endif #ifdef RANDR #include #endif #include "bind.h" #include "client.h" #include "display.h" #include "events.h" #include "evilwm.h" #include "ewmh.h" #include "list.h" #include "log.h" #include "screen.h" #include "util.h" // Event loop will run until this flag is set _Bool end_event_loop; // Flags that the client list should be scanned and marked clients removed. // Set by unhandled X errors and unmap requests. int need_client_tidy = 0; // Apply the changes from an XWindowChanges struct to a client. static void do_window_changes(int value_mask, XWindowChanges *wc, struct client *c, int gravity) { LOG_XENTER("do_window_changes(window=%lx), was: %dx%d+%d+%d", (unsigned long)c->window, c->width, c->height, c->x, c->y); if (gravity == 0) gravity = c->win_gravity_hint; c->win_gravity = gravity; if (value_mask & CWX) { c->x = wc->x; LOG_XDEBUG("CWX x=%d\n", wc->x); } if (value_mask & CWY) { c->y = wc->y; LOG_XDEBUG("CWY y=%d\n", wc->y); } if (value_mask & (CWWidth|CWHeight)) { int dw = 0, dh = 0; if (!(value_mask & (CWX|CWY))) { client_gravitate(c, -c->border); } if (value_mask & CWWidth) { LOG_XDEBUG("CWWidth width=%d\n", wc->width); 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) { LOG_XDEBUG("CWHeight height=%d\n", wc->height); 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; client_gravitate(c, c->border); } } wc->x = c->x - c->border; wc->y = c->y - c->border; wc->border_width = c->border; XConfigureWindow(display.dpy, c->parent, value_mask, wc); XMoveResizeWindow(display.dpy, c->window, 0, 0, c->width, c->height); if ((value_mask & (CWX|CWY)) && !(value_mask & (CWWidth|CWHeight))) { send_config(c); } LOG_XLEAVE(); } static void handle_configure_request(XConfigureRequestEvent *e) { struct 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) { struct 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 long)e->window, e->value_mask); XConfigureWindow(display.dpy, e->window, e->value_mask, &wc); LOG_XLEAVE(); } } static void handle_map_request(XMapRequestEvent *e) { struct client *c = find_client(e->window); LOG_ENTER("handle_map_request(window=%lx)", (unsigned long)e->window); if (c) { if (!is_fixed(c) && c->vdesk != c->screen->vdesk) switch_vdesk(c->screen, c->vdesk); client_show(c); client_raise(c); } else { XWindowAttributes attr; XGetWindowAttributes(display.dpy, e->window, &attr); client_manage_new(e->window, find_screen(attr.root)); } LOG_LEAVE(); } static void handle_unmap_event(XUnmapEvent *e) { struct client *c = find_client(e->window); LOG_ENTER("handle_unmap_event(window=%lx)", (unsigned long)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) { struct client *c = find_client(e->window); if (c && e->new) { c->cmap = e->colormap; XInstallColormap(display.dpy, c->cmap); } } static void handle_property_change(XPropertyEvent *e) { struct client *c = find_client(e->window); if (c) { LOG_ENTER("handle_property_change(window=%lx, atom=%s)", (unsigned long)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 == X_ATOM(_NET_WM_WINDOW_TYPE)) { get_window_type(c); if (!c->is_dock && (is_fixed(c) || (c->vdesk == c->screen->vdesk))) { client_show(c); } } LOG_LEAVE(); } } static void handle_enter_event(XCrossingEvent *e) { struct client *c; if ((c = find_client(e->window))) { if (!is_fixed(c) && c->vdesk != c->screen->vdesk) return; select_client(c); clients_tab_order = list_to_head(clients_tab_order, c); } } static void handle_mappingnotify_event(XMappingEvent *e) { XRefreshKeyboardMapping(e); if (e->request == MappingKeyboard) { int i; for (i = 0; i < display.nscreens; i++) { bind_grab_for_screen(&display.screens[i]); } } } #ifdef SHAPE static void handle_shape_event(XShapeEvent *e) { struct client *c = find_client(e->window); if (c) set_shape(c); } #endif #ifdef RANDR static void handle_randr_event(XRRScreenChangeNotifyEvent *e) { struct screen *s = find_screen(e->root); // Record geometries of clients relative to monitor scan_clients_before_resize(s); // Update Xlib's idea of screen size XRRUpdateConfiguration((XEvent*)e); // Scan new monitor list screen_probe_monitors(s); // Fix any clients that are now not visible on any monitor. Also // adjusts maximised geometries where appropriate. fix_screen_after_resize(s); // Update various EWMH properties that reflect screen geometry ewmh_set_screen_workarea(s); } #endif // Events sent to clients static void handle_client_message(XClientMessageEvent *e) { struct screen *s = find_current_screen(); struct client *c; LOG_ENTER("handle_client_message(window=%lx, format=%d, type=%s)", (unsigned long)e->window, e->format, debug_atom_name(e->message_type)); if (e->message_type == X_ATOM(_NET_CURRENT_DESKTOP)) { switch_vdesk(s, e->data.l[0]); LOG_LEAVE(); return; } c = find_client(e->window); if (!c) { // _NET_REQUEST_FRAME_EXTENTS is intended to be sent from // unmapped windows. The reply only needs to be an _estimate_ // of the border widths, but calculate it anyway - the only // thing that affects this for us is MWM hints. if (e->message_type == X_ATOM(_NET_REQUEST_FRAME_EXTENTS)) { int bw = window_normal_border(e->window); ewmh_set_net_frame_extents(e->window, bw); } LOG_LEAVE(); return; } if (e->message_type == X_ATOM(_NET_ACTIVE_WINDOW)) { // Only do this if it came from direct user action if (e->data.l[0] == 2) { if (c->screen == s) select_client(c); } LOG_LEAVE(); return; } if (e->message_type == X_ATOM(_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 == X_ATOM(_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 == X_ATOM(_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; } if (e->message_type == X_ATOM(_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; } if (e->message_type == X_ATOM(_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] == X_ATOM(_NET_WM_STATE_MAXIMIZED_VERT)) { maximise_hv |= MAXIMISE_VERT; } else if ((Atom)e->data.l[i] == X_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ)) { maximise_hv |= MAXIMISE_HORZ; } else if ((Atom)e->data.l[i] == X_ATOM(_NET_WM_STATE_FULLSCREEN)) { maximise_hv |= MAXIMISE_VERT|MAXIMISE_HORZ; } } if (maximise_hv) { client_maximise(c, e->data.l[0], maximise_hv); } LOG_LEAVE(); return; } LOG_LEAVE(); } // Run the main event loop. This will run until something tells us to quit // (generally, a signal). void event_main_loop(void) { // XEvent is a big union of all the core event types, but we also need // to handle events about extensions, so make a union of the union... union { XEvent xevent; #ifdef SHAPE XShapeEvent xshape; #endif #ifdef RANDR XRRScreenChangeNotifyEvent xrandr; #endif } ev; // Main event loop while (!end_event_loop) { if (interruptibleXNextEvent(&ev.xevent)) { switch (ev.xevent.type) { case KeyPress: bind_handle_key(&ev.xevent.xkey); break; case ButtonPress: bind_handle_button(&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 (display.have_shape && ev.xevent.type == display.shape_event) { handle_shape_event(&ev.xshape); } #endif #ifdef RANDR if (display.have_randr && ev.xevent.type == display.randr_event_base + RRScreenChangeNotify) { handle_randr_event(&ev.xrandr); } #endif break; } } // Scan list for clients flagged to be removed if (need_client_tidy) { struct list *iter, *niter; need_client_tidy = 0; for (iter = clients_tab_order; iter; iter = niter) { struct client *c = iter->data; niter = iter->next; if (c->remove) remove_client(c); } } } } evilwm-1.4.2/events.h000066400000000000000000000011771433251723200145060ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // X11 event processing #ifndef EVILWM_EVENTS_H_ #define EVILWM_EVENTS_H_ #include #include // Event loop will run until this flag is set extern _Bool end_event_loop; // Flags that the client list should be scanned and marked clients removed. // Set by unhandled X errors and unmap requests. extern int need_client_tidy; // The main event loop - this will run until something signals the window // manager to quit. void event_main_loop(void); #endif evilwm-1.4.2/evilwm.1000066400000000000000000000326431433251723200144200ustar00rootroot00000000000000'\" t . .\" ASCII for Unicode ellipsis is three dots .schar \[u2026] ... .\" New escape [...] maps to Unicode ellipsis .char \[...] \[u2026] . .\" an-ext.tmac: Check whether we are using grohtml. .nr mH 0 .if \n(.g \ . if '\*(.T'html' \ . nr mH 1 . .\" What about gropdf? .nr mP 0 .if \n(.g \ . if '\*(.T'pdf' \ . nr mP 1 . .\" an-ext.tmac: Start example. .de EX . nr mE \\n(.f . nf . nh . ft CW .. . .\" an-ext.tmac: End example. .de EE . ft \\n(mE . fi . hy \\n(HY .. . .\" Top level heading; wraps .SH. This seems to confuse lintian. .de H1 . if \\n(mP .pdfhref O 1 \\$* . SH \\$* .. . .\" 2nd level heading; wraps .SS .de H2 . if \\n(mP .pdfhref O 2 \\$* . SS \\$* .. . .\" 3rd level heading; bold font, no indent .de H3 . if \\n(.$ \{\ . if \\n(mP .pdfhref O 3 \\$* . .B \&"\\$*" . \} . br .. . .\" Render URL .de UU . ie \\n(mH \{\ \\$1\c . do HTML-NS "" \\$2\c . do HTML-NS "" \\$3 . \} . el \{\ . ie \\n(mP \{\ . pdfhref -W -P "\\$1" -A "\\$3" "\\$2" . \} . el \{\ \\$1\\$2\\$3 . \} . \} .. . .\" . .ie \\n(mP \{\ . nr PDFOUTLINE.FOLDLEVEL 3 . pdfview /PageMode /UseOutlines . pdfinfo /Title evilwm 1.4 . pdfinfo /Author Ciaran Anscomb .\} . .TH "evilwm" "1" "October 2022" "evilwm-1.4" .hy 0 .nh .H1 NAME .PP evilwm\[em]minimalist window manager for X11 .H1 SYNOPSIS .PP \fBevilwm\fR \[lB]\fIOPTION\fR\[rB]\[...] .H1 DESCRIPTION .PP \fBevilwm\fR is a minimalist window manager for the X Window System. It features plenty of reconfigurable mouse and keyboard controls while providing a clean display, uncluttered by less useful window furniture (like title bars). .H1 OPTIONS .TP \f(CB\-\-display\fR \fIdisplay\fR specifies the X display to run on. Usually this can be inferred from the \f(CBDISPLAY\fR environment variable. .TP \f(CB\-\-term\fR \fItermprog\fR 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 \f(CB\-\-fn\fR \fIfontname\fR specify a font to use when resizing or displaying window titles. .TP \f(CB\-\-fg\fR \fIcolour\fR .TQ \f(CB\-\-fc\fR \fIcolour\fR .TQ \f(CB\-\-bg\fR \fIcolour\fR frame colour of currently active, fixed active, and inactive windows respectively. Either specify an X11 colour name like \f(CBgoldenrod\fR, or a hex triplet like \f(CB#DAA520\fR. .TP \f(CB\-\-bw\fR \fIborderwidth\fR width of window borders in pixels. .TP \f(CB\-\-snap\fR \fIdistance\fR enable snap-to-border support. \fIdistance\fR is the proximity in pixels to snap to. .TP \f(CB\-\-wholescreen\fR ignore monitor geometry and use the whole screen dimensions. This is the old behaviour from before multi-monitor support was implemented, and may still be useful, eg when one large monitor is driven from multiple outputs. .TP \f(CB\-\-numvdesks\fR \fIvalue\fR number of virtual desktops to provide. Defaults to 8. Any extras will only be accessible by pagers or using Control+Alt+(Left/Right). .TP \f(CB\-\-nosoliddrag\fR draw a window outline while moving or resizing. .TP \f(CB\-\-mask1\fR \fImodifier\fR\[lB]+\fImodifier\fR\[rB]\[...] .TQ \f(CB\-\-mask2\fR \fImodifier\fR\[lB]+\fImodifier\fR\[rB]\[...] .TQ \f(CB\-\-altmask\fR \fImodifier\fR\[lB]+\fImodifier\fR\[rB]\[...] override the default keyboard modifiers used to grab keys for window manager functionality. .IP \f(CBmask1\fR is used for most keyboard controls (default: control+alt), and \f(CBmask2\fR is used for mouse button controls and cycling windows (default: alt). \f(CBaltmask\fR is used to modify the behaviour of certain controls (default: shift). Modifiers may be separated with + signs. Valid modifiers are \[aq]shift\[aq], \[aq]control\[aq], \[aq]alt\[aq], \[aq]mod1\[aq]\[...]\[aq]mod5\[aq]. .TP \f(CB\-\-bind\fR \fIkey\fR\[lB]+\fImodifier\fR\[rB]\[...]=\[lB]\fIfunction\fR\[lB],\fIflag\fR\[rB]\[...]\[rB] bind a key pressed with specified modifiers to a window manager function. \fIkey\fR is an X11 keysym name, \fImodifiers\fR are as above, but may also include \[aq]mask1\[aq], \[aq]mask2\[aq] and \[aq]altmask\[aq] to refer to the globally-configured combinations. See FUNCTIONS for a list of available functions and the flags they recognise. If \fIfunction\fR is empty, a bind is removed. .TP \f(CB\-\-bind\fR \fIbutton\fR=\[lB]\fIfunction\fR\[lB],\fIflag\fR\[rB]\[...]\[rB] bind a mouse button to a window manager function. While modifiers can be specified, they will be ignored; the button on its own will trigger if pressed within a window\[aq]s frame, or with \[aq]mask2\[aq] held anywhere within a window. Function and flags is as with key binds above. Valid buttons are \[aq]button1\[aq]\[...]\[aq]button5\[aq]. .TP \f(CB\-\-app\fR \fIname/class\fR match an application by instance name and class (for help in finding these, use the \fIxprop\fR tool to extract the \fIWM_CLASS\fR property). .IP Subsequent \f(CB\-\-geometry\fR, \f(CB\-\-dock\fR, \f(CB\-\-vdesk\fR and \f(CB\-\-fixed\fR options will apply to this match. .TP \f(CB\-g\fR, \f(CB\-\-geometry\fR \fIgeometry\fR apply a geometry (using a standard X geometry string) to applications matching the last \f(CB\-\-app\fR. .TP \f(CB\-\-dock\fR specify that application should be considered to be a dock, even if it lacks the appropriate property. .TP \f(CB\-v\fR, \f(CB\-\-vdesk\fR \fIvdesk\fR specify a default virtual desktop for applications matching the last \f(CB\-\-app\fR. Note that virtual desktops are numbered from zero. .TP \f(CB\-f\fR, \f(CB\-\-fixed\fR specify that application is to start with a fixed client window. .TP \f(CB\-h\fR, \f(CB\-\-help\fR show help .TP \f(CB\-V\fR, \f(CB\-\-version\fR show program version .PP \fBevilwm\fR will also read options, one per line, from a file called \fI.evilwmrc\fR in the user\[aq]s home directory. Options listed in a configuration file should omit the leading dash(es). Options specified on the command line override those found in the configuration file. .H1 USAGE .PP In \fBevilwm\fR, 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 (unless it is fixed, in which case blue), with other windows left as a dark grey. .PP You can use the mouse to manipulate windows either by click/dragging the single-pixel border (easier when they align with a screen edge), or by holding down Alt and doing so anywhere in the 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 the top-left, top-right, bottom-left or bottom-right of the current monitor. .TP I Show information about current window. .TP Equals Maximise current window vertically on current monitor (toggle). Holding Shift toggles horizontal maximization. .TP X Maximise current window to current monitor (toggle). .TP D Toggle visible state of docks, eg pagers and launch bars. .PP If compiled with virtual desktop support, these functions are also available: .TP F Fix or unfix current window. Fixed windows remain visible when you switch virtual desktop. .TP 1\[em]8 Switch to specific virtual desktop (internally, desktops are numbered from zero, so this actually switches to desktops 0\[em]7; this only becomes important if you use application matching). .TP Left Switch to next lower numbered virtual desktop. .TP Right Switch to next higher numbered virtual desktop. .TP A Switch to the previously selected virtual desktop. .PP In addition to the above, Alt+Tab can be used to cycle through windows. .PP To make \fBevilwm\fR reread its config, send a HUP signal to the process. To make it quit, kill it, ie send a TERM signal. .H1 FUNCTIONS .PP The keyboard and mouse button controls can be configured with the \f(CB\-\-bind\fR option to a number of built-in functions. Typically, these functions respond to an additional set of flags that modify their behaviour. .TP \f(CBdelete\fR Delete a window. This is the co-operative way of closing applications, as it sends the client a signal indicating that they should shut down. .TP \f(CBdock\fR When called with the \f(CBtoggle\fR flag, toggles visibility of any window claiming to be a dock. .TP \f(CBfix\fR With the \f(CBtoggle\fR flag, toggle whether a window is fixed (visible on all virtual desktops) or not. .TP \f(CBinfo\fR Shows extra information about the current window for as long as the key is held. .TP \f(CBkill\fR Kill a window. A more forceful way of closing an application if it is not responding to delete requests. .TP \f(CBlower\fR Lower the current window. .TP \f(CBmove\fR When bound to a button, moves a window with the mouse. .IP When bound to a key, if the \f(CBrelative\fR flag is specified, moves a window in the direction indicated by other flags: \f(CBup\fR, \f(CBdown\fR, \f(CBleft\fR or \f(CBright\fR. Without the \f(CBrelative\fR flag, moves a window in the direction specified by other flag to the edge of the monitor. .TP \f(CBnext\fR Cycle to the next window. .TP \f(CBraise\fR .PP Raises the current window. .TP \f(CBresize\fR When bound to a button, resizes a window with the mouse. .IP When bound to a key, if the \f(CBrelative\fR flag is specified, modifies the width or height of the window as indicated by other flags: \f(CBup\fR (reduce height), \f(CBdown\fR (increase height), \f(CBleft\fR (reduce width) or \f(CBright\fR (increase width). If instead the \f(CBtoggle\fR flag is specified, maximises along axes specified by other flags: \f(CBhorizontal\fR, \f(CBvertical\fR or both. .TP \f(CBspawn\fR Start a terminal. .TP \f(CBvdesk\fR With the \f(CBtoggle\fR flag specified, switch to the previously visible vdesk. With the \f(CBrelative\fR flag set, either increase vdesk number (with \f(CBup\fR flag) or decrease it (with \f(CBdown\fR flag). .IP If neither flag is specified, a numerical argument indicates which vdesk to switch to. .H1 DEFAULT BINDS .PP These are the default lists of modifiers, button and keyboard binds. The built-in binds use the globally-configurable modifier combinations \[aq]mask1\[aq], \[aq]mask2\[aq] and \[aq]altmask\[aq], making a sweeping change to a different modifier combination easy. .PP Note that \[aq]mod1\[aq] typically refers to the Alt key. .H2 Modifiers .IP .EX mask1\ control+mod1 mask2\ mod1 altmask\ shift .EE .H2 Button binds .IP .EX bind\ button1=move bind\ button2=resize bind\ button3=lower .EE .H2 Keyboard binds .IP .EX bind\ mask1+Return=spawn bind\ mask1+Escape=delete bind\ mask1+altmask+Escape=kill bind\ mask1+Insert=lower bind\ mask1+KP_Insert=lower bind\ mask1+i=info bind\ mask2+Tab=next bind\ mask1+h=move,relative+left bind\ mask1+j=move,relative+down bind\ mask1+k=move,relative+up bind\ mask1+l=move,relative+right bind\ mask1+y=move,top+left bind\ mask1+u=move,top+right bind\ mask1+b=move,bottom+left bind\ mask1+n=move,bottom+right bind\ mask1+altmask+h=resize,relative+left bind\ mask1+altmask+j=resize,relative+down bind\ mask1+altmask+k=resize,relative+up bind\ mask1+altmask+l=resize,relative+right bind\ mask1+equal=resize,toggle+v bind\ mask1+altmask+equal=resize,toggle+h bind\ mask1+x=resize,toggle+v+h bind\ mask1+d=dock,toggle bind\ mask1+f=fix,toggle bind\ mask1+1=vdesk,0 bind\ mask1+2=vdesk,1 bind\ mask1+3=vdesk,2 bind\ mask1+4=vdesk,3 bind\ mask1+5=vdesk,4 bind\ mask1+6=vdesk,5 bind\ mask1+7=vdesk,6 bind\ mask1+8=vdesk,7 bind\ mask1+Left=vdesk,relative+down bind\ mask1+Right=vdesk,relative+up bind\ mask1+a=vdesk,toggle .EE .H1 FILES .PP \fI$HOME/.evilwmrc\fR .H1 LICENCE .PP Copyright (C) 1999-2022 Ciaran Anscomb .PP 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. .H1 AEWM LICENCE .PP Copyright (c) 1998-2000 Decklin Foster. .PP 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. .PP 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. .PP Portions of the code were based on 9wm, which contains this license: .IP .EX 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. .EE .H1 SEE ALSO .PP \fBxterm\fR (1), \fBxprop\fR (1) evilwm-1.4.2/evilwm.desktop000066400000000000000000000003561433251723200157250ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=evilwm Exec=evilwm NoDisplay=false 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.4.2/evilwm.h000066400000000000000000000025051433251723200145010ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Default option values #define DEF_FONT "variable" #define DEF_FG "goldenrod" #define DEF_BG "grey50" #define DEF_BW 1 #define DEF_FC "blue" #ifdef DEBIAN #define DEF_TERM "x-terminal-emulator" #else #define DEF_TERM "xterm" #endif // Options struct options { // Display string (e.g., ":0") char *display; // Text font char *font; // Border colours char *fg; // selected (foreground) char *bg; // unselected (background) char *fc; // fixed & selected // Border width int bw; // Number of virtual desktops unsigned vdesks; // Snap to border flag int snap; // Whole screen flag (ignore monitor information) int wholescreen; #ifdef SOLIDDRAG // Solid drag disabled flag int no_solid_drag; #endif // NULL-terminated array passed to execvp() to launch terminal char **term; }; extern struct options option; #ifndef SOLIDDRAG # define option.no_solid_drag 1 #endif extern unsigned numlockmask; // Application matching struct application { char *res_name; char *res_class; int geometry_mask; int x, y; unsigned width, height; int is_dock; unsigned vdesk; }; extern struct list *applications; evilwm-1.4.2/ewmh.c000066400000000000000000000170011433251723200141260ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Extended Window Manager Hints #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "client.h" #include "display.h" #include "ewmh.h" #include "list.h" #include "log.h" #include "screen.h" #include "util.h" // 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); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Update various properties that reflect the screen geometry. void ewmh_set_screen_workarea(struct screen *s) { unsigned long workarea[4] = { 0, 0, DisplayWidth(display.dpy, s->screen), DisplayHeight(display.dpy, s->screen) }; XChangeProperty(display.dpy, s->root, X_ATOM(_NET_DESKTOP_GEOMETRY), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&workarea[2], 2); XChangeProperty(display.dpy, s->root, X_ATOM(_NET_DESKTOP_VIEWPORT), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&workarea[0], 2); XChangeProperty(display.dpy, s->root, X_ATOM(_NET_WORKAREA), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&workarea, 4); } // Update the _NET_CLIENT_LIST property for a screen. This is a simple list of // all client windows in the order they were mapped. void ewmh_set_net_client_list(struct screen *s) { Window *windows = alloc_window_array(); int i = 0; if (windows) { for (struct list *iter = clients_mapping_order; iter; iter = iter->next) { struct client *c = iter->data; if (c->screen == s) { windows[i++] = c->window; } } } XChangeProperty(display.dpy, s->root, X_ATOM(_NET_CLIENT_LIST), XA_WINDOW, 32, PropModeReplace, (unsigned char *)windows, i); } // Update the _NET_CLIENT_LIST_STACKING property for a screen. Similar to // _NET_CLIENT_LIST, but in stacking order (bottom to top). void ewmh_set_net_client_list_stacking(struct screen *s) { Window *windows = alloc_window_array(); int i = 0; if (windows) { for (struct list *iter = clients_stacking_order; iter; iter = iter->next) { struct client *c = iter->data; if (c->screen == s) { windows[i++] = c->window; } } } XChangeProperty(display.dpy, s->root, X_ATOM(_NET_CLIENT_LIST_STACKING), XA_WINDOW, 32, PropModeReplace, (unsigned char *)windows, i); } // Update _NET_CURRENT_DESKTOP for screen to currently selected vdesk. void ewmh_set_net_current_desktop(struct screen *s) { unsigned long vdesk = s->vdesk; XChangeProperty(display.dpy, s->root, X_ATOM(_NET_CURRENT_DESKTOP), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&vdesk, 1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Set the _NET_WM_ALLOWED_ACTIONS on a client advertising what we support. void ewmh_set_allowed_actions(struct client *c) { Atom allowed_actions[] = { X_ATOM(_NET_WM_ACTION_MOVE), X_ATOM(_NET_WM_ACTION_MAXIMIZE_HORZ), X_ATOM(_NET_WM_ACTION_MAXIMIZE_VERT), X_ATOM(_NET_WM_ACTION_FULLSCREEN), X_ATOM(_NET_WM_ACTION_CHANGE_DESKTOP), X_ATOM(_NET_WM_ACTION_CLOSE), // nelements reduced to omit this if not possible: X_ATOM(_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(display.dpy, c->window, X_ATOM(_NET_WM_ALLOWED_ACTIONS), XA_ATOM, 32, PropModeReplace, (unsigned char *)&allowed_actions, nelements); } // When window manager is shutting down, the _NET_WM_ALLOWED_ACTIONS property // is removed from all clients (as no WM now exists to service these actions). void ewmh_remove_allowed_actions(struct client *c) { XDeleteProperty(display.dpy, c->window, X_ATOM(_NET_WM_ALLOWED_ACTIONS)); } // These properties are removed when a window is "withdrawn". void ewmh_withdraw_client(struct client *c) { XDeleteProperty(display.dpy, c->window, X_ATOM(_NET_WM_DESKTOP)); XDeleteProperty(display.dpy, c->window, X_ATOM(_NET_WM_STATE)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Update _NET_WM_DESKTOP to reflect virtual desktop of client (including // fixed, 0xffffffff). void ewmh_set_net_wm_desktop(struct client *c) { unsigned long vdesk = c->vdesk; XChangeProperty(display.dpy, c->window, X_ATOM(_NET_WM_DESKTOP), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&vdesk, 1); } // Check _NET_WM_WINDOW_TYPE property and build a bitmask of EWMH_WINDOW_TYPE_* unsigned ewmh_get_net_wm_window_type(Window w) { Atom *aprop; unsigned long nitems, i; unsigned type = 0; if ( (aprop = get_property(w, X_ATOM(_NET_WM_WINDOW_TYPE), XA_ATOM, &nitems)) ) { for (i = 0; i < nitems; i++) { if (aprop[i] == X_ATOM(_NET_WM_WINDOW_TYPE_DESKTOP)) type |= EWMH_WINDOW_TYPE_DESKTOP; if (aprop[i] == X_ATOM(_NET_WM_WINDOW_TYPE_DOCK)) type |= EWMH_WINDOW_TYPE_DOCK; if (aprop[i] == X_ATOM(_NET_WM_WINDOW_TYPE_NOTIFICATION)) type |= EWMH_WINDOW_TYPE_NOTIFICATION; } XFree(aprop); } return type; } // Update _NET_WM_STATE_* properties on a window. Also updates // _NET_ACTIVE_WINDOW on the client's screen if necessary. void ewmh_set_net_wm_state(struct client *c) { Atom state[4]; int i = 0; if (c->oldh) state[i++] = X_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); if (c->oldw) state[i++] = X_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); if (c->oldh && c->oldw) state[i++] = X_ATOM(_NET_WM_STATE_FULLSCREEN); if (c == current) { state[i++] = X_ATOM(_NET_WM_STATE_FOCUSED); if (c->screen->active != c->window) { XChangeProperty(display.dpy, c->screen->root, X_ATOM(_NET_ACTIVE_WINDOW), XA_WINDOW, 32, PropModeReplace, (unsigned char *)&c->window, 1); c->screen->active = c->window; } } else if (c->screen->active == c->window) { Window w = None; XChangeProperty(display.dpy, c->screen->root, X_ATOM(_NET_ACTIVE_WINDOW), XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); c->screen->active = None; } XChangeProperty(display.dpy, c->window, X_ATOM(_NET_WM_STATE), XA_ATOM, 32, PropModeReplace, (unsigned char *)&state, i); } // When we receive _NET_REQUEST_FRAME_EXTENTS from an unmapped window, we are // to set _NET_FRAME_EXTENTS on that window even before it becomes managed. // TODO: set this property on a client once the border size for it is known // during client_manage_new(). void ewmh_set_net_frame_extents(Window w, unsigned long border) { unsigned long extents[4]; extents[0] = extents[1] = extents[2] = extents[3] = border; XChangeProperty(display.dpy, w, X_ATOM(_NET_FRAME_EXTENTS), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&extents, 4); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Allocate/resize an array suitable to hold all client window ids. // // XXX should test that this can be allocated before we commit to managing a // window, in the same way that we test the client structure allocation. static Window *alloc_window_array(void) { unsigned count = 0; for (struct list *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.4.2/ewmh.h000066400000000000000000000020601433251723200141320ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ #ifndef EVILWM_EWMH_H_ #define EVILWM_EWMH_H_ // Extended Window Manager Hints // https://specifications.freedesktop.org/wm-spec/latest/ #include // 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) struct client; struct screen; void ewmh_set_screen_workarea(struct screen *s); void ewmh_set_net_client_list(struct screen *s); void ewmh_set_net_client_list_stacking(struct screen *s); void ewmh_set_net_current_desktop(struct screen *s); void ewmh_set_allowed_actions(struct client *c); void ewmh_remove_allowed_actions(struct client *c); void ewmh_withdraw_client(struct client *c); void ewmh_set_net_wm_desktop(struct client *c); unsigned ewmh_get_net_wm_window_type(Window w); void ewmh_set_net_wm_state(struct client *c); void ewmh_set_net_frame_extents(Window w, unsigned long border); #endif evilwm-1.4.2/func.c000066400000000000000000000130321433251723200141210ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Bindable functions // All functions present the same call interface, so they can be mapped // reasonably arbitrarily to user inputs. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "client.h" #include "display.h" #include "evilwm.h" #include "func.h" #include "list.h" #include "log.h" #include "screen.h" #include "util.h" static void do_client_move(struct client *c) { if (abs(c->x) == c->border && c->oldw != 0) c->x = 0; if (abs(c->y) == c->border && c->oldh != 0) c->y = 0; if (c->width < c->min_width) { c->width = c->min_width; } if (c->max_width && c->width > c->max_width) { c->width = c->max_width; } if (c->height < c->min_height) { c->height = c->min_height; } if (c->max_height && c->height > c->max_height) { c->height = c->max_height; } client_moveresizeraise(c); #ifdef WARP_POINTER setmouse(c->window, c->width + c->border - 1, c->height + c->border - 1); #endif discard_enter_events(c); } void func_delete(void *sptr, XEvent *e, unsigned flags) { if (!(flags & FL_CLIENT)) return; struct client *c = sptr; (void)e; send_wm_delete(c, flags & FL_VALUEMASK); } void func_dock(void *sptr, XEvent *e, unsigned flags) { if (!(flags & FL_SCREEN)) return; struct screen *current_screen = sptr; (void)e; if (flags & FL_TOGGLE) { set_docks_visible(current_screen, !current_screen->docks_visible); } } void func_info(void *sptr, XEvent *e, unsigned flags) { if (!(flags & FL_CLIENT)) return; struct client *c = sptr; client_show_info(c, e); } void func_lower(void *sptr, XEvent *e, unsigned flags) { struct client *c = sptr; if (!(flags & FL_CLIENT) || !c) return; (void)e; client_lower(c); } void func_move(void *sptr, XEvent *e, unsigned flags) { struct client *c = sptr; if (!(flags & FL_CLIENT) || !c) return; struct monitor *monitor = client_monitor(c, NULL); int width_inc = (c->width_inc > 1) ? c->width_inc : 16; int height_inc = (c->height_inc > 1) ? c->height_inc : 16; if (e->type == ButtonPress) { XButtonEvent *xbutton = (XButtonEvent *)e; client_move_drag(c, xbutton->button); return; } if (flags & FL_RELATIVE) { if (flags & FL_RIGHT) { c->x += width_inc; } if (flags & FL_LEFT) { c->x -= width_inc; } if (flags & FL_DOWN) { c->y += height_inc; } if (flags & FL_UP) { c->y -= height_inc; } } else { if (flags & FL_RIGHT) { c->x = monitor->x + monitor->width - c->width-c->border; } if (flags & FL_LEFT) { c->x = monitor->x + c->border; } if (flags & FL_BOTTOM) { c->y = monitor->y + monitor->height - c->height-c->border; } if (flags & FL_TOP) { c->y = monitor->y + c->border; } } do_client_move(c); } void func_next(void *sptr, XEvent *e, unsigned flags) { (void)sptr; (void)flags; if (e->type != KeyPress) return; XKeyEvent *xkey = (XKeyEvent *)e; client_select_next(); if (XGrabKeyboard(display.dpy, xkey->root, False, GrabModeAsync, GrabModeAsync, CurrentTime) == GrabSuccess) { XEvent ev; do { XMaskEvent(display.dpy, KeyPressMask|KeyReleaseMask, &ev); if (ev.type == KeyPress && ev.xkey.keycode == xkey->keycode) client_select_next(); } while (ev.type == KeyPress || ev.xkey.keycode == xkey->keycode); XUngrabKeyboard(display.dpy, CurrentTime); } clients_tab_order = list_to_head(clients_tab_order, current); } void func_raise(void *sptr, XEvent *e, unsigned flags) { struct client *c = sptr; if (!(flags & FL_CLIENT) || !c) return; (void)e; client_raise(c); } void func_resize(void *sptr, XEvent *e, unsigned flags) { struct client *c = sptr; if (!(flags & FL_CLIENT) || !c) return; int width_inc = (c->width_inc > 1) ? c->width_inc : 16; int height_inc = (c->height_inc > 1) ? c->height_inc : 16; if (e->type == ButtonPress) { XButtonEvent *xbutton = (XButtonEvent *)e; client_resize_sweep(c, xbutton->button); return; } if (flags & FL_RELATIVE) { if (flags & FL_RIGHT) { c->width += width_inc; } if (flags & FL_LEFT) { c->width -= width_inc; } if (flags & FL_DOWN) { c->height += height_inc; } if (flags & FL_UP) { c->height -= height_inc; } } else if (flags & FL_TOGGLE) { int hv = 0; if ((flags & FL_HORZ) == FL_HORZ) { hv |= MAXIMISE_HORZ; } if ((flags & FL_VERT) == FL_VERT) { hv |= MAXIMISE_VERT; } client_maximise(c, NET_WM_STATE_TOGGLE, hv); return; } do_client_move(c); } void func_spawn(void *sptr, XEvent *e, unsigned flags) { // TODO: configurable array of commands to run indexed by FL_VALUEMASK? (void)sptr; (void)e; (void)flags; spawn((const char *const *)option.term); } void func_vdesk(void *sptr, XEvent *e, unsigned flags) { (void)e; if (flags & FL_CLIENT) { struct client *c = sptr; if (flags & FL_TOGGLE) { if (is_fixed(c)) { client_to_vdesk(c, c->screen->vdesk); } else { client_to_vdesk(c, VDESK_FIXED); } } } else if (flags & FL_SCREEN) { struct screen *current_screen = sptr; if (flags & FL_TOGGLE) { switch_vdesk(current_screen, current_screen->old_vdesk); } else if (flags & FL_RELATIVE) { if (flags & FL_DOWN) { if (current_screen->vdesk > 0) { switch_vdesk(current_screen, current_screen->vdesk - 1); } } if (flags & FL_UP) { if (current_screen->vdesk < VDESK_MAX) { switch_vdesk(current_screen, current_screen->vdesk + 1); } } } else { int v = flags & FL_VALUEMASK; switch_vdesk(current_screen, v); } } } evilwm-1.4.2/func.h000066400000000000000000000026161433251723200141340ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ #ifndef EVILWM_FUNC_H_ #define EVILWM_FUNC_H_ // Function flags #define FL_VALUEMASK (0xff) // 8 bits of value #define FL_UP (1 << 8) #define FL_DOWN (1 << 9) #define FL_LEFT (1 << 10) #define FL_RIGHT (1 << 11) #define FL_TOP (1 << 12) #define FL_BOTTOM (1 << 13) #define FL_RELATIVE (1 << 14) #define FL_SCREEN (1 << 15) #define FL_CLIENT (1 << 16) #define FL_TOGGLE (1 << 18) #define FL_CORNERMASK (FL_LEFT|FL_RIGHT|FL_TOP|FL_BOTTOM) #define FL_TOPLEFT (FL_LEFT|FL_TOP) #define FL_TOPRIGHT (FL_RIGHT|FL_TOP) #define FL_BOTTOMLEFT (FL_LEFT|FL_BOTTOM) #define FL_BOTTOMRIGHT (FL_RIGHT|FL_BOTTOM) #define FL_MAX (FL_LEFT|FL_RIGHT|FL_TOP|FL_BOTTOM) #define FL_VERT (FL_TOP|FL_BOTTOM) #define FL_HORZ (FL_LEFT|FL_RIGHT) // Bindable functions void func_delete(void *, XEvent *, unsigned); void func_dock(void *, XEvent *, unsigned); void func_info(void *, XEvent *, unsigned); void func_lower(void *, XEvent *, unsigned); void func_move(void *, XEvent *, unsigned); void func_next(void *, XEvent *, unsigned); void func_raise(void *, XEvent *, unsigned); void func_resize(void *, XEvent *, unsigned); void func_spawn(void *, XEvent *, unsigned); void func_vdesk(void *, XEvent *, unsigned); #endif evilwm-1.4.2/list.c000066400000000000000000000044111433251723200141420ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Basic linked list handling code. Operations that modify the list // return the new list head. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "list.h" // Wrap data in a new list container static struct list *list_new(void *data) { struct list *new = malloc(sizeof(*new)); 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); if (!elem) return list; if (!list) return elem; elem->next = before; if (before == list) return elem; for (struct list *iter = list; iter; iter = iter->next) { if (iter->next == before) { iter->next = elem; return list; } if (!iter->next) { elem->next = NULL; iter->next = elem; return list; } } abort(); } // 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) { if (!data) return list; for (struct list **elemp = &list; *elemp; elemp = &(*elemp)->next) { if ((*elemp)->data == data) { struct list *elem = *elemp; *elemp = elem->next; free(elem); break; } } 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) { for (struct list *elem = list; elem; elem = elem->next) { if (elem->data == data) return elem; } return NULL; } evilwm-1.4.2/list.h000066400000000000000000000016661433251723200141600ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Basic linked list handling code. Operations that modify the list // return the new list head. #ifndef EVILWM_LIST_H__ #define EVILWM_LIST_H__ // Each list element is of this deliberately transparent type: 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 evilwm-1.4.2/log.c000066400000000000000000000052601433251723200137530ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Logging support functions #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "display.h" #include "log.h" #if defined(DEBUG) || defined(XDEBUG) int log_indent = 0; #endif #ifdef DEBUG const char *debug_atom_name(Atom a) { static char buf[48]; char *atom_name = XGetAtomName(display.dpy, a); strncpy(buf, atom_name, sizeof(buf)); buf[sizeof(buf)-1] = 0; return buf; } #endif #ifdef XDEBUG 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)gravity < 11) ? gravities[gravity] : gravities[11]; } void debug_wm_normal_hints(XSizeHints *size) { if (size->flags & 15) { LOG_INDENT(); 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)); } } static const char *map_state_string(int map_state) { const char *map_states[4] = { "IsUnmapped", "IsUnviewable", "IsViewable", "Unknown" }; return ((unsigned)map_state < 3) ? map_states[map_state] : map_states[3]; } void debug_window_attributes(XWindowAttributes *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); } #endif evilwm-1.4.2/log.h000066400000000000000000000043111433251723200137540ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Debugging macros and support functions. #ifndef EVILWM_LOG_H__ #define EVILWM_LOG_H__ #include #if defined(DEBUG) || defined(XDEBUG) # include #endif #ifdef XDEBUG # include # include #endif #if defined(DEBUG) || defined(XDEBUG) extern int log_indent; # define LOG_INDENT() do { for (int ii = 0; ii < log_indent; ii++) fprintf(stderr, " "); } while (0) #endif #define LOG_INFO(...) printf(__VA_ARGS__); #define LOG_ERROR(...) fprintf(stderr, __VA_ARGS__); // Debug macros: // // LOG_ENTER(...) on function entry; prints message, increases indent level // LOG_LEAVE(...) on function exit; decreases indent level, prints message // LOG_DEBUG(...) print message at current indent level // LOG_DEBUG_(...) print continuation message (no indent) #ifdef DEBUG # 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__) const char *debug_atom_name(Atom a); #else # define LOG_ENTER(...) # define LOG_LEAVE(...) # define LOG_DEBUG(...) # define LOG_DEBUG_(...) #endif // X call debugging macros: #ifdef XDEBUG # define LOG_XENTER(...) do { LOG_INDENT(); log_indent++; fprintf(stderr, __VA_ARGS__); fprintf(stderr, " at %s:%d\n", __FILE__, __LINE__); } while (0) # define LOG_XLEAVE(...) do { if (log_indent > 0) log_indent--; } while (0) # define LOG_XDEBUG(...) do { LOG_INDENT(); fprintf(stderr, __VA_ARGS__); } while (0) # define LOG_XDEBUG_(...) fprintf(stderr, __VA_ARGS__) // Print window geometry void debug_window_attributes(XWindowAttributes *attr); // Dump size hints void debug_wm_normal_hints(XSizeHints *size); #else # define LOG_XENTER(...) # define LOG_XLEAVE(...) # define LOG_XDEBUG(...) # define LOG_XDEBUG_(...) # define debug_window_attributes(a) # define debug_wm_normal_hints(s) #endif #endif evilwm-1.4.2/main.c000066400000000000000000000237741433251723200141300ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // main() function parses options and kicks off the main event loop. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "bind.h" #include "client.h" #include "display.h" #include "events.h" #include "evilwm.h" #include "list.h" #include "log.h" #include "xalloc.h" #include "xconfig.h" #define CONFIG_FILE ".evilwmrc" #define xstr(s) str(s) #define str(s) #s // WM will manage/process events/unmanage until this flag is set static _Bool wm_exit; struct options option; static struct list *opt_bind = NULL; static char *opt_grabmask1 = NULL; static char *opt_grabmask2 = NULL; static char *opt_altmask = NULL; unsigned numlockmask = 0; struct list *applications = NULL; static void set_bind(const char *arg); static void set_app(const char *arg); static void set_app_geometry(const char *arg); static void set_app_dock(void); static void set_app_vdesk(const char *arg); static void set_app_fixed(void); static struct xconfig_option evilwm_options[] = { { XCONFIG_STRING, "fn", { .s = &option.font } }, { XCONFIG_STRING, "display", { .s = &option.display } }, { XCONFIG_UINT, "numvdesks", { .u = &option.vdesks } }, { XCONFIG_STRING, "fg", { .s = &option.fg } }, { XCONFIG_STRING, "bg", { .s = &option.bg } }, { XCONFIG_STRING, "fc", { .s = &option.fc } }, { XCONFIG_INT, "bw", { .i = &option.bw } }, { XCONFIG_STR_LIST, "term", { .sl = &option.term } }, { XCONFIG_INT, "snap", { .i = &option.snap } }, { XCONFIG_BOOL, "wholescreen", { .i = &option.wholescreen } }, { XCONFIG_STRING, "mask1", { .s = &opt_grabmask1 } }, { XCONFIG_STRING, "mask2", { .s = &opt_grabmask2 } }, { XCONFIG_STRING, "altmask", { .s = &opt_altmask } }, { XCONFIG_CALL_1, "bind", { .c1 = &set_bind } }, { XCONFIG_CALL_1, "app", { .c1 = &set_app } }, { XCONFIG_CALL_1, "geometry", { .c1 = &set_app_geometry } }, { XCONFIG_CALL_1, "g", { .c1 = &set_app_geometry } }, { XCONFIG_CALL_0, "dock", { .c0 = &set_app_dock } }, { XCONFIG_CALL_1, "vdesk", { .c1 = &set_app_vdesk } }, { XCONFIG_CALL_1, "v", { .c1 = &set_app_vdesk } }, { XCONFIG_CALL_0, "fixed", { .c0 = &set_app_fixed } }, { XCONFIG_CALL_0, "f", { .c0 = &set_app_fixed } }, { XCONFIG_CALL_0, "s", { .c0 = &set_app_fixed } }, #ifdef SOLIDDRAG { XCONFIG_BOOL, "nosoliddrag", { .i = &option.no_solid_drag } }, #endif { XCONFIG_END, NULL, { .i = NULL } } }; static void handle_signal(int signo); static void helptext(void) { puts( "Usage: evilwm [OPTION]...\n" "evilwm is a minimalist window manager for X11.\n" "\n Options:\n" " --display DISPLAY X display [from environment]\n" " --term PROGRAM binary used to spawn terminal [" DEF_TERM "]\n" " --fn FONTNAME font used to display text [" DEF_FONT "]\n" " --fg COLOUR colour of active window frames [" DEF_FG "]\n" " --fc COLOUR colour of fixed window frames [" DEF_FC "]\n" " --bg COLOUR colour of inactive window frames [" DEF_BG "]\n" " --bw PIXELS window border width [" xstr(DEF_BW) "]\n" " --snap PIXELS snap distance when dragging windows [0; disabled]\n" " --wholescreen ignore monitor geometries when maximising\n" " --numvdesks N total number of virtual desktops [8]\n" #ifdef SOLIDDRAG " --nosoliddrag draw outline when moving or resizing\n" #endif " --mask1 MASK modifiers for most keyboard controls [control+alt]\n" " --mask2 MASK modifiers for mouse button controls [alt]\n" " --altmask MASK modifiers selecting alternate control behaviour\n" " --bind CTL[=FUNC] bind (or unbind) input to window manager function\n" "\n Application matching options:\n" " --app NAME/CLASS match application by instance name & class\n" " -g, --geometry GEOM apply X geometry to matched application\n" " --dock treat matched app as a dock\n" " -v, --vdesk VDESK move app to numbered vdesk (indexed from 0)\n" " -f, --fixed matched app should start fixed\n" "\n Other options:\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" "\nWhen binding a control, CTL contains a (case-sensitive) list of modifiers,\n" "buttons or keys (using the X11 keysym name) and FUNC lists a function\n" "name and optional extra flags. List entries can be separated with ','\n" "or '+'. If FUNC is missing or empty, the control is unbound. Modifiers are\n" "ignored when binding buttons.\n" "\nModifiers: mask1, mask2, altmask, shift, control, alt, mod1..mod5\n" "Buttons: button1..button5\n" "Functions: delete, dock, fix, info, kill, lower, move, next, resize,\n" " spawn, vdesk\n" "Flags: up, down, left, right, top, bottom, relative (rel), drag, toggle,\n" " vertical (v), horizontal (h)\n" ); } static const char *default_options[] = { "display", "term " DEF_TERM, "fn " DEF_FONT, "fg " DEF_FG, "bg " DEF_BG, "bw " xstr(DEF_BW), "fc " DEF_FC, "numvdesks 8", }; #define NUM_DEFAULT_OPTIONS (sizeof(default_options)/sizeof(default_options[0])) int main(int argc, char *argv[]) { struct sigaction act; int argn = 1, ret; Window old_current_window = None; 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); // Run until something signals to quit. wm_exit = 0; while (!wm_exit) { // Default options option = (struct options){0}; for (unsigned i = 0; i < NUM_DEFAULT_OPTIONS; i++) xconfig_parse_line(evilwm_options, default_options[i]); // Read configuration file 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); } // Parse CLI options 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]); fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); exit(1); } else if (ret == XCONFIG_BAD_OPTION) { if (0 == strcmp(argv[argn], "-h") || 0 == strcmp(argv[argn], "--help")) { helptext(); exit(0); } else if (0 == strcmp(argv[argn], "-V") || 0 == strcmp(argv[argn], "--version")) { LOG_INFO("evilwm version " VERSION "\n"); exit(0); } else { fprintf(stderr, "%s: unrecognised option '%s'\n", argv[0], argv[argn]); fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); exit(1); } } bind_modifier("mask1", opt_grabmask1); bind_modifier("mask2", opt_grabmask2); bind_modifier("altmask", opt_altmask); bind_reset(); while (opt_bind) { char *arg = opt_bind->data; opt_bind = list_delete(opt_bind, arg); char *ctlstr = strtok(arg, "="); if (!ctlstr) { free(arg); continue; } char *funcstr = strtok(NULL, ""); bind_control(ctlstr, funcstr); free(arg); } // Open display only if not already open if (!display.dpy) { display_open(); } // Manage all eligible clients across all screens display_manage_clients(); // Restore "old current window", if known if (old_current_window != None) { struct client *c = find_client(old_current_window); if (c) select_client(c); } // Event loop will run until interrupted end_event_loop = 0; event_main_loop(); // Record "old current window" across SIGHUPs old_current_window = current ? current->window : None; // Important to clean up anything now that might be reallocated // after rereading config, re-managing windows, etc. // Free any allocated strings in parsed options xconfig_free(evilwm_options); // Free application configuration while (applications) { struct application *app = applications->data; applications = list_delete(applications, app); if (app->res_name) free(app->res_name); if (app->res_class) free(app->res_class); free(app); } display_unmanage_clients(); XSync(display.dpy, True); } // Close display display_close(); return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Option parsing callbacks static void set_bind(const char *arg) { char *argdup = xstrdup(arg); if (!argdup) return; opt_bind = list_prepend(opt_bind, argdup); } static void set_app(const char *arg) { struct application *new = xmalloc(sizeof(struct application)); char *tmp; new->res_name = new->res_class = NULL; new->geometry_mask = 0; new->is_dock = 0; new->vdesk = VDESK_NONE; if ((tmp = strchr(arg, '/'))) { *(tmp++) = 0; } if (*arg) { new->res_name = xstrdup(arg); } if (tmp && *tmp) { new->res_class = xstrdup(tmp); } applications = list_prepend(applications, new); } static void set_app_geometry(const char *arg) { if (applications) { struct 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) { struct application *app = applications->data; app->is_dock = 1; } } static void set_app_vdesk(const char *arg) { unsigned v = strtoul(arg, NULL, 0); if (applications && valid_vdesk(v)) { struct application *app = applications->data; app->vdesk = v; } } static void set_app_fixed(void) { if (applications) { struct application *app = applications->data; app->vdesk = VDESK_FIXED; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Signals configured in main() trigger a clean shutdown static void handle_signal(int signo) { if (signo != SIGHUP) { wm_exit = 1; } end_event_loop = 1; } evilwm-1.4.2/screen.c000066400000000000000000000320271433251723200144520ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Screen management. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifdef RANDR #include #endif #include "bind.h" #include "client.h" #include "display.h" #include "evilwm.h" #include "ewmh.h" #include "list.h" #include "log.h" #include "screen.h" #include "util.h" #include "xalloc.h" // Set up DISPLAY environment variable to use static char *screen_to_display_str(int i) { char *ds = DisplayString(display.dpy); char *dpy_str = xmalloc(20 + strlen(ds)); strcpy(dpy_str, "DISPLAY="); strcat(dpy_str, ds); char *colon = strrchr(dpy_str, ':'); if (!colon || display.nscreens < 2) return dpy_str; char *dot = strchr(colon, '.'); if (!dot) dot = colon + strlen(colon); snprintf(dot, 12, ".%d", i); return dpy_str; } // Called once per screen when display is being initialised. void screen_init(struct screen *s) { int i = s->screen; // Used to set the DISPLAY environment variable to something like // ":0.x" depending on which screen a terminal is launched from. s->display = screen_to_display_str(i); s->root = RootWindow(display.dpy, i); #ifdef RANDR s->nmonitors = 0; s->monitors = NULL; if (display.have_randr) { XRRSelectInput(display.dpy, s->root, RRScreenChangeNotifyMask); } #endif screen_probe_monitors(s); // Default to first virtual desktop. TODO: consider checking the // _NET_WM_DESKTOP property of the window with focus when we start to // change this default? s->vdesk = 0; // In case the visual for this screen uses a colourmap, ensure our // border colours are in it. XColor dummy; XAllocNamedColor(display.dpy, DefaultColormap(display.dpy, i), option.fg, &s->fg, &dummy); XAllocNamedColor(display.dpy, DefaultColormap(display.dpy, i), option.bg, &s->bg, &dummy); XAllocNamedColor(display.dpy, DefaultColormap(display.dpy, i), option.fc, &s->fc, &dummy); // When dragging an outline, we use an inverting graphics context // (GCFunction + GXinvert) so that simply drawing it again will erase // it from the screen. XGCValues gv; gv.function = GXinvert; gv.subwindow_mode = IncludeInferiors; gv.line_width = 1; // option.bw gv.font = display.font->fid; s->invert_gc = XCreateGC(display.dpy, s->root, GCFunction | GCSubwindowMode | GCLineWidth | GCFont, &gv); // We handle events to the root window: // SubstructureRedirectMask - create, destroy, configure window notifications // SubstructureNotifyMask - configure window requests // EnterWindowMask - enter events // ColormapChangeMask - when a new colourmap is needed XSetWindowAttributes attr; attr.event_mask = SubstructureRedirectMask | SubstructureNotifyMask | EnterWindowMask | ColormapChangeMask; XChangeWindowAttributes(display.dpy, s->root, CWEventMask, &attr); // Grab the various keyboard shortcuts bind_grab_for_screen(s); s->active = None; s->docks_visible = 1; Atom supported[] = { X_ATOM(_NET_CLIENT_LIST), X_ATOM(_NET_CLIENT_LIST_STACKING), X_ATOM(_NET_NUMBER_OF_DESKTOPS), X_ATOM(_NET_DESKTOP_GEOMETRY), X_ATOM(_NET_DESKTOP_VIEWPORT), X_ATOM(_NET_CURRENT_DESKTOP), X_ATOM(_NET_ACTIVE_WINDOW), X_ATOM(_NET_WORKAREA), X_ATOM(_NET_SUPPORTING_WM_CHECK), X_ATOM(_NET_CLOSE_WINDOW), X_ATOM(_NET_MOVERESIZE_WINDOW), X_ATOM(_NET_RESTACK_WINDOW), X_ATOM(_NET_REQUEST_FRAME_EXTENTS), X_ATOM(_NET_WM_DESKTOP), X_ATOM(_NET_WM_WINDOW_TYPE), X_ATOM(_NET_WM_WINDOW_TYPE_DESKTOP), X_ATOM(_NET_WM_WINDOW_TYPE_DOCK), X_ATOM(_NET_WM_STATE), X_ATOM(_NET_WM_STATE_MAXIMIZED_VERT), X_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ), X_ATOM(_NET_WM_STATE_HIDDEN), X_ATOM(_NET_WM_STATE_FULLSCREEN), X_ATOM(_NET_WM_STATE_FOCUSED), X_ATOM(_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. X_ATOM(_NET_WM_ACTION_MOVE), X_ATOM(_NET_WM_ACTION_RESIZE), X_ATOM(_NET_WM_ACTION_MAXIMIZE_HORZ), X_ATOM(_NET_WM_ACTION_MAXIMIZE_VERT), X_ATOM(_NET_WM_ACTION_FULLSCREEN), X_ATOM(_NET_WM_ACTION_CHANGE_DESKTOP), X_ATOM(_NET_WM_ACTION_CLOSE), X_ATOM(_NET_FRAME_EXTENTS), }; unsigned long num_desktops = option.vdesks; unsigned long vdesk = s->vdesk; unsigned long pid = getpid(); s->supporting = XCreateSimpleWindow(display.dpy, s->root, 0, 0, 1, 1, 0, 0, 0); XChangeProperty(display.dpy, s->root, X_ATOM(_NET_SUPPORTED), XA_ATOM, 32, PropModeReplace, (unsigned char *)&supported, sizeof(supported) / sizeof(Atom)); XChangeProperty(display.dpy, s->root, X_ATOM(_NET_NUMBER_OF_DESKTOPS), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&num_desktops, 1); XChangeProperty(display.dpy, s->root, X_ATOM(_NET_CURRENT_DESKTOP), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&vdesk, 1); XChangeProperty(display.dpy, s->root, X_ATOM(_NET_ACTIVE_WINDOW), XA_WINDOW, 32, PropModeReplace, (unsigned char *)&s->active, 1); XChangeProperty(display.dpy, s->root, X_ATOM(_NET_SUPPORTING_WM_CHECK), XA_WINDOW, 32, PropModeReplace, (unsigned char *)&s->supporting, 1); XChangeProperty(display.dpy, s->supporting, X_ATOM(_NET_SUPPORTING_WM_CHECK), XA_WINDOW, 32, PropModeReplace, (unsigned char *)&s->supporting, 1); XChangeProperty(display.dpy, s->supporting, X_ATOM(_NET_WM_NAME), XA_STRING, 8, PropModeReplace, (const unsigned char *)"evilwm", 6); XChangeProperty(display.dpy, s->supporting, X_ATOM(_NET_WM_PID), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); ewmh_set_screen_workarea(s); } void screen_deinit(struct screen *s) { XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_SUPPORTED)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_CLIENT_LIST)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_CLIENT_LIST_STACKING)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_NUMBER_OF_DESKTOPS)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_DESKTOP_GEOMETRY)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_DESKTOP_VIEWPORT)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_CURRENT_DESKTOP)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_ACTIVE_WINDOW)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_WORKAREA)); XDeleteProperty(display.dpy, s->root, X_ATOM(_NET_SUPPORTING_WM_CHECK)); XDestroyWindow(display.dpy, s->supporting); free(s->monitors); } // Get a list of monitors for the screen. If Randr >= 1.5 is unavailable, or // the "wholescreen" option has been specified, assume a single monitor // covering the whole screen. void screen_probe_monitors(struct screen *s) { #if defined(RANDR) && (RANDR_MAJOR == 1) && (RANDR_MINOR >= 5) if (display.have_randr && !option.wholescreen) { int nmonitors; XRRMonitorInfo *monitors; // Populate list of active monitors LOG_XENTER("XRRGetMonitors(screen=%d)", s->screen); monitors = XRRGetMonitors(display.dpy, s->root, True, &nmonitors); if (monitors) { struct monitor *new_monitors = s->monitors; if (nmonitors != s->nmonitors) { // allocating in multiple of 4 should stop us // having to reallocate at all in the most // common uses int n = (nmonitors | 3) + 1; new_monitors = realloc(s->monitors, n * sizeof(struct monitor)); } if (new_monitors) { s->monitors = new_monitors; for (int i = 0; i < nmonitors; i++) { LOG_XDEBUG("monitor %d: %dx%d+%d+%d\n", i, monitors[i].width, monitors[i].height, monitors[i].x, monitors[i].y); s->monitors[i].x = monitors[i].x; s->monitors[i].y = monitors[i].y; s->monitors[i].width = monitors[i].width; s->monitors[i].height = monitors[i].height; s->monitors[i].area = monitors[i].width * monitors[i].height; } s->nmonitors = nmonitors; } LOG_XLEAVE(); XRRFreeMonitors(monitors); return; } LOG_XLEAVE(); } #endif s->nmonitors = 1; if (!s->monitors) { s->monitors = xmalloc(sizeof(struct monitor)); } s->monitors[0].x = 0; s->monitors[0].y = 0; s->monitors[0].width = DisplayWidth(display.dpy, s->screen); s->monitors[0].height = DisplayHeight(display.dpy, s->screen); s->monitors[0].area = s->monitors[0].width * s->monitors[0].height; } // Switch virtual desktop. Hides clients on different vdesks, shows clients on // the selected one. Docks are always shown (unless user has hidden them // explicitly). Fixed clients are always shown. void switch_vdesk(struct screen *s, unsigned v) { #ifdef DEBUG int nhidden = 0, nraised = 0; #endif // Sanity check vdesk number. if (!valid_vdesk(v)) return; // Selected == current? Do nothing. if (v == s->vdesk) return; LOG_ENTER("switch_vdesk(screen=%d, from=%d, to=%d)", s->screen, s->vdesk, v); // If current client is not fixed, deselect it. An enter event from // mapping clients may select a new one. if (current && !is_fixed(current)) { select_client(NULL); } for (struct list *iter = clients_tab_order; iter; iter = iter->next) { struct client *c = iter->data; if (c->screen != s) continue; if (c->vdesk == s->vdesk) { client_hide(c); #ifdef DEBUG nhidden++; #endif } else if (c->vdesk == v) { if (!c->is_dock || s->docks_visible) client_show(c); #ifdef DEBUG nraised++; #endif } } // Store previous vdesk, so that user may toggle back to it s->old_vdesk = s->vdesk; // Update current vdesk (including EWMH properties) s->vdesk = v; ewmh_set_net_current_desktop(s); LOG_DEBUG("%d hidden, %d raised\n", nhidden, nraised); LOG_LEAVE(); } // Set whether docks are visible on the current screen. void set_docks_visible(struct screen *s, int is_visible) { LOG_ENTER("set_docks_visible(screen=%d, is_visible=%d)", s->screen, is_visible); s->docks_visible = is_visible; // Traverse client list and hide or show any docks on this screen as // appropriate. for (struct list *iter = clients_tab_order; iter; iter = iter->next) { struct client *c = iter->data; if (c->screen != s) continue; if (c->is_dock) { if (is_visible) { // XXX I've assumed that if you want to see // them, you also want them raised... if (is_fixed(c) || (c->vdesk == s->vdesk)) { client_show(c); client_raise(c); } } else { client_hide(c); } } } LOG_LEAVE(); } #ifdef RANDR // If a screen has been resized (due to RandR), some windows have the // possibility of: // // a) not being visible // b) being vertically/horizontally maximised to the wrong extent // // Our approach is now to: // // 1) record client positions as proportions of offset into their current // monitor // 2) apply screen geometry changes and rescan list of monitors // 3) move any client that no longer intersects a monitor to the same // proportional position within its nearest monitor // 4) adjust geometry of maximised clients to any "new" monitor // Record old monitor offset for each client before resize. void scan_clients_before_resize(struct screen *s) { for (struct list *iter = clients_tab_order; iter; iter = iter->next) { struct client *c = iter->data; // only handle clients on the screen being resized if (c->screen != s) continue; struct monitor *m = client_monitor(c, NULL); int mw = m->width; int mh = m->height; int cx = c->oldw ? c->oldx : c->x; int cy = c->oldh ? c->oldy : c->y; c->mon_offx = (double)(cx - m->x) / (double)mw; c->mon_offy = (double)(cy - m->y) / (double)mh; } } // Fix up maximised and non-intersecting clients after resize. void fix_screen_after_resize(struct screen *s) { for (struct list *iter = clients_tab_order; iter; iter = iter->next) { struct client *c = iter->data; // only handle clients on the screen being resized if (c->screen != s) continue; Bool intersects; struct monitor *m = client_monitor(c, &intersects); if (c->oldw) { // horiz maximised: update width, update old x pos c->x = m->x - c->border; c->width = m->width; c->oldx = m->x + c->mon_offx * m->width; } else { // horiz normal: update x pos if (!intersects) c->x = m->x + c->mon_offx * m->width; } if (c->oldh) { // vert maximised: update height, update old y pos c->y = m->y - c->border; c->height = m->height; c->oldy = m->y + c->mon_offy * m->height; } else { // vert normal: update y pos if (!intersects) c->y = m->y + c->mon_offy * m->height; } client_moveresize(c); } } #endif // Find screen corresponding to specified root window. struct screen *find_screen(Window root) { for (int i = 0; i < display.nscreens; i++) { if (display.screens[i].root == root) return &display.screens[i]; } return NULL; } // Find screen corresponding to the root window the pointer is currently on. struct screen *find_current_screen(void) { Window cur_root; Window dw; // dummy int di; // dummy unsigned dui; // dummy // XQueryPointer is useful for getting the current pointer root XQueryPointer(display.dpy, display.screens[0].root, &cur_root, &dw, &di, &di, &di, &di, &dui); return find_screen(cur_root); } evilwm-1.4.2/screen.h000066400000000000000000000045041433251723200144560ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Screen management. // // One evilwm process can manage one display, but a display can comprise // multiple screens. Each screen has its own root window and generally, // clients can't move from one to the other. // // Xinerama and Xrandr extensions may allow combining multiple monitors into // one logical "screen". #ifndef EVILWM_SCREEN_H_ #define EVILWM_SCREEN_H_ #include #include #ifdef RANDR #include #endif struct monitor { int x, y; int width, height; int area; }; struct screen { int screen; // screen index for display char *display; // DISPLAY string, eg ":0.0" Window root; // root window of screen Window supporting; // dummy window for EWMH Window active; // current _NET_ACTIVE_WINDOW value for root GC invert_gc; // used to draw outlines XColor fg, bg, fc; // allocated colours; active, inactive, fixed unsigned vdesk; // current vdesk for screen unsigned old_vdesk; // previous vdesk, so user may toggle back to it int docks_visible; // docks can be toggled visible/hidden // from randr, or just one entry with screen dimensions if no randr int nmonitors; // number of monitors struct monitor *monitors; }; // Setup and shutdown. void screen_init(struct screen *s); void screen_deinit(struct screen *s); // Probe monitors (Randr) void screen_probe_monitors(struct screen *s); // Switch vdesks; hides & shows clients accordingly. void switch_vdesk(struct screen *s, unsigned v); // Show or hide docks. void set_docks_visible(struct screen *s, int is_visible); // Record old monitor size in each client before resize. void scan_clients_before_resize(struct screen *s); // Xrandr allows a screen to resize; this function adjusts the position of // clients so they remain visible. void fix_screen_after_resize(struct screen *s); // Find screen corresponding to specified root window. struct screen *find_screen(Window root); // Find screen corresponding to the root window the pointer is currently on. struct screen *find_current_screen(void); // Grab all the keys we're interested in for the specified screen. void grab_keys_for_screen(struct screen *s); #endif evilwm-1.4.2/util.c000066400000000000000000000141521433251723200141470ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Miscellaneous utility functions #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "client.h" #include "display.h" #include "events.h" #include "evilwm.h" #include "log.h" #include "screen.h" #include "util.h" // For get_property() #define MAXIMUM_PROPERTY_LENGTH 4096 // Error handler interaction int ignore_xerror = 0; volatile Window initialising = None; // Spawn a subprocess by fork()ing twice so we don't have to worry about // SIGCHLDs. void spawn(const char *const cmd[]) { struct screen *current_screen = find_current_screen(); pid_t pid; if (current_screen && current_screen->display) putenv(current_screen->display); if (!(pid = fork())) { // Put first fork in a new session 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); break; default: _exit(0); } } if (pid > 0) wait(NULL); } // When something we do raises an X error, we get sent here. There are several // specific types of error that we know we want to ignore, or that indicate a // fatal error. For the rest, cease managing the client, as it should indicate // that the window has disappeared. int handle_xerror(Display *dsply, XErrorEvent *e) { struct 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); // Some parts of the code deliberately disable error checking. if (ignore_xerror) { LOG_DEBUG("ignoring...\n"); LOG_LEAVE(); return 0; } // client_manage_new() sets initialising to non-None to test if a // window still exists. If we end up here, the test failed, so // indicate that by setting it back to None. if (initialising != None && e->resourceid == initialising) { LOG_DEBUG("error caught while initialising window=%lx\n", (unsigned long)initialising); initialising = None; LOG_LEAVE(); return 0; } // This error is generally raised when trying to start evilwm while // another window manager is running. if (e->error_code == BadAccess && e->request_code == X_ChangeWindowAttributes) { LOG_ERROR("root window unavailable (maybe another wm is running?)\n"); exit(1); } // Batching of events sometimes leads to us calling XSetInputFocus() // for enter events to windows that were subsequently deleted. Ignore // these errors. if (e->request_code == X_SetInputFocus) { LOG_DEBUG("ignoring harmless error caused by possible race\n"); LOG_LEAVE(); return 0; } // For any other raised error, remove the client that triggered it from // management, as it's probably gone. 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; } // Simplify calls to XQueryPointer(), and make destination pointers optional Bool get_pointer_root_xy(Window w, int *x, int *y) { Window root_r, child_r; int root_x_r, root_y_r; int win_x_r, win_y_r; unsigned mask_r; if (!x) x = &root_x_r; if (!y) y = &root_y_r; return XQueryPointer(display.dpy, w, &root_r, &child_r, x, y, &win_x_r, &win_y_r, &mask_r); } // Wraps XGetWindowProperty() 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(display.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; } // Determine the normal border size for a window. MWM hints seem to be the // only way clients can signal they don't want a border. int window_normal_border(Window w) { int bw = option.bw; PropMwmHints *mprop; unsigned long nitems; if ( (mprop = get_property(w, X_ATOM(_MOTIF_WM_HINTS), X_ATOM(_MOTIF_WM_HINTS), &nitems)) ) { if (nitems >= PROP_MWM_HINTS_ELEMENTS && (mprop->flags & MWM_HINTS_DECORATIONS) && !(mprop->decorations & MWM_DECOR_ALL) && !(mprop->decorations & MWM_DECOR_BORDER)) { bw = 0; } XFree(mprop); } return bw; } // 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. int interruptibleXNextEvent(XEvent *event) { fd_set fds; int rc; int dpy_fd = ConnectionNumber(display.dpy); for (;;) { if (XPending(display.dpy)) { XNextEvent(display.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"); } } } } // Remove enter events from the queue, preserving only the last one // corresponding to "except"s parent. void discard_enter_events(struct client *except) { XEvent tmp, putback_ev; int putback = 0; XSync(display.dpy, False); while (XCheckMaskEvent(display.dpy, EnterWindowMask, &tmp)) { if (tmp.xcrossing.window == except->parent) { memcpy(&putback_ev, &tmp, sizeof(XEvent)); putback = 1; } } if (putback) { XPutBackEvent(display.dpy, &putback_ev); } } evilwm-1.4.2/util.h000066400000000000000000000043051433251723200141530ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ #ifndef EVILWM_UTIL_H_ #define EVILWM_UTIL_H_ #include #include // Required for interpreting MWM 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; // 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 */ // Grab pointer using specified cursor. Report button press/release, and // pointer motion events. #define grab_pointer(w, curs) \ (XGrabPointer(display.dpy, w, False, \ ButtonPressMask|ButtonReleaseMask|PointerMotionMask, \ GrabModeAsync, GrabModeAsync, \ None, curs, CurrentTime) == GrabSuccess) // Move the mouse pointer. #define setmouse(w, x, y) XWarpPointer(display.dpy, None, w, 0, 0, 0, 0, x, y) // Error handler interaction extern int ignore_xerror; extern volatile Window initialising; // Spawn a subprocess (usually xterm or similar) void spawn(const char *const cmd[]); // Global X11 error handler. Various actions interact with this. int handle_xerror(Display *dsply, XErrorEvent *e); // Simplify calls to XQueryPointer(), and make destination pointers optional Bool get_pointer_root_xy(Window w, int *x, int *y); // Wraps XQueryPointer() // Wraps XGetWindowProperty() void *get_property(Window w, Atom property, Atom req_type, unsigned long *nitems_return); // Determine the normal border size for a window. int window_normal_border(Window w); // Alternative to XNextEvent(). Unlike XNextEvent, if a signal arrives, // interruptibleXNextEvent will return zero. int interruptibleXNextEvent(XEvent *event); // Remove enter events from the queue, preserving only the last one // corresponding to "except"s parent. void discard_enter_events(struct client *except); #endif evilwm-1.4.2/xalloc.h000066400000000000000000000007161433251723200144620ustar00rootroot00000000000000/* Memory allocation with checking Copyright 2014-2018 Ciaran Anscomb A small set of convenience functions that wrap standard system calls and provide out of memory checking. See Gnulib for a far more complete set. */ #ifndef EVILWM_XALLOC_H_ #define EVILWM_XALLOC_H_ #include void *xmalloc(size_t s); void *xzalloc(size_t s); void *xrealloc(void *p, size_t s); void *xmemdup(const void *p, size_t s); char *xstrdup(const char *str); #endif evilwm-1.4.2/xconfig.c000066400000000000000000000123661433251723200146340ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Configuration parsing. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "log.h" #include "xalloc.h" #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; } // Find option by name in list. List terminated by XCONFIG_END entry. static struct xconfig_option *find_option(struct xconfig_option *options, const char *opt) { for (int i = 0; options[i].type != XCONFIG_END; i++) { if (0 == strcmp(options[i].name, opt)) { return &options[i]; } } return NULL; } static void unset_option(struct xconfig_option *option) { switch (option->type) { case XCONFIG_STRING: if (*(char **)option->dest.s) { free(*(char **)option->dest.s); *(char **)option->dest.s = NULL; } break; case XCONFIG_STR_LIST: if (*(char ***)option->dest.sl) { if ((*(char ***)option->dest.sl)[0]) { free((*(char ***)option->dest.sl)[0]); } free(*(char ***)option->dest.sl); *(char ***)option->dest.sl = NULL; } break; default: break; } } // Store option value in memory according to its type static void set_option(struct xconfig_option *option, const char *arg) { unset_option(option); switch (option->type) { case XCONFIG_BOOL: *(int *)option->dest.i = 1; break; case XCONFIG_INT: *(int *)option->dest.i = strtol(arg, NULL, 0); break; case XCONFIG_UINT: *(unsigned *)option->dest.u = strtoul(arg, NULL, 0); break; case XCONFIG_STRING: *(char **)option->dest.s = xstrdup(arg); break; case XCONFIG_STR_LIST: *(char ***)option->dest.sl = split_string(arg); break; case XCONFIG_CALL_0: ((void (*)(void))option->dest.c0)(); break; case XCONFIG_CALL_1: ((void (*)(const char *))option->dest.c1)(arg); break; default: break; } } void xconfig_parse_line(struct xconfig_option *options, const char *line) { // skip leading spaces while (isspace((int)*line)) line++; // end of line or comment? if (*line == 0 || *line == '#') return; // from here on, work on a copy of the string char *linedup = xstrdup(line); // whitespace separates option from arguments char *optstr = strtok(linedup, "\t\n\v\f\r "); if (optstr == NULL) { goto done; } struct xconfig_option *opt = find_option(options, optstr); if (opt == NULL) { LOG_INFO("Ignoring unknown option `%s'\n", optstr); goto done; } char *arg; if (opt->type == XCONFIG_STR_LIST) { // special case: spaces here mean something arg = strtok(NULL, "\n\v\f\r"); while (isspace(*arg)) { arg++; } } else { arg = strtok(NULL, "\t\n\v\f\r "); } set_option(opt, arg ? arg : ""); done: free(linedup); return; } // Simple parser: one directive per line, "option argument" enum xconfig_result xconfig_parse_file(struct xconfig_option *options, const char *filename) { char buf[256]; char *line; FILE *cfg; cfg = fopen(filename, "r"); if (cfg == NULL) return XCONFIG_FILE_ERROR; while ((line = fgets(buf, sizeof(buf), cfg))) { xconfig_parse_line(options, line); } fclose(cfg); return XCONFIG_OK; } // Command line argument processing enum xconfig_result xconfig_parse_cli(struct xconfig_option *options, int argc, char **argv, int *argn) { int _argn; const char *optstr; _argn = argn ? *argn : 1; while (_argn < argc) { // No leading '-' means end of options if (argv[_argn][0] != '-') { break; } // "--" ends options, per tradition if (0 == strcmp("--", argv[_argn])) { _argn++; break; } optstr = argv[_argn]+1; // Allow double '-' to introduce option if (*optstr == '-') optstr++; struct xconfig_option *opt = find_option(options, optstr); if (opt == NULL) { if (argn) *argn = _argn; return XCONFIG_BAD_OPTION; } // Option types with no argument if (opt->type == XCONFIG_BOOL || opt->type == XCONFIG_CALL_0) { set_option(opt, NULL); _argn++; continue; } // Option types with one argument if ((_argn + 1) >= argc) { if (argn) *argn = _argn; return XCONFIG_MISSING_ARG; } set_option(opt, argv[_argn+1]); _argn += 2; } if (argn) { *argn = _argn; } return XCONFIG_OK; } void xconfig_free(struct xconfig_option *options) { for (int i = 0; options[i].type != XCONFIG_END; i++) { unset_option(&options[i]); } } evilwm-1.4.2/xconfig.h000066400000000000000000000027211433251723200146330ustar00rootroot00000000000000/* evilwm - minimalist window manager for X11 * Copyright (C) 1999-2022 Ciaran Anscomb * see README for license and other details. */ // Configuration parsing. // // Scans options either from an array (e.g., from command line) or file. #ifndef EVILWM_XCONFIG_H__ #define EVILWM_XCONFIG_H__ enum xconfig_result { XCONFIG_OK = 0, XCONFIG_BAD_OPTION, XCONFIG_MISSING_ARG, XCONFIG_FILE_ERROR }; // Option types. enum xconfig_option_type { XCONFIG_BOOL, // int XCONFIG_INT, // int XCONFIG_UINT, // unsigned XCONFIG_STRING, // char * XCONFIG_STR_LIST, // char ** XCONFIG_CALL_0, // (void (*)(void) XCONFIG_CALL_1, // (void (*)(const char *) XCONFIG_END }; // An array of struct xconfig_option passed to the parsing functions specifies // recognised options, their type, and where to store any result. Mark the end // of the list with entry of type XCONFIG_END. struct xconfig_option { enum xconfig_option_type type; const char *name; union { int *i; unsigned *u; char **s; char ***sl; void (*c0)(void); void (*c1)(const char *); } dest; }; void xconfig_parse_line(struct xconfig_option *options, const char *line); 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); // Free all allocated strings pointed to by options void xconfig_free(struct xconfig_option *options); #endif evilwm-1.4.2/xmalloc.c000066400000000000000000000013661433251723200146340ustar00rootroot00000000000000/* Memory allocation with checking Copyright 2014-2018 Ciaran Anscomb */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "xalloc.h" void *xmalloc(size_t s) { void *mem = malloc(s); if (!mem) { perror(NULL); exit(EXIT_FAILURE); } return mem; } void *xzalloc(size_t s) { void *mem = xmalloc(s); memset(mem, 0, s); return mem; } void *xrealloc(void *p, size_t s) { void *mem = realloc(p, s); if (!mem && s != 0) { perror(NULL); exit(EXIT_FAILURE); } return mem; } void *xmemdup(const void *p, size_t s) { if (!p) return NULL; void *mem = xmalloc(s); memcpy(mem, p, s); return mem; } char *xstrdup(const char *str) { return xmemdup(str, strlen(str) + 1); }