summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--LICENSE2
-rw-r--r--Makefile31
-rw-r--r--NEWS33
-rw-r--r--TODO145
-rw-r--r--WISHLIST2
-rw-r--r--config.h20
-rw-r--r--hidden.c253
-rw-r--r--hidden.man19
-rw-r--r--mcmenu.c517
-rw-r--r--mcwm.c478
-rw-r--r--mcwm.man155
-rwxr-xr-xscripts/9icon18
-rwxr-xr-xscripts/mcicon6
-rwxr-xr-xscripts/mcmenu11
15 files changed, 1462 insertions, 232 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eba3c85
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*~
+*.o
+mcwm
+hidden
diff --git a/LICENSE b/LICENSE
index 57c29ef..6be2048 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2010,2011 Michael Cardell Widerkrantz <mc@hack.org>
+Copyright (c) 2010,2011,2012 Michael Cardell Widerkrantz <mc@hack.org>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Makefile b/Makefile
index e9f1358..73ccb76 100644
--- a/Makefile
+++ b/Makefile
@@ -1,27 +1,29 @@
-VERSION=20110818
+VERSION=20130209-2
DIST=mcwm-$(VERSION)
-SRC=mcwm.c list.c config.h events.h list.h
-DISTFILES=LICENSE Makefile NEWS README TODO WISHLIST mcwm.man $(SRC)
+SRC=mcwm.c list.c config.h events.h list.h hidden.c
+DISTFILES=LICENSE Makefile NEWS README TODO WISHLIST mcwm.man hidden.man scripts $(SRC)
-CC=gcc
-CFLAGS=-g -std=c99 -Wall -Wextra -O2 -I/usr/local/include #-DDEBUG #-DDMALLOC
-LDFLAGS=-L/usr/local/lib -lxcb -lxcb-randr -lxcb-keysyms -lxcb-icccm \
- -lxcb-atom #-ldmalloc
+CFLAGS+=-g -std=c99 -Wall -Wextra -I/usr/local/include #-DDEBUG #-DDMALLOC
+LDFLAGS+=-L/usr/local/lib -lxcb -lxcb-randr -lxcb-keysyms -lxcb-icccm \
+ -lxcb-util #-ldmalloc
RM=/bin/rm
PREFIX=$(DESTDIR)
-TARGETS=mcwm
+TARGETS=mcwm hidden
OBJS=mcwm.o list.o
all: $(TARGETS)
mcwm: $(OBJS)
- $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS)
+ $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@
+
+hidden: hidden.c
+ $(CC) $(CFLAGS) hidden.c $(LDFLAGS) -o $@
mcwm-static: $(OBJS)
- $(CC) -o $@ $(OBJS) -static $(CFLAGS) $(LDFLAGS) \
- -lxcb-property -lxcb-event -lXau -lXdmcp
+ $(CC) $(OBJS) -static $(CFLAGS) $(LDFLAGS) \
+ -lXau -lXdmcp -o $@
mcwm.o: mcwm.c events.h list.h config.h Makefile
@@ -31,14 +33,19 @@ install: $(TARGETS)
install -m 755 mcwm $(PREFIX)/bin
install -m 755 -d $(PREFIX)/man/man1/
install -m 644 mcwm.man $(PREFIX)/man/man1/mcwm.1
+ install -m 755 hidden $(PREFIX)/bin
+ install -m 644 hidden.man $(PREFIX)/man/man1/hidden.1
+uninstall: deinstall
deinstall:
$(RM) $(PREFIX)/bin/mcwm
$(RM) $(PREFIX)/man/man1/mcwm.1
+ $(RM) $(PREFIX)/bin/hidden
+ $(RM) $(PREFIX)/man/man1/hidden.1
$(DIST).tar.bz2:
mkdir $(DIST)
- cp $(DISTFILES) $(DIST)/
+ cp -r $(DISTFILES) $(DIST)/
tar cf $(DIST).tar --exclude .git $(DIST)
bzip2 -9 $(DIST).tar
$(RM) -rf $(DIST)
diff --git a/NEWS b/NEWS
index 46f0b1e..58b3330 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,39 @@
User visible changes
+2013-02-08
+
+ * New keys: MODKEY + c,v moves to previous or next workspace. Thanks
+ to Per Cederqvist.
+
+ * New key: Shift-TAB moves backwards in on screen window list.
+ Thanks to Per Cederqvist.
+
+2012-03-05
+
+ * New key: MODKEY + i hides windows.
+
+ * New program: hidden. Lists hidden windows.
+
+2012-02-15
+
+ * Added option -i to allow icons.
+
+ * Added scripts: mcmenu (configurable start meny) and 9icon (to map
+ iconified windows).
+
+2012-02-07
+
+ * Check for socket errors. Should prevent busy loops.
+
+2011-11-22
+
+ * Now optionally starts programs when MODKEY + mouse buttons are
+ pressed on root window. See config.h for configuration and the
+ manual page for an example.
+
+ Also simplified starting programs.
+
2011-11-17
* In a ConfigureRequest we no longer obey window movements not
diff --git a/TODO b/TODO
index 49dd388..9133d96 100644
--- a/TODO
+++ b/TODO
@@ -1,20 +1,105 @@
-*- text -*-
-* When a window tries to map itself outside the physical screens,
- always map it to the screen closest to the coordinates it asked for
- instead of always mapping on the first screen in the list.
+* IconicState
+
+ Currently we set IconicState only on windows we hide and not when we
+ unmap them to switch to another desktop. ICCCM and EWMH says we
+ should set IconicState in both situations.
+
+ We should also set _NET_WM_STATE_HIDDEN when hiding windows.
+
+ hidden.c should list windows with _NET_WM_STATE_HIDDEN and,
+ optionally, all with IconicState.
+
+* When unhiding a window we want it to re-appear in the same position.
+ How?
+
+ Setting XCB_ICCCM_SIZE_HINT_US_POSITION before hiding would probably
+ work without adding more code, but does ICCCM allow that?
+
+ PPosition?
+
+* Feature: Handle several screens (DISPLAY=0.x) in classical X.
+
+ This means we will have several root windows.
+
+ setup = xcb_get_setup(conn);
+ screens = xcb_setup_roots_length(setup);
+
+ returns the number of screens available.
+
+ We can walk over them with xcb_setup_roots_iterator(setup).
+
+* Feature: Xinerama support. Needed when XRANDR above 1.1 not
+ supported, for instance with Nvidia cards in Twinview configuration.
+
+* Bug: Ignore other modifiers, such as NumLock and CapsLock.
+
+ We can use something like this to find the modifier mask for
+ NumLock:
+
+ xcb_keycode_t *num_lock;
+ num_lock = xcb_key_symbols_get_keycode(symbols, XK_Num_Lock);
+
+ and get an array of keycodes finished by XCB_NO_SYMBOL.
+
+ then compare the keycodes in the array with the keycodes all the
+ modifier masks give. See getmodkeys().
+
+* Extended Window Manager Hints (EWMH)
+
+ - Use the new xcb-ewmh for the EWMH hints.
+
+ I suggest listing least these in _NET_SUPPORTED (* marks
+ implemented):
+
+ _NET_NUMBER_OF_DESKTOPS, _NET_WM_DESKTOP*, _NET_CURRENT_DESKTOP,
+ _NET_WM_STATE, _NET_WM_STATE_STICKY,
+ _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_HIDDEN,
+ _NET_WM_STATE_FULLSCREEN, _NET_ACTIVE_WINDOW
+
+ _NET_NUMBER_OF_DESKTOPS can be set to the constant WORKSPACES.
+
+ _NET_CURRENT_DESKTOP is just curws. Set the hint when starting and
+ when changing workspaces.
+
+ We *may* want to support a message to the root window that *sets*
+ _NET_CURRENT_DESKTOP and then switch to it.
+
+ We might want to support _NET_WM_WINDOW_TYPE_DESKTOP as well.
+
+* Bug: We grab MODKEY all the time! We can grab it only when we start
+ tabbing instead and release it when tabbing is complete.
+
+* Key to move to previous and next workspace. David Jacobs has a patch
+ for mod4+shift + I/O.
+
+ https://gist.github.com/1478365
+
+* Key to move windows to other workspaces, perhaps mod4+Shift + 0..9
+ and mod4+shift + I/O.
+
+* Full-screen windows that are resized from client requests should be
+ kept full-screen.
+
+* When moving windows between monitors, try to place the window on
+ roughly the same place on a new monitor, if possible.
+
+* When a window tries to map itself outside the monitors always map it
+ on the monitor closest to the coordinates it asked for instead of
+ always mapping on the first monitor in the list.
* Feature: We need to continue dragging for move and resize until
*both* MODKEY and mouse button has been released. Will be much nicer
with trackball.
-* Move to first window (or middle of screen) on another screen
- with MODKEY + . and , instead of moving windows to new screen.
- Shifted these keys will move window to new screen?
+* Move to first window (or middle of monitor) on another monitor with
+ MODKEY + . and , instead of moving windows to new monitor. Shifted
+ these keys will move window to new monitor?
* Handle new modes on physical outputs. What do we have to do?
-* A separate workspace list for every physical output.
+* A separate workspace list for every monitor.
* Allow hexadecimal colour values on command line.
@@ -42,27 +127,23 @@
- A window might be on one, *several* or all virtual screens.
- We already have a way of fixing a window on all screens (Mod2-f),
- but we need a way of saying "stick on this workspace". Perhaps
- something like Mod2-a <n>, where <n> is 1--9 for virtual screens.
- Better ways? Note that this seems to be mildly incompatible with
- the EWMH _NET_WM_DESKTOP hint we're currently using: We will only
- be able to save one of the desktops used.
-
-* Hide windows
-
- Instead of iconifying, hide them à la 9wm. Even if we use a key to
- hide them, this probably means we have to have a menu to get them
- back. Perhaps use an external program somehow? Needs to talk to mcwm
- anyway. Unix socket?
+ We already have a way of fixing a window on all screens
+ (MODKEY-f), but we need a way of saying "stick on this workspace".
+ Perhaps something like MODKEY-a <n>, where <n> is 1--9 for virtual
+ screens. Better ways? Note that this seems to be mildly
+ incompatible with the EWMH _NET_WM_DESKTOP hint we're currently
+ using: We will only be able to save one of the desktops used.
* Menu
- We might need a menu for hidden windows (see above). Since I'm
- probably implementing menu windows anyway, perhaps I should add a
- menu with basic window functions, like 9wm and twm. This way, one
- might use the window manager without keyboard, if necessary. Not
- much work if I have to do the menu anyway... But also chords?
+ We might need a menu for hidden windows. Since I'm probably
+ implementing menu windows anyway, perhaps I should add a menu with
+ basic window functions, like 9wm and twm. This way, one might use
+ the window manager without keyboard, if necessary. Not much work if
+ I have to do the menu anyway... But also chords?
+
+ On the other hand, see hidden.c and forthcoming external menu
+ program.
* Chords
@@ -88,8 +169,6 @@
* Configuration file.
-* Start configurable programs when clicking on root window.
-
* Handle Urgency hint
Some windows might need attention and marks this with an urgency
@@ -97,13 +176,6 @@
the BEL character). Do we want to handle it? How do we tell the
user? Can this be done with some stand-alone program instead?
-* Support sensible portions of ICCCM and EWMH
-
- EWMH hints that tells applications about active workspace and
- focused window...
-
- And a few others.
-
* Code cleaning
- Obivous cleanup: The event switch is way too big.
@@ -111,9 +183,10 @@
- The states are known everywhere. A tight state machine would be
nicer.
- - Dispatch table for key bindings instead of keysym->enum->case?
+ - Dispatch table with function pointers for key bindings instead of
+ keysym->enum->case?
- - Use bitfields instead of extra lists for virtual screens?
+ - Use bitfields instead of extra lists for workspaces?
* Resize behaviour
diff --git a/WISHLIST b/WISHLIST
index 58a5591..d0f2a22 100644
--- a/WISHLIST
+++ b/WISHLIST
@@ -43,7 +43,7 @@ v Know about physical screens dynamically (RandR).
- Snap to border and screen edge, which favours the edge.
-- Mouse buttons on root window should start some configurable
+v Mouse buttons on root window should start some configurable
programs.
- Feedback window for size when resizing.
diff --git a/config.h b/config.h
index 35176a7..0bfb3c3 100644
--- a/config.h
+++ b/config.h
@@ -33,6 +33,20 @@
#define TERMINAL "urxvt"
/*
+ * Do we allow windows to be iconified? Set to true if you want this
+ * behaviour to be default. Can also be set by calling mcwm with -i.
+ */
+#define ALLOWICONS false
+
+/*
+ * Start these programs when pressing MOUSEMODKEY and mouse buttons on
+ * root window.
+ */
+#define MOUSE1 ""
+#define MOUSE2 ""
+#define MOUSE3 "mcmenu"
+
+/*
* Default colour on border for focused windows. Can be set from
* command line with "-f colour".
*/
@@ -49,7 +63,7 @@
/*
* Keysym codes for window operations. Look in X11/keysymdefs.h for
- * actual symbols.
+ * actual symbols. Use XK_VoidSymbol to disable a function.
*/
#define USERKEY_FIX XK_F
#define USERKEY_MOVE_LEFT XK_H
@@ -61,6 +75,7 @@
#define USERKEY_TERMINAL XK_Return
#define USERKEY_MAX XK_X
#define USERKEY_CHANGE XK_Tab
+#define USERKEY_BACKCHANGE XK_VoidSymbol
#define USERKEY_WS1 XK_1
#define USERKEY_WS2 XK_2
#define USERKEY_WS3 XK_3
@@ -71,6 +86,8 @@
#define USERKEY_WS8 XK_8
#define USERKEY_WS9 XK_9
#define USERKEY_WS10 XK_0
+#define USERKEY_PREVWS XK_C
+#define USERKEY_NEXTWS XK_V
#define USERKEY_TOPLEFT XK_Y
#define USERKEY_TOPRIGHT XK_U
#define USERKEY_BOTLEFT XK_B
@@ -78,3 +95,4 @@
#define USERKEY_DELETE XK_End
#define USERKEY_PREVSCREEN XK_comma
#define USERKEY_NEXTSCREEN XK_period
+#define USERKEY_ICONIFY XK_I
diff --git a/hidden.c b/hidden.c
new file mode 100644
index 0000000..f2df679
--- /dev/null
+++ b/hidden.c
@@ -0,0 +1,253 @@
+/*
+ * hidden - A small program to listen all windows with WM_STATE set to
+ * Iconic.
+ *
+ * Copyright (c) 2012 Michael Cardell Widerkrantz, mc at the domain
+ * hack.org.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <getopt.h>
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+
+xcb_connection_t *conn;
+xcb_screen_t *screen;
+
+xcb_atom_t wm_state;
+xcb_atom_t wm_icon_name;
+
+bool printcommand = false;
+
+static uint32_t get_wm_state(xcb_drawable_t win);
+static int findhidden(void);
+static void init(void);
+static void cleanup(void);
+static xcb_atom_t getatom(char *atom_name);
+static void printhelp(void);
+
+uint32_t get_wm_state(xcb_drawable_t win)
+{
+ xcb_get_property_reply_t *reply;
+ xcb_get_property_cookie_t cookie;
+ uint32_t *statep;
+ uint32_t state = 0;
+
+ cookie = xcb_get_property(conn, false, win, wm_state, wm_state, 0,
+ sizeof (int32_t));
+
+ reply = xcb_get_property_reply(conn, cookie, NULL);
+ if (NULL == reply)
+ {
+ fprintf(stderr, "mcwm: Couldn't get properties for win %d\n", win);
+ return -1;
+ }
+
+ /* Length is 0 if we didn't find it. */
+ if (0 == xcb_get_property_value_length(reply))
+ {
+ goto bad;
+ }
+
+ statep = xcb_get_property_value(reply);
+ state = *statep;
+
+bad:
+ free(reply);
+ return state;
+}
+
+/*
+ * List all hidden windows.
+ *
+ */
+int findhidden(void)
+{
+ xcb_query_tree_reply_t *reply;
+ int i;
+ int len;
+ xcb_window_t *children;
+ xcb_get_window_attributes_reply_t *attr;
+ uint32_t state;
+ xcb_get_property_cookie_t cookie;
+ xcb_icccm_get_text_property_reply_t prop;
+ xcb_generic_error_t *error;
+
+ /* Get all children. */
+ reply = xcb_query_tree_reply(conn,
+ xcb_query_tree(conn, screen->root), 0);
+ if (NULL == reply)
+ {
+ return -1;
+ }
+
+ len = xcb_query_tree_children_length(reply);
+ children = xcb_query_tree_children(reply);
+
+ /* List all hidden windows on this root. */
+ for (i = 0; i < len; i ++)
+ {
+ attr = xcb_get_window_attributes_reply(
+ conn, xcb_get_window_attributes(conn, children[i]), NULL);
+
+ if (!attr)
+ {
+ fprintf(stderr, "Couldn't get attributes for window %d.",
+ children[i]);
+ continue;
+ }
+
+ /*
+ * Don't bother windows in override redirect mode.
+ *
+ * This mode means they wouldn't have been reported to us
+ * with a MapRequest if we had been running, so in the
+ * normal case we wouldn't have seen them.
+ */
+ if (!attr->override_redirect)
+ {
+ state = get_wm_state(children[i]);
+ if (state == XCB_ICCCM_WM_STATE_ICONIC)
+ {
+ /*
+ * Example names:
+ *
+ * _NET_WM_ICON_NAME(UTF8_STRING) = 0x75, 0x72, 0x78,
+ * 0x76, 0x74 WM_ICON_NAME(STRING) = "urxvt"
+ * _NET_WM_NAME(UTF8_STRING) = 0x75, 0x72, 0x78, 0x76,
+ * 0x74 WM_NAME(STRING) = "urxvt"
+ */
+ cookie = xcb_icccm_get_wm_icon_name(conn, children[i]);
+ xcb_icccm_get_wm_icon_name_reply(conn, cookie, &prop, &error);
+
+ prop.name[prop.name_len] = '\0';
+ if (printcommand)
+ {
+ /* FIXME: Need to escape : in prop.name. */
+ printf("'%s':'xdotool windowmap 0x%x windowraise 0x%x'\n",
+ prop.name, children[i], children[i]);
+ }
+ else
+ {
+ puts(prop.name);
+ }
+ }
+ } /* if not override redirect */
+
+ free(attr);
+ } /* for */
+
+ free(reply);
+
+ return 0;
+}
+
+void init(void)
+{
+ int scrno;
+ xcb_screen_iterator_t iter;
+
+ conn = xcb_connect(NULL, &scrno);
+ if (!conn)
+ {
+ fprintf(stderr, "can't connect to an X server\n");
+ exit(1);
+ }
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(conn));
+
+ for (int i = 0; i < scrno; ++i)
+ {
+ xcb_screen_next(&iter);
+ }
+
+ screen = iter.data;
+
+ if (!screen)
+ {
+ fprintf(stderr, "can't get the current screen\n");
+ xcb_disconnect(conn);
+ exit(1);
+ }
+}
+
+void cleanup(void)
+{
+ xcb_disconnect(conn);
+}
+
+/*
+ * Get a defined atom from the X server.
+ */
+xcb_atom_t getatom(char *atom_name)
+{
+ xcb_intern_atom_cookie_t atom_cookie;
+ xcb_atom_t atom;
+ xcb_intern_atom_reply_t *rep;
+
+ atom_cookie = xcb_intern_atom(conn, 0, strlen(atom_name), atom_name);
+ rep = xcb_intern_atom_reply(conn, atom_cookie, NULL);
+ if (NULL != rep)
+ {
+ atom = rep->atom;
+ free(rep);
+ return atom;
+ }
+
+ /*
+ * XXX Note that we return 0 as an atom if anything goes wrong.
+ * Might become interesting.
+ */
+ return 0;
+}
+
+void printhelp(void)
+{
+ printf("hidden: Usage: hidden [-c]\n");
+ printf(" -c print 9menu/xdotool compatible output.\n");
+}
+
+int main(int argc, char **argv)
+{
+ int ch; /* Option character */
+
+ while (1)
+ {
+ ch = getopt(argc, argv, "c");
+ if (-1 == ch)
+ {
+ /* No more options, break out of while loop. */
+ break;
+ }
+ switch (ch)
+ {
+ case 'c':
+ printcommand = true;
+ break;
+
+ default:
+ printhelp();
+ exit(0);
+ } /* switch ch */
+ } /* while 1 */
+
+ init();
+ wm_state = getatom("WM_STATE");
+ findhidden();
+ cleanup();
+ exit(0);
+}
diff --git a/hidden.man b/hidden.man
new file mode 100644
index 0000000..2a4edeb
--- /dev/null
+++ b/hidden.man
@@ -0,0 +1,19 @@
+.TH hidden 1 "Mar 09, 2012" "" ""
+.SH NAME
+hidden \- list iconified windows
+.SH SYNOPSIS
+.B hidden
+[
+.B \-c
+]
+
+.SH DESCRIPTION
+.B hidden\fP lists all windows on an X server with WM_STATE Iconic.
+.SH OPTIONS
+.PP
+\-c prints a command suitable to get the window back again.
+
+.SH ENVIRONMENT
+.B hidden\fP obeys the $DISPLAY variable.
+.SH AUTHOR
+Michael Cardell Widerkrantz <mc@hack.org>.
diff --git a/mcmenu.c b/mcmenu.c
new file mode 100644
index 0000000..a8fdac8
--- /dev/null
+++ b/mcmenu.c
@@ -0,0 +1,517 @@
+/*
+ * mcmenu - small menu program using only XCB.
+ *
+ * MC, mc at the domain hack.org
+ * http://hack.org/mc/
+ *
+ * Copyright (c) 2010, 2011, 2012 Michael Cardell Widerkrantz, mc at
+ * the domain hack.org.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/select.h>
+
+#include <X11/keysym.h>
+
+#include <xcb/xcb.h>
+#include <xcb/xcb_keysyms.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * FIXME calculate height and width.
+ * How? We get font size *after* opening a window! Resize afterwards?
+ *
+ * Can I ask a font about information before using setfont()? I need
+ * pixel sizes.
+ *
+ */
+#define WIDTH 100
+#define HEIGHT 200
+
+xcb_connection_t *conn;
+xcb_screen_t *screen;
+
+int sigcode; /* Signal code. Non-zero if we've been
+ * interruped by a signal. */
+xcb_keycode_t keycode_j;
+xcb_keycode_t keycode_k;
+xcb_keycode_t keycode_ret;
+
+struct font
+{
+ char *name;
+ /* width, height. */
+};
+
+struct window
+{
+ xcb_window_t id;
+ xcb_gc_t curfontc;
+};
+
+static uint32_t getcolor(const char *);
+static int setfont(struct window *, const char *, const char *, const char *);
+static void printat(struct window *, int16_t, int16_t, const char *);
+static struct window *window(int16_t, int16_t, uint16_t, uint16_t);
+static void init(void);
+static void cleanup(void);
+static xcb_keycode_t keysymtokeycode(xcb_keysym_t, xcb_key_symbols_t *);
+static void grabkeys(struct window *);
+static void printrows(struct window *, char *[], int, int);
+static void invertrow(struct window *, char *[], int, int);
+static void normalrow(struct window *, char *[], int, int);
+static int keypress(xcb_key_press_event_t *, struct window *, char *[], int,
+ int, int);
+static void redraw(struct window *, char *[], int, int, int);
+
+/*
+ * Get the pixel values of a named colour colstr.
+ *
+ * Returns pixel values.
+ */
+uint32_t getcolor(const char *colstr)
+{
+ xcb_alloc_named_color_reply_t *col_reply;
+ xcb_colormap_t colormap;
+ xcb_generic_error_t *error;
+ xcb_alloc_named_color_cookie_t colcookie;
+
+ colormap = screen->default_colormap;
+
+ colcookie = xcb_alloc_named_color(conn, colormap, strlen(colstr), colstr);
+
+ col_reply = xcb_alloc_named_color_reply(conn, colcookie, &error);
+ if (NULL != error)
+ {
+ fprintf(stderr, "mcwm: Couldn't get pixel value for colour %s. "
+ "Exiting.\n", colstr);
+
+ xcb_disconnect(conn);
+ exit(1);
+ }
+
+ return col_reply->pixel;
+}
+
+int setfont(struct window *win, const char *fontname, const char *fg,
+ const char *bg)
+{
+ uint32_t fgcol; /* Focused border colour. */
+ uint32_t bgcol; /* Focused border colour. */
+ xcb_font_t font;
+ xcb_gcontext_t gc;
+ uint32_t mask;
+ uint32_t values[3];
+
+ fgcol = getcolor(fg);
+ bgcol = getcolor(bg);
+
+ font = xcb_generate_id(conn);
+ xcb_open_font(conn, font, strlen(fontname), fontname);
+
+ gc = xcb_generate_id(conn);
+ mask = XCB_GC_FOREGROUND | XCB_GC_BACKGROUND | XCB_GC_FONT;
+ values[0] = fgcol;
+ values[1] = bgcol;
+ values[2] = font;
+ xcb_void_cookie_t cookie = xcb_create_gc_checked(conn, gc, win->id, mask,
+ values);
+
+ xcb_generic_error_t *error = xcb_request_check(conn, cookie);
+ if (error)
+ {
+ fprintf(stderr, "ERROR: can't print text : %d\n", error->error_code);
+ xcb_disconnect(conn);
+ exit(-1);
+ }
+
+ xcb_close_font(conn, font);
+
+ win->curfontc = gc;
+
+ return 0;
+}
+
+void printat(struct window *win, int16_t x, int16_t y, const char *text)
+{
+ xcb_void_cookie_t cookie;
+ xcb_generic_error_t *error;
+
+ cookie = xcb_image_text_8_checked(conn, strlen(text),
+ win->id, win->curfontc, x, y, text);
+
+ error = xcb_request_check(conn, cookie);
+
+ if (error)
+ {
+ fprintf(stderr, "ERROR: can't print text : %d\n", error->error_code);
+ xcb_disconnect(conn);
+ exit(-1);
+ }
+
+ xcb_flush(conn);
+}
+
+struct window *window(int16_t x, int16_t y, uint16_t width, uint16_t height)
+{
+ struct window *win;
+ xcb_void_cookie_t cookie;
+ uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+ uint32_t values[2];
+
+ win = malloc(sizeof (struct window));
+ if (NULL == win)
+ {
+ perror("malloc");
+ return NULL;
+ }
+
+ values[0] = screen->black_pixel;
+ values[1] = XCB_EVENT_MASK_KEY_RELEASE |
+ XCB_EVENT_MASK_BUTTON_PRESS |
+ XCB_EVENT_MASK_EXPOSURE |
+ XCB_EVENT_MASK_POINTER_MOTION;
+
+ win->id = xcb_generate_id(conn);
+
+ cookie = xcb_create_window(conn, screen->root_depth, win->id, screen->root,
+ x, y,
+ width, height,
+ 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
+ screen->root_visual,
+ mask, values);
+
+ xcb_map_window(conn, win->id);
+
+ xcb_flush(conn);
+
+ return win;
+}
+
+void init(void)
+{
+ int scrno;
+ xcb_screen_iterator_t iter;
+
+ conn = xcb_connect(NULL, &scrno);
+ if (!conn)
+ {
+ fprintf(stderr, "can't connect to an X server\n");
+ exit(1);
+ }
+
+ iter = xcb_setup_roots_iterator(xcb_get_setup(conn));
+
+ for (int i = 0; i < scrno; ++i)
+ {
+ xcb_screen_next(&iter);
+ }
+
+ screen = iter.data;
+
+ if (!screen)
+ {
+ fprintf(stderr, "can't get the current screen\n");
+ xcb_disconnect(conn);
+ exit(1);
+ }
+}
+
+void cleanup(void)
+{
+ xcb_disconnect(conn);
+}
+
+/*
+ * Get a keycode from a keysym.
+ *
+ * Returns keycode value.
+ */
+xcb_keycode_t keysymtokeycode(xcb_keysym_t keysym, xcb_key_symbols_t *keysyms)
+{
+ xcb_keycode_t *keyp;
+ xcb_keycode_t key;
+
+ /* We only use the first keysymbol, even if there are more. */
+ keyp = xcb_key_symbols_get_keycode(keysyms, keysym);
+ if (NULL == keyp)
+ {
+ fprintf(stderr, "mcmenu: Couldn't look up key. Exiting.\n");
+ exit(1);
+ return 0;
+ }
+
+ key = *keyp;
+ free(keyp);
+
+ return key;
+}
+
+void grabkeys(struct window *win)
+{
+ xcb_key_symbols_t *keysyms;
+
+ /* Get all the keysymbols. */
+ keysyms = xcb_key_symbols_alloc(conn);
+
+ keycode_j = keysymtokeycode(XK_J, keysyms);
+ keycode_k = keysymtokeycode(XK_K, keysyms);
+ keycode_ret = keysymtokeycode(XK_Return, keysyms);
+
+ xcb_grab_key(conn, 1, win->id, XCB_MOD_MASK_ANY,
+ keycode_j,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+
+ xcb_grab_key(conn, 1, win->id, XCB_MOD_MASK_ANY,
+ keycode_k,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+
+ xcb_grab_key(conn, 1, win->id, XCB_MOD_MASK_ANY,
+ keycode_ret,
+ XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
+
+ xcb_flush(conn);
+
+ /* Get rid of the key symbols table. */
+ xcb_key_symbols_free(keysyms);
+}
+
+/*
+ * Print some rows of text in window win, stopping at max
+ * automatically moving fontheight pixels down at a time.
+ */
+void printrows(struct window *win, char *rows[], int max, int fontheight)
+{
+ int i;
+ int y;
+
+ if (0 != setfont(win, "6x13", "white", "black"))
+ {
+ fprintf(stderr, "Couldn't set new font.\n");
+ exit(1);
+ }
+
+ for (i = 0, y = fontheight; i < max; i ++, y += fontheight)
+ {
+ printat(win, 1, y, rows[i]);
+ }
+}
+
+/*
+ * Invert row row (counting from 1).
+ */
+void invertrow(struct window *win, char *rows[], int row, int fontheight)
+{
+ int y = fontheight * row;
+
+ if (0 != setfont(win, "6x13", "black", "white"))
+ {
+ fprintf(stderr, "Couldn't set new font.\n");
+ exit(1);
+ }
+
+ /* TODO Pad to window width. */
+ printat(win, 1, y, rows[row - 1]);
+}
+
+void normalrow(struct window *win, char *rows[], int row, int fontheight)
+{
+ int y = fontheight * row;
+
+ if (0 != setfont(win, "6x13", "white", "black"))
+ {
+ fprintf(stderr, "Couldn't set new font.\n");
+ exit(1);
+ }
+
+ /* TODO Pad to window width. */
+ printat(win, 1, y, rows[row - 1]);
+}
+
+int keypress(xcb_key_press_event_t *ev, struct window *win, char *menu[],
+ int currow, int max, int height)
+{
+ int oldrow;
+
+ oldrow = currow;
+
+ if (ev->detail == keycode_j)
+ {
+ printf("DOWN!\n");
+ currow ++;
+ }
+ else if (ev->detail == keycode_k)
+ {
+ printf("UP!\n");
+ currow --;
+ }
+ else if (ev->detail == keycode_ret)
+ {
+ printf("Choice: %s\n", menu[currow - 1]);
+ cleanup();
+ exit(0);
+ }
+ else
+ {
+ printf("Unknown key pressed.\n");
+ }
+
+ if (currow < 1)
+ {
+ currow = max;
+ }
+ else if (currow > max)
+ {
+ currow = 1;
+ }
+
+ invertrow(win, menu, currow, height);
+
+ if (currow != oldrow)
+ {
+ normalrow(win, menu, oldrow, height);
+ }
+
+ return currow;
+}
+
+void redraw(struct window *win, char *menu[], int max, int cur, int height)
+{
+ printrows(win, menu, max, height);
+ invertrow(win, menu, cur, height);
+}
+
+int main(void)
+{
+ struct window *win;
+ char *menu[] =
+ {
+ "foo",
+ "gurka",
+ "sallad"
+ };
+ int currow = 1;
+ int maxrow = 3;
+ char *font = "6x13";
+ int fontheight = 13;
+ xcb_generic_event_t *ev;
+ int fd; /* Our X file descriptor */
+ fd_set in; /* For select */
+ int found; /* Ditto. */
+
+ init();
+
+ /*
+ * This creates a new window. I might want to have a window
+ * created already after init(). The default window, where the
+ * program was started. Of course, this won't work in X but might
+ * work in another window systems.
+ */
+ win = window(1, 1, WIDTH, HEIGHT);
+ if (NULL == win)
+ {
+ fprintf(stderr, "Couldn't create window.\n");
+ exit(1);
+ }
+
+ if (0 != setfont(win, font, "white", "black"))
+ {
+ fprintf(stderr, "Couldn't set new font.\n");
+ exit(1);
+ }
+
+ grabkeys(win);
+
+ redraw(win, menu, maxrow, currow, fontheight);
+
+ /* Get the file descriptor so we can do select() on it. */
+ fd = xcb_get_file_descriptor(conn);
+
+ for (sigcode = 0; 0 == sigcode;)
+ {
+ /* Prepare for select(). */
+ FD_ZERO(&in);
+ FD_SET(fd, &in);
+
+ /*
+ * Check for events, again and again. When poll returns NULL,
+ * we block on select() until the event file descriptor gets
+ * readable again.
+ */
+ ev = xcb_poll_for_event(conn);
+ if (NULL == ev)
+ {
+ printf("xcb_poll_for_event() returned NULL.\n");
+
+ /* Check if we have an unrecoverably error. */
+ if (xcb_connection_has_error(conn))
+ {
+ cleanup();
+ exit(1);
+ }
+
+ found = select(fd + 1, &in, NULL, NULL, NULL);
+ if (-1 == found)
+ {
+ if (EINTR == errno)
+ {
+ /* We received a signal. Break out of loop. */
+ break;
+ }
+ else
+ {
+ /* Something was seriously wrong with select(). */
+ fprintf(stderr, "mcwm: select failed.");
+ cleanup();
+ exit(1);
+ }
+ }
+ else
+ {
+ /* We found more events. Goto start of loop. */
+ continue;
+ }
+ }
+
+ switch (ev->response_type & ~0x80)
+ {
+ /* TODO Add mouse events and mouse button click. */
+
+ case XCB_KEY_PRESS:
+ {
+ xcb_key_press_event_t *e = (xcb_key_press_event_t *)ev;
+
+ printf("Key %d pressed\n", e->detail);
+
+ currow = keypress(e, win, menu, currow, maxrow, fontheight);
+ }
+ break;
+
+ case XCB_EXPOSE:
+ redraw(win, menu, maxrow, currow, fontheight);
+ break;
+
+ default:
+ printf("Unknown event.\n");
+
+ } /* switch */
+ } /* for */
+
+ cleanup();
+ exit(0);
+}
diff --git a/mcwm.c b/mcwm.c
index 613d53e..5aa5197 100644
--- a/mcwm.c
+++ b/mcwm.c
@@ -7,8 +7,8 @@
* MC, mc at the domain hack.org
* http://hack.org/mc/
*
- * Copyright (c) 2010,2011 Michael Cardell Widerkrantz, mc at the
- * domain hack.org.
+ * Copyright (c) 2010, 2011, 2012 Michael Cardell Widerkrantz, mc at
+ * the domain hack.org.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -45,6 +45,9 @@
#include <X11/keysym.h>
+#include <xcb/xproto.h>
+#include <xcb/xcb_util.h>
+
#ifdef DEBUG
#include "events.h"
#endif
@@ -106,6 +109,7 @@ typedef enum {
KEY_RET,
KEY_X,
KEY_TAB,
+ KEY_BACKTAB,
KEY_1,
KEY_2,
KEY_3,
@@ -123,10 +127,12 @@ typedef enum {
KEY_END,
KEY_PREVSCR,
KEY_NEXTSCR,
+ KEY_ICONIFY,
+ KEY_PREVWS,
+ KEY_NEXTWS,
KEY_MAX
} key_enum_t;
-
struct monitor
{
xcb_randr_output_t id;
@@ -234,6 +240,7 @@ struct keys
{ USERKEY_TERMINAL, 0 },
{ USERKEY_MAX, 0 },
{ USERKEY_CHANGE, 0 },
+ { USERKEY_BACKCHANGE, 0 },
{ USERKEY_WS1, 0 },
{ USERKEY_WS2, 0 },
{ USERKEY_WS3, 0 },
@@ -250,7 +257,10 @@ struct keys
{ USERKEY_BOTRIGHT, 0 },
{ USERKEY_DELETE, 0 },
{ USERKEY_PREVSCREEN, 0 },
- { USERKEY_NEXTSCREEN, 0 },
+ { USERKEY_NEXTSCREEN, 0 },
+ { USERKEY_ICONIFY, 0 },
+ { USERKEY_PREVWS, 0 },
+ { USERKEY_NEXTWS, 0 },
};
/* All keycodes generating our MODKEY mask. */
@@ -272,6 +282,7 @@ struct conf
uint32_t focuscol; /* Focused border colour. */
uint32_t unfocuscol; /* Unfocused border colour. */
uint32_t fixedcol; /* Fixed windows border colour. */
+ bool allowicons; /* Allow windows to be unmapped. */
} conf;
xcb_atom_t atom_desktop; /*
@@ -281,6 +292,8 @@ xcb_atom_t atom_desktop; /*
*/
xcb_atom_t wm_delete_window; /* WM_DELETE_WINDOW event to close windows. */
+xcb_atom_t wm_change_state;
+xcb_atom_t wm_state;
xcb_atom_t wm_protocols; /* WM_PROTOCOLS. */
@@ -323,10 +336,10 @@ static void raiseorlower(struct client *client);
static void movelim(struct client *client);
static void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y);
static struct client *findclient(xcb_drawable_t win);
-static void focusnext(void);
+static void focusnext(bool reverse);
static void setunfocus(xcb_drawable_t win);
static void setfocus(struct client *client);
-static int start_terminal(void);
+static int start(char *program);
static void resizelim(struct client *client);
static void moveresize(xcb_drawable_t win, uint16_t x, uint16_t y,
uint16_t width, uint16_t height);
@@ -339,6 +352,7 @@ static void setborders(struct client *client, int width);
static void unmax(struct client *client);
static void maximize(struct client *client);
static void maxvert(struct client *client);
+static void hide(struct client *client);
static bool getpointer(xcb_drawable_t win, int16_t *x, int16_t *y);
static bool getgeom(xcb_drawable_t win, int16_t *x, int16_t *y, uint16_t *width,
uint16_t *height);
@@ -355,6 +369,7 @@ static void configurerequest(xcb_configure_request_event_t *e);
static void events(void);
static void printhelp(void);
static void sigcatch(int sig);
+static xcb_atom_t getatom(char *atom_name);
/* Function bodies. */
@@ -372,7 +387,6 @@ static void sigcatch(int sig);
*/
void finishtabbing(void)
{
-
mode = 0;
if (NULL != lastfocuswin)
@@ -503,7 +517,7 @@ void setwmdesktop(xcb_drawable_t win, uint32_t ws)
PDEBUG("Changing _NET_WM_DESKTOP on window %d to %d\n", win, ws);
xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win,
- atom_desktop, CARDINAL, 32, 1,
+ atom_desktop, XCB_ATOM_CARDINAL, 32, 1,
&ws);
}
@@ -521,8 +535,9 @@ int32_t getwmdesktop(xcb_drawable_t win)
uint32_t *wsp;
uint32_t ws;
- cookie = xcb_get_any_property(conn, false, win, atom_desktop,
- sizeof (int32_t));
+ cookie = xcb_get_property(conn, false, win, atom_desktop,
+ XCB_GET_PROPERTY_TYPE_ANY, 0,
+ sizeof (int32_t));
reply = xcb_get_property_reply(conn, cookie, NULL);
if (NULL == reply)
@@ -737,9 +752,7 @@ uint32_t getcolor(const char *colstr)
xcb_alloc_named_color_cookie_t colcookie;
colormap = screen->default_colormap;
-
colcookie = xcb_alloc_named_color(conn, colormap, strlen(colstr), colstr);
-
col_reply = xcb_alloc_named_color_reply(conn, colcookie, &error);
if (NULL != error)
{
@@ -826,7 +839,6 @@ void forgetwin(xcb_window_t win)
}
free(item->data);
-
delitem(&winlist, item);
return;
@@ -925,7 +937,8 @@ void fitonscreen(struct client *client)
willmove = true;
willresize = true;
}
- else if (client->x + client->width + conf.borderwidth * 2 > mon_x + mon_width)
+ else if (client->x + client->width + conf.borderwidth * 2
+ > mon_x + mon_width)
{
client->x = mon_x + mon_width - (client->width + conf.borderwidth * 2);
willmove = true;
@@ -938,10 +951,12 @@ void fitonscreen(struct client *client)
willmove = true;
willresize = true;
}
- else if (client->y + client->height + conf.borderwidth * 2 > mon_y + mon_height)
+ else if (client->y + client->height + conf.borderwidth * 2
+ > mon_y + mon_height)
{
- client->y = mon_y + mon_height - (client->height + conf.borderwidth * 2);
- willmove = true;
+ client->y = mon_y + mon_height - (client->height + conf.borderwidth
+ * 2);
+ willmove = true;
}
if (willmove)
@@ -963,8 +978,6 @@ void fitonscreen(struct client *client)
*/
void newwin(xcb_window_t win)
{
- int16_t pointx;
- int16_t pointy;
struct client *client;
if (NULL != findclient(win))
@@ -978,14 +991,6 @@ void newwin(xcb_window_t win)
return;
}
- /* Get pointer position so we can move the window to the cursor. */
- if (!getpointer(screen->root, &pointx, &pointy))
- {
- PDEBUG("Failed to get pointer coords!\n");
- pointx = 0;
- pointy = 0;
- }
-
/*
* Set up stuff, like borders, add the window to the client list,
* et cetera.
@@ -1002,12 +1007,23 @@ void newwin(xcb_window_t win)
/*
* If the client doesn't say the user specified the coordinates
- * for the window we store it where our pointer is instead.
+ * for the window we map it where our pointer is instead.
*/
if (!client->usercoord)
{
+ int16_t pointx;
+ int16_t pointy;
PDEBUG("Coordinates not set by user. Using pointer: %d,%d.\n",
pointx, pointy);
+
+ /* Get pointer position so we can move the window to the cursor. */
+ if (!getpointer(screen->root, &pointx, &pointy))
+ {
+ PDEBUG("Failed to get pointer coords!\n");
+ pointx = 0;
+ pointy = 0;
+ }
+
client->x = pointx;
client->y = pointy;
@@ -1021,7 +1037,7 @@ void newwin(xcb_window_t win)
/* Find the physical output this window will be on if RANDR is active. */
if (-1 != randrbase)
{
- client->monitor = findmonbycoord(pointx, pointy);
+ client->monitor = findmonbycoord(client->x, client->y);
if (NULL == client->monitor)
{
/*
@@ -1040,6 +1056,11 @@ void newwin(xcb_window_t win)
/* Show window on screen. */
xcb_map_window(conn, client->id);
+ /* Declare window normal. */
+ long data[] = { XCB_ICCCM_WM_STATE_NORMAL, XCB_NONE };
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->id,
+ wm_state, wm_state, 32, 2, data);
+
/*
* Move cursor into the middle of the window so we don't lose the
* pointer to another window.
@@ -1136,8 +1157,8 @@ struct client *setupwin(xcb_window_t win)
/*
* Get the window's incremental size step, if any.
*/
- if (!xcb_get_wm_normal_hints_reply(
- conn, xcb_get_wm_normal_hints_unchecked(
+ if (!xcb_icccm_get_wm_normal_hints_reply(
+ conn, xcb_icccm_get_wm_normal_hints_unchecked(
conn, win), &hints, NULL))
{
PDEBUG("Couldn't get size hints.\n");
@@ -1147,25 +1168,25 @@ struct client *setupwin(xcb_window_t win)
* The user specified the position coordinates. Remember that so
* we can use geometry later.
*/
- if (hints.flags & XCB_SIZE_HINT_US_POSITION)
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_US_POSITION)
{
client->usercoord = true;
}
- if (hints.flags & XCB_SIZE_HINT_P_MIN_SIZE)
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
{
client->min_width = hints.min_width;
client->min_height = hints.min_height;
}
- if (hints.flags & XCB_SIZE_HINT_P_MAX_SIZE)
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
{
client->max_width = hints.max_width;
client->max_height = hints.max_height;
}
- if (hints.flags & XCB_SIZE_HINT_P_RESIZE_INC)
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)
{
client->width_inc = hints.width_inc;
client->height_inc = hints.height_inc;
@@ -1174,7 +1195,7 @@ struct client *setupwin(xcb_window_t win)
client->height_inc);
}
- if (hints.flags & XCB_SIZE_HINT_BASE_SIZE)
+ if (hints.flags & XCB_ICCCM_SIZE_HINT_BASE_SIZE)
{
client->base_width = hints.base_width;
client->base_height = hints.base_height;
@@ -1252,6 +1273,12 @@ int setupkeys(void)
/* Now grab the rest of the keys with the MODKEY modifier. */
for (i = KEY_F; i < KEY_MAX; i ++)
{
+ if (XK_VoidSymbol == keys[i].keysym)
+ {
+ keys[i].keycode = 0;
+ continue;
+ }
+
keys[i].keycode = keysymtokeycode(keys[i].keysym, keysyms);
if (0 == keys[i].keycode)
{
@@ -1441,7 +1468,7 @@ int setuprandr(void)
extension = xcb_get_extension_data(conn, &xcb_randr_id);
if (!extension->present)
{
- printf("No RANDR.\n");
+ PDEBUG("No RANDR extension.\n");
return -1;
}
else
@@ -1866,7 +1893,8 @@ void movelim(struct client *client)
if (client->y + client->height > mon_y + mon_height - conf.borderwidth * 2)
{
- client->y = (mon_y + mon_height - conf.borderwidth * 2) - client->height;
+ client->y = (mon_y + mon_height - conf.borderwidth * 2)
+ - client->height;
}
movewindow(client->id, client->x, client->y);
@@ -1890,11 +1918,10 @@ void movewindow(xcb_drawable_t win, uint16_t x, uint16_t y)
| XCB_CONFIG_WINDOW_Y, values);
xcb_flush(conn);
-
}
/* Change focus to next in window ring. */
-void focusnext(void)
+void focusnext(bool reverse)
{
struct client *client = NULL;
@@ -1938,25 +1965,54 @@ void focusnext(void)
}
else
{
- if (NULL == focuswin->wsitem[curws]->next)
+ if (reverse)
{
- /*
- * We were at the end of list. Focusing on first window in
- * list unless we were already there.
- */
- if (focuswin->wsitem[curws] != wslist[curws]->data)
+ if (NULL == focuswin->wsitem[curws]->prev)
{
- PDEBUG("End of list. Focusing first in list: %p\n",
- wslist[curws]);
- client = wslist[curws]->data;
+ /*
+ * We were at the head of list. Focusing on last
+ * window in list unless we were already there.
+ */
+ struct item *last = wslist[curws];
+ while (NULL != last->next)
+ last = last->next;
+ if (focuswin->wsitem[curws] != last->data)
+ {
+ PDEBUG("Beginning of list. Focusing last in list: %p\n",
+ last);
+ client = last->data;
+ }
+ }
+ else
+ {
+ /* Otherwise, focus the next in list. */
+ PDEBUG("Tabbing. Focusing next: %p.\n",
+ focuswin->wsitem[curws]->prev);
+ client = focuswin->wsitem[curws]->prev->data;
}
}
else
{
- /* Otherwise, focus the next in list. */
- PDEBUG("Tabbing. Focusing next: %p.\n",
- focuswin->wsitem[curws]->next);
- client = focuswin->wsitem[curws]->next->data;
+ if (NULL == focuswin->wsitem[curws]->next)
+ {
+ /*
+ * We were at the end of list. Focusing on first window in
+ * list unless we were already there.
+ */
+ if (focuswin->wsitem[curws] != wslist[curws]->data)
+ {
+ PDEBUG("End of list. Focusing first in list: %p\n",
+ wslist[curws]);
+ client = wslist[curws]->data;
+ }
+ }
+ else
+ {
+ /* Otherwise, focus the next in list. */
+ PDEBUG("Tabbing. Focusing next: %p.\n",
+ focuswin->wsitem[curws]->next);
+ client = focuswin->wsitem[curws]->next->data;
+ }
}
} /* if NULL focuswin */
@@ -2085,15 +2141,10 @@ void setfocus(struct client *client)
focuswin = client;
}
-/*
- * Start a program specified in conf.terminal.
- *
- * Returns 0 on success.
- */
-int start_terminal(void)
+int start(char *program)
{
pid_t pid;
-
+
pid = fork();
if (-1 == pid)
{
@@ -2102,10 +2153,10 @@ int start_terminal(void)
}
else if (0 == pid)
{
- pid_t termpid;
-
- /* In our first child. */
+ char *argv[2];
+ /* In the child. */
+
/*
* Make this process a new process leader, otherwise the
* terminal will die when the wm dies. Also, this makes any
@@ -2117,39 +2168,15 @@ int start_terminal(void)
exit(1);
}
- /*
- * Fork again for the terminal process. This way, the wm won't
- * know anything about it.
- */
- termpid = fork();
- if (-1 == termpid)
+ argv[0] = program;
+ argv[1] = NULL;
+
+ if (-1 == execvp(program, argv))
{
- perror("fork");
+ perror("execve");
exit(1);
}
- else if (0 == termpid)
- {
- char *argv[2];
-
- /* In the second child, now starting terminal. */
-
- argv[0] = conf.terminal;
- argv[1] = NULL;
-
- if (-1 == execvp(conf.terminal, argv))
- {
- perror("execve");
- exit(1);
- }
- } /* second child */
-
- /* Exit our first child so the wm can pick up and continue. */
exit(0);
- } /* first child */
- else
- {
- /* Wait for the first forked process to exit. */
- waitpid(pid, NULL, 0);
}
return 0;
@@ -2189,14 +2216,16 @@ void resizelim(struct client *client)
client->width = client->min_width;
}
- if (client->x + client->width + conf.borderwidth * 2 > mon_x + mon_width)
+ if (client->x + client->width + conf.borderwidth * 2 > mon_x + mon_width)
{
- client->width = mon_width - ((client->x - mon_x) + conf.borderwidth * 2);
+ client->width = mon_width - ((client->x - mon_x) + conf.borderwidth
+ * 2);
}
if (client->y + client->height + conf.borderwidth * 2 > mon_y + mon_height)
{
- client->height = mon_height - ((client->y - mon_y) + conf.borderwidth * 2);
+ client->height = mon_height - ((client->y - mon_y) + conf.borderwidth
+ * 2);
}
resize(client->id, client->width, client->height);
@@ -2419,8 +2448,9 @@ void movestep(struct client *client, char direction)
* If the pointer was inside the window to begin with, move
* pointer back to where it was, relative to the window.
*/
- if (start_x > 0 - conf.borderwidth && start_x < client->width + conf.borderwidth
- && start_y > 0 - conf.borderwidth && start_y < client->height + conf.borderwidth )
+ if (start_x > 0 - conf.borderwidth && start_x < client->width
+ + conf.borderwidth && start_y > 0 - conf.borderwidth && start_y
+ < client->height + conf.borderwidth)
{
xcb_warp_pointer(conn, XCB_NONE, client->id, 0, 0, 0, 0,
start_x, start_y);
@@ -2632,13 +2662,28 @@ void maxvert(struct client *client)
client->vertmaxed = true;
}
+void hide(struct client *client)
+{
+ long data[] = { XCB_ICCCM_WM_STATE_ICONIC, XCB_NONE };
+
+ /*
+ * Unmap window and declare iconic.
+ *
+ * Unmapping will generate an UnmapNotify event so we can forget
+ * about the window later.
+ */
+ xcb_unmap_window(conn, client->id);
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, client->id,
+ wm_state, wm_state, 32, 2, data);
+ xcb_flush(conn);
+}
+
bool getpointer(xcb_drawable_t win, int16_t *x, int16_t *y)
{
xcb_query_pointer_reply_t *pointer;
- pointer = xcb_query_pointer_reply(
- conn, xcb_query_pointer(conn, win), 0);
-
+ pointer
+ = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, win), 0);
if (NULL == pointer)
{
return false;
@@ -2657,9 +2702,8 @@ bool getgeom(xcb_drawable_t win, int16_t *x, int16_t *y, uint16_t *width,
{
xcb_get_geometry_reply_t *geom;
- geom = xcb_get_geometry_reply(conn,
- xcb_get_geometry(conn, win), NULL);
-
+ geom
+ = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL);
if (NULL == geom)
{
return false;
@@ -2758,7 +2802,6 @@ void topright(void)
xcb_flush(conn);
}
-
void botleft(void)
{
int16_t pointx;
@@ -2793,10 +2836,9 @@ void botleft(void)
}
focuswin->x = mon_x;
- focuswin->y = mon_y + mon_height - (focuswin->height + conf.borderwidth * 2);
-
+ focuswin->y = mon_y + mon_height - (focuswin->height + conf.borderwidth
+ * 2);
movewindow(focuswin->id, focuswin->x, focuswin->y);
-
xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0,
pointx, pointy);
xcb_flush(conn);
@@ -2839,11 +2881,9 @@ void botright(void)
}
focuswin->x = mon_x + mon_width - (focuswin->width + conf.borderwidth * 2);
-
- focuswin->y = mon_y + mon_height - (focuswin->height + conf.borderwidth * 2);
-
+ focuswin->y = mon_y + mon_height - (focuswin->height + conf.borderwidth
+ * 2);
movewindow(focuswin->id, focuswin->x, focuswin->y);
-
xcb_warp_pointer(conn, XCB_NONE, focuswin->id, 0, 0, 0, 0,
pointx, pointy);
xcb_flush(conn);
@@ -2852,7 +2892,7 @@ void botright(void)
void deletewin(void)
{
xcb_get_property_cookie_t cookie;
- xcb_get_wm_protocols_reply_t protocols;
+ xcb_icccm_get_wm_protocols_reply_t protocols;
bool use_delete = false;
uint32_t i;
@@ -2862,14 +2902,20 @@ void deletewin(void)
}
/* Check if WM_DELETE is supported. */
- cookie = xcb_get_wm_protocols_unchecked(conn, focuswin->id, wm_protocols);
- if (xcb_get_wm_protocols_reply(conn, cookie, &protocols, NULL) == 1) {
+ cookie = xcb_icccm_get_wm_protocols_unchecked(conn, focuswin->id,
+ wm_protocols);
+ if (xcb_icccm_get_wm_protocols_reply(conn, cookie, &protocols, NULL) == 1)
+ {
for (i = 0; i < protocols.atoms_len; i++)
+ {
if (protocols.atoms[i] == wm_delete_window)
+ {
use_delete = true;
+ }
+ }
}
- xcb_get_wm_protocols_reply_wipe(&protocols);
+ xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
if (use_delete)
{
@@ -2954,9 +3000,10 @@ void handle_keypress(xcb_key_press_event_t *ev)
for (key = KEY_MAX, i = KEY_F; i < KEY_MAX; i ++)
{
- if (ev->detail == keys[i].keycode)
+ if (ev->detail == keys[i].keycode && 0 != keys[i].keycode)
{
key = i;
+ break;
}
}
if (key == KEY_MAX)
@@ -2973,7 +3020,7 @@ void handle_keypress(xcb_key_press_event_t *ev)
return;
}
- if (MCWM_TABBING == mode && key != KEY_TAB)
+ if (MCWM_TABBING == mode && key != KEY_TAB && key != KEY_BACKTAB)
{
/* First finish tabbing around. Then deal with the next key. */
finishtabbing();
@@ -3000,6 +3047,10 @@ void handle_keypress(xcb_key_press_event_t *ev)
resizestep(focuswin, 'l');
break;
+ case KEY_TAB: /* shifted tab counts as backtab */
+ focusnext(true);
+ break;
+
default:
/* Ignore other shifted keys. */
break;
@@ -3010,7 +3061,7 @@ void handle_keypress(xcb_key_press_event_t *ev)
switch (key)
{
case KEY_RET: /* return */
- start_terminal();
+ start(conf.terminal);
break;
case KEY_F: /* f */
@@ -3034,7 +3085,11 @@ void handle_keypress(xcb_key_press_event_t *ev)
break;
case KEY_TAB: /* tab */
- focusnext();
+ focusnext(false);
+ break;
+
+ case KEY_BACKTAB: /* backtab */
+ focusnext(true);
break;
case KEY_M: /* m */
@@ -3117,6 +3172,28 @@ void handle_keypress(xcb_key_press_event_t *ev)
nextscreen();
break;
+ case KEY_ICONIFY:
+ if (conf.allowicons)
+ {
+ hide(focuswin);
+ }
+ break;
+
+ case KEY_PREVWS:
+ if (curws > 0)
+ {
+ changeworkspace(curws - 1);
+ }
+ else
+ {
+ changeworkspace(WORKSPACES - 1);
+ }
+ break;
+
+ case KEY_NEXTWS:
+ changeworkspace((curws + 1) % WORKSPACES);
+ break;
+
default:
/* Ignore other keys. */
break;
@@ -3373,17 +3450,29 @@ void events(void)
FD_SET(fd, &in);
/*
- * Check for events, again and again. When poll returns NULL,
- * we block on select() until the event file descriptor gets
- * readable again.
+ * Check for events, again and again. When poll returns NULL
+ * (and it does that a lot), we block on select() until the
+ * event file descriptor gets readable again.
*
* We do it this way instead of xcb_wait_for_event() since
- * select() will return if we we're interrupted by a signal.
- * We like that.
+ * select() will return if we were interrupted by a signal. We
+ * like that.
*/
ev = xcb_poll_for_event(conn);
if (NULL == ev)
{
+ PDEBUG("xcb_poll_for_event() returned NULL.\n");
+
+ /*
+ * Check if we have an unrecoverable connection error,
+ * like a disconnected X server.
+ */
+ if (xcb_connection_has_error(conn))
+ {
+ cleanup(0);
+ exit(1);
+ }
+
found = select(fd + 1, &in, NULL, NULL, NULL);
if (-1 == found)
{
@@ -3486,6 +3575,32 @@ void events(void)
e->detail, (long)e->event, e->child, e->event_x,
e->event_y);
+ if (0 == e->child)
+ {
+ /* Mouse click on root window. Start programs? */
+
+ switch (e->detail)
+ {
+ case 1: /* Mouse button one. */
+ start(MOUSE1);
+ break;
+
+ case 2: /* Middle mouse button. */
+ start(MOUSE2);
+ break;
+
+ case 3: /* Mouse button three. */
+ start(MOUSE3);
+ break;
+
+ default:
+ break;
+ } /* switch */
+
+ /* Break out of event switch. */
+ break;
+ }
+
/*
* If we don't have any currently focused window, we can't
* do anything. We don't want to do anything if the mouse
@@ -3498,11 +3613,6 @@ void events(void)
}
/*
- * XXX if 0 == e->child, we're on the root window. Do
- * something on the root when mouse buttons are pressed?
- */
-
- /*
* If middle button was pressed, raise window or lower
* it if it was already on top.
*/
@@ -3877,6 +3987,30 @@ void events(void)
configurerequest((xcb_configure_request_event_t *) ev);
break;
+ case XCB_CLIENT_MESSAGE:
+ {
+ xcb_client_message_event_t *e
+ = (xcb_client_message_event_t *)ev;
+
+ if (conf.allowicons)
+ {
+ if (e->type == wm_change_state
+ && e->format == 32
+ && e->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC)
+ {
+ long data[] = { XCB_ICCCM_WM_STATE_ICONIC, XCB_NONE };
+
+ /* Unmap window and declare iconic. */
+
+ xcb_unmap_window(conn, e->window);
+ xcb_change_property(conn, XCB_PROP_MODE_REPLACE, e->window,
+ wm_state, wm_state, 32, 2, data);
+ xcb_flush(conn);
+ }
+ } /* if */
+ }
+ break;
+
case XCB_CIRCULATE_REQUEST:
{
xcb_circulate_request_event_t *e
@@ -3990,6 +4124,31 @@ void sigcatch(int sig)
sigcode = sig;
}
+/*
+ * Get a defined atom from the X server.
+ */
+xcb_atom_t getatom(char *atom_name)
+{
+ xcb_intern_atom_cookie_t atom_cookie;
+ xcb_atom_t atom;
+ xcb_intern_atom_reply_t *rep;
+
+ atom_cookie = xcb_intern_atom(conn, 0, strlen(atom_name), atom_name);
+ rep = xcb_intern_atom_reply(conn, atom_cookie, NULL);
+ if (NULL != rep)
+ {
+ atom = rep->atom;
+ free(rep);
+ return atom;
+ }
+
+ /*
+ * XXX Note that we return 0 as an atom if anything goes wrong.
+ * Might become interesting.
+ */
+ return 0;
+}
+
int main(int argc, char **argv)
{
uint32_t mask = 0;
@@ -4001,9 +4160,18 @@ int main(int argc, char **argv)
char *focuscol;
char *unfocuscol;
char *fixedcol;
-
+ int scrno;
+ xcb_screen_iterator_t iter;
+
/* Install signal handlers. */
+ /* We ignore child exists. Don't create zombies. */
+ if (SIG_ERR == signal(SIGCHLD, SIG_IGN))
+ {
+ perror("mcwm: signal");
+ exit(1);
+ }
+
if (SIG_ERR == signal(SIGINT, sigcatch))
{
perror("mcwm: signal");
@@ -4020,13 +4188,14 @@ int main(int argc, char **argv)
conf.borderwidth = BORDERWIDTH;
conf.terminal = TERMINAL;
+ conf.allowicons = ALLOWICONS;
focuscol = FOCUSCOL;
unfocuscol = UNFOCUSCOL;
fixedcol = FIXEDCOL;
while (1)
{
- ch = getopt(argc, argv, "b:t:f:u:x:");
+ ch = getopt(argc, argv, "b:it:f:u:x:");
if (-1 == ch)
{
@@ -4041,6 +4210,10 @@ int main(int argc, char **argv)
conf.borderwidth = atoi(optarg);
break;
+ case 'i':
+ conf.allowicons = true;
+ break;
+
case 't':
conf.terminal = optarg;
break;
@@ -4062,16 +4235,33 @@ int main(int argc, char **argv)
exit(0);
} /* switch */
}
-
- conn = xcb_connect(NULL, NULL);
+
+ /*
+ * Use $DISPLAY. After connecting scrno will contain the value of
+ * the display's screen.
+ */
+ conn = xcb_connect(NULL, &scrno);
if (xcb_connection_has_error(conn))
{
perror("xcb_connect");
exit(1);
}
-
- /* Get the first screen */
- screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
+
+ /* Find our screen. */
+ iter = xcb_setup_roots_iterator(xcb_get_setup(conn));
+ for (int i = 0; i < scrno; ++ i)
+ {
+ xcb_screen_next(&iter);
+ }
+
+ screen = iter.data;
+ if (!screen)
+ {
+ fprintf (stderr, "mcwm: Can't get the current screen. Exiting.\n");
+ xcb_disconnect(conn);
+ exit(1);
+ }
+
root = screen->root;
PDEBUG("Screen size: %dx%d\nRoot window: %d\n", screen->width_in_pixels,
@@ -4083,17 +4273,20 @@ int main(int argc, char **argv)
conf.fixedcol = getcolor(fixedcol);
/* Get some atoms. */
- atom_desktop = xcb_atom_get(conn, "_NET_WM_DESKTOP");
- wm_delete_window = xcb_atom_get(conn, "WM_DELETE_WINDOW");
- wm_protocols = xcb_atom_get(conn, "WM_PROTOCOLS");
-
+ atom_desktop = getatom("_NET_WM_DESKTOP");
+ wm_delete_window = getatom("WM_DELETE_WINDOW");
+ wm_change_state = getatom("WM_CHANGE_STATE");
+ wm_state = getatom("WM_STATE");
+ wm_protocols = getatom("WM_PROTOCOLS");
+
/* Check for RANDR extension and configure. */
randrbase = setuprandr();
/* Loop over all clients and set up stuff. */
if (0 != setupscreen())
{
- fprintf(stderr, "Failed to initialize windows. Exiting.\n");
+ fprintf(stderr, "mcwm: Failed to initialize windows. Exiting.\n");
+ xcb_disconnect(conn);
exit(1);
}
@@ -4101,6 +4294,7 @@ int main(int argc, char **argv)
if (0 != setupkeys())
{
fprintf(stderr, "mcwm: Couldn't set up keycodes. Exiting.");
+ xcb_disconnect(conn);
exit(1);
}
@@ -4111,7 +4305,7 @@ int main(int argc, char **argv)
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
1 /* left mouse button */,
MOUSEMODKEY);
-
+
xcb_grab_button(conn, 0, root, XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE,
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
diff --git a/mcwm.man b/mcwm.man
index 5b234db..6d6cd4a 100644
--- a/mcwm.man
+++ b/mcwm.man
@@ -1,11 +1,14 @@
-.TH mcwm 1 "Nov 07, 2011" "" ""
+.TH mcwm 1 "Apr 30, 2012" "" ""
.SH NAME
mcwm \- MC's Window Manager for X11.
.SH SYNOPSIS
.B mcwm
[
.B \-b
-] width
+width ]
+[
+.B \-i
+]
[
.B \-t
.I terminal-program
@@ -27,6 +30,12 @@ mcwm \- MC's Window Manager for X11.
.PP
\-b width sets border width to this many pixels.
.PP
+\-i turns on icons/hidden windows.
+.B Please note
+that there is no way from mcwm to get a hidden window back! You have
+to use an external program such as a panel or the mcicon or 9icon
+scripts (see below) to get the window mapped again.
+.PP
\-t urxvt will start urxvt when MODKEY + Return is pressed. Change to
your prefered terminal program or something else entirely.
.PP
@@ -65,44 +74,57 @@ Note that the mouse cursor needs to be inside the window you want to
move, raise/lower or resize even if it currently has the focus. This
is a feature, not a bug.
.PP
-Mod4 + key:
+Mod4 + key on focused window:
.RS
.IP \(bu 2
.B r
-raise or lower (toggles)
+raise or lower (toggles).
.IP \(bu 2
.B x
-maximize (toggles)
+maximize (toggles).
.IP \(bu 2
.B m
-maximize vertically (toggles)
+maximize vertically (toggles).
+.IP \(bu 2
+.B H
+resize left.
+.IP \(bu 2
+.B J
+resize down.
+.IP \(bu 2
+.B K
+resize up.
+.IP \(bu 2
+.B L
+resize right.
.IP \(bu 2
.B h
-move left
+move left.
.IP \(bu 2
.B j
-move down
+move down.
.IP \(bu 2
.B k
-move up
+move up.
.IP \(bu 2
.B l
-move right
+move right.
.IP \(bu 2
-.B H
-resize left
+.B y
+move to upper left corner of monitor.
.IP \(bu 2
-.B J
-resize down
+.B u
+move to upper right corner of monitor.
.IP \(bu 2
-.B K
-resize up
+.B b
+move to lower left corner of monitor.
.IP \(bu 2
-.B L
-resize right
+.B n
+move to lower right corner of monitor.
.IP \(bu 2
.B Return
-start terminal
+start terminal or whatever program you have configured with -t or in
+the config.h.
.IP \(bu 2
.B Tab
go to next window in the current workspace window ring. If you release
@@ -110,35 +132,41 @@ MODKEY or press another command key mcwm will change focus to the new
window. A new press of MODKEY + Tab will bring you back to the window
where you last had focus.
.IP \(bu 2
-.B f
-fix window so it is visible on all workspaces. Toggles. Press again to
-unfix window. Also used to move windows between workspaces: First fix
-the window, change to the workspace you want, then unfix the window on
-the new workspace.
-.IP \(bu 2
-.B y
-move to upper left corner of physical screen.
-.IP \(bu 2
-.B u
-move to upper right corner of physical screen..
+.B Shift-Tab
+go to previous window in the current workspace window ring. This is
+most useful while you are tabbing: if you accidentally pressed Tab one
+time too many, you can move back by pressing Shift + TAB (all the
+while holding down the MODKEY).
.IP \(bu 2
-.B b
-move to lower left corner of physical screen..
+.B f
+fix window so it is visible on all workspaces (toggles). Note that
+this is also used to move windows between workspaces: First fix the
+window, change to the workspace you want, then unfix the window on the
+new workspace.
.IP \(bu 2
-.B n
-move to lower right corner of physical screen..
+.B i
+iconify (or hide) window from the display. Only usable when mcwm has
+been started with -i. Currently there is no way to get a hidden window
+back. You have to use an external program such as a panel or the
+mcicon or 9icon script in the mcwm distribution.
.IP \(bu 2
.B 0\-9
go to workspace n, 0-9.
.IP \(bu 2
+.B c
+go to previous workspace.
+.IP \(bu 2
+.B v
+go to next workspace.
+.IP \(bu 2
.B End
-close focused window.
+close window.
.IP \(bu 2
.B ,
-move window to previous physical screen.
+move window to previous monitor.
.IP \(bu 2
.B .
-move window to next physical screen.
+move window to next monitor.
.RE
.PP
Note that all functions activated from the keyboard work on the
@@ -147,8 +175,12 @@ cursor. Of course, changing workspaces has nothing to do with the
focused window.
.PP
If you don't like the default key bindings, border width, et cetera,
-look in the config.h file, change and recompile.
-.PP
+look in the config.h file, change and recompile. In the config.h file
+you can also define mouse button actions on the root window. By
+default button 3 starts the command mcmenu. You can write your own
+mcmenu by using, for instance, 9menu, dmenu or ratmenu.
+.SH ENVIRONMENT
+.B mcwm\fP obeys the $DISPLAY variable.
.SH STARTING
Typically the window manager is started from a script, either run by
.B startx(1)
@@ -174,10 +206,55 @@ xrdb \-load ~/.Xresources
# Start window manager in the background. If it dies, X still lives.
mcwm &
+# If you want to allow windows to be hidden, use this instead:
+# mcwm -i &
+
# Start a terminal in the foreground. If this dies, X dies.
exec urxvt
.fi
.in -4
.sp
+.SH SCRIPTS
+You may want to define a menu program for use with mcwm (see
+config.h). In the source distribution you can find an example as
+mcmenu (the default menu program in config.h) in the scripts
+directory.
+.PP
+Christian Neukirchen wrote a little script you can use to get
+iconified windows mapped again if you are running mcwm in allow icons
+mode (-i). You need awk, xdotool, xprop and xwininfo installed. You
+can find the script as scripts/9icon.
+.PP
+Inspired by Christian's work I wrote a small program, hidden(1), which
+is included with mcwm. You can use hidden(1) with the -c option
+together with 9menu. See scripts/mcicon for an example.
+.PP
+You might also be interested in the following shell function that
+might come in handy to give your terminal emulators good titles before
+hiding them.
+.sp
+.in +4
+.nf
+# Set the title and icon name of an xterm or clone.
+function title
+{
+ # icon name
+ echo -e '\\033]1;'$1'\\007'
+ # title
+ echo -e '\\033]2;'$1'\\007'
+}
+.fi
+.in -4
+.sp
+Use it like this:
+.sp
+.in +4
+.nf
+% title 'really descriptive title'
+.fi
+.in -4
+.sp
+.SH SEE ALSO
+.B hidden(1)
.SH AUTHOR
Michael Cardell Widerkrantz <mc@hack.org>.
diff --git a/scripts/9icon b/scripts/9icon
new file mode 100755
index 0000000..73edc6e
--- /dev/null
+++ b/scripts/9icon
@@ -0,0 +1,18 @@
+#! /bin/sh
+
+# 9icon - show 9menu of hidden windows for mapping
+# Originally by Christian Neukirchen <chneukirchen@gmail.com> and
+# slightly changed by MC.
+
+IFS="
+"
+
+for win in $(xwininfo -root -children | awk '$1~/0x/ && $2~/"/ {print $1}'); do
+ xprop -id $win WM_NAME WM_STATE |
+ awk -F'"' -v win=$win '
+ /^WM_NAME/ { name=$2 }
+ /window state: Iconic/ {
+ print "'\''" name "'\''" "'\''" ":xdotool windowmap " win " windowraise " win "'\''"
+ }
+ '
+done | xargs 9menu -popup -label 9icon -bg grey20 -fg grey80 -font fixed
diff --git a/scripts/mcicon b/scripts/mcicon
new file mode 100755
index 0000000..f002a55
--- /dev/null
+++ b/scripts/mcicon
@@ -0,0 +1,6 @@
+#! /bin/sh
+
+# mcicon - list iconified windows in menu and map the chosen one.
+# Needs hidden (distributed with mcwm), 9menu and xdotool.
+
+hidden -c | xargs 9menu -popup -label 9icon -bg grey20 -fg grey80 -font 9x15
diff --git a/scripts/mcmenu b/scripts/mcmenu
new file mode 100755
index 0000000..6d32940
--- /dev/null
+++ b/scripts/mcmenu
@@ -0,0 +1,11 @@
+#! /bin/sh
+exec 9menu -bg grey20 -fg grey80 -popup \
+ 'hide:xdotool selectwindow windowminimize' \
+ 'cpu:urxvt -e ssh cpu.example.org' \
+ ':' \
+ 'VGA On: xrandr --output VGA --on' \
+ 'VGA Off: xrandr --output VGA --off' \
+ ':' \
+ 'HHKB: xkbcomp -I$HOME/conf/xkb $HOME/conf/xkb/hhkb.xkb $DISPLAY' \
+ ':' \
+ 'close:'